[Midnightbsd-cvs] src [12396] trunk/sys/arm/nvidia: sync with freebsd 11

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Fri Mar 6 12:08:20 EST 2020


Revision: 12396
          http://svnweb.midnightbsd.org/src/?rev=12396
Author:   laffer1
Date:     2020-03-06 12:08:20 -0500 (Fri, 06 Mar 2020)
Log Message:
-----------
sync with freebsd 11

Added Paths:
-----------
    trunk/sys/arm/nvidia/
    trunk/sys/arm/nvidia/as3722.c
    trunk/sys/arm/nvidia/as3722.h
    trunk/sys/arm/nvidia/as3722_gpio.c
    trunk/sys/arm/nvidia/as3722_regulators.c
    trunk/sys/arm/nvidia/as3722_rtc.c
    trunk/sys/arm/nvidia/drm2/
    trunk/sys/arm/nvidia/drm2/hdmi.c
    trunk/sys/arm/nvidia/drm2/hdmi.h
    trunk/sys/arm/nvidia/drm2/tegra_bo.c
    trunk/sys/arm/nvidia/drm2/tegra_dc.c
    trunk/sys/arm/nvidia/drm2/tegra_dc_if.m
    trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h
    trunk/sys/arm/nvidia/drm2/tegra_drm.h
    trunk/sys/arm/nvidia/drm2/tegra_drm_if.m
    trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c
    trunk/sys/arm/nvidia/drm2/tegra_fb.c
    trunk/sys/arm/nvidia/drm2/tegra_hdmi.c
    trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h
    trunk/sys/arm/nvidia/drm2/tegra_host1x.c
    trunk/sys/arm/nvidia/tegra124/
    trunk/sys/arm/nvidia/tegra124/files.tegra124
    trunk/sys/arm/nvidia/tegra124/std.tegra124
    trunk/sys/arm/nvidia/tegra124/tegra124_car.c
    trunk/sys/arm/nvidia/tegra124/tegra124_car.h
    trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c
    trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c
    trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c
    trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c
    trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
    trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c
    trunk/sys/arm/nvidia/tegra124/tegra124_mp.c
    trunk/sys/arm/nvidia/tegra124/tegra124_mp.h
    trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c
    trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c
    trunk/sys/arm/nvidia/tegra_abpmisc.c
    trunk/sys/arm/nvidia/tegra_ahci.c
    trunk/sys/arm/nvidia/tegra_efuse.c
    trunk/sys/arm/nvidia/tegra_efuse.h
    trunk/sys/arm/nvidia/tegra_ehci.c
    trunk/sys/arm/nvidia/tegra_gpio.c
    trunk/sys/arm/nvidia/tegra_i2c.c
    trunk/sys/arm/nvidia/tegra_lic.c
    trunk/sys/arm/nvidia/tegra_mc.c
    trunk/sys/arm/nvidia/tegra_pcie.c
    trunk/sys/arm/nvidia/tegra_pinmux.c
    trunk/sys/arm/nvidia/tegra_pmc.h
    trunk/sys/arm/nvidia/tegra_rtc.c
    trunk/sys/arm/nvidia/tegra_sdhci.c
    trunk/sys/arm/nvidia/tegra_soctherm.c
    trunk/sys/arm/nvidia/tegra_soctherm_if.m
    trunk/sys/arm/nvidia/tegra_uart.c
    trunk/sys/arm/nvidia/tegra_usbphy.c
    trunk/sys/arm/nvidia/tegra_xhci.c

Added: trunk/sys/arm/nvidia/as3722.c
===================================================================
--- trunk/sys/arm/nvidia/as3722.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/as3722.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,412 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * AS3722 PMIC driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <gnu/dts/include/dt-bindings/mfd/as3722.h>
+
+#include "clock_if.h"
+#include "regdev_if.h"
+
+#include "as3722.h"
+
+static struct ofw_compat_data compat_data[] = {
+	{"ams,as3722",		1},
+	{NULL,			0},
+};
+
+#define	LOCK(_sc)		sx_xlock(&(_sc)->lock)
+#define	UNLOCK(_sc)		sx_xunlock(&(_sc)->lock)
+#define	LOCK_INIT(_sc)		sx_init(&(_sc)->lock, "as3722")
+#define	LOCK_DESTROY(_sc)	sx_destroy(&(_sc)->lock);
+#define	ASSERT_LOCKED(_sc)	sx_assert(&(_sc)->lock, SA_XLOCKED);
+#define	ASSERT_UNLOCKED(_sc)	sx_assert(&(_sc)->lock, SA_UNLOCKED);
+
+#define	AS3722_DEVICE_ID	0x0C
+
+/*
+ * Raw register access function.
+ */
+int
+as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val)
+{
+	uint8_t addr;
+	int rv;
+	struct iic_msg msgs[2] = {
+		{0, IIC_M_WR, 1, &addr},
+		{0, IIC_M_RD, 1, val},
+	};
+
+	msgs[0].slave = sc->bus_addr;
+	msgs[1].slave = sc->bus_addr;
+	addr = reg;
+
+	rv = iicbus_transfer(sc->dev, msgs, 2);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+    size_t size)
+{
+	uint8_t addr;
+	int rv;
+	struct iic_msg msgs[2] = {
+		{0, IIC_M_WR, 1, &addr},
+		{0, IIC_M_RD, size, buf},
+	};
+
+	msgs[0].slave = sc->bus_addr;
+	msgs[1].slave = sc->bus_addr;
+	addr = reg;
+
+	rv = iicbus_transfer(sc->dev, msgs, 2);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+int
+as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val)
+{
+	uint8_t data[2];
+	int rv;
+
+	struct iic_msg msgs[1] = {
+		{0, IIC_M_WR, 2, data},
+	};
+
+	msgs[0].slave = sc->bus_addr;
+	data[0] = reg;
+	data[1] = val;
+
+	rv = iicbus_transfer(sc->dev, msgs, 1);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
+		return (EIO);
+	}
+	return (0);
+}
+
+int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+    size_t size)
+{
+	uint8_t data[1];
+	int rv;
+	struct iic_msg msgs[2] = {
+		{0, IIC_M_WR, 1, data},
+		{0, IIC_M_WR | IIC_M_NOSTART, size, buf},
+	};
+
+	msgs[0].slave = sc->bus_addr;
+	msgs[1].slave = sc->bus_addr;
+	data[0] = reg;
+
+	rv = iicbus_transfer(sc->dev, msgs, 2);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
+		return (EIO);
+	}
+	return (0);
+}
+
+int
+as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set)
+{
+	uint8_t val;
+	int rv;
+
+	rv = as3722_read(sc, reg, &val);
+	if (rv != 0)
+		return (rv);
+
+	val &= ~clear;
+	val |= set;
+
+	rv = as3722_write(sc, reg, val);
+	if (rv != 0)
+		return (rv);
+
+	return (0);
+}
+
+static int
+as3722_get_version(struct as3722_softc *sc)
+{
+	uint8_t reg;
+	int rv;
+
+	/* Verify AS3722 ID and version. */
+	rv = RD1(sc, AS3722_ASIC_ID1, &reg);
+	if (rv != 0)
+		return (ENXIO);
+
+	if (reg != AS3722_DEVICE_ID) {
+		device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg);
+		return (ENXIO);
+	}
+
+	rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev);
+	if (rv != 0)
+		return (ENXIO);
+
+	if (bootverbose)
+		device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev);
+	return (0);
+}
+
+static int
+as3722_init(struct as3722_softc *sc)
+{
+	uint32_t reg;
+	int rv;
+
+	reg = 0;
+	if (sc->int_pullup)
+		reg |= AS3722_INT_PULL_UP;
+	if (sc->i2c_pullup)
+		reg |= AS3722_I2C_PULL_UP;
+
+	rv = RM1(sc, AS3722_IO_VOLTAGE,
+	    AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg);
+	if (rv != 0)
+		return (ENXIO);
+
+	/* mask interrupts */
+	rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0);
+	if (rv != 0)
+		return (ENXIO);
+	rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0);
+	if (rv != 0)
+		return (ENXIO);
+	rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0);
+	if (rv != 0)
+		return (ENXIO);
+	rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0);
+	if (rv != 0)
+		return (ENXIO);
+	return (0);
+}
+
+static int
+as3722_parse_fdt(struct as3722_softc *sc, phandle_t node)
+{
+
+	sc->int_pullup =
+	    OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0;
+	sc->i2c_pullup =
+	    OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0;
+	return 0;
+}
+
+static void
+as3722_intr(void *arg)
+{
+	struct as3722_softc *sc;
+
+	sc = (struct as3722_softc *)arg;
+	/* XXX Finish temperature alarms. */
+}
+
+static int
+as3722_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc(dev, "AS3722 PMIC");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+as3722_attach(device_t dev)
+{
+	struct as3722_softc *sc;
+	const char *dname;
+	int dunit, rv, rid;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->bus_addr = iicbus_get_addr(dev);
+	node = ofw_bus_get_node(sc->dev);
+	dname = device_get_name(dev);
+	dunit = device_get_unit(dev);
+	rv = 0;
+	LOCK_INIT(sc);
+
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate interrupt.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	rv = as3722_parse_fdt(sc, node);
+	if (rv != 0)
+		goto fail;
+	rv = as3722_get_version(sc);
+	if (rv != 0)
+		goto fail;
+	rv = as3722_init(sc);
+	if (rv != 0)
+		goto fail;
+	rv = as3722_regulator_attach(sc, node);
+	if (rv != 0)
+		goto fail;
+	rv = as3722_gpio_attach(sc, node);
+	if (rv != 0)
+		goto fail;
+	rv = as3722_rtc_attach(sc, node);
+	if (rv != 0)
+		goto fail;
+
+	fdt_pinctrl_register(dev, NULL);
+	fdt_pinctrl_configure_by_name(dev, "default");
+
+	/* Setup  interrupt. */
+	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, as3722_intr, sc, &sc->irq_h);
+	if (rv) {
+		device_printf(dev, "Cannot setup interrupt.\n");
+		goto fail;
+	}
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	LOCK_DESTROY(sc);
+	return (rv);
+}
+
+static int
+as3722_detach(device_t dev)
+{
+	struct as3722_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	LOCK_DESTROY(sc);
+
+	return (bus_generic_detach(dev));
+}
+
+static phandle_t
+as3722_gpio_get_node(device_t bus, device_t dev)
+{
+
+	/* We only have one child, the GPIO bus, which needs our own node. */
+	return (ofw_bus_get_node(bus));
+}
+
+static device_method_t as3722_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		as3722_probe),
+	DEVMETHOD(device_attach,	as3722_attach),
+	DEVMETHOD(device_detach,	as3722_detach),
+
+	/* Regdev interface */
+	DEVMETHOD(regdev_map,		as3722_regulator_map),
+
+	/* RTC interface */
+	DEVMETHOD(clock_gettime,	as3722_rtc_gettime),
+	DEVMETHOD(clock_settime,	as3722_rtc_settime),
+
+	/* GPIO protocol interface */
+	DEVMETHOD(gpio_get_bus,		as3722_gpio_get_bus),
+	DEVMETHOD(gpio_pin_max,		as3722_gpio_pin_max),
+	DEVMETHOD(gpio_pin_getname,	as3722_gpio_pin_getname),
+	DEVMETHOD(gpio_pin_getflags,	as3722_gpio_pin_getflags),
+	DEVMETHOD(gpio_pin_getcaps,	as3722_gpio_pin_getcaps),
+	DEVMETHOD(gpio_pin_setflags,	as3722_gpio_pin_setflags),
+	DEVMETHOD(gpio_pin_get,		as3722_gpio_pin_get),
+	DEVMETHOD(gpio_pin_set,		as3722_gpio_pin_set),
+	DEVMETHOD(gpio_pin_toggle,	as3722_gpio_pin_toggle),
+	DEVMETHOD(gpio_map_gpios,	as3722_gpio_map_gpios),
+
+	/* fdt_pinctrl interface */
+	DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,	as3722_gpio_get_node),
+
+	DEVMETHOD_END
+};
+
+static devclass_t as3722_devclass;
+static DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods,
+    sizeof(struct as3722_softc));
+EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass,
+    NULL, NULL, 74);


Property changes on: trunk/sys/arm/nvidia/as3722.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/as3722.h
===================================================================
--- trunk/sys/arm/nvidia/as3722.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/as3722.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,324 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/as3722.h 296936 2016-03-16 13:01:48Z mmel $
+ */
+
+#ifndef _AS3722_H_
+
+#include <sys/clock.h>
+
+#define	AS3722_SD0_VOLTAGE			0x00
+#define	  AS3722_SD_VSEL_MASK				0x7F /* For all SD */
+#define	  AS3722_SD0_VSEL_MIN				0x01
+#define	  AS3722_SD0_VSEL_MAX				0x5A
+#define	  AS3722_SD0_VSEL_LOW_VOL_MAX			0x6E
+
+#define	AS3722_SD1_VOLTAGE			0x01
+#define	AS3722_SD2_VOLTAGE			0x02
+#define	  AS3722_SD2_VSEL_MIN				0x01
+#define	  AS3722_SD2_VSEL_MAX				0x7F
+#define	AS3722_SD3_VOLTAGE			0x03
+#define	AS3722_SD4_VOLTAGE			0x04
+#define	AS3722_SD5_VOLTAGE			0x05
+#define	AS3722_SD6_VOLTAGE			0x06
+#define	AS3722_GPIO0_CONTROL			0x08
+#define	  AS3722_GPIO_INVERT				0x80
+#define	  AS3722_GPIO_IOSF_MASK				0x0F
+#define	  AS3722_GPIO_IOSF_SHIFT			3
+#define	  AS3722_GPIO_MODE_MASK				0x07
+#define	  AS3722_GPIO_MODE_SHIFT			0
+
+#define	AS3722_GPIO1_CONTROL			0x09
+#define	AS3722_GPIO2_CONTROL			0x0A
+#define	AS3722_GPIO3_CONTROL			0x0B
+#define	AS3722_GPIO4_CONTROL			0x0C
+#define	AS3722_GPIO5_CONTROL			0x0D
+#define	AS3722_GPIO6_CONTROL			0x0E
+#define	AS3722_GPIO7_CONTROL			0x0F
+#define	AS3722_LDO0_VOLTAGE			0x10
+#define	  AS3722_LDO0_VSEL_MASK				0x1F
+#define	  AS3722_LDO0_VSEL_MIN				0x01
+#define	  AS3722_LDO0_VSEL_MAX				0x12
+#define	  AS3722_LDO0_NUM_VOLT				0x12
+
+#define	AS3722_LDO1_VOLTAGE			0x11
+#define	  AS3722_LDO_VSEL_MASK				0x7F
+#define	  AS3722_LDO_VSEL_MIN				0x01
+#define	  AS3722_LDO_VSEL_MAX				0x7F
+#define	  AS3722_LDO_VSEL_DNU_MIN			0x25
+#define	  AS3722_LDO_VSEL_DNU_MAX			0x3F
+#define	  AS3722_LDO_NUM_VOLT				0x80
+
+#define	AS3722_LDO2_VOLTAGE			0x12
+#define	AS3722_LDO3_VOLTAGE			0x13
+#define	  AS3722_LDO3_VSEL_MASK				0x3F
+#define	  AS3722_LDO3_VSEL_MIN				0x01
+#define	  AS3722_LDO3_VSEL_MAX				0x2D
+#define	  AS3722_LDO3_NUM_VOLT				0x2D
+#define	  AS3722_LDO3_MODE_MASK				(0x3 << 6)
+#define	  AS3722_LDO3_MODE_GET(x)			(((x) >> 6) & 0x3)
+#define	  AS3722_LDO3_MODE(x)				(((x) & 0x3) << 6)
+#define	  AS3722_LDO3_MODE_PMOS				AS3722_LDO3_MODE(0)
+#define	  AS3722_LDO3_MODE_PMOS_TRACKING		AS3722_LDO3_MODE(1)
+#define	  AS3722_LDO3_MODE_NMOS				AS3722_LDO3_MODE(2)
+#define	  AS3722_LDO3_MODE_SWITCH			AS3722_LDO3_MODE(3)
+
+#define	AS3722_LDO4_VOLTAGE			0x14
+#define	AS3722_LDO5_VOLTAGE			0x15
+#define	AS3722_LDO6_VOLTAGE			0x16
+#define	  AS3722_LDO6_SEL_BYPASS			0x3F
+#define	AS3722_LDO7_VOLTAGE			0x17
+#define	AS3722_LDO9_VOLTAGE			0x19
+#define	AS3722_LDO10_VOLTAGE			0x1A
+#define	AS3722_LDO11_VOLTAGE			0x1B
+#define	AS3722_LDO3_SETTINGS			0x1D
+#define	AS3722_GPIO_DEB1			0x1E
+#define	AS3722_GPIO_DEB2			0x1F
+#define	AS3722_GPIO_SIGNAL_OUT			0x20
+#define	AS3722_GPIO_SIGNAL_IN			0x21
+#define	AS3722_REG_SEQU_MOD1			0x22
+#define	AS3722_REG_SEQU_MOD2			0x23
+#define	AS3722_REG_SEQU_MOD3			0x24
+#define	AS3722_SD_PHSW_CTRL			0x27
+#define	AS3722_SD_PHSW_STATUS			0x28
+
+#define	AS3722_SD0_CONTROL			0x29
+#define	 AS3722_SD0_MODE_FAST				(1 << 4)
+
+#define	AS3722_SD1_CONTROL			0x2A
+#define	 AS3722_SD1_MODE_FAST				(1 << 4)
+
+#define	AS3722_SDMPH_CONTROL			0x2B
+#define	AS3722_SD23_CONTROL			0x2C
+#define	 AS3722_SD3_MODE_FAST				(1 << 6)
+#define	 AS3722_SD2_MODE_FAST				(1 << 2)
+
+#define	AS3722_SD4_CONTROL			0x2D
+#define	 AS3722_SD4_MODE_FAST				(1 << 2)
+
+#define	AS3722_SD5_CONTROL			0x2E
+#define	 AS3722_SD5_MODE_FAST				(1 << 2)
+
+#define	AS3722_SD6_CONTROL			0x2F
+#define	 AS3722_SD6_MODE_FAST				(1 << 4)
+
+#define	AS3722_SD_DVM				0x30
+#define	AS3722_RESET_REASON			0x31
+#define	AS3722_BATTERY_VOLTAGE_MONITOR		0x32
+#define	AS3722_STARTUP_CONTROL			0x33
+#define	AS3722_RESET_TIMER			0x34
+#define	AS3722_REFERENCE_CONTROL		0x35
+#define	AS3722_RESET_CONTROL			0x36
+#define	AS3722_OVERTEMPERATURE_CONTROL		0x37
+#define	AS3722_WATCHDOG_CONTROL			0x38
+#define	AS3722_REG_STANDBY_MOD1			0x39
+#define	AS3722_REG_STANDBY_MOD2			0x3A
+#define	AS3722_REG_STANDBY_MOD3			0x3B
+#define	AS3722_ENABLE_CTRL1			0x3C
+#define	 AS3722_SD3_EXT_ENABLE_MASK			0xC0
+#define	 AS3722_SD2_EXT_ENABLE_MASK			0x30
+#define	 AS3722_SD1_EXT_ENABLE_MASK			0x0C
+#define	 AS3722_SD0_EXT_ENABLE_MASK			0x03
+
+#define	AS3722_ENABLE_CTRL2			0x3D
+#define	 AS3722_SD6_EXT_ENABLE_MASK			0x30
+#define	 AS3722_SD5_EXT_ENABLE_MASK			0x0C
+#define	 AS3722_SD4_EXT_ENABLE_MASK			0x03
+
+#define	AS3722_ENABLE_CTRL3			0x3E
+#define	 AS3722_LDO3_EXT_ENABLE_MASK			0xC0
+#define	 AS3722_LDO2_EXT_ENABLE_MASK			0x30
+#define	 AS3722_LDO1_EXT_ENABLE_MASK			0x0C
+#define	 AS3722_LDO0_EXT_ENABLE_MASK			0x03
+
+#define	AS3722_ENABLE_CTRL4			0x3F
+#define	 AS3722_LDO7_EXT_ENABLE_MASK			0xC0
+#define	 AS3722_LDO6_EXT_ENABLE_MASK			0x30
+#define	 AS3722_LDO5_EXT_ENABLE_MASK			0x0C
+#define	 AS3722_LDO4_EXT_ENABLE_MASK			0x03
+
+#define	AS3722_ENABLE_CTRL5			0x40
+#define	 AS3722_LDO11_EXT_ENABLE_MASK			0xC0
+#define	 AS3722_LDO10_EXT_ENABLE_MASK			0x30
+#define	 AS3722_LDO9_EXT_ENABLE_MASK			0x0C
+
+#define	AS3722_PWM_CONTROL_L			0x41
+#define	AS3722_PWM_CONTROL_H			0x42
+#define	AS3722_WATCHDOG_TIMER			0x46
+#define	AS3722_WATCHDOG_SOFTWARE_SIGNAL		0x48
+#define	AS3722_IO_VOLTAGE			0x49
+#define	 AS3722_I2C_PULL_UP				(1 << 4)
+#define	 AS3722_INT_PULL_UP				(1 << 5)
+
+#define	AS3722_BATTERY_VOLTAGE_MONITOR2		0x4A
+#define	AS3722_SD_CONTROL			0x4D
+#define	  AS3722_SDN_CTRL(x)				(1 << (x))
+
+#define	AS3722_LDO_CONTROL0			0x4E
+#define	 AS3722_LDO7_CTRL				(1 << 7)
+#define	 AS3722_LDO6_CTRL				(1 << 6)
+#define	 AS3722_LDO5_CTRL				(1 << 5)
+#define	 AS3722_LDO4_CTRL				(1 << 4)
+#define	 AS3722_LDO3_CTRL				(1 << 3)
+#define	 AS3722_LDO2_CTRL				(1 << 2)
+#define	 AS3722_LDO1_CTRL				(1 << 1)
+#define	 AS3722_LDO0_CTRL				(1 << 0)
+
+#define	AS3722_LDO_CONTROL1			0x4F
+#define	 AS3722_LDO11_CTRL				(1 << 3)
+#define	 AS3722_LDO10_CTRL				(1 << 2)
+#define	 AS3722_LDO9_CTRL				(1 << 1)
+
+#define	AS3722_SD0_PROTECT			0x50
+#define	AS3722_SD6_PROTECT			0x51
+#define	AS3722_PWM_VCONTROL1			0x52
+#define	AS3722_PWM_VCONTROL2			0x53
+#define	AS3722_PWM_VCONTROL3			0x54
+#define	AS3722_PWM_VCONTROL4			0x55
+#define	AS3722_BB_CHARGER			0x57
+#define	AS3722_CTRL_SEQU1			0x58
+#define	AS3722_CTRL_SEQU2			0x59
+#define	AS3722_OV_CURRENT			0x5A
+#define	AS3722_OV_CURRENT_DEB			0x5B
+#define	AS3722_SDLV_DEB				0x5C
+#define	AS3722_OC_PG_CTRL			0x5D
+#define	AS3722_OC_PG_CTRL2			0x5E
+#define	AS3722_CTRL_STATUS			0x5F
+#define	AS3722_RTC_CONTROL			0x60
+#define	 AS3722_RTC_AM_PM_MODE				(1 << 7)
+#define	 AS3722_RTC_CLK32K_OUT_EN			(1 << 5)
+#define	 AS3722_RTC_IRQ_MODE				(1 << 3)
+#define	 AS3722_RTC_ON					(1 << 2)
+#define	 AS3722_RTC_ALARM_WAKEUP_EN			(1 << 1)
+#define	 AS3722_RTC_REP_WAKEUP_EN			(1 << 0)
+
+#define	AS3722_RTC_SECOND			0x61
+#define	AS3722_RTC_MINUTE			0x62
+#define	AS3722_RTC_HOUR				0x63
+#define	AS3722_RTC_DAY				0x64
+#define	AS3722_RTC_MONTH			0x65
+#define	AS3722_RTC_YEAR				0x66
+#define	AS3722_RTC_ALARM_SECOND			0x67
+#define	AS3722_RTC_ALARM_MINUTE			0x68
+#define	AS3722_RTC_ALARM_HOUR			0x69
+#define	AS3722_RTC_ALARM_DAY			0x6A
+#define	AS3722_RTC_ALARM_MONTH			0x6B
+#define	AS3722_RTC_ALARM_YEAR			0x6C
+#define	AS3722_SRAM				0x6D
+#define	AS3722_RTC_ACCESS			0x6F
+#define	AS3722_REG_STATUS			0x73
+#define	AS3722_INTERRUPT_MASK1			0x74
+#define	AS3722_INTERRUPT_MASK2			0x75
+#define	AS3722_INTERRUPT_MASK3			0x76
+#define	AS3722_INTERRUPT_MASK4			0x77
+#define	AS3722_INTERRUPT_STATUS1		0x78
+#define	AS3722_INTERRUPT_STATUS2		0x79
+#define	AS3722_INTERRUPT_STATUS3		0x7A
+#define	AS3722_INTERRUPT_STATUS4		0x7B
+#define	AS3722_TEMP_STATUS			0x7D
+#define	AS3722_ADC0_CONTROL			0x80
+#define	AS3722_ADC1_CONTROL			0x81
+#define	AS3722_ADC0_MSB_RESULT			0x82
+#define	AS3722_ADC0_LSB_RESULT			0x83
+#define	AS3722_ADC1_MSB_RESULT			0x84
+#define	AS3722_ADC1_LSB_RESULT			0x85
+#define	AS3722_ADC1_THRESHOLD_HI_MSB		0x86
+#define	AS3722_ADC1_THRESHOLD_HI_LSB		0x87
+#define	AS3722_ADC1_THRESHOLD_LO_MSB		0x88
+#define	AS3722_ADC1_THRESHOLD_LO_LSB		0x89
+#define	AS3722_ADC_CONFIGURATION		0x8A
+#define	AS3722_ASIC_ID1				0x90
+#define	AS3722_ASIC_ID2				0x91
+#define	AS3722_LOCK				0x9E
+#define	AS3722_FUSE7				0x9E
+#define	  AS3722_FUSE7_SD0_LOW_VOLTAGE			(1 << 4)
+
+struct as3722_reg_sc;
+struct as3722_gpio_pin;
+
+struct as3722_softc {
+	device_t		dev;
+	struct sx		lock;
+	int			bus_addr;
+	struct resource		*irq_res;
+	void			*irq_h;
+
+	uint8_t			chip_rev;
+	int			int_pullup;
+	int			i2c_pullup;
+
+	/* Regulators. */
+	struct as3722_reg_sc	**regs;
+	int			nregs;
+
+	/* GPIO */
+	device_t		gpio_busdev;
+	struct as3722_gpio_pin	**gpio_pins;
+	int			gpio_npins;
+	struct sx		gpio_lock;
+
+};
+
+#define	RD1(sc, reg, val)	as3722_read(sc, reg, val)
+#define	WR1(sc, reg, val)	as3722_write(sc, reg, val)
+#define	RM1(sc, reg, clr, set)	as3722_modify(sc, reg, clr, set)
+
+int as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val);
+int as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val);
+int as3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear,
+    uint8_t set);
+int as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+    size_t size);
+int as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
+    size_t size);
+
+/* Regulators */
+int as3722_regulator_attach(struct as3722_softc *sc, phandle_t node);
+int as3722_regulator_map(device_t dev, phandle_t xref, int ncells,
+    pcell_t *cells, int *num);
+
+/* RTC */
+int as3722_rtc_attach(struct as3722_softc *sc, phandle_t node);
+int as3722_rtc_gettime(device_t dev, struct timespec *ts);
+int as3722_rtc_settime(device_t dev, struct timespec *ts);
+
+/* GPIO */
+device_t as3722_gpio_get_bus(device_t dev);
+int as3722_gpio_pin_max(device_t dev, int *maxpin);
+int as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name);
+int as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags);
+int as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
+int as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
+int as3722_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
+int as3722_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
+int as3722_gpio_pin_toggle(device_t dev, uint32_t pin);
+int as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+    int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags);
+int as3722_gpio_attach(struct as3722_softc *sc, phandle_t node);
+int as3722_pinmux_configure(device_t dev, phandle_t cfgxref);
+
+#endif /* _AS3722_H_ */


Property changes on: trunk/sys/arm/nvidia/as3722.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/as3722_gpio.c
===================================================================
--- trunk/sys/arm/nvidia/as3722_gpio.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/as3722_gpio.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,578 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_gpio.c 299715 2016-05-14 05:00:17Z gonzo $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/sx.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include "as3722.h"
+
+MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO");
+
+/* AS3722_GPIOx_CONTROL	 MODE and IOSF definition. */
+#define	AS3722_IOSF_GPIO				0x00
+#define	AS3722_IOSF_INTERRUPT_OUT			0x01
+#define	AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT	0x02
+#define	AS3722_IOSF_GPIO_IN_INTERRUPT			0x03
+#define	AS3722_IOSF_PWM_IN				0x04
+#define	AS3722_IOSF_VOLTAGE_IN_STANDBY			0x05
+#define	AS3722_IOSF_OC_PG_SD0				0x06
+#define	AS3722_IOSF_POWERGOOD_OUT			0x07
+#define	AS3722_IOSF_CLK32K_OUT				0x08
+#define	AS3722_IOSF_WATCHDOG_IN				0x09
+#define	AS3722_IOSF_SOFT_RESET_IN			0x0b
+#define	AS3722_IOSF_PWM_OUT				0x0c
+#define	AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT		0x0d
+#define	AS3722_IOSF_OC_PG_SD6				0x0e
+
+#define	AS3722_MODE_INPUT				0
+#define	AS3722_MODE_PUSH_PULL				1
+#define	AS3722_MODE_OPEN_DRAIN				2
+#define	AS3722_MODE_TRISTATE				3
+#define	AS3722_MODE_INPUT_PULL_UP_LV			4
+#define	AS3722_MODE_INPUT_PULL_DOWN			5
+#define	AS3722_MODE_OPEN_DRAIN_LV			6
+#define	AS3722_MODE_PUSH_PULL_LV			7
+
+#define	NGPIO		8
+
+#define	GPIO_LOCK(_sc)	sx_slock(&(_sc)->gpio_lock)
+#define	GPIO_UNLOCK(_sc)	sx_unlock(&(_sc)->gpio_lock)
+#define	GPIO_ASSERT(_sc)	sx_assert(&(_sc)->gpio_lock, SA_LOCKED)
+
+#define	AS3722_CFG_BIAS_DISABLE		0x0001
+#define	AS3722_CFG_BIAS_PULL_UP		0x0002
+#define	AS3722_CFG_BIAS_PULL_DOWN	0x0004
+#define	AS3722_CFG_BIAS_HIGH_IMPEDANCE	0x0008
+#define	AS3722_CFG_OPEN_DRAIN		0x0010
+
+static const struct {
+	const char	*name;
+	int  		config;		/* AS3722_CFG_  */
+} as3722_cfg_names[] = {
+	{"bias-disable",	AS3722_CFG_BIAS_DISABLE},
+	{"bias-pull-up",	AS3722_CFG_BIAS_PULL_UP},
+	{"bias-pull-down",	AS3722_CFG_BIAS_PULL_DOWN},
+	{"bias-high-impedance",	AS3722_CFG_BIAS_HIGH_IMPEDANCE},
+	{"drive-open-drain",	AS3722_CFG_OPEN_DRAIN},
+};
+
+static struct {
+	const char *name;
+	int fnc_val;
+} as3722_fnc_table[] = {
+	{"gpio",			AS3722_IOSF_GPIO},
+	{"interrupt-out",		AS3722_IOSF_INTERRUPT_OUT},
+	{"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT},
+	{"gpio-in-interrupt",		AS3722_IOSF_GPIO_IN_INTERRUPT},
+	{"pwm-in",			AS3722_IOSF_PWM_IN},
+	{"voltage-in-standby",		AS3722_IOSF_VOLTAGE_IN_STANDBY},
+	{"oc-pg-sd0",			AS3722_IOSF_OC_PG_SD0},
+	{"powergood-out",		AS3722_IOSF_POWERGOOD_OUT},
+	{"clk32k-out",			AS3722_IOSF_CLK32K_OUT},
+	{"watchdog-in",			AS3722_IOSF_WATCHDOG_IN},
+	{"soft-reset-in",		AS3722_IOSF_SOFT_RESET_IN},
+	{"pwm-out",			AS3722_IOSF_PWM_OUT},
+	{"vsup-vbat-low-debounce-out",	AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT},
+	{"oc-pg-sd6",			AS3722_IOSF_OC_PG_SD6},
+};
+
+struct as3722_pincfg {
+	char	*function;
+	int	flags;
+};
+
+struct as3722_gpio_pin {
+	int	pin_caps;
+	uint8_t	pin_ctrl_reg;
+	char	pin_name[GPIOMAXNAME];
+	int	pin_cfg_flags;
+};
+
+
+/* --------------------------------------------------------------------------
+ *
+ *  Pinmux functions.
+ */
+static int
+as3722_pinmux_get_function(struct as3722_softc *sc, char *name)
+{
+	int i;
+
+	for (i = 0; i < nitems(as3722_fnc_table); i++) {
+		if (strcmp(as3722_fnc_table[i].name, name) == 0)
+			 return (as3722_fnc_table[i].fnc_val);
+	}
+	return (-1);
+}
+
+
+
+static int
+as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name,
+    struct as3722_pincfg *cfg)
+{
+	uint8_t ctrl;
+	int rv, fnc, pin;
+
+	for (pin = 0; pin < sc->gpio_npins; pin++) {
+		if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0)
+			 break;
+	}
+	if (pin >= sc->gpio_npins) {
+		device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
+		return (ENXIO);
+	}
+
+	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+	sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags;
+	if (cfg->function != NULL) {
+		fnc = as3722_pinmux_get_function(sc, cfg->function);
+		if (fnc == -1) {
+			device_printf(sc->dev,
+			    "Unknown function %s for pin %s\n", cfg->function,
+			    sc->gpio_pins[pin]->pin_name);
+			return (ENXIO);
+		}
+		switch (fnc) {
+		case AS3722_IOSF_INTERRUPT_OUT:
+		case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT:
+		case AS3722_IOSF_OC_PG_SD0:
+		case AS3722_IOSF_POWERGOOD_OUT:
+		case AS3722_IOSF_CLK32K_OUT:
+		case AS3722_IOSF_PWM_OUT:
+		case AS3722_IOSF_OC_PG_SD6:
+			ctrl &= ~(AS3722_GPIO_MODE_MASK <<
+			    AS3722_GPIO_MODE_SHIFT);
+			ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
+			/* XXX Handle flags (OC + pullup) */
+			break;
+		case AS3722_IOSF_GPIO_IN_INTERRUPT:
+		case AS3722_IOSF_PWM_IN:
+		case AS3722_IOSF_VOLTAGE_IN_STANDBY:
+		case AS3722_IOSF_WATCHDOG_IN:
+		case AS3722_IOSF_SOFT_RESET_IN:
+			ctrl &= ~(AS3722_GPIO_MODE_MASK <<
+			    AS3722_GPIO_MODE_SHIFT);
+			ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT;
+			/* XXX Handle flags (pulldown + pullup) */
+
+		default:
+			break;
+		}
+		ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT);
+		ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT;
+	}
+	rv = 0;
+	if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
+		rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
+		sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
+	}
+	return (rv);
+}
+
+static int
+as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node,
+     struct as3722_pincfg *cfg, char **pins, int *lpins)
+{
+	int rv, i;
+
+	*lpins = OF_getprop_alloc(node, "pins", 1, (void **)pins);
+	if (*lpins <= 0)
+		return (ENOENT);
+
+	/* Read function (mux) settings. */
+	rv = OF_getprop_alloc(node, "function", 1, (void **)&cfg->function);
+	if (rv <= 0)
+		cfg->function = NULL;
+
+	/* Read boolean properties. */
+	for (i = 0; i < nitems(as3722_cfg_names); i++) {
+		if (OF_hasprop(node, as3722_cfg_names[i].name))
+			cfg->flags |= as3722_cfg_names[i].config;
+	}
+	return (0);
+}
+
+static int
+as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node)
+{
+	struct as3722_pincfg cfg;
+	char *pins, *pname;
+	int i, len, lpins, rv;
+
+	rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins);
+	if (rv != 0)
+		return (rv);
+
+	len = 0;
+	pname = pins;
+	do {
+		i = strlen(pname) + 1;
+		rv = as3722_pinmux_config_node(sc, pname, &cfg);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot configure pin: %s: %d\n", pname, rv);
+		}
+		len += i;
+		pname += i;
+	} while (len < lpins);
+
+	if (pins != NULL)
+		OF_prop_free(pins);
+	if (cfg.function != NULL)
+		OF_prop_free(cfg.function);
+
+	return (rv);
+}
+
+int as3722_pinmux_configure(device_t dev, phandle_t cfgxref)
+{
+	struct as3722_softc *sc;
+	phandle_t node, cfgnode;
+	int rv;
+
+	sc = device_get_softc(dev);
+	cfgnode = OF_node_from_xref(cfgxref);
+
+	for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
+		if (!fdt_is_enabled(node))
+			continue;
+		rv = as3722_pinmux_process_node(sc, node);
+		if (rv != 0)
+			device_printf(dev, "Failed to process pinmux");
+
+	}
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ *  GPIO
+ */
+device_t
+as3722_gpio_get_bus(device_t dev)
+{
+	struct as3722_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (sc->gpio_busdev);
+}
+
+int
+as3722_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+	*maxpin = NGPIO - 1;
+	return (0);
+}
+
+int
+as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+	struct as3722_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+	GPIO_LOCK(sc);
+	*caps = sc->gpio_pins[pin]->pin_caps;
+	GPIO_UNLOCK(sc);
+	return (0);
+}
+
+int
+as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+	struct as3722_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+	GPIO_LOCK(sc);
+	memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME);
+	GPIO_UNLOCK(sc);
+	return (0);
+}
+
+int
+as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags)
+{
+	struct as3722_softc *sc;
+	uint8_t tmp, mode, iosf;
+	uint32_t flags;
+	bool inverted;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	tmp = sc->gpio_pins[pin]->pin_ctrl_reg;
+	GPIO_UNLOCK(sc);
+	iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
+	mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
+	inverted = (tmp & AS3722_GPIO_INVERT) != 0;
+	/* Is pin in GPIO mode ? */
+	if (iosf != AS3722_IOSF_GPIO)
+		return (ENXIO);
+
+	flags = 0;
+	switch (mode) {
+	case AS3722_MODE_INPUT:
+		flags = GPIO_PIN_INPUT;
+		break;
+	case AS3722_MODE_PUSH_PULL:
+	case AS3722_MODE_PUSH_PULL_LV:
+		flags = GPIO_PIN_OUTPUT;
+		break;
+	case AS3722_MODE_OPEN_DRAIN:
+	case AS3722_MODE_OPEN_DRAIN_LV:
+		flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN;
+		break;
+	case AS3722_MODE_TRISTATE:
+		flags = GPIO_PIN_TRISTATE;
+		break;
+	case AS3722_MODE_INPUT_PULL_UP_LV:
+		flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
+		break;
+
+	case AS3722_MODE_INPUT_PULL_DOWN:
+		flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN;
+		break;
+	}
+	if (inverted)
+		flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
+	*out_flags = flags;
+	return (0);
+}
+
+static int
+as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags)
+{
+	uint8_t ctrl;
+	int flags;
+
+	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+	flags =  sc->gpio_pins[pin]->pin_cfg_flags;
+
+	/* Tristate mode. */
+	if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE ||
+	    gpio_flags & GPIO_PIN_TRISTATE)
+		return (AS3722_MODE_TRISTATE);
+
+	/* Open drain modes. */
+	if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) {
+		/* Only pull up have effect */
+		if (flags & AS3722_CFG_BIAS_PULL_UP ||
+		    gpio_flags & GPIO_PIN_PULLUP)
+			return (AS3722_MODE_OPEN_DRAIN_LV);
+		return (AS3722_MODE_OPEN_DRAIN);
+	}
+	/* Input modes. */
+	if (gpio_flags & GPIO_PIN_INPUT) {
+		/* Accept pull up or pull down. */
+		if (flags & AS3722_CFG_BIAS_PULL_UP ||
+		    gpio_flags & GPIO_PIN_PULLUP)
+			return (AS3722_MODE_INPUT_PULL_UP_LV);
+
+		if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
+		    gpio_flags & GPIO_PIN_PULLDOWN)
+			return (AS3722_MODE_INPUT_PULL_DOWN);
+		return (AS3722_MODE_INPUT);
+	}
+	/*
+	 * Output modes.
+	 * Pull down is used as indicator of low voltage output.
+	 */
+	if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
+		    gpio_flags & GPIO_PIN_PULLDOWN)
+		return (AS3722_MODE_PUSH_PULL_LV);
+	return (AS3722_MODE_PUSH_PULL);
+}
+
+int
+as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+	struct as3722_softc *sc;
+	uint8_t ctrl, mode, iosf;
+	int rv;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+	iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
+	/* Is pin in GPIO mode ? */
+	if (iosf != AS3722_IOSF_GPIO) {
+		GPIO_UNLOCK(sc);
+		return (ENXIO);
+	}
+	mode = as3722_gpio_get_mode(sc, pin, flags);
+	ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT);
+	ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
+	rv = 0;
+	if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
+		rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
+		sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
+	}
+	GPIO_UNLOCK(sc);
+	return (rv);
+}
+
+int
+as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
+{
+	struct as3722_softc *sc;
+	uint8_t tmp;
+	int rv;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	tmp =  (val != 0) ? 1 : 0;
+	if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT)
+		tmp ^= 1;
+
+	GPIO_LOCK(sc);
+	rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin));
+	GPIO_UNLOCK(sc);
+	return (rv);
+}
+
+int
+as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
+{
+	struct as3722_softc *sc;
+	uint8_t tmp, mode, ctrl;
+	int rv;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
+	mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
+	if ((mode == AS3722_MODE_PUSH_PULL) ||
+	    (mode == AS3722_MODE_PUSH_PULL_LV))
+		rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
+	else
+		rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp);
+	GPIO_UNLOCK(sc);
+	if (rv != 0)
+		return (rv);
+
+	*val = tmp & (1 << pin) ? 1 : 0;
+	if (ctrl & AS3722_GPIO_INVERT)
+		*val ^= 1;
+	return (0);
+}
+
+int
+as3722_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+	struct as3722_softc *sc;
+	uint8_t tmp;
+	int rv;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
+	if (rv != 0) {
+		GPIO_UNLOCK(sc);
+		return (rv);
+	}
+	tmp ^= (1 <<pin);
+	rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp);
+	GPIO_UNLOCK(sc);
+	return (0);
+}
+
+int
+as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+    int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+
+	if (gcells != 2)
+		return (ERANGE);
+	*pin = gpios[0];
+	*flags= gpios[1];
+	return (0);
+}
+
+int
+as3722_gpio_attach(struct as3722_softc *sc, phandle_t node)
+{
+	struct as3722_gpio_pin *pin;
+	int i, rv;
+
+	sx_init(&sc->gpio_lock, "AS3722 GPIO lock");
+	sc->gpio_npins = NGPIO;
+	sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) *
+	    sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO);
+
+
+	sc->gpio_busdev = gpiobus_attach_bus(sc->dev);
+	if (sc->gpio_busdev == NULL)
+		return (ENXIO);
+	for (i = 0; i < sc->gpio_npins; i++) {
+		sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin),
+		    M_AS3722_GPIO, M_WAITOK | M_ZERO);
+		pin = sc->gpio_pins[i];
+		sprintf(pin->pin_name, "gpio%d", i);
+		pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT  |
+		    GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
+		    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN |
+		    GPIO_PIN_INVOUT;
+		rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot read configuration for pin %s\n",
+			    sc->gpio_pins[i]->pin_name);
+		}
+	}
+	return (0);
+}


Property changes on: trunk/sys/arm/nvidia/as3722_gpio.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/as3722_regulators.c
===================================================================
--- trunk/sys/arm/nvidia/as3722_regulators.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/as3722_regulators.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,718 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_regulators.c 308328 2016-11-05 04:40:58Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/regulator/regulator.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include <gnu/dts/include/dt-bindings/mfd/as3722.h>
+
+#include "as3722.h"
+
+MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator");
+
+#define	DIV_ROUND_UP(n,d) howmany(n, d)
+
+enum as3722_reg_id {
+	AS3722_REG_ID_SD0,
+	AS3722_REG_ID_SD1,
+	AS3722_REG_ID_SD2,
+	AS3722_REG_ID_SD3,
+	AS3722_REG_ID_SD4,
+	AS3722_REG_ID_SD5,
+	AS3722_REG_ID_SD6,
+	AS3722_REG_ID_LDO0,
+	AS3722_REG_ID_LDO1,
+	AS3722_REG_ID_LDO2,
+	AS3722_REG_ID_LDO3,
+	AS3722_REG_ID_LDO4,
+	AS3722_REG_ID_LDO5,
+	AS3722_REG_ID_LDO6,
+	AS3722_REG_ID_LDO7,
+	AS3722_REG_ID_LDO9,
+	AS3722_REG_ID_LDO10,
+	AS3722_REG_ID_LDO11,
+};
+
+
+/* Regulator HW definition. */
+struct reg_def {
+	intptr_t		id;		/* ID */
+	char			*name;		/* Regulator name */
+	char			*supply_name;	/* Source property name */
+	uint8_t			volt_reg;
+	uint8_t			volt_vsel_mask;
+	uint8_t			enable_reg;
+	uint8_t			enable_mask;
+	uint8_t			ext_enable_reg;
+	uint8_t			ext_enable_mask;
+	struct regulator_range	*ranges;
+	int			nranges;
+};
+
+struct as3722_reg_sc {
+	struct regnode		*regnode;
+	struct as3722_softc	*base_sc;
+	struct reg_def		*def;
+	phandle_t		xref;
+
+	struct regnode_std_param *param;
+	int 			ext_control;
+	int	 		enable_tracking;
+
+	int			enable_usec;
+};
+
+static struct regulator_range as3722_sd016_ranges[] = {
+	REG_RANGE_INIT(0x00, 0x00,       0,     0),
+	REG_RANGE_INIT(0x01, 0x5A,  610000, 10000),
+};
+
+static struct regulator_range as3722_sd0_lv_ranges[] = {
+	REG_RANGE_INIT(0x00, 0x00,       0,     0),
+	REG_RANGE_INIT(0x01, 0x6E,  410000, 10000),
+};
+
+static struct regulator_range as3722_sd_ranges[] = {
+	REG_RANGE_INIT(0x00, 0x00,       0,     0),
+	REG_RANGE_INIT(0x01, 0x40,  612500, 12500),
+	REG_RANGE_INIT(0x41, 0x70, 1425000, 25000),
+	REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000),
+};
+
+static struct regulator_range as3722_ldo3_ranges[] = {
+	REG_RANGE_INIT(0x00, 0x00,       0,     0),
+	REG_RANGE_INIT(0x01, 0x2D,  620000, 20000),
+};
+
+static struct regulator_range as3722_ldo_ranges[] = {
+	REG_RANGE_INIT(0x00, 0x00,       0,     0),
+	REG_RANGE_INIT(0x01, 0x24,  825000, 25000),
+	REG_RANGE_INIT(0x40, 0x7F, 1725000, 25000),
+};
+
+static struct reg_def as3722s_def[] = {
+	{
+		.id = AS3722_REG_ID_SD0,
+		.name = "sd0",
+		.volt_reg = AS3722_SD0_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(0),
+		.ext_enable_reg = AS3722_ENABLE_CTRL1,
+		.ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK,
+		.ranges = as3722_sd016_ranges,
+		.nranges = nitems(as3722_sd016_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_SD1,
+		.name = "sd1",
+		.volt_reg = AS3722_SD1_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(1),
+		.ext_enable_reg = AS3722_ENABLE_CTRL1,
+		.ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK,
+		.ranges = as3722_sd_ranges,
+		.nranges = nitems(as3722_sd_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_SD2,
+		.name = "sd2",
+		.supply_name = "vsup-sd2",
+		.volt_reg = AS3722_SD2_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(2),
+		.ext_enable_reg = AS3722_ENABLE_CTRL1,
+		.ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK,
+		.ranges = as3722_sd_ranges,
+		.nranges = nitems(as3722_sd_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_SD3,
+		.name = "sd3",
+		.supply_name = "vsup-sd3",
+		.volt_reg = AS3722_SD3_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(3),
+		.ext_enable_reg = AS3722_ENABLE_CTRL1,
+		.ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK,
+		.ranges = as3722_sd_ranges,
+		.nranges = nitems(as3722_sd_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_SD4,
+		.name = "sd4",
+		.supply_name = "vsup-sd4",
+		.volt_reg = AS3722_SD4_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(4),
+		.ext_enable_reg = AS3722_ENABLE_CTRL2,
+		.ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK,
+		.ranges = as3722_sd_ranges,
+		.nranges = nitems(as3722_sd_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_SD5,
+		.name = "sd5",
+		.supply_name = "vsup-sd5",
+		.volt_reg = AS3722_SD5_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(5),
+		.ext_enable_reg = AS3722_ENABLE_CTRL2,
+		.ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK,
+		.ranges = as3722_sd_ranges,
+		.nranges = nitems(as3722_sd_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_SD6,
+		.name = "sd6",
+		.volt_reg = AS3722_SD6_VOLTAGE,
+		.volt_vsel_mask = AS3722_SD_VSEL_MASK,
+		.enable_reg = AS3722_SD_CONTROL,
+		.enable_mask = AS3722_SDN_CTRL(6),
+		.ext_enable_reg = AS3722_ENABLE_CTRL2,
+		.ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK,
+		.ranges = as3722_sd016_ranges,
+		.nranges = nitems(as3722_sd016_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO0,
+		.name = "ldo0",
+		.supply_name = "vin-ldo0",
+		.volt_reg = AS3722_LDO0_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO0_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO0_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL3,
+		.ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO1,
+		.name = "ldo1",
+		.supply_name = "vin-ldo1-6",
+		.volt_reg = AS3722_LDO1_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO1_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL3,
+		.ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO2,
+		.name = "ldo2",
+		.supply_name = "vin-ldo2-5-7",
+		.volt_reg = AS3722_LDO2_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO2_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL3,
+		.ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO3,
+		.name = "ldo3",
+		.supply_name = "vin-ldo3-4",
+		.volt_reg = AS3722_LDO3_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO3_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO3_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL3,
+		.ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo3_ranges,
+		.nranges = nitems(as3722_ldo3_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO4,
+		.name = "ldo4",
+		.supply_name = "vin-ldo3-4",
+		.volt_reg = AS3722_LDO4_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO4_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL4,
+		.ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO5,
+		.name = "ldo5",
+		.supply_name = "vin-ldo2-5-7",
+		.volt_reg = AS3722_LDO5_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO5_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL4,
+		.ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO6,
+		.name = "ldo6",
+		.supply_name = "vin-ldo1-6",
+		.volt_reg = AS3722_LDO6_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO6_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL4,
+		.ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO7,
+		.name = "ldo7",
+		.supply_name = "vin-ldo2-5-7",
+		.volt_reg = AS3722_LDO7_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL0,
+		.enable_mask = AS3722_LDO7_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL4,
+		.ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO9,
+		.name = "ldo9",
+		.supply_name = "vin-ldo9-10",
+		.volt_reg = AS3722_LDO9_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL1,
+		.enable_mask = AS3722_LDO9_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL5,
+		.ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO10,
+		.name = "ldo10",
+		.supply_name = "vin-ldo9-10",
+		.volt_reg = AS3722_LDO10_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL1,
+		.enable_mask = AS3722_LDO10_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL5,
+		.ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+	{
+		.id = AS3722_REG_ID_LDO11,
+		.name = "ldo11",
+		.supply_name = "vin-ldo11",
+		.volt_reg = AS3722_LDO11_VOLTAGE,
+		.volt_vsel_mask = AS3722_LDO_VSEL_MASK,
+		.enable_reg = AS3722_LDO_CONTROL1,
+		.enable_mask = AS3722_LDO11_CTRL,
+		.ext_enable_reg = AS3722_ENABLE_CTRL5,
+		.ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK,
+		.ranges = as3722_ldo_ranges,
+		.nranges = nitems(as3722_ldo_ranges),
+	},
+};
+
+
+struct as3722_regnode_init_def {
+	struct regnode_init_def	reg_init_def;
+	int 			ext_control;
+	int	 		enable_tracking;
+};
+
+static int as3722_regnode_init(struct regnode *regnode);
+static int as3722_regnode_enable(struct regnode *regnode, bool enable,
+    int *udelay);
+static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt,
+    int max_uvolt, int *udelay);
+static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt);
+static regnode_method_t as3722_regnode_methods[] = {
+	/* Regulator interface */
+	REGNODEMETHOD(regnode_init,		as3722_regnode_init),
+	REGNODEMETHOD(regnode_enable,		as3722_regnode_enable),
+	REGNODEMETHOD(regnode_set_voltage,	as3722_regnode_set_volt),
+	REGNODEMETHOD(regnode_get_voltage,	as3722_regnode_get_volt),
+	REGNODEMETHOD_END
+};
+DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods,
+   sizeof(struct as3722_reg_sc), regnode_class);
+
+static int
+as3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel)
+{
+	int rv;
+
+	rv = RD1(sc->base_sc, sc->def->volt_reg, sel);
+	if (rv != 0)
+		return (rv);
+	*sel &= sc->def->volt_vsel_mask;
+	*sel >>= ffs(sc->def->volt_vsel_mask) - 1;
+	return (0);
+}
+
+static int
+as3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel)
+{
+	int rv;
+
+	sel <<= ffs(sc->def->volt_vsel_mask) - 1;
+	sel &= sc->def->volt_vsel_mask;
+
+	rv = RM1(sc->base_sc, sc->def->volt_reg,
+	    sc->def->volt_vsel_mask, sel);
+	if (rv != 0)
+		return (rv);
+	return (rv);
+}
+
+static bool
+as3722_sd0_is_low_voltage(struct as3722_reg_sc *sc)
+{
+	uint8_t val;
+	int rv;
+
+	rv = RD1(sc->base_sc, AS3722_FUSE7, &val);
+	if (rv != 0)
+		return (rv);
+	return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false);
+}
+
+static int
+as3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl)
+{
+	uint8_t val;
+	int rv;
+
+	val =  ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1);
+	rv = RM1(sc->base_sc, sc->def->ext_enable_reg,
+	    sc->def->ext_enable_mask, val);
+	return (rv);
+}
+
+static int
+as3722_reg_enable(struct as3722_reg_sc *sc)
+{
+	int rv;
+
+	rv = RM1(sc->base_sc, sc->def->enable_reg,
+	    sc->def->enable_mask, sc->def->enable_mask);
+	return (rv);
+}
+
+static int
+as3722_reg_disable(struct as3722_reg_sc *sc)
+{
+	int rv;
+
+	rv = RM1(sc->base_sc, sc->def->enable_reg,
+	    sc->def->enable_mask, 0);
+	return (rv);
+}
+
+static int
+as3722_regnode_init(struct regnode *regnode)
+{
+	struct as3722_reg_sc *sc;
+	int rv;
+
+	sc = regnode_get_softc(regnode);
+
+	sc->enable_usec = 500;
+	if (sc->def->id == AS3722_REG_ID_SD0) {
+		if (as3722_sd0_is_low_voltage(sc)) {
+			sc->def->ranges = as3722_sd0_lv_ranges;
+			sc->def->nranges = nitems(as3722_sd0_lv_ranges);
+		}
+		sc->enable_usec = 600;
+	} else if (sc->def->id == AS3722_REG_ID_LDO3) {
+		if (sc->enable_tracking) {
+			rv = RM1(sc->base_sc, sc->def->volt_reg,
+			    AS3722_LDO3_MODE_MASK,
+			    AS3722_LDO3_MODE_PMOS_TRACKING);
+			if (rv < 0) {
+				device_printf(sc->base_sc->dev,
+					"LDO3 tracking failed: %d\n", rv);
+				return (rv);
+			}
+		}
+	}
+
+	if (sc->ext_control) {
+
+		rv = as3722_reg_enable(sc);
+		if (rv < 0) {
+			device_printf(sc->base_sc->dev,
+				"Failed to enable %s regulator: %d\n",
+				sc->def->name, rv);
+			return (rv);
+		}
+		rv = as3722_reg_extreg_setup(sc, sc->ext_control);
+		if (rv < 0) {
+			device_printf(sc->base_sc->dev,
+				"%s ext control failed: %d", sc->def->name, rv);
+			return (rv);
+		}
+	}
+	return (0);
+}
+
+static void
+as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def,
+struct as3722_regnode_init_def *init_def)
+{
+	int rv;
+	phandle_t parent, supply_node;
+	char prop_name[64]; /* Maximum OFW property name length. */
+
+	rv = regulator_parse_ofw_stdparam(sc->dev, node,
+	    &init_def->reg_init_def);
+
+	rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control,
+	    sizeof(init_def->ext_control));
+	if (rv <= 0)
+		init_def->ext_control = 0;
+	if (init_def->ext_control > 3) {
+		device_printf(sc->dev,
+		    "Invalid value for ams,ext-control property: %d\n",
+		    init_def->ext_control);
+		init_def->ext_control = 0;
+	}
+	if (OF_hasprop(node, "ams,enable-tracking"))
+		init_def->enable_tracking = 1;
+
+
+	/* Get parent supply. */
+	if (def->supply_name == NULL)
+		 return;
+
+	parent = OF_parent(node);
+	snprintf(prop_name, sizeof(prop_name), "%s-supply",
+	    def->supply_name);
+	rv = OF_getencprop(parent, prop_name, &supply_node,
+	    sizeof(supply_node));
+	if (rv <= 0)
+		return;
+	supply_node = OF_node_from_xref(supply_node);
+	rv = OF_getprop_alloc(supply_node, "regulator-name", 1,
+	    (void **)&init_def->reg_init_def.parent_name);
+	if (rv <= 0)
+		init_def->reg_init_def.parent_name = NULL;
+}
+
+static struct as3722_reg_sc *
+as3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def)
+{
+	struct as3722_reg_sc *reg_sc;
+	struct as3722_regnode_init_def init_def;
+	struct regnode *regnode;
+
+	bzero(&init_def, sizeof(init_def));
+
+	as3722_fdt_parse(sc, node, def, &init_def);
+	init_def.reg_init_def.id = def->id;
+	init_def.reg_init_def.ofw_node = node;
+	regnode = regnode_create(sc->dev, &as3722_regnode_class,
+	    &init_def.reg_init_def);
+	if (regnode == NULL) {
+		device_printf(sc->dev, "Cannot create regulator.\n");
+		return (NULL);
+	}
+	reg_sc = regnode_get_softc(regnode);
+
+	/* Init regulator softc. */
+	reg_sc->regnode = regnode;
+	reg_sc->base_sc = sc;
+	reg_sc->def = def;
+	reg_sc->xref = OF_xref_from_node(node);
+
+	reg_sc->param = regnode_get_stdparam(regnode);
+	reg_sc->ext_control = init_def.ext_control;
+	reg_sc->enable_tracking = init_def.enable_tracking;
+
+	regnode_register(regnode);
+	if (bootverbose) {
+		int volt, rv;
+		regnode_topo_slock();
+		rv = regnode_get_voltage(regnode, &volt);
+		if (rv == ENODEV) {
+			device_printf(sc->dev,
+			   " Regulator %s: parent doesn't exist yet.\n",
+			   regnode_get_name(regnode));
+		} else if (rv != 0) {
+			device_printf(sc->dev,
+			   " Regulator %s: voltage: INVALID!!!\n",
+			   regnode_get_name(regnode));
+		} else {
+			device_printf(sc->dev,
+			    " Regulator %s: voltage: %d uV\n",
+			    regnode_get_name(regnode), volt);
+		}
+		regnode_topo_unlock();
+	}
+
+	return (reg_sc);
+}
+
+int
+as3722_regulator_attach(struct as3722_softc *sc, phandle_t node)
+{
+	struct as3722_reg_sc *reg;
+	phandle_t child, rnode;
+	int i;
+
+	rnode = ofw_bus_find_child(node, "regulators");
+	if (rnode <= 0) {
+		device_printf(sc->dev, " Cannot find regulators subnode\n");
+		return (ENXIO);
+	}
+
+	sc->nregs = nitems(as3722s_def);
+	sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs,
+	    M_AS3722_REG, M_WAITOK | M_ZERO);
+
+
+	/* Attach all known regulators if exist in DT. */
+	for (i = 0; i < sc->nregs; i++) {
+		child = ofw_bus_find_child(rnode, as3722s_def[i].name);
+		if (child == 0) {
+			if (bootverbose)
+				device_printf(sc->dev,
+				    "Regulator %s missing in DT\n",
+				    as3722s_def[i].name);
+			continue;
+		}
+		reg = as3722_attach(sc, child, as3722s_def + i);
+		if (reg == NULL) {
+			device_printf(sc->dev, "Cannot attach regulator: %s\n",
+			    as3722s_def[i].name);
+			return (ENXIO);
+		}
+		sc->regs[i] = reg;
+	}
+	return (0);
+}
+
+int
+as3722_regulator_map(device_t dev, phandle_t xref, int ncells,
+    pcell_t *cells, int *num)
+{
+	struct as3722_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+	for (i = 0; i < sc->nregs; i++) {
+		if (sc->regs[i] == NULL)
+			continue;
+		if (sc->regs[i]->xref == xref) {
+			*num = sc->regs[i]->def->id;
+			return (0);
+		}
+	}
+	return (ENXIO);
+}
+
+static int
+as3722_regnode_enable(struct regnode *regnode, bool val, int *udelay)
+{
+	struct as3722_reg_sc *sc;
+	int rv;
+
+	sc = regnode_get_softc(regnode);
+
+	if (val)
+		rv = as3722_reg_enable(sc);
+	else
+		rv = as3722_reg_disable(sc);
+	*udelay = sc->enable_usec;
+	return (rv);
+}
+
+static int
+as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt,
+    int *udelay)
+{
+	struct as3722_reg_sc *sc;
+	uint8_t sel;
+	int rv;
+
+	sc = regnode_get_softc(regnode);
+
+	*udelay = 0;
+	rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges,
+	    min_uvolt, max_uvolt, &sel);
+	if (rv != 0)
+		return (rv);
+	rv = as3722_write_sel(sc, sel);
+	return (rv);
+
+}
+
+static int
+as3722_regnode_get_volt(struct regnode *regnode, int *uvolt)
+{
+	struct as3722_reg_sc *sc;
+	uint8_t sel;
+	int rv;
+
+	sc = regnode_get_softc(regnode);
+	rv = as3722_read_sel(sc, &sel);
+	if (rv != 0)
+		return (rv);
+
+	/* LDO6 have bypass. */
+	if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS)
+		return (ENOENT);
+	rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges,
+	    sel, uvolt);
+	return (rv);
+}


Property changes on: trunk/sys/arm/nvidia/as3722_regulators.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/as3722_rtc.c
===================================================================
--- trunk/sys/arm/nvidia/as3722_rtc.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/as3722_rtc.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,116 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_rtc.c 296936 2016-03-16 13:01:48Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/ofw_bus.h>
+
+#include "clock_if.h"
+#include "as3722.h"
+
+#define	AS3722_RTC_START_YEAR	2000
+
+int
+as3722_rtc_gettime(device_t dev, struct timespec *ts)
+{
+	struct as3722_softc *sc;
+	struct clocktime ct;
+	uint8_t buf[6];
+	int rv;
+
+	sc = device_get_softc(dev);
+
+	rv = as3722_read_buf(sc, AS3722_RTC_SECOND, buf, 6);
+	if (rv != 0) {
+		device_printf(sc->dev, "Failed to read RTC data\n");
+		return (rv);
+	}
+	ct.nsec = 0;
+	ct.sec = bcd2bin(buf[0] & 0x7F);
+	ct.min = bcd2bin(buf[1] & 0x7F);
+	ct.hour = bcd2bin(buf[2] & 0x3F);
+	ct.day = bcd2bin(buf[3] & 0x3F);
+	ct.mon = bcd2bin(buf[4] & 0x1F);
+	ct.year = bcd2bin(buf[5] & 0x7F) + AS3722_RTC_START_YEAR;
+	ct.dow = -1;
+
+	return clock_ct_to_ts(&ct, ts);
+}
+
+int
+as3722_rtc_settime(device_t dev, struct timespec *ts)
+{
+	struct as3722_softc *sc;
+	struct clocktime ct;
+	uint8_t buf[6];
+	int rv;
+
+	sc = device_get_softc(dev);
+	clock_ts_to_ct(ts, &ct);
+
+	if (ct.year < AS3722_RTC_START_YEAR)
+		return (EINVAL);
+
+	buf[0] = bin2bcd(ct.sec);
+	buf[1] = bin2bcd(ct.min);
+	buf[2] = bin2bcd(ct.hour);
+	buf[3] = bin2bcd(ct.day);
+	buf[4] = bin2bcd(ct.mon);
+	buf[5] = bin2bcd(ct.year - AS3722_RTC_START_YEAR);
+
+	rv = as3722_write_buf(sc, AS3722_RTC_SECOND, buf, 6);
+	if (rv != 0) {
+		device_printf(sc->dev, "Failed to write RTC data\n");
+		return (rv);
+	}
+	return (0);
+}
+
+int
+as3722_rtc_attach(struct as3722_softc *sc, phandle_t node)
+{
+	int rv;
+
+	/* Enable RTC, set 24 hours mode  and alarms */
+	rv = RM1(sc, AS3722_RTC_CONTROL,
+	    AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN | AS3722_RTC_AM_PM_MODE,
+	    AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN);
+	if (rv < 0) {
+		device_printf(sc->dev, "Failed to initialize RTC controller\n");
+		return (ENXIO);
+	}
+	clock_register(sc->dev, 1000000);
+
+	return (0);
+}


Property changes on: trunk/sys/arm/nvidia/as3722_rtc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/hdmi.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/hdmi.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/hdmi.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1230 @@
+/* $MidnightBSD$ */
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/hdmi.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <arm/nvidia/drm2/hdmi.h>
+
+#define EXPORT_SYMBOL(x)
+#ifndef BIT
+#define BIT(x) (1U << (x))
+#endif
+#define hdmi_log(fmt, ...) printf(fmt, ##__VA_ARGS__)
+
+static uint8_t hdmi_infoframe_checksum(uint8_t *ptr, size_t size)
+{
+	uint8_t csum = 0;
+	size_t i;
+
+	/* compute checksum */
+	for (i = 0; i < size; i++)
+		csum += ptr[i];
+
+	return 256 - csum;
+}
+
+static void hdmi_infoframe_set_checksum(void *buffer, size_t size)
+{
+	uint8_t *ptr = buffer;
+
+	ptr[3] = hdmi_infoframe_checksum(buffer, size);
+}
+
+/**
+ * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
+ * @frame: HDMI AVI infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	frame->type = HDMI_INFOFRAME_TYPE_AVI;
+	frame->version = 2;
+	frame->length = HDMI_AVI_INFOFRAME_SIZE;
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_avi_infoframe_init);
+
+/**
+ * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer
+ * @frame: HDMI AVI infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
+				size_t size)
+{
+	uint8_t *ptr = buffer;
+	size_t length;
+
+	length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+	if (size < length)
+		return -ENOSPC;
+
+	memset(buffer, 0, size);
+
+	ptr[0] = frame->type;
+	ptr[1] = frame->version;
+	ptr[2] = frame->length;
+	ptr[3] = 0; /* checksum */
+
+	/* start infoframe payload */
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3);
+
+	/*
+	 * Data byte 1, bit 4 has to be set if we provide the active format
+	 * aspect ratio
+	 */
+	if (frame->active_aspect & 0xf)
+		ptr[0] |= BIT(4);
+
+	/* Bit 3 and 2 indicate if we transmit horizontal/vertical bar data */
+	if (frame->top_bar || frame->bottom_bar)
+		ptr[0] |= BIT(3);
+
+	if (frame->left_bar || frame->right_bar)
+		ptr[0] |= BIT(2);
+
+	ptr[1] = ((frame->colorimetry & 0x3) << 6) |
+		 ((frame->picture_aspect & 0x3) << 4) |
+		 (frame->active_aspect & 0xf);
+
+	ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) |
+		 ((frame->quantization_range & 0x3) << 2) |
+		 (frame->nups & 0x3);
+
+	if (frame->itc)
+		ptr[2] |= BIT(7);
+
+	ptr[3] = frame->video_code & 0x7f;
+
+	ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) |
+		 ((frame->content_type & 0x3) << 4) |
+		 (frame->pixel_repeat & 0xf);
+
+	ptr[5] = frame->top_bar & 0xff;
+	ptr[6] = (frame->top_bar >> 8) & 0xff;
+	ptr[7] = frame->bottom_bar & 0xff;
+	ptr[8] = (frame->bottom_bar >> 8) & 0xff;
+	ptr[9] = frame->left_bar & 0xff;
+	ptr[10] = (frame->left_bar >> 8) & 0xff;
+	ptr[11] = frame->right_bar & 0xff;
+	ptr[12] = (frame->right_bar >> 8) & 0xff;
+
+	hdmi_infoframe_set_checksum(buffer, length);
+
+	return length;
+}
+EXPORT_SYMBOL(hdmi_avi_infoframe_pack);
+
+/**
+ * hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe
+ * @frame: HDMI SPD infoframe
+ * @vendor: vendor string
+ * @product: product string
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
+			    const char *vendor, const char *product)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	frame->type = HDMI_INFOFRAME_TYPE_SPD;
+	frame->version = 1;
+	frame->length = HDMI_SPD_INFOFRAME_SIZE;
+
+	strncpy(frame->vendor, vendor, sizeof(frame->vendor));
+	strncpy(frame->product, product, sizeof(frame->product));
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_spd_infoframe_init);
+
+/**
+ * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer
+ * @frame: HDMI SPD infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
+				size_t size)
+{
+	uint8_t *ptr = buffer;
+	size_t length;
+
+	length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+	if (size < length)
+		return -ENOSPC;
+
+	memset(buffer, 0, size);
+
+	ptr[0] = frame->type;
+	ptr[1] = frame->version;
+	ptr[2] = frame->length;
+	ptr[3] = 0; /* checksum */
+
+	/* start infoframe payload */
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	memcpy(ptr, frame->vendor, sizeof(frame->vendor));
+	memcpy(ptr + 8, frame->product, sizeof(frame->product));
+
+	ptr[24] = frame->sdi;
+
+	hdmi_infoframe_set_checksum(buffer, length);
+
+	return length;
+}
+EXPORT_SYMBOL(hdmi_spd_infoframe_pack);
+
+/**
+ * hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe
+ * @frame: HDMI audio infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	frame->type = HDMI_INFOFRAME_TYPE_AUDIO;
+	frame->version = 1;
+	frame->length = HDMI_AUDIO_INFOFRAME_SIZE;
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_init);
+
+/**
+ * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer
+ * @frame: HDMI audio infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
+				  void *buffer, size_t size)
+{
+	unsigned char channels;
+	uint8_t *ptr = buffer;
+	size_t length;
+
+	length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+	if (size < length)
+		return -ENOSPC;
+
+	memset(buffer, 0, size);
+
+	if (frame->channels >= 2)
+		channels = frame->channels - 1;
+	else
+		channels = 0;
+
+	ptr[0] = frame->type;
+	ptr[1] = frame->version;
+	ptr[2] = frame->length;
+	ptr[3] = 0; /* checksum */
+
+	/* start infoframe payload */
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
+	ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
+		 (frame->sample_size & 0x3);
+	ptr[2] = frame->coding_type_ext & 0x1f;
+	ptr[3] = frame->channel_allocation;
+	ptr[4] = (frame->level_shift_value & 0xf) << 3;
+
+	if (frame->downmix_inhibit)
+		ptr[4] |= BIT(7);
+
+	hdmi_infoframe_set_checksum(buffer, length);
+
+	return length;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
+
+/**
+ * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe
+ * @frame: HDMI vendor infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	frame->type = HDMI_INFOFRAME_TYPE_VENDOR;
+	frame->version = 1;
+
+	frame->oui = HDMI_IEEE_OUI;
+
+	/*
+	 * 0 is a valid value for s3d_struct, so we use a special "not set"
+	 * value
+	 */
+	frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID;
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_vendor_infoframe_init);
+
+/**
+ * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer
+ * @frame: HDMI infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
+				 void *buffer, size_t size)
+{
+	uint8_t *ptr = buffer;
+	size_t length;
+
+	/* empty info frame */
+	if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID)
+		return -EINVAL;
+
+	/* only one of those can be supplied */
+	if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
+		return -EINVAL;
+
+	/* for side by side (half) we also need to provide 3D_Ext_Data */
+	if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+		frame->length = 6;
+	else
+		frame->length = 5;
+
+	length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+	if (size < length)
+		return -ENOSPC;
+
+	memset(buffer, 0, size);
+
+	ptr[0] = frame->type;
+	ptr[1] = frame->version;
+	ptr[2] = frame->length;
+	ptr[3] = 0; /* checksum */
+
+	/* HDMI OUI */
+	ptr[4] = 0x03;
+	ptr[5] = 0x0c;
+	ptr[6] = 0x00;
+
+	if (frame->vic) {
+		ptr[7] = 0x1 << 5;	/* video format */
+		ptr[8] = frame->vic;
+	} else {
+		ptr[7] = 0x2 << 5;	/* video format */
+		ptr[8] = (frame->s3d_struct & 0xf) << 4;
+		if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+			ptr[9] = (frame->s3d_ext_data & 0xf) << 4;
+	}
+
+	hdmi_infoframe_set_checksum(buffer, length);
+
+	return length;
+}
+EXPORT_SYMBOL(hdmi_vendor_infoframe_pack);
+
+/*
+ * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer
+ */
+static ssize_t
+hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame,
+			   void *buffer, size_t size)
+{
+	/* we only know about HDMI vendor infoframes */
+	if (frame->any.oui != HDMI_IEEE_OUI)
+		return -EINVAL;
+
+	return hdmi_vendor_infoframe_pack(&frame->hdmi, buffer, size);
+}
+
+/**
+ * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer
+ * @frame: HDMI infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t
+hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size)
+{
+	ssize_t length;
+
+	switch (frame->any.type) {
+	case HDMI_INFOFRAME_TYPE_AVI:
+		length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size);
+		break;
+	case HDMI_INFOFRAME_TYPE_SPD:
+		length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size);
+		break;
+	case HDMI_INFOFRAME_TYPE_AUDIO:
+		length = hdmi_audio_infoframe_pack(&frame->audio, buffer, size);
+		break;
+	case HDMI_INFOFRAME_TYPE_VENDOR:
+		length = hdmi_vendor_any_infoframe_pack(&frame->vendor,
+							buffer, size);
+		break;
+	default:
+		printf("Bad infoframe type %d\n", frame->any.type);
+		length = -EINVAL;
+	}
+
+	return length;
+}
+EXPORT_SYMBOL(hdmi_infoframe_pack);
+
+static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type)
+{
+	if (type < 0x80 || type > 0x9f)
+		return "Invalid";
+	switch (type) {
+	case HDMI_INFOFRAME_TYPE_VENDOR:
+		return "Vendor";
+	case HDMI_INFOFRAME_TYPE_AVI:
+		return "Auxiliary Video Information (AVI)";
+	case HDMI_INFOFRAME_TYPE_SPD:
+		return "Source Product Description (SPD)";
+	case HDMI_INFOFRAME_TYPE_AUDIO:
+		return "Audio";
+	}
+	return "Reserved";
+}
+
+static void hdmi_infoframe_log_header(struct hdmi_any_infoframe *frame)
+{
+	hdmi_log("HDMI infoframe: %s, version %u, length %u\n",
+		hdmi_infoframe_type_get_name(frame->type),
+		frame->version, frame->length);
+}
+
+static const char *hdmi_colorspace_get_name(enum hdmi_colorspace colorspace)
+{
+	switch (colorspace) {
+	case HDMI_COLORSPACE_RGB:
+		return "RGB";
+	case HDMI_COLORSPACE_YUV422:
+		return "YCbCr 4:2:2";
+	case HDMI_COLORSPACE_YUV444:
+		return "YCbCr 4:4:4";
+	case HDMI_COLORSPACE_YUV420:
+		return "YCbCr 4:2:0";
+	case HDMI_COLORSPACE_RESERVED4:
+		return "Reserved (4)";
+	case HDMI_COLORSPACE_RESERVED5:
+		return "Reserved (5)";
+	case HDMI_COLORSPACE_RESERVED6:
+		return "Reserved (6)";
+	case HDMI_COLORSPACE_IDO_DEFINED:
+		return "IDO Defined";
+	}
+	return "Invalid";
+}
+
+static const char *hdmi_scan_mode_get_name(enum hdmi_scan_mode scan_mode)
+{
+	switch (scan_mode) {
+	case HDMI_SCAN_MODE_NONE:
+		return "No Data";
+	case HDMI_SCAN_MODE_OVERSCAN:
+		return "Overscan";
+	case HDMI_SCAN_MODE_UNDERSCAN:
+		return "Underscan";
+	case HDMI_SCAN_MODE_RESERVED:
+		return "Reserved";
+	}
+	return "Invalid";
+}
+
+static const char *hdmi_colorimetry_get_name(enum hdmi_colorimetry colorimetry)
+{
+	switch (colorimetry) {
+	case HDMI_COLORIMETRY_NONE:
+		return "No Data";
+	case HDMI_COLORIMETRY_ITU_601:
+		return "ITU601";
+	case HDMI_COLORIMETRY_ITU_709:
+		return "ITU709";
+	case HDMI_COLORIMETRY_EXTENDED:
+		return "Extended";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect)
+{
+	switch (picture_aspect) {
+	case HDMI_PICTURE_ASPECT_NONE:
+		return "No Data";
+	case HDMI_PICTURE_ASPECT_4_3:
+		return "4:3";
+	case HDMI_PICTURE_ASPECT_16_9:
+		return "16:9";
+	case HDMI_PICTURE_ASPECT_RESERVED:
+		return "Reserved";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_active_aspect_get_name(enum hdmi_active_aspect active_aspect)
+{
+	if (active_aspect > 0xf)
+		return "Invalid";
+
+	switch (active_aspect) {
+	case HDMI_ACTIVE_ASPECT_16_9_TOP:
+		return "16:9 Top";
+	case HDMI_ACTIVE_ASPECT_14_9_TOP:
+		return "14:9 Top";
+	case HDMI_ACTIVE_ASPECT_16_9_CENTER:
+		return "16:9 Center";
+	case HDMI_ACTIVE_ASPECT_PICTURE:
+		return "Same as Picture";
+	case HDMI_ACTIVE_ASPECT_4_3:
+		return "4:3";
+	case HDMI_ACTIVE_ASPECT_16_9:
+		return "16:9";
+	case HDMI_ACTIVE_ASPECT_14_9:
+		return "14:9";
+	case HDMI_ACTIVE_ASPECT_4_3_SP_14_9:
+		return "4:3 SP 14:9";
+	case HDMI_ACTIVE_ASPECT_16_9_SP_14_9:
+		return "16:9 SP 14:9";
+	case HDMI_ACTIVE_ASPECT_16_9_SP_4_3:
+		return "16:9 SP 4:3";
+	}
+	return "Reserved";
+}
+
+static const char *
+hdmi_extended_colorimetry_get_name(enum hdmi_extended_colorimetry ext_col)
+{
+	switch (ext_col) {
+	case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601:
+		return "xvYCC 601";
+	case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709:
+		return "xvYCC 709";
+	case HDMI_EXTENDED_COLORIMETRY_S_YCC_601:
+		return "sYCC 601";
+	case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601:
+		return "Adobe YCC 601";
+	case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB:
+		return "Adobe RGB";
+	case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM:
+		return "BT.2020 Constant Luminance";
+	case HDMI_EXTENDED_COLORIMETRY_BT2020:
+		return "BT.2020";
+	case HDMI_EXTENDED_COLORIMETRY_RESERVED:
+		return "Reserved";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_quantization_range_get_name(enum hdmi_quantization_range qrange)
+{
+	switch (qrange) {
+	case HDMI_QUANTIZATION_RANGE_DEFAULT:
+		return "Default";
+	case HDMI_QUANTIZATION_RANGE_LIMITED:
+		return "Limited";
+	case HDMI_QUANTIZATION_RANGE_FULL:
+		return "Full";
+	case HDMI_QUANTIZATION_RANGE_RESERVED:
+		return "Reserved";
+	}
+	return "Invalid";
+}
+
+static const char *hdmi_nups_get_name(enum hdmi_nups nups)
+{
+	switch (nups) {
+	case HDMI_NUPS_UNKNOWN:
+		return "Unknown Non-uniform Scaling";
+	case HDMI_NUPS_HORIZONTAL:
+		return "Horizontally Scaled";
+	case HDMI_NUPS_VERTICAL:
+		return "Vertically Scaled";
+	case HDMI_NUPS_BOTH:
+		return "Horizontally and Vertically Scaled";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_ycc_quantization_range_get_name(enum hdmi_ycc_quantization_range qrange)
+{
+	switch (qrange) {
+	case HDMI_YCC_QUANTIZATION_RANGE_LIMITED:
+		return "Limited";
+	case HDMI_YCC_QUANTIZATION_RANGE_FULL:
+		return "Full";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_content_type_get_name(enum hdmi_content_type content_type)
+{
+	switch (content_type) {
+	case HDMI_CONTENT_TYPE_GRAPHICS:
+		return "Graphics";
+	case HDMI_CONTENT_TYPE_PHOTO:
+		return "Photo";
+	case HDMI_CONTENT_TYPE_CINEMA:
+		return "Cinema";
+	case HDMI_CONTENT_TYPE_GAME:
+		return "Game";
+	}
+	return "Invalid";
+}
+
+/**
+ * hdmi_avi_infoframe_log() - log info of HDMI AVI infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI AVI infoframe
+ */
+static void hdmi_avi_infoframe_log(struct hdmi_avi_infoframe *frame)
+{
+	hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+	hdmi_log("    colorspace: %s\n",
+			hdmi_colorspace_get_name(frame->colorspace));
+	hdmi_log("    scan mode: %s\n",
+			hdmi_scan_mode_get_name(frame->scan_mode));
+	hdmi_log("    colorimetry: %s\n",
+			hdmi_colorimetry_get_name(frame->colorimetry));
+	hdmi_log("    picture aspect: %s\n",
+			hdmi_picture_aspect_get_name(frame->picture_aspect));
+	hdmi_log("    active aspect: %s\n",
+			hdmi_active_aspect_get_name(frame->active_aspect));
+	hdmi_log("    itc: %s\n", frame->itc ? "IT Content" : "No Data");
+	hdmi_log("    extended colorimetry: %s\n",
+			hdmi_extended_colorimetry_get_name(frame->extended_colorimetry));
+	hdmi_log("    quantization range: %s\n",
+			hdmi_quantization_range_get_name(frame->quantization_range));
+	hdmi_log("    nups: %s\n", hdmi_nups_get_name(frame->nups));
+	hdmi_log("    video code: %u\n", frame->video_code);
+	hdmi_log("    ycc quantization range: %s\n",
+			hdmi_ycc_quantization_range_get_name(frame->ycc_quantization_range));
+	hdmi_log("    hdmi content type: %s\n",
+			hdmi_content_type_get_name(frame->content_type));
+	hdmi_log("    pixel repeat: %u\n", frame->pixel_repeat);
+	hdmi_log("    bar top %u, bottom %u, left %u, right %u\n",
+			frame->top_bar, frame->bottom_bar,
+			frame->left_bar, frame->right_bar);
+}
+
+static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi)
+{
+;
+	switch (sdi) {
+	case HDMI_SPD_SDI_UNKNOWN:
+		return "Unknown";
+	case HDMI_SPD_SDI_DSTB:
+		return "Digital STB";
+	case HDMI_SPD_SDI_DVDP:
+		return "DVD Player";
+	case HDMI_SPD_SDI_DVHS:
+		return "D-VHS";
+	case HDMI_SPD_SDI_HDDVR:
+		return "HDD Videorecorder";
+	case HDMI_SPD_SDI_DVC:
+		return "DVC";
+	case HDMI_SPD_SDI_DSC:
+		return "DSC";
+	case HDMI_SPD_SDI_VCD:
+		return "Video CD";
+	case HDMI_SPD_SDI_GAME:
+		return "Game";
+	case HDMI_SPD_SDI_PC:
+		return "PC General";
+	case HDMI_SPD_SDI_BD:
+		return "Blu-Ray Disc (BD)";
+	case HDMI_SPD_SDI_SACD:
+		return "Super Audio CD";
+	case HDMI_SPD_SDI_HDDVD:
+		return "HD DVD";
+	case HDMI_SPD_SDI_PMP:
+		return "PMP";
+	}
+	return "Reserved";
+}
+
+/**
+ * hdmi_spd_infoframe_log() - log info of HDMI SPD infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI SPD infoframe
+ */
+static void hdmi_spd_infoframe_log(struct hdmi_spd_infoframe *frame)
+{
+	uint8_t buf[17];
+
+	hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+	memset(buf, 0, sizeof(buf));
+
+	strncpy(buf, frame->vendor, 8);
+	hdmi_log("    vendor: %s\n", buf);
+	strncpy(buf, frame->product, 16);
+	hdmi_log("    product: %s\n", buf);
+	hdmi_log("    source device information: %s (0x%x)\n",
+		hdmi_spd_sdi_get_name(frame->sdi), frame->sdi);
+}
+
+static const char *
+hdmi_audio_coding_type_get_name(enum hdmi_audio_coding_type coding_type)
+{
+	switch (coding_type) {
+	case HDMI_AUDIO_CODING_TYPE_STREAM:
+		return "Refer to Stream Header";
+	case HDMI_AUDIO_CODING_TYPE_PCM:
+		return "PCM";
+	case HDMI_AUDIO_CODING_TYPE_AC3:
+		return "AC-3";
+	case HDMI_AUDIO_CODING_TYPE_MPEG1:
+		return "MPEG1";
+	case HDMI_AUDIO_CODING_TYPE_MP3:
+		return "MP3";
+	case HDMI_AUDIO_CODING_TYPE_MPEG2:
+		return "MPEG2";
+	case HDMI_AUDIO_CODING_TYPE_AAC_LC:
+		return "AAC";
+	case HDMI_AUDIO_CODING_TYPE_DTS:
+		return "DTS";
+	case HDMI_AUDIO_CODING_TYPE_ATRAC:
+		return "ATRAC";
+	case HDMI_AUDIO_CODING_TYPE_DSD:
+		return "One Bit Audio";
+	case HDMI_AUDIO_CODING_TYPE_EAC3:
+		return "Dolby Digital +";
+	case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+		return "DTS-HD";
+	case HDMI_AUDIO_CODING_TYPE_MLP:
+		return "MAT (MLP)";
+	case HDMI_AUDIO_CODING_TYPE_DST:
+		return "DST";
+	case HDMI_AUDIO_CODING_TYPE_WMA_PRO:
+		return "WMA PRO";
+	case HDMI_AUDIO_CODING_TYPE_CXT:
+		return "Refer to CXT";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_audio_sample_size_get_name(enum hdmi_audio_sample_size sample_size)
+{
+	switch (sample_size) {
+	case HDMI_AUDIO_SAMPLE_SIZE_STREAM:
+		return "Refer to Stream Header";
+	case HDMI_AUDIO_SAMPLE_SIZE_16:
+		return "16 bit";
+	case HDMI_AUDIO_SAMPLE_SIZE_20:
+		return "20 bit";
+	case HDMI_AUDIO_SAMPLE_SIZE_24:
+		return "24 bit";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_audio_sample_frequency_get_name(enum hdmi_audio_sample_frequency freq)
+{
+	switch (freq) {
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM:
+		return "Refer to Stream Header";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
+		return "32 kHz";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
+		return "44.1 kHz (CD)";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
+		return "48 kHz";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
+		return "88.2 kHz";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
+		return "96 kHz";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_176400:
+		return "176.4 kHz";
+	case HDMI_AUDIO_SAMPLE_FREQUENCY_192000:
+		return "192 kHz";
+	}
+	return "Invalid";
+}
+
+static const char *
+hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx)
+{
+
+	switch (ctx) {
+	case HDMI_AUDIO_CODING_TYPE_EXT_CT:
+		return "Refer to CT";
+	case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC:
+		return "HE AAC";
+	case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2:
+		return "HE AAC v2";
+	case HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND:
+		return "MPEG SURROUND";
+	case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC:
+		return "MPEG-4 HE AAC";
+	case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2:
+		return "MPEG-4 HE AAC v2";
+	case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC:
+		return "MPEG-4 AAC LC";
+	case HDMI_AUDIO_CODING_TYPE_EXT_DRA:
+		return "DRA";
+	case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND:
+		return "MPEG-4 HE AAC + MPEG Surround";
+	case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND:
+		return "MPEG-4 AAC LC + MPEG Surround";
+	}
+	return "Reserved";
+}
+
+/**
+ * hdmi_audio_infoframe_log() - log info of HDMI AUDIO infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI AUDIO infoframe
+ */
+static void hdmi_audio_infoframe_log(struct hdmi_audio_infoframe *frame)
+{
+	hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+	if (frame->channels)
+		hdmi_log("    channels: %u\n", frame->channels - 1);
+	else
+		hdmi_log("    channels: Refer to stream header\n");
+	hdmi_log("    coding type: %s\n",
+			hdmi_audio_coding_type_get_name(frame->coding_type));
+	hdmi_log("    sample size: %s\n",
+			hdmi_audio_sample_size_get_name(frame->sample_size));
+	hdmi_log("    sample frequency: %s\n",
+			hdmi_audio_sample_frequency_get_name(frame->sample_frequency));
+	hdmi_log("    coding type ext: %s\n",
+			hdmi_audio_coding_type_ext_get_name(frame->coding_type_ext));
+	hdmi_log("    channel allocation: 0x%x\n",
+			frame->channel_allocation);
+	hdmi_log("    level shift value: %u dB\n",
+			frame->level_shift_value);
+	hdmi_log("    downmix inhibit: %s\n",
+			frame->downmix_inhibit ? "Yes" : "No");
+}
+
+static const char *
+hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct)
+{
+	if (s3d_struct < 0 || s3d_struct > 0xf)
+		return "Invalid";
+
+	switch (s3d_struct) {
+	case HDMI_3D_STRUCTURE_FRAME_PACKING:
+		return "Frame Packing";
+	case HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE:
+		return "Field Alternative";
+	case HDMI_3D_STRUCTURE_LINE_ALTERNATIVE:
+		return "Line Alternative";
+	case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL:
+		return "Side-by-side (Full)";
+	case HDMI_3D_STRUCTURE_L_DEPTH:
+		return "L + Depth";
+	case HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH:
+		return "L + Depth + Graphics + Graphics-depth";
+	case HDMI_3D_STRUCTURE_TOP_AND_BOTTOM:
+		return "Top-and-Bottom";
+	case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF:
+		return "Side-by-side (Half)";
+	default:
+		break;
+	}
+	return "Reserved";
+}
+
+/**
+ * hdmi_vendor_infoframe_log() - log info of HDMI VENDOR infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI VENDOR infoframe
+ */
+static void
+hdmi_vendor_any_infoframe_log(union hdmi_vendor_any_infoframe *frame)
+{
+	struct hdmi_vendor_infoframe *hvf = &frame->hdmi;
+
+	hdmi_infoframe_log_header((struct hdmi_any_infoframe *)frame);
+
+	if (frame->any.oui != HDMI_IEEE_OUI) {
+		hdmi_log("    not a HDMI vendor infoframe\n");
+		return;
+	}
+	if (hvf->vic == 0 && hvf->s3d_struct == HDMI_3D_STRUCTURE_INVALID) {
+		hdmi_log("    empty frame\n");
+		return;
+	}
+
+	if (hvf->vic)
+		hdmi_log("    HDMI VIC: %u\n", hvf->vic);
+	if (hvf->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
+		hdmi_log("    3D structure: %s\n",
+				hdmi_3d_structure_get_name(hvf->s3d_struct));
+		if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+			hdmi_log("    3D extension data: %d\n",
+					hvf->s3d_ext_data);
+	}
+}
+
+/**
+ * hdmi_infoframe_log() - log info of HDMI infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI infoframe
+ */
+void hdmi_infoframe_log(union hdmi_infoframe *frame)
+{
+	switch (frame->any.type) {
+	case HDMI_INFOFRAME_TYPE_AVI:
+		hdmi_avi_infoframe_log(&frame->avi);
+		break;
+	case HDMI_INFOFRAME_TYPE_SPD:
+		hdmi_spd_infoframe_log(&frame->spd);
+		break;
+	case HDMI_INFOFRAME_TYPE_AUDIO:
+		hdmi_audio_infoframe_log(&frame->audio);
+		break;
+	case HDMI_INFOFRAME_TYPE_VENDOR:
+		hdmi_vendor_any_infoframe_log(&frame->vendor);
+		break;
+	}
+}
+EXPORT_SYMBOL(hdmi_infoframe_log);
+
+/**
+ * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe
+ * @buffer: source buffer
+ * @frame: HDMI AVI infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Auxiliary Video (AVI) information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame,
+				     void *buffer)
+{
+	uint8_t *ptr = buffer;
+	int ret;
+
+	if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI ||
+	    ptr[1] != 2 ||
+	    ptr[2] != HDMI_AVI_INFOFRAME_SIZE)
+		return -EINVAL;
+
+	if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0)
+		return -EINVAL;
+
+	ret = hdmi_avi_infoframe_init(frame);
+	if (ret)
+		return ret;
+
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	frame->colorspace = (ptr[0] >> 5) & 0x3;
+	if (ptr[0] & 0x10)
+		frame->active_aspect = ptr[1] & 0xf;
+	if (ptr[0] & 0x8) {
+		frame->top_bar = (ptr[5] << 8) + ptr[6];
+		frame->bottom_bar = (ptr[7] << 8) + ptr[8];
+	}
+	if (ptr[0] & 0x4) {
+		frame->left_bar = (ptr[9] << 8) + ptr[10];
+		frame->right_bar = (ptr[11] << 8) + ptr[12];
+	}
+	frame->scan_mode = ptr[0] & 0x3;
+
+	frame->colorimetry = (ptr[1] >> 6) & 0x3;
+	frame->picture_aspect = (ptr[1] >> 4) & 0x3;
+	frame->active_aspect = ptr[1] & 0xf;
+
+	frame->itc = ptr[2] & 0x80 ? true : false;
+	frame->extended_colorimetry = (ptr[2] >> 4) & 0x7;
+	frame->quantization_range = (ptr[2] >> 2) & 0x3;
+	frame->nups = ptr[2] & 0x3;
+
+	frame->video_code = ptr[3] & 0x7f;
+	frame->ycc_quantization_range = (ptr[4] >> 6) & 0x3;
+	frame->content_type = (ptr[4] >> 4) & 0x3;
+
+	frame->pixel_repeat = ptr[4] & 0xf;
+
+	return 0;
+}
+
+/**
+ * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe
+ * @buffer: source buffer
+ * @frame: HDMI SPD infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Source Product Description (SPD) information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame,
+				     void *buffer)
+{
+	uint8_t *ptr = buffer;
+	int ret;
+
+	if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD ||
+	    ptr[1] != 1 ||
+	    ptr[2] != HDMI_SPD_INFOFRAME_SIZE) {
+		return -EINVAL;
+	}
+
+	if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(SPD)) != 0)
+		return -EINVAL;
+
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	ret = hdmi_spd_infoframe_init(frame, ptr, ptr + 8);
+	if (ret)
+		return ret;
+
+	frame->sdi = ptr[24];
+
+	return 0;
+}
+
+/**
+ * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe
+ * @buffer: source buffer
+ * @frame: HDMI Audio infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Audio information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame,
+				       void *buffer)
+{
+	uint8_t *ptr = buffer;
+	int ret;
+
+	if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO ||
+	    ptr[1] != 1 ||
+	    ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) {
+		return -EINVAL;
+	}
+
+	if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AUDIO)) != 0)
+		return -EINVAL;
+
+	ret = hdmi_audio_infoframe_init(frame);
+	if (ret)
+		return ret;
+
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	frame->channels = ptr[0] & 0x7;
+	frame->coding_type = (ptr[0] >> 4) & 0xf;
+	frame->sample_size = ptr[1] & 0x3;
+	frame->sample_frequency = (ptr[1] >> 2) & 0x7;
+	frame->coding_type_ext = ptr[2] & 0x1f;
+	frame->channel_allocation = ptr[3];
+	frame->level_shift_value = (ptr[4] >> 3) & 0xf;
+	frame->downmix_inhibit = ptr[4] & 0x80 ? true : false;
+
+	return 0;
+}
+
+/**
+ * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe
+ * @buffer: source buffer
+ * @frame: HDMI Vendor infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI Vendor information frame.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame,
+				 void *buffer)
+{
+	uint8_t *ptr = buffer;
+	size_t length;
+	int ret;
+	uint8_t hdmi_video_format;
+	struct hdmi_vendor_infoframe *hvf = &frame->hdmi;
+
+	if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR ||
+	    ptr[1] != 1 ||
+	    (ptr[2] != 5 && ptr[2] != 6))
+		return -EINVAL;
+
+	length = ptr[2];
+
+	if (hdmi_infoframe_checksum(buffer,
+				    HDMI_INFOFRAME_HEADER_SIZE + length) != 0)
+		return -EINVAL;
+
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	/* HDMI OUI */
+	if ((ptr[0] != 0x03) ||
+	    (ptr[1] != 0x0c) ||
+	    (ptr[2] != 0x00))
+		return -EINVAL;
+
+	hdmi_video_format = ptr[3] >> 5;
+
+	if (hdmi_video_format > 0x2)
+		return -EINVAL;
+
+	ret = hdmi_vendor_infoframe_init(hvf);
+	if (ret)
+		return ret;
+
+	hvf->length = length;
+
+	if (hdmi_video_format == 0x1) {
+		hvf->vic = ptr[4];
+	} else if (hdmi_video_format == 0x2) {
+		hvf->s3d_struct = ptr[4] >> 4;
+		if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
+			if (length == 6)
+				hvf->s3d_ext_data = ptr[5] >> 4;
+			else
+				return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe
+ * @buffer: source buffer
+ * @frame: HDMI infoframe
+ *
+ * Unpacks the information contained in binary buffer @buffer into a structured
+ * @frame of a HDMI infoframe.
+ * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4
+ * specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer)
+{
+	int ret;
+	uint8_t *ptr = buffer;
+
+	switch (ptr[0]) {
+	case HDMI_INFOFRAME_TYPE_AVI:
+		ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer);
+		break;
+	case HDMI_INFOFRAME_TYPE_SPD:
+		ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer);
+		break;
+	case HDMI_INFOFRAME_TYPE_AUDIO:
+		ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer);
+		break;
+	case HDMI_INFOFRAME_TYPE_VENDOR:
+		ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(hdmi_infoframe_unpack);


Property changes on: trunk/sys/arm/nvidia/drm2/hdmi.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/hdmi.h
===================================================================
--- trunk/sys/arm/nvidia/drm2/hdmi.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/hdmi.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,336 @@
+/* $MidnightBSD$ */
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/drm2/hdmi.h 310600 2016-12-26 14:36:05Z mmel $
+ */
+
+#ifndef _HDMI_H_
+#define _HDMI_H_
+
+
+enum hdmi_infoframe_type {
+	HDMI_INFOFRAME_TYPE_VENDOR = 0x81,
+	HDMI_INFOFRAME_TYPE_AVI = 0x82,
+	HDMI_INFOFRAME_TYPE_SPD = 0x83,
+	HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
+};
+
+#define HDMI_IEEE_OUI 0x000c03
+#define HDMI_INFOFRAME_HEADER_SIZE  4
+#define HDMI_AVI_INFOFRAME_SIZE    13
+#define HDMI_SPD_INFOFRAME_SIZE    25
+#define HDMI_AUDIO_INFOFRAME_SIZE  10
+
+#define HDMI_INFOFRAME_SIZE(type)	\
+	(HDMI_INFOFRAME_HEADER_SIZE + HDMI_ ## type ## _INFOFRAME_SIZE)
+
+struct hdmi_any_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+};
+
+enum hdmi_colorspace {
+	HDMI_COLORSPACE_RGB,
+	HDMI_COLORSPACE_YUV422,
+	HDMI_COLORSPACE_YUV444,
+	HDMI_COLORSPACE_YUV420,
+	HDMI_COLORSPACE_RESERVED4,
+	HDMI_COLORSPACE_RESERVED5,
+	HDMI_COLORSPACE_RESERVED6,
+	HDMI_COLORSPACE_IDO_DEFINED,
+};
+
+enum hdmi_scan_mode {
+	HDMI_SCAN_MODE_NONE,
+	HDMI_SCAN_MODE_OVERSCAN,
+	HDMI_SCAN_MODE_UNDERSCAN,
+	HDMI_SCAN_MODE_RESERVED,
+};
+
+enum hdmi_colorimetry {
+	HDMI_COLORIMETRY_NONE,
+	HDMI_COLORIMETRY_ITU_601,
+	HDMI_COLORIMETRY_ITU_709,
+	HDMI_COLORIMETRY_EXTENDED,
+};
+
+enum hdmi_picture_aspect {
+	HDMI_PICTURE_ASPECT_NONE,
+	HDMI_PICTURE_ASPECT_4_3,
+	HDMI_PICTURE_ASPECT_16_9,
+	HDMI_PICTURE_ASPECT_RESERVED,
+};
+
+enum hdmi_active_aspect {
+	HDMI_ACTIVE_ASPECT_16_9_TOP = 2,
+	HDMI_ACTIVE_ASPECT_14_9_TOP = 3,
+	HDMI_ACTIVE_ASPECT_16_9_CENTER = 4,
+	HDMI_ACTIVE_ASPECT_PICTURE = 8,
+	HDMI_ACTIVE_ASPECT_4_3 = 9,
+	HDMI_ACTIVE_ASPECT_16_9 = 10,
+	HDMI_ACTIVE_ASPECT_14_9 = 11,
+	HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13,
+	HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14,
+	HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15,
+};
+
+enum hdmi_extended_colorimetry {
+	HDMI_EXTENDED_COLORIMETRY_XV_YCC_601,
+	HDMI_EXTENDED_COLORIMETRY_XV_YCC_709,
+	HDMI_EXTENDED_COLORIMETRY_S_YCC_601,
+	HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601,
+	HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB,
+
+	/* The following EC values are only defined in CEA-861-F. */
+	HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM,
+	HDMI_EXTENDED_COLORIMETRY_BT2020,
+	HDMI_EXTENDED_COLORIMETRY_RESERVED,
+};
+
+enum hdmi_quantization_range {
+	HDMI_QUANTIZATION_RANGE_DEFAULT,
+	HDMI_QUANTIZATION_RANGE_LIMITED,
+	HDMI_QUANTIZATION_RANGE_FULL,
+	HDMI_QUANTIZATION_RANGE_RESERVED,
+};
+
+/* non-uniform picture scaling */
+enum hdmi_nups {
+	HDMI_NUPS_UNKNOWN,
+	HDMI_NUPS_HORIZONTAL,
+	HDMI_NUPS_VERTICAL,
+	HDMI_NUPS_BOTH,
+};
+
+enum hdmi_ycc_quantization_range {
+	HDMI_YCC_QUANTIZATION_RANGE_LIMITED,
+	HDMI_YCC_QUANTIZATION_RANGE_FULL,
+};
+
+enum hdmi_content_type {
+	HDMI_CONTENT_TYPE_GRAPHICS,
+	HDMI_CONTENT_TYPE_PHOTO,
+	HDMI_CONTENT_TYPE_CINEMA,
+	HDMI_CONTENT_TYPE_GAME,
+};
+
+struct hdmi_avi_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	enum hdmi_colorspace colorspace;
+	enum hdmi_scan_mode scan_mode;
+	enum hdmi_colorimetry colorimetry;
+	enum hdmi_picture_aspect picture_aspect;
+	enum hdmi_active_aspect active_aspect;
+	bool itc;
+	enum hdmi_extended_colorimetry extended_colorimetry;
+	enum hdmi_quantization_range quantization_range;
+	enum hdmi_nups nups;
+	unsigned char video_code;
+	enum hdmi_ycc_quantization_range ycc_quantization_range;
+	enum hdmi_content_type content_type;
+	unsigned char pixel_repeat;
+	unsigned short top_bar;
+	unsigned short bottom_bar;
+	unsigned short left_bar;
+	unsigned short right_bar;
+};
+
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
+ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
+				size_t size);
+
+enum hdmi_spd_sdi {
+	HDMI_SPD_SDI_UNKNOWN,
+	HDMI_SPD_SDI_DSTB,
+	HDMI_SPD_SDI_DVDP,
+	HDMI_SPD_SDI_DVHS,
+	HDMI_SPD_SDI_HDDVR,
+	HDMI_SPD_SDI_DVC,
+	HDMI_SPD_SDI_DSC,
+	HDMI_SPD_SDI_VCD,
+	HDMI_SPD_SDI_GAME,
+	HDMI_SPD_SDI_PC,
+	HDMI_SPD_SDI_BD,
+	HDMI_SPD_SDI_SACD,
+	HDMI_SPD_SDI_HDDVD,
+	HDMI_SPD_SDI_PMP,
+};
+
+struct hdmi_spd_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	char vendor[8];
+	char product[16];
+	enum hdmi_spd_sdi sdi;
+};
+
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
+			    const char *vendor, const char *product);
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
+				size_t size);
+
+enum hdmi_audio_coding_type {
+	HDMI_AUDIO_CODING_TYPE_STREAM,
+	HDMI_AUDIO_CODING_TYPE_PCM,
+	HDMI_AUDIO_CODING_TYPE_AC3,
+	HDMI_AUDIO_CODING_TYPE_MPEG1,
+	HDMI_AUDIO_CODING_TYPE_MP3,
+	HDMI_AUDIO_CODING_TYPE_MPEG2,
+	HDMI_AUDIO_CODING_TYPE_AAC_LC,
+	HDMI_AUDIO_CODING_TYPE_DTS,
+	HDMI_AUDIO_CODING_TYPE_ATRAC,
+	HDMI_AUDIO_CODING_TYPE_DSD,
+	HDMI_AUDIO_CODING_TYPE_EAC3,
+	HDMI_AUDIO_CODING_TYPE_DTS_HD,
+	HDMI_AUDIO_CODING_TYPE_MLP,
+	HDMI_AUDIO_CODING_TYPE_DST,
+	HDMI_AUDIO_CODING_TYPE_WMA_PRO,
+	HDMI_AUDIO_CODING_TYPE_CXT,
+};
+
+enum hdmi_audio_sample_size {
+	HDMI_AUDIO_SAMPLE_SIZE_STREAM,
+	HDMI_AUDIO_SAMPLE_SIZE_16,
+	HDMI_AUDIO_SAMPLE_SIZE_20,
+	HDMI_AUDIO_SAMPLE_SIZE_24,
+};
+
+enum hdmi_audio_sample_frequency {
+	HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_32000,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_44100,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_48000,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_88200,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_96000,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_176400,
+	HDMI_AUDIO_SAMPLE_FREQUENCY_192000,
+};
+
+enum hdmi_audio_coding_type_ext {
+	/* Refer to Audio Coding Type (CT) field in Data Byte 1 */
+	HDMI_AUDIO_CODING_TYPE_EXT_CT,
+
+	/*
+	 * The next three CXT values are defined in CEA-861-E only.
+	 * They do not exist in older versions, and in CEA-861-F they are
+	 * defined as 'Not in use'.
+	 */
+	HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC,
+	HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND,
+
+	/* The following CXT values are only defined in CEA-861-F. */
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC,
+	HDMI_AUDIO_CODING_TYPE_EXT_DRA,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND,
+	HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND = 10,
+};
+
+struct hdmi_audio_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	unsigned char channels;
+	enum hdmi_audio_coding_type coding_type;
+	enum hdmi_audio_sample_size sample_size;
+	enum hdmi_audio_sample_frequency sample_frequency;
+	enum hdmi_audio_coding_type_ext coding_type_ext;
+	unsigned char channel_allocation;
+	unsigned char level_shift_value;
+	bool downmix_inhibit;
+
+};
+
+int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame);
+ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
+				  void *buffer, size_t size);
+
+enum hdmi_3d_structure {
+	HDMI_3D_STRUCTURE_INVALID = -1,
+	HDMI_3D_STRUCTURE_FRAME_PACKING = 0,
+	HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE,
+	HDMI_3D_STRUCTURE_LINE_ALTERNATIVE,
+	HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL,
+	HDMI_3D_STRUCTURE_L_DEPTH,
+	HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH,
+	HDMI_3D_STRUCTURE_TOP_AND_BOTTOM,
+	HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8,
+};
+
+
+struct hdmi_vendor_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	unsigned int oui;
+	uint8_t vic;
+	enum hdmi_3d_structure s3d_struct;
+	unsigned int s3d_ext_data;
+};
+
+int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame);
+ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
+				   void *buffer, size_t size);
+
+union hdmi_vendor_any_infoframe {
+	struct {
+		enum hdmi_infoframe_type type;
+		unsigned char version;
+		unsigned char length;
+		unsigned int oui;
+	} any;
+	struct hdmi_vendor_infoframe hdmi;
+};
+
+/**
+ * union hdmi_infoframe - overall union of all abstract infoframe representations
+ * @any: generic infoframe
+ * @avi: avi infoframe
+ * @spd: spd infoframe
+ * @vendor: union of all vendor infoframes
+ * @audio: audio infoframe
+ *
+ * This is used by the generic pack function. This works since all infoframes
+ * have the same header which also indicates which type of infoframe should be
+ * packed.
+ */
+union hdmi_infoframe {
+	struct hdmi_any_infoframe any;
+	struct hdmi_avi_infoframe avi;
+	struct hdmi_spd_infoframe spd;
+	union hdmi_vendor_any_infoframe vendor;
+	struct hdmi_audio_infoframe audio;
+};
+
+ssize_t
+hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size);
+int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer);
+void hdmi_infoframe_log(union hdmi_infoframe *frame);
+
+#endif /* _HDMI_H */


Property changes on: trunk/sys/arm/nvidia/drm2/hdmi.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_bo.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_bo.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_bo.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,370 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_bo.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+#include <sys/vmem.h>
+#include <sys/vmem.h>
+#include <vm/vm.h>
+#include <vm/vm_pageout.h>
+
+static void
+tegra_bo_destruct(struct tegra_bo *bo)
+{
+	vm_page_t m;
+	size_t size;
+	int i;
+
+	if (bo->cdev_pager == NULL)
+		return;
+
+	size = round_page(bo->gem_obj.size);
+	if (bo->vbase != 0)
+		pmap_qremove(bo->vbase, bo->npages);
+
+	VM_OBJECT_WLOCK(bo->cdev_pager);
+	for (i = 0; i < bo->npages; i++) {
+		m = bo->m[i];
+		cdev_pager_free_page(bo->cdev_pager, m);
+		vm_page_lock(m);
+		m->flags &= ~PG_FICTITIOUS;
+		vm_page_unwire(m, PQ_NONE);
+		vm_page_free(m);
+		vm_page_unlock(m);
+	}
+	VM_OBJECT_WUNLOCK(bo->cdev_pager);
+
+	vm_object_deallocate(bo->cdev_pager);
+	if (bo->vbase != 0)
+		vmem_free(kmem_arena, bo->vbase, size);
+}
+
+static void
+tegra_bo_free_object(struct drm_gem_object *gem_obj)
+{
+	struct tegra_bo *bo;
+
+	bo = container_of(gem_obj, struct tegra_bo, gem_obj);
+	drm_gem_free_mmap_offset(gem_obj);
+	drm_gem_object_release(gem_obj);
+
+	tegra_bo_destruct(bo);
+
+	free(bo->m, DRM_MEM_DRIVER);
+	free(bo, DRM_MEM_DRIVER);
+}
+
+static int
+tegra_bo_alloc_contig(size_t npages, u_long alignment, vm_memattr_t memattr,
+    vm_page_t **ret_page)
+{
+	vm_page_t m;
+	int pflags, tries, i;
+	vm_paddr_t low, high, boundary;
+
+	low = 0;
+	high = -1UL;
+	boundary = 0;
+	pflags = VM_ALLOC_NORMAL  | VM_ALLOC_NOOBJ | VM_ALLOC_NOBUSY |
+	    VM_ALLOC_WIRED | VM_ALLOC_ZERO;
+	tries = 0;
+retry:
+	m = vm_page_alloc_contig(NULL, 0, pflags, npages, low, high, alignment,
+	    boundary, memattr);
+	if (m == NULL) {
+		if (tries < 3) {
+			if (!vm_page_reclaim_contig(pflags, npages, low, high,
+			    alignment, boundary))
+				VM_WAIT;
+			tries++;
+			goto retry;
+		}
+		return (ENOMEM);
+	}
+
+	for (i = 0; i < npages; i++, m++) {
+		if ((m->flags & PG_ZERO) == 0)
+			pmap_zero_page(m);
+		m->valid = VM_PAGE_BITS_ALL;
+		(*ret_page)[i] = m;
+	}
+
+	return (0);
+}
+
+/* Initialize pager and insert all object pages to it*/
+static int
+tegra_bo_init_pager(struct tegra_bo *bo)
+{
+	vm_page_t m;
+	size_t size;
+	int i;
+
+	size = round_page(bo->gem_obj.size);
+
+	bo->pbase = VM_PAGE_TO_PHYS(bo->m[0]);
+	if (vmem_alloc(kmem_arena, size, M_WAITOK | M_BESTFIT, &bo->vbase))
+		return (ENOMEM);
+
+	VM_OBJECT_WLOCK(bo->cdev_pager);
+	for (i = 0; i < bo->npages; i++) {
+		m = bo->m[i];
+		/*
+		 * XXX This is a temporary hack.
+		 * We need pager suitable for paging (mmap) managed
+		 * real (non-fictitious) pages.
+		 * - managed pages are needed for clean module unload.
+		 * - aliasing fictitious page to real one is bad,
+		 *   pmap cannot handle this situation without issues
+		 *   It expects that
+		 *    paddr = PHYS_TO_VM_PAGE(VM_PAGE_TO_PHYS(paddr))
+		 *   for every single page passed to pmap.
+		 */
+		m->oflags &= ~VPO_UNMANAGED;
+		m->flags |= PG_FICTITIOUS;
+		if (vm_page_insert(m, bo->cdev_pager, i) != 0)
+			return (EINVAL);
+	}
+	VM_OBJECT_WUNLOCK(bo->cdev_pager);
+
+	pmap_qenter(bo->vbase, bo->m, bo->npages);
+	return (0);
+}
+
+/* Allocate memory for frame buffer */
+static int
+tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo)
+{
+	size_t size;
+	int rv;
+
+	size = bo->gem_obj.size;
+
+	bo->npages = atop(size);
+	bo->m = malloc(sizeof(vm_page_t *) * bo->npages, DRM_MEM_DRIVER,
+	    M_WAITOK | M_ZERO);
+
+	rv = tegra_bo_alloc_contig(bo->npages, PAGE_SIZE,
+	    VM_MEMATTR_WRITE_COMBINING, &(bo->m));
+	if (rv != 0) {
+		DRM_WARNING("Cannot allocate memory for gem object.\n");
+		return (rv);
+	}
+	rv = tegra_bo_init_pager(bo);
+	if (rv != 0) {
+		DRM_WARNING("Cannot initialize gem object pager.\n");
+		return (rv);
+	}
+	return (0);
+}
+
+int
+tegra_bo_create(struct drm_device *drm, size_t size, struct tegra_bo **res_bo)
+{
+	struct tegra_bo *bo;
+	int rv;
+
+	if (size <= 0)
+		return (-EINVAL);
+
+	bo = malloc(sizeof(*bo), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
+
+	size = round_page(size);
+	rv = drm_gem_object_init(drm, &bo->gem_obj, size);
+	if (rv != 0) {
+		free(bo, DRM_MEM_DRIVER);
+		return (rv);
+	}
+	rv = drm_gem_create_mmap_offset(&bo->gem_obj);
+	if (rv != 0) {
+		drm_gem_object_release(&bo->gem_obj);
+		free(bo, DRM_MEM_DRIVER);
+		return (rv);
+	}
+
+	bo->cdev_pager = cdev_pager_allocate(&bo->gem_obj, OBJT_MGTDEVICE,
+	    drm->driver->gem_pager_ops, size, 0, 0, NULL);
+	rv = tegra_bo_alloc(drm, bo);
+	if (rv != 0) {
+		tegra_bo_free_object(&bo->gem_obj);
+		return (rv);
+	}
+
+	*res_bo = bo;
+	return (0);
+}
+
+
+
+static int
+tegra_bo_create_with_handle(struct drm_file *file, struct drm_device *drm,
+    size_t size, uint32_t *handle, struct tegra_bo **res_bo)
+{
+	int rv;
+	struct tegra_bo *bo;
+
+	rv = tegra_bo_create(drm, size, &bo);
+	if (rv != 0)
+		return (rv);
+
+	rv = drm_gem_handle_create(file, &bo->gem_obj, handle);
+	if (rv != 0) {
+		tegra_bo_free_object(&bo->gem_obj);
+		drm_gem_object_release(&bo->gem_obj);
+		return (rv);
+	}
+
+	drm_gem_object_unreference_unlocked(&bo->gem_obj);
+
+	*res_bo = bo;
+	return (0);
+}
+
+static int
+tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm_dev,
+    struct drm_mode_create_dumb *args)
+{
+	struct tegra_drm *drm;
+	struct tegra_bo *bo;
+	int rv;
+
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+
+	args->pitch= (args->width * args->bpp + 7) / 8;
+	args->pitch = roundup(args->pitch, drm->pitch_align);
+	args->size = args->pitch * args->height;
+	rv = tegra_bo_create_with_handle(file, drm_dev, args->size,
+	    &args->handle, &bo);
+
+	return (rv);
+}
+
+static int
+tegra_bo_dumb_map_offset(struct drm_file *file_priv,
+    struct drm_device *drm_dev, uint32_t handle, uint64_t *offset)
+{
+	struct drm_gem_object *gem_obj;
+	int rv;
+
+	DRM_LOCK(drm_dev);
+	gem_obj = drm_gem_object_lookup(drm_dev, file_priv, handle);
+	if (gem_obj == NULL) {
+		device_printf(drm_dev->dev, "Object not found\n");
+		DRM_UNLOCK(drm_dev);
+		return (-EINVAL);
+	}
+	rv = drm_gem_create_mmap_offset(gem_obj);
+	if (rv != 0)
+		goto fail;
+
+	*offset = DRM_GEM_MAPPING_OFF(gem_obj->map_list.key) |
+	    DRM_GEM_MAPPING_KEY;
+
+	drm_gem_object_unreference(gem_obj);
+	DRM_UNLOCK(drm_dev);
+	return (0);
+
+fail:
+	drm_gem_object_unreference(gem_obj);
+	DRM_UNLOCK(drm_dev);
+	return (rv);
+}
+
+static int
+tegra_bo_dumb_destroy(struct drm_file *file_priv, struct drm_device *drm_dev,
+    unsigned int handle)
+{
+	int rv;
+
+	rv = drm_gem_handle_delete(file_priv, handle);
+	return (rv);
+}
+
+/*
+ * mmap support
+ */
+static int
+tegra_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot,
+    vm_page_t *mres)
+{
+
+#ifdef DRM_PAGER_DEBUG
+	DRM_DEBUG("object %p offset %jd prot %d mres %p\n",
+	    vm_obj, (intmax_t)offset, prot, mres);
+#endif
+	return (VM_PAGER_FAIL);
+
+}
+
+static int
+tegra_gem_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
+    vm_ooffset_t foff, struct ucred *cred, u_short *color)
+{
+
+	if (color != NULL)
+		*color = 0;
+	return (0);
+}
+
+static void
+tegra_gem_pager_dtor(void *handle)
+{
+
+}
+
+static struct cdev_pager_ops tegra_gem_pager_ops = {
+	.cdev_pg_fault = tegra_gem_pager_fault,
+	.cdev_pg_ctor  = tegra_gem_pager_ctor,
+	.cdev_pg_dtor  = tegra_gem_pager_dtor
+};
+
+/* Fill up relevant fields in drm_driver ops */
+void
+tegra_bo_driver_register(struct drm_driver *drm_drv)
+{
+	drm_drv->gem_free_object = tegra_bo_free_object;
+	drm_drv->gem_pager_ops = &tegra_gem_pager_ops;
+	drm_drv->dumb_create = tegra_bo_dumb_create;
+	drm_drv->dumb_map_offset = tegra_bo_dumb_map_offset;
+	drm_drv->dumb_destroy = tegra_bo_dumb_destroy;
+}


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_bo.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_dc.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_dc.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_dc.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1448 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_dc.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/drm2/drm_fixed.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_dc_reg.h>
+#include <arm/nvidia/drm2/tegra_drm.h>
+#include <arm/nvidia/tegra_pmc.h>
+
+#include "tegra_drm_if.h"
+#include "tegra_dc_if.h"
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, 4 * (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, 4 * (_r))
+
+#define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	SLEEP(_sc, timeout)						\
+	mtx_sleep(sc, &sc->mtx, 0, "tegra_dc_wait", timeout);
+#define	LOCK_INIT(_sc)							\
+	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_dc", MTX_DEF)
+#define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
+#define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
+#define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+
+#define	SYNCPT_VBLANK0 26
+#define	SYNCPT_VBLANK1 27
+
+#define	DC_MAX_PLANES 2		/* Maximum planes */
+
+/* DRM Formats supported by DC */
+/* XXXX expand me */
+static uint32_t dc_plane_formats[] = {
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YUV422,
+};
+
+
+/* Complete description of one window (plane) */
+struct dc_window {
+	/* Source (in framebuffer) rectangle, in pixels */
+	u_int			src_x;
+	u_int			src_y;
+	u_int			src_w;
+	u_int			src_h;
+
+	/* Destination (on display) rectangle, in pixels */
+	u_int			dst_x;
+	u_int			dst_y;
+	u_int			dst_w;
+	u_int			dst_h;
+
+	/* Parsed pixel format */
+	u_int			bits_per_pixel;
+	bool			is_yuv;		/* any YUV mode */
+	bool			is_yuv_planar;	/* planar YUV mode */
+	uint32_t 		color_mode;	/* DC_WIN_COLOR_DEPTH */
+	uint32_t		swap;		/* DC_WIN_BYTE_SWAP */
+	uint32_t		surface_kind;	/* DC_WINBUF_SURFACE_KIND */
+	uint32_t		block_height;	/* DC_WINBUF_SURFACE_KIND */
+
+	/* Parsed flipping, rotation is not supported for pitched modes */
+	bool			flip_x;		/* inverted X-axis */
+	bool			flip_y;		/* inverted Y-axis */
+	bool			transpose_xy;	/* swap X and Y-axis */
+
+	/* Color planes base addresses and strides */
+	bus_size_t		base[3];
+	uint32_t		stride[3];	/* stride[2] isn't used by HW */
+};
+
+struct dc_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+	struct resource		*irq_res;
+	void			*irq_ih;
+	struct mtx		mtx;
+
+	clk_t			clk_parent;
+	clk_t			clk_dc;
+	hwreset_t		hwreset_dc;
+
+	int			pitch_align;
+
+	struct tegra_crtc 	tegra_crtc;
+	struct drm_pending_vblank_event *event;
+	struct drm_gem_object 	*cursor_gem;
+};
+
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-dc",	1},
+	{NULL,			0},
+};
+
+/* Convert standard drm pixel format to tegra windows parameters. */
+static int
+dc_parse_drm_format(struct tegra_fb *fb, struct dc_window *win)
+{
+	struct tegra_bo *bo;
+	uint32_t cm;
+	uint32_t sw;
+	bool is_yuv, is_yuv_planar;
+	int nplanes, i;
+
+	switch (fb->drm_fb.pixel_format) {
+	case DRM_FORMAT_XBGR8888:
+		sw = BYTE_SWAP(NOSWAP);
+		cm = WIN_COLOR_DEPTH_R8G8B8A8;
+		is_yuv = false;
+		is_yuv_planar = false;
+		break;
+
+	case DRM_FORMAT_XRGB8888:
+		sw = BYTE_SWAP(NOSWAP);
+		cm = WIN_COLOR_DEPTH_B8G8R8A8;
+		is_yuv = false;
+		is_yuv_planar = false;
+		break;
+
+	case DRM_FORMAT_RGB565:
+		sw = BYTE_SWAP(NOSWAP);
+		cm = WIN_COLOR_DEPTH_B5G6R5;
+		is_yuv = false;
+		is_yuv_planar = false;
+		break;
+
+	case DRM_FORMAT_UYVY:
+		sw = BYTE_SWAP(NOSWAP);
+		cm = WIN_COLOR_DEPTH_YCbCr422;
+		is_yuv = true;
+		is_yuv_planar = false;
+		break;
+
+	case DRM_FORMAT_YUYV:
+		sw = BYTE_SWAP(SWAP2);
+		cm = WIN_COLOR_DEPTH_YCbCr422;
+		is_yuv = true;
+		is_yuv_planar = false;
+		break;
+
+	case DRM_FORMAT_YUV420:
+		sw = BYTE_SWAP(NOSWAP);
+		cm = WIN_COLOR_DEPTH_YCbCr420P;
+		is_yuv = true;
+		is_yuv_planar = true;
+		break;
+
+	case DRM_FORMAT_YUV422:
+		sw = BYTE_SWAP(NOSWAP);
+		cm = WIN_COLOR_DEPTH_YCbCr422P;
+		is_yuv = true;
+		is_yuv_planar = true;
+		break;
+
+	default:
+		/* Unsupported format */
+		return (-EINVAL);
+	}
+
+	/* Basic check of arguments. */
+	switch (fb->rotation) {
+	case 0:
+	case 180:
+		break;
+
+	case 90: 		/* Rotation is supported only */
+	case 270:		/*  for block linear surfaces */
+		if (!fb->block_linear)
+			return (-EINVAL);
+		break;
+
+	default:
+		return (-EINVAL);
+	}
+	/* XXX Add more checks (sizes, scaling...) */
+
+	if (win == NULL)
+		return (0);
+
+	win->surface_kind =
+	    fb->block_linear ? SURFACE_KIND_BL_16B2: SURFACE_KIND_PITCH;
+	win->block_height = fb->block_height;
+	switch (fb->rotation) {
+	case 0:					/* (0,0,0) */
+		win->transpose_xy = false;
+		win->flip_x = false;
+		win->flip_y = false;
+		break;
+
+	case 90:				/* (1,0,1) */
+		win->transpose_xy = true;
+		win->flip_x = false;
+		win->flip_y = true;
+		break;
+
+	case 180:				/* (0,1,1) */
+		win->transpose_xy = false;
+		win->flip_x = true;
+		win->flip_y = true;
+		break;
+
+	case 270:				/* (1,1,0) */
+		win->transpose_xy = true;
+		win->flip_x = true;
+		win->flip_y = false;
+		break;
+	}
+	win->flip_x ^= fb->flip_x;
+	win->flip_y ^= fb->flip_y;
+
+	win->color_mode = cm;
+	win->swap = sw;
+	win->bits_per_pixel = fb->drm_fb.bits_per_pixel;
+	win->is_yuv = is_yuv;
+	win->is_yuv_planar = is_yuv_planar;
+
+	nplanes = drm_format_num_planes(fb->drm_fb.pixel_format);
+	for (i = 0; i < nplanes; i++) {
+		bo = fb->planes[i];
+		win->base[i] = bo->pbase + fb->drm_fb.offsets[i];
+		win->stride[i] = fb->drm_fb.pitches[i];
+	}
+	return (0);
+}
+
+/*
+ * Scaling functions.
+ *
+ * It's unclear if we want/must program the fractional portion
+ * (aka bias) of init_dda registers, mainly when mirrored axis
+ * modes are used.
+ * For now, we use 1.0 as recommended by TRM.
+ */
+static inline uint32_t
+dc_scaling_init(uint32_t start)
+{
+
+	return (1 << 12);
+}
+
+static inline uint32_t
+dc_scaling_incr(uint32_t src, uint32_t dst, uint32_t maxscale)
+{
+	uint32_t val;
+
+	val = (src - 1) << 12 ; /* 4.12 fixed float */
+	val /= (dst - 1);
+	if (val  > (maxscale << 12))
+		val = maxscale << 12;
+	return val;
+}
+
+/* -------------------------------------------------------------------
+ *
+ *    HW Access.
+ *
+ */
+
+/*
+ * Setup pixel clock.
+ * Minimal frequency is pixel clock, but output is free to select
+ * any higher.
+ */
+static int
+dc_setup_clk(struct dc_softc *sc, struct drm_crtc *crtc,
+    struct drm_display_mode *mode, uint32_t *div)
+{
+	uint64_t pclk, freq;
+	struct tegra_drm_encoder *output;
+	struct drm_encoder *encoder;
+	long rv;
+
+	pclk = mode->clock * 1000;
+
+	/* Find attached encoder */
+	output = NULL;
+	list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list,
+	    head) {
+		if (encoder->crtc == crtc) {
+			output = container_of(encoder, struct tegra_drm_encoder,
+			    encoder);
+			break;
+		}
+	}
+	if (output == NULL)
+		return (-ENODEV);
+
+	if (output->setup_clock == NULL)
+		panic("Output have not setup_clock function.\n");
+	rv = output->setup_clock(output, sc->clk_dc, pclk);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot setup pixel clock: %llu\n",
+		    pclk);
+		return (rv);
+	}
+
+	rv = clk_get_freq(sc->clk_dc, &freq);
+	*div = (freq * 2 / pclk) - 2;
+
+	DRM_DEBUG_KMS("frequency: %llu, DC divider: %u\n", freq, *div);
+
+	return 0;
+}
+
+static void
+dc_setup_window(struct dc_softc *sc, unsigned int index, struct dc_window *win)
+{
+	uint32_t h_offset, v_offset, h_size, v_size, bpp;
+	uint32_t h_init_dda, v_init_dda, h_incr_dda, v_incr_dda;
+	uint32_t val;
+
+#ifdef DMR_DEBUG_WINDOW
+	printf("%s window: %d\n", __func__, index);
+	printf("  src: x: %d, y: %d, w: %d, h: %d\n",
+	   win->src_x, win->src_y, win->src_w, win->src_h);
+	printf("  dst: x: %d, y: %d, w: %d, h: %d\n",
+	   win->dst_x, win->dst_y, win->dst_w, win->dst_h);
+	printf("  bpp: %d, color_mode: %d, swap: %d\n",
+	   win->bits_per_pixel, win->color_mode, win->swap);
+#endif
+
+	if (win->is_yuv)
+		bpp = win->is_yuv_planar ? 1 : 2;
+	else
+		bpp = (win->bits_per_pixel + 7) / 8;
+
+	if (!win->transpose_xy) {
+		h_size = win->src_w * bpp;
+		v_size = win->src_h;
+	} else {
+		h_size = win->src_h * bpp;
+		v_size = win->src_w;
+	}
+
+	h_offset = win->src_x * bpp;;
+	v_offset = win->src_y;
+	if (win->flip_x) {
+		h_offset += win->src_w * bpp - 1;
+	}
+	if (win->flip_y)
+		v_offset += win->src_h - 1;
+
+	/* Adjust offsets for planar yuv modes */
+	if (win->is_yuv_planar) {
+		h_offset &= ~1;
+		if (win->flip_x )
+			h_offset |= 1;
+		v_offset &= ~1;
+		if (win->flip_y )
+			v_offset |= 1;
+	}
+
+	/* Setup scaling. */
+	if (!win->transpose_xy) {
+		h_init_dda = dc_scaling_init(win->src_x);
+		v_init_dda = dc_scaling_init(win->src_y);
+		h_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 4);
+		v_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 15);
+	} else {
+		h_init_dda =  dc_scaling_init(win->src_y);
+		v_init_dda =  dc_scaling_init(win->src_x);
+		h_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 4);
+		v_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 15);
+	}
+#ifdef DMR_DEBUG_WINDOW
+	printf("\n");
+	printf("  bpp: %d, size: h: %d v: %d, offset: h:%d v: %d\n",
+	   bpp, h_size, v_size, h_offset, v_offset);
+	printf("  init_dda: h: %d v: %d, incr_dda: h: %d v: %d\n",
+	   h_init_dda, v_init_dda, h_incr_dda, v_incr_dda);
+#endif
+
+	LOCK(sc);
+
+	/* Select target window  */
+	val = WINDOW_A_SELECT << index;
+	WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, val);
+
+	/* Sizes */
+	WR4(sc, DC_WIN_POSITION, WIN_POSITION(win->dst_x, win->dst_y));
+	WR4(sc, DC_WIN_SIZE, WIN_SIZE(win->dst_w, win->dst_h));
+	WR4(sc, DC_WIN_PRESCALED_SIZE, WIN_PRESCALED_SIZE(h_size, v_size));
+
+	/* DDA */
+	WR4(sc, DC_WIN_DDA_INCREMENT,
+	    WIN_DDA_INCREMENT(h_incr_dda, v_incr_dda));
+	WR4(sc, DC_WIN_H_INITIAL_DDA, h_init_dda);
+	WR4(sc, DC_WIN_V_INITIAL_DDA, v_init_dda);
+
+	/* Color planes base addresses and strides */
+	WR4(sc, DC_WINBUF_START_ADDR, win->base[0]);
+	if (win->is_yuv_planar) {
+		WR4(sc, DC_WINBUF_START_ADDR_U, win->base[1]);
+		WR4(sc, DC_WINBUF_START_ADDR_V, win->base[2]);
+		WR4(sc, DC_WIN_LINE_STRIDE,
+		     win->stride[1] << 16 | win->stride[0]);
+	} else {
+		WR4(sc, DC_WIN_LINE_STRIDE, win->stride[0]);
+	}
+
+	/* Offsets for rotation and axis flip */
+	WR4(sc, DC_WINBUF_ADDR_H_OFFSET, h_offset);
+	WR4(sc, DC_WINBUF_ADDR_V_OFFSET, v_offset);
+
+	/* Color format */
+	WR4(sc, DC_WIN_COLOR_DEPTH, win->color_mode);
+	WR4(sc, DC_WIN_BYTE_SWAP, win->swap);
+
+	/* Tiling */
+	val = win->surface_kind;
+	if (win->surface_kind == SURFACE_KIND_BL_16B2)
+		val |= SURFACE_KIND_BLOCK_HEIGHT(win->block_height);
+	WR4(sc, DC_WINBUF_SURFACE_KIND, val);
+
+	/* Color space coefs for YUV modes */
+	if (win->is_yuv) {
+		WR4(sc, DC_WINC_CSC_YOF,   0x00f0);
+		WR4(sc, DC_WINC_CSC_KYRGB, 0x012a);
+		WR4(sc, DC_WINC_CSC_KUR,   0x0000);
+		WR4(sc, DC_WINC_CSC_KVR,   0x0198);
+		WR4(sc, DC_WINC_CSC_KUG,   0x039b);
+		WR4(sc, DC_WINC_CSC_KVG,   0x032f);
+		WR4(sc, DC_WINC_CSC_KUB,   0x0204);
+		WR4(sc, DC_WINC_CSC_KVB,   0x0000);
+	}
+
+	val = WIN_ENABLE;
+	if (win->is_yuv)
+		val |= CSC_ENABLE;
+	else if (win->bits_per_pixel < 24)
+		val |= COLOR_EXPAND;
+	if (win->flip_y)
+		val |= V_DIRECTION;
+	if (win->flip_x)
+		val |= H_DIRECTION;
+	if (win->transpose_xy)
+		val |= SCAN_COLUMN;
+	WR4(sc, DC_WINC_WIN_OPTIONS, val);
+
+#ifdef DMR_DEBUG_WINDOW
+	/* Set underflow debug mode -> highlight missing pixels. */
+	WR4(sc, DC_WINBUF_UFLOW_CTRL, UFLOW_CTR_ENABLE);
+	WR4(sc, DC_WINBUF_UFLOW_DBG_PIXEL, 0xFFFF0000);
+#endif
+
+	UNLOCK(sc);
+}
+
+/* -------------------------------------------------------------------
+ *
+ *    Plane functions.
+ *
+ */
+static int
+dc_plane_update(struct drm_plane *drm_plane, struct drm_crtc *drm_crtc,
+    struct drm_framebuffer *drm_fb,
+    int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h,
+    uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+	struct tegra_plane *plane;
+	struct tegra_crtc *crtc;
+	struct tegra_fb *fb;
+	struct dc_softc *sc;
+	struct dc_window win;
+	int rv;
+
+	plane = container_of(drm_plane, struct tegra_plane, drm_plane);
+	fb = container_of(drm_fb, struct tegra_fb, drm_fb);
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+
+	memset(&win, 0, sizeof(win));
+	win.src_x = src_x >> 16;
+	win.src_y = src_y >> 16;
+	win.src_w = src_w >> 16;
+	win.src_h = src_h >> 16;
+	win.dst_x = crtc_x;
+	win.dst_y = crtc_y;
+	win.dst_w = crtc_w;
+	win.dst_h = crtc_h;
+
+	rv = dc_parse_drm_format(fb, &win);
+	if (rv != 0) {
+		DRM_WARNING("unsupported pixel format %d\n",
+		    fb->drm_fb.pixel_format);
+		return (rv);
+	}
+
+	dc_setup_window(sc, plane->index, &win);
+
+	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << plane->index);
+	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << plane->index);
+
+	return (0);
+}
+
+static int
+dc_plane_disable(struct drm_plane *drm_plane)
+{
+	struct tegra_plane *plane;
+	struct tegra_crtc *crtc;
+	struct dc_softc *sc;
+	uint32_t val, idx;
+
+	if (drm_plane->crtc == NULL)
+		return (0);
+	plane = container_of(drm_plane, struct tegra_plane, drm_plane);
+	crtc = container_of(drm_plane->crtc, struct tegra_crtc, drm_crtc);
+
+	sc = device_get_softc(crtc->dev);
+	idx = plane->index;
+
+	LOCK(sc);
+
+	WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT << idx);
+
+	val = RD4(sc, DC_WINC_WIN_OPTIONS);
+	val &= ~WIN_ENABLE;
+	WR4(sc, DC_WINC_WIN_OPTIONS, val);
+
+	UNLOCK(sc);
+
+	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << idx);
+	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << idx);
+
+	return (0);
+}
+
+static void
+dc_plane_destroy(struct drm_plane *plane)
+{
+
+	dc_plane_disable(plane);
+	drm_plane_cleanup(plane);
+	free(plane, DRM_MEM_KMS);
+}
+
+static const struct drm_plane_funcs dc_plane_funcs = {
+	.update_plane = dc_plane_update,
+	.disable_plane = dc_plane_disable,
+	.destroy = dc_plane_destroy,
+};
+
+/* -------------------------------------------------------------------
+ *
+ *    CRTC helper functions.
+ *
+ */
+static void
+dc_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	/* Empty function */
+}
+
+static bool
+dc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
+    struct drm_display_mode *adjusted)
+{
+
+	return (true);
+}
+
+static int
+dc_set_base(struct dc_softc *sc, int x, int y, struct tegra_fb *fb)
+{
+	struct dc_window win;
+	int rv;
+
+	memset(&win, 0, sizeof(win));
+	win.src_x = x;
+	win.src_y = y;
+	win.src_w = fb->drm_fb.width;
+	win.src_h = fb->drm_fb.height;
+	win.dst_x = x;
+	win.dst_y = y;
+	win.dst_w = fb->drm_fb.width;
+	win.dst_h = fb->drm_fb.height;
+
+	rv = dc_parse_drm_format(fb, &win);
+	if (rv != 0) {
+		DRM_WARNING("unsupported pixel format %d\n",
+		    fb->drm_fb.pixel_format);
+		return (rv);
+	}
+	dc_setup_window(sc, 0, &win);
+
+	return (0);
+}
+
+static int
+dc_crtc_mode_set(struct drm_crtc *drm_crtc, struct drm_display_mode *mode,
+    struct drm_display_mode *adjusted, int x, int y,
+    struct drm_framebuffer *old_fb)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	struct tegra_fb *fb;
+	struct dc_window win;
+	uint32_t div, h_ref_to_sync, v_ref_to_sync;
+	int rv;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+
+
+	h_ref_to_sync = 1;
+	v_ref_to_sync = 1;
+	/* Setup timing */
+	rv = dc_setup_clk(sc, drm_crtc, mode, &div);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot set pixel clock\n");
+		return (rv);
+	}
+
+	/* Timing */
+	WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, 0);
+
+	WR4(sc, DC_DISP_REF_TO_SYNC,
+	    (v_ref_to_sync << 16) |
+	     h_ref_to_sync);
+
+	WR4(sc, DC_DISP_SYNC_WIDTH,
+	    ((mode->vsync_end - mode->vsync_start) << 16) |
+	    ((mode->hsync_end - mode->hsync_start) <<  0));
+
+	WR4(sc, DC_DISP_BACK_PORCH,
+	    ((mode->vtotal - mode->vsync_end) << 16) |
+	    ((mode->htotal - mode->hsync_end) <<  0));
+
+	WR4(sc, DC_DISP_FRONT_PORCH,
+	    ((mode->vsync_start - mode->vdisplay) << 16) |
+	    ((mode->hsync_start - mode->hdisplay) <<  0));
+
+	WR4(sc, DC_DISP_DISP_ACTIVE,
+	    (mode->vdisplay << 16) | mode->hdisplay);
+
+	WR4(sc, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT(DF1P1C));
+
+	WR4(sc,DC_DISP_DISP_CLOCK_CONTROL,
+	    SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER(PCD1));
+
+	memset(&win, 0, sizeof(win));
+	win.src_x = x;
+	win.src_y = y;
+	win.src_w = mode->hdisplay;
+	win.src_h = mode->vdisplay;
+	win.dst_x = x;
+	win.dst_y = y;
+	win.dst_w = mode->hdisplay;
+	win.dst_h = mode->vdisplay;
+
+	rv = dc_parse_drm_format(fb, &win);
+	if (rv != 0) {
+		DRM_WARNING("unsupported pixel format %d\n",
+		    drm_crtc->fb->pixel_format);
+		return (rv);
+	}
+
+	dc_setup_window(sc, 0, &win);
+
+	return (0);
+
+}
+
+static int
+dc_crtc_mode_set_base(struct drm_crtc *drm_crtc, int x, int y,
+    struct drm_framebuffer *old_fb)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	struct tegra_fb *fb;
+	int rv;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+	sc = device_get_softc(crtc->dev);
+
+	rv = dc_set_base(sc, x, y, fb);
+
+	/* Commit */
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ);
+	return (rv);
+}
+
+
+static void
+dc_crtc_prepare(struct drm_crtc *drm_crtc)
+{
+
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	uint32_t val;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+
+	WR4(sc, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL);
+	/* XXX allocate syncpoint from host1x */
+	WR4(sc, DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE |
+	    (sc->tegra_crtc.nvidia_head == 0 ? SYNCPT_VBLANK0: SYNCPT_VBLANK1));
+
+	WR4(sc, DC_CMD_DISPLAY_POWER_CONTROL,
+	    PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+	    PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
+
+	val = RD4(sc, DC_CMD_DISPLAY_COMMAND);
+	val |= DISPLAY_CTRL_MODE(CTRL_MODE_C_DISPLAY);
+	WR4(sc, DC_CMD_DISPLAY_COMMAND, val);
+
+	WR4(sc, DC_CMD_INT_MASK,
+	    WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+
+	WR4(sc, DC_CMD_INT_ENABLE,
+	    VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+}
+
+static void
+dc_crtc_commit(struct drm_crtc *drm_crtc)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	uint32_t val;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
+
+	val = RD4(sc, DC_CMD_INT_MASK);
+	val |= FRAME_END_INT;
+	WR4(sc, DC_CMD_INT_MASK, val);
+
+	val = RD4(sc, DC_CMD_INT_ENABLE);
+	val |= FRAME_END_INT;
+	WR4(sc, DC_CMD_INT_ENABLE, val);
+
+	WR4(sc, DC_CMD_STATE_CONTROL,  GENERAL_ACT_REQ | WIN_A_ACT_REQ);
+}
+
+static void
+dc_crtc_load_lut(struct drm_crtc *crtc)
+{
+
+	/* empty function */
+}
+
+static const struct drm_crtc_helper_funcs dc_crtc_helper_funcs = {
+	.dpms = dc_crtc_dpms,
+	.mode_fixup = dc_crtc_mode_fixup,
+	.mode_set = dc_crtc_mode_set,
+	.mode_set_base = dc_crtc_mode_set_base,
+	.prepare = dc_crtc_prepare,
+	.commit = dc_crtc_commit,
+	.load_lut = dc_crtc_load_lut,
+};
+
+static int
+drm_crtc_index(struct drm_crtc *crtc)
+{
+	int idx;
+	struct drm_crtc *tmp;
+
+	idx = 0;
+	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
+		if (tmp == crtc)
+			return (idx);
+		idx++;
+	}
+	panic("Cannot find CRTC");
+}
+
+/* -------------------------------------------------------------------
+ *
+ *   Exported functions (mainly vsync related).
+ *
+ * XXX revisit this -> convert to bus methods?
+ */
+int
+tegra_dc_get_pipe(struct drm_crtc *drm_crtc)
+{
+	struct tegra_crtc *crtc;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	return (crtc->nvidia_head);
+}
+
+void
+tegra_dc_enable_vblank(struct drm_crtc *drm_crtc)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	uint32_t val;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+
+	LOCK(sc);
+	val = RD4(sc, DC_CMD_INT_MASK);
+	val |= VBLANK_INT;
+	WR4(sc, DC_CMD_INT_MASK, val);
+	UNLOCK(sc);
+}
+
+void
+tegra_dc_disable_vblank(struct drm_crtc *drm_crtc)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	uint32_t val;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+
+	LOCK(sc);
+	val = RD4(sc, DC_CMD_INT_MASK);
+	val &= ~VBLANK_INT;
+	WR4(sc, DC_CMD_INT_MASK, val);
+	UNLOCK(sc);
+}
+
+static void
+dc_finish_page_flip(struct dc_softc *sc)
+{
+	struct drm_crtc *drm_crtc;
+	struct drm_device *drm;
+	struct tegra_fb *fb;
+	struct tegra_bo *bo;
+	uint32_t base;
+	int idx;
+
+	drm_crtc = &sc->tegra_crtc.drm_crtc;
+	drm = drm_crtc->dev;
+	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+
+	mtx_lock(&drm->event_lock);
+
+	if (sc->event == NULL) {
+		mtx_unlock(&drm->event_lock);
+		return;
+	}
+
+	LOCK(sc);
+	/* Read active copy of WINBUF_START_ADDR */
+	WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT);
+	WR4(sc, DC_CMD_STATE_ACCESS, READ_MUX);
+	base = RD4(sc, DC_WINBUF_START_ADDR);
+	WR4(sc, DC_CMD_STATE_ACCESS, 0);
+	UNLOCK(sc);
+
+	/* Is already active */
+	bo = tegra_fb_get_plane(fb, 0);
+	if (base == (bo->pbase + fb->drm_fb.offsets[0])) {
+		idx = drm_crtc_index(drm_crtc);
+		drm_send_vblank_event(drm, idx, sc->event);
+		drm_vblank_put(drm, idx);
+		sc->event = NULL;
+	}
+
+	mtx_unlock(&drm->event_lock);
+}
+
+
+void
+tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc, struct drm_file *file)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	struct drm_device *drm;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+	drm = drm_crtc->dev;
+	mtx_lock(&drm->event_lock);
+
+	if ((sc->event != NULL) && (sc->event->base.file_priv == file)) {
+		sc->event->base.destroy(&sc->event->base);
+		drm_vblank_put(drm, drm_crtc_index(drm_crtc));
+		sc->event = NULL;
+	}
+	mtx_unlock(&drm->event_lock);
+}
+
+/* -------------------------------------------------------------------
+ *
+ *    CRTC functions.
+ *
+ */
+static int
+dc_page_flip(struct drm_crtc *drm_crtc, struct drm_framebuffer *drm_fb,
+    struct drm_pending_vblank_event *event)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	struct tegra_fb *fb;
+	struct drm_device *drm;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
+	drm = drm_crtc->dev;
+
+	if (sc->event != NULL)
+		return (-EBUSY);
+
+	if (event != NULL) {
+		event->pipe = sc->tegra_crtc.nvidia_head;
+		sc->event = event;
+		drm_vblank_get(drm, event->pipe);
+	}
+
+	dc_set_base(sc, drm_crtc->x, drm_crtc->y, fb);
+	drm_crtc->fb = drm_fb;
+
+	/* Commit */
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
+
+	return (0);
+}
+
+static int
+dc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file,
+    uint32_t handle, uint32_t width, uint32_t height)
+{
+
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+	struct drm_gem_object *gem;
+	struct tegra_bo *bo;
+	int i;
+	uint32_t val, *src, *dst;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+
+	if (width != height)
+		return (-EINVAL);
+
+	switch (width) {
+	case 32:
+		val = CURSOR_SIZE(C32x32);
+		break;
+	case 64:
+		val = CURSOR_SIZE(C64x64);
+		break;
+	case 128:
+		val = CURSOR_SIZE(C128x128);
+		break;
+	case 256:
+		val = CURSOR_SIZE(C256x256);
+		break;
+	default:
+		return (-EINVAL);
+	}
+
+	bo = NULL;
+	gem = NULL;
+	if (handle != 0) {
+		gem = drm_gem_object_lookup(drm_crtc->dev, file, handle);
+		if (gem == NULL)
+			return (-ENOENT);
+		bo = container_of(gem, struct tegra_bo, gem_obj);
+	}
+
+	if (sc->cursor_gem != NULL) {
+		drm_gem_object_unreference(sc->cursor_gem);
+	}
+	sc->cursor_gem = gem;
+
+	if (bo != NULL) {
+		/*
+		 * Copy cursor into cache and convert it from ARGB to RGBA.
+		 * XXXX - this is broken by design - client can write to BO at
+		 * any time. We can dedicate other window for cursor or switch
+		 * to sw cursor in worst case.
+		 */
+		src = (uint32_t *)bo->vbase;
+		dst = (uint32_t *)crtc->cursor_vbase;
+		for (i = 0; i < width * height; i++)
+			dst[i] = (src[i] << 8) | (src[i] >> 24);
+
+		val |= CURSOR_CLIP(CC_DISPLAY);
+		val |= CURSOR_START_ADDR(crtc->cursor_pbase);
+		WR4(sc, DC_DISP_CURSOR_START_ADDR, val);
+
+		val = RD4(sc, DC_DISP_BLEND_CURSOR_CONTROL);
+		val &= ~CURSOR_DST_BLEND_FACTOR_SELECT(~0);
+		val &= ~CURSOR_SRC_BLEND_FACTOR_SELECT(~0);
+		val |= CURSOR_MODE_SELECT;
+		val |= CURSOR_DST_BLEND_FACTOR_SELECT(DST_NEG_K1_TIMES_SRC);
+		val |= CURSOR_SRC_BLEND_FACTOR_SELECT(SRC_BLEND_K1_TIMES_SRC);
+		val |= CURSOR_ALPHA(~0);
+		WR4(sc, DC_DISP_BLEND_CURSOR_CONTROL, val);
+
+		val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
+		val |= CURSOR_ENABLE;
+		WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
+	} else {
+		val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
+		val &= ~CURSOR_ENABLE;
+		WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
+	}
+
+	/* XXX This fixes cursor underflow issues, but why ?  */
+	WR4(sc, DC_DISP_CURSOR_UNDERFLOW_CTRL, CURSOR_UFLOW_CYA);
+
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | CURSOR_UPDATE );
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | CURSOR_ACT_REQ);
+	return (0);
+}
+
+static int
+dc_cursor_move(struct drm_crtc *drm_crtc, int x, int y)
+{
+	struct dc_softc *sc;
+	struct tegra_crtc *crtc;
+
+	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
+	sc = device_get_softc(crtc->dev);
+	WR4(sc, DC_DISP_CURSOR_POSITION, CURSOR_POSITION(x, y));
+
+	WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_UPDATE);
+	WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_ACT_REQ);
+
+	return (0);
+}
+
+static void
+dc_destroy(struct drm_crtc *crtc)
+{
+
+	drm_crtc_cleanup(crtc);
+	memset(crtc, 0, sizeof(*crtc));
+}
+
+static const struct drm_crtc_funcs dc_crtc_funcs = {
+	.page_flip = dc_page_flip,
+	.cursor_set = dc_cursor_set,
+	.cursor_move = dc_cursor_move,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = dc_destroy,
+};
+
+/* -------------------------------------------------------------------
+ *
+ *    Bus and infrastructure.
+ *
+ */
+static int
+dc_init_planes(struct dc_softc *sc, struct tegra_drm *drm)
+{
+	int i, rv;
+	struct tegra_plane *plane;
+
+	rv = 0;
+	for (i = 0; i < DC_MAX_PLANES; i++) {
+		plane = malloc(sizeof(*plane), DRM_MEM_KMS, M_WAITOK | M_ZERO);
+		plane->index = i + 1;
+		rv = drm_plane_init(&drm->drm_dev, &plane->drm_plane,
+		    1 << sc->tegra_crtc.nvidia_head, &dc_plane_funcs,
+		    dc_plane_formats, nitems(dc_plane_formats), false);
+		if (rv != 0) {
+			free(plane, DRM_MEM_KMS);
+			return (rv);
+		}
+	}
+	return 0;
+}
+
+static void
+dc_display_enable(device_t dev, bool enable)
+{
+	struct dc_softc *sc;
+	uint32_t val;
+
+	sc = device_get_softc(dev);
+
+	/* Set display mode */
+	val = enable ? CTRL_MODE_C_DISPLAY: CTRL_MODE_STOP;
+	WR4(sc, DC_CMD_DISPLAY_COMMAND, DISPLAY_CTRL_MODE(val));
+
+	/* and commit it*/
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE);
+	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ);
+}
+
+static void
+dc_hdmi_enable(device_t dev, bool enable)
+{
+	struct dc_softc *sc;
+	uint32_t val;
+
+	sc = device_get_softc(dev);
+
+	val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
+	if (enable)
+		val |= HDMI_ENABLE;
+	else
+		val &= ~HDMI_ENABLE;
+	WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
+
+}
+
+static void
+dc_setup_timing(device_t dev, int h_pulse_start)
+{
+	struct dc_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Setup display timing */
+	WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, VSYNC_H_POSITION(1));
+	WR4(sc, DC_DISP_DISP_COLOR_CONTROL,
+	    DITHER_CONTROL(DITHER_DISABLE) | BASE_COLOR_SIZE(SIZE_BASE888));
+
+	WR4(sc, DC_DISP_DISP_SIGNAL_OPTIONS0, H_PULSE2_ENABLE);
+	WR4(sc, DC_DISP_H_PULSE2_CONTROL,
+	    PULSE_CONTROL_QUAL(QUAL_VACTIVE) | PULSE_CONTROL_LAST(LAST_END_A));
+
+	WR4(sc, DC_DISP_H_PULSE2_POSITION_A,
+	    PULSE_START(h_pulse_start) | PULSE_END(h_pulse_start + 8));
+}
+
+static void
+dc_intr(void *arg)
+{
+	struct dc_softc *sc;
+	uint32_t status;
+
+	sc = arg;
+
+	/* Confirm interrupt */
+	status = RD4(sc, DC_CMD_INT_STATUS);
+	WR4(sc, DC_CMD_INT_STATUS, status);
+	if (status & VBLANK_INT) {
+		drm_handle_vblank(sc->tegra_crtc.drm_crtc.dev,
+		    sc->tegra_crtc.nvidia_head);
+		dc_finish_page_flip(sc);
+	}
+}
+
+static int
+dc_init_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+	struct dc_softc *sc;
+	int rv;
+
+	sc = device_get_softc(dev);
+
+	if (drm->pitch_align < sc->pitch_align)
+		drm->pitch_align = sc->pitch_align;
+
+	drm_crtc_init(&drm->drm_dev, &sc->tegra_crtc.drm_crtc, &dc_crtc_funcs);
+	drm_mode_crtc_set_gamma_size(&sc->tegra_crtc.drm_crtc, 256);
+	drm_crtc_helper_add(&sc->tegra_crtc.drm_crtc, &dc_crtc_helper_funcs);
+
+	rv = dc_init_planes(sc, drm);
+	if (rv!= 0){
+		device_printf(dev, "Cannot init planes\n");
+		return (rv);
+	}
+
+	WR4(sc, DC_CMD_INT_TYPE,
+	    WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+
+	WR4(sc, DC_CMD_INT_POLARITY,
+	    WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
+
+	WR4(sc, DC_CMD_INT_ENABLE, 0);
+	WR4(sc, DC_CMD_INT_MASK, 0);
+
+	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, dc_intr, sc, &sc->irq_ih);
+	if (rv != 0) {
+		device_printf(dev, "Cannot register interrupt handler\n");
+		return (rv);
+	}
+
+	/* allocate memory for cursor cache */
+	sc->tegra_crtc.cursor_vbase = kmem_alloc_contig(kernel_arena,
+	    256 * 256 * 4, M_WAITOK | M_ZERO,
+	    0, -1UL, PAGE_SIZE, 0, VM_MEMATTR_WRITE_COMBINING);
+	sc->tegra_crtc.cursor_pbase = vtophys(sc->tegra_crtc.cursor_vbase);
+	return (0);
+}
+
+static int
+dc_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+	struct dc_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	sc->irq_ih = NULL;
+
+	return (0);
+}
+
+static int
+get_fdt_resources(struct dc_softc *sc, phandle_t node)
+{
+	int rv;
+
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "dc", &sc->hwreset_dc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'dc' reset\n");
+		return (rv);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'parent' clock\n");
+		return (rv);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "dc", &sc->clk_dc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'dc' clock\n");
+		return (rv);
+	}
+
+	rv = OF_getencprop(node, "nvidia,head", &sc->tegra_crtc.nvidia_head,
+	    sizeof(sc->tegra_crtc.nvidia_head));
+	if (rv <= 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'nvidia,head' property\n");
+		return (rv);
+	}
+	return (0);
+}
+
+static int
+enable_fdt_resources(struct dc_softc *sc)
+{
+	int id, rv;
+
+	rv = clk_set_parent_by_clk(sc->clk_dc, sc->clk_parent);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot set parent for 'dc' clock\n");
+		return (rv);
+	}
+
+	id = (sc->tegra_crtc.nvidia_head == 0) ?
+	    TEGRA_POWERGATE_DIS: TEGRA_POWERGATE_DISB;
+	rv = tegra_powergate_sequence_power_up(id, sc->clk_dc, sc->hwreset_dc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'DIS' powergate\n");
+		return (rv);
+	}
+
+	return (0);
+}
+
+static int
+dc_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra Display Controller");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+dc_attach(device_t dev)
+{
+	struct dc_softc *sc;
+	phandle_t node;
+	int rid, rv;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->tegra_crtc.dev = dev;
+
+	node = ofw_bus_get_node(sc->dev);
+	LOCK_INIT(sc);
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		goto fail;
+	}
+
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate IRQ resources\n");
+		goto fail;
+	}
+
+	rv = get_fdt_resources(sc, node);
+	if (rv != 0) {
+		device_printf(dev, "Cannot parse FDT resources\n");
+		goto fail;
+	}
+	rv = enable_fdt_resources(sc);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable FDT resources\n");
+		goto fail;
+	}
+
+	/*
+	 * Tegra124
+	 *  -  64 for RGB modes
+	 *  - 128 for YUV planar modes
+	 *  - 256 for block linear modes
+	 */
+	sc->pitch_align = 256;
+
+	rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+	if (rv != 0) {
+		device_printf(dev, "Cannot register DRM device\n");
+		goto fail;
+	}
+
+	return (bus_generic_attach(dev));
+
+fail:
+	TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	if (sc->clk_parent != NULL)
+		clk_release(sc->clk_parent);
+	if (sc->clk_dc != NULL)
+		clk_release(sc->clk_dc);
+	if (sc->hwreset_dc != NULL)
+		hwreset_release(sc->hwreset_dc);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+
+	return (ENXIO);
+}
+
+static int
+dc_detach(device_t dev)
+{
+	struct dc_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	if (sc->clk_parent != NULL)
+		clk_release(sc->clk_parent);
+	if (sc->clk_dc != NULL)
+		clk_release(sc->clk_dc);
+	if (sc->hwreset_dc != NULL)
+		hwreset_release(sc->hwreset_dc);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_dc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,			dc_probe),
+	DEVMETHOD(device_attach,		dc_attach),
+	DEVMETHOD(device_detach,		dc_detach),
+
+	/* tegra drm interface */
+	DEVMETHOD(tegra_drm_init_client,	dc_init_client),
+	DEVMETHOD(tegra_drm_exit_client,	dc_exit_client),
+
+	/* tegra dc interface */
+	DEVMETHOD(tegra_dc_display_enable,	dc_display_enable),
+	DEVMETHOD(tegra_dc_hdmi_enable,		dc_hdmi_enable),
+	DEVMETHOD(tegra_dc_setup_timing,	dc_setup_timing),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_dc_devclass;
+DEFINE_CLASS_0(tegra_dc, tegra_dc_driver, tegra_dc_methods,
+    sizeof(struct dc_softc));
+DRIVER_MODULE(tegra_dc, host1x, tegra_dc_driver, tegra_dc_devclass, NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_dc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_dc_if.m
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_dc_if.m	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_dc_if.m	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,58 @@
+/* $MidnightBSD$ */
+#-
+# Copyright (c) 2015 Michal Meloun
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_dc_if.m 310600 2016-12-26 14:36:05Z mmel $
+#
+
+#include <machine/bus.h>
+
+INTERFACE tegra_dc;
+
+
+METHOD void write_4{
+	device_t	dev;
+	bus_size_t	offset;
+	uint32_t	val;
+};
+METHOD uint32_t read_4{
+	device_t	dev;
+	bus_size_t	offset;
+};
+
+METHOD void display_enable{
+	device_t	dev;
+	bool		enable;
+};
+
+METHOD void hdmi_enable{
+	device_t	dev;
+	bool		enable;
+};
+
+METHOD void setup_timing{
+	device_t	dev;
+	int 		h_pulse_start;
+};


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_dc_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,401 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright 1992-2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_dc_reg.h 310600 2016-12-26 14:36:05Z mmel $
+ */
+#ifndef _TEGRA_DC_REG_H_
+#define	_TEGRA_DC_REG_H_
+
+/*
+ * !!! WARNING !!!
+ * Tegra manual uses registers index (and not register addreses).
+ * We follow the TRM notation and index is converted to offset in
+ * WR4 / RD4 macros
+ */
+
+/* --------------------------- DC CMD -------------------------------------- */
+#define	DC_CMD_GENERAL_INCR_SYNCPT		0x000
+#define	DC_CMD_GENERAL_INCR_SYNCPT_CNTRL	0x001
+#define	 SYNCPT_CNTRL_NO_STALL				(1 << 8)
+#define	 SYNCPT_CNTRL_SOFT_RESET			(1 << 0)
+
+#define	DC_CMD_GENERAL_INCR_SYNCPT_ERROR	0x002
+#define	DC_CMD_WIN_A_INCR_SYNCPT		0x008
+#define	DC_CMD_WIN_A_INCR_SYNCPT_CNTRL		0x009
+#define	DC_CMD_WIN_A_INCR_SYNCPT_ERROR		0x00a
+#define	DC_CMD_WIN_B_INCR_SYNCPT		0x010
+#define	DC_CMD_WIN_B_INCR_SYNCPT_CNTRL		0x011
+#define	DC_CMD_WIN_B_INCR_SYNCPT_ERROR		0x012
+#define	DC_CMD_WIN_C_INCR_SYNCPT		0x018
+#define	DC_CMD_WIN_C_INCR_SYNCPT_CNTRL		0x019
+#define	DC_CMD_WIN_C_INCR_SYNCPT_ERROR		0x01a
+#define	DC_CMD_CONT_SYNCPT_VSYNC		0x028
+#define	 SYNCPT_VSYNC_ENABLE				(1 << 8)
+
+#define	DC_CMD_CTXSW				0x030
+#define	DC_CMD_DISPLAY_COMMAND_OPTION0		0x031
+#define	DC_CMD_DISPLAY_COMMAND			0x032
+#define	 DISPLAY_CTRL_MODE(x)				((x) << 5)
+#define	   CTRL_MODE_STOP					0
+#define	   CTRL_MODE_C_DISPLAY					1
+#define	   CTRL_MODE_NC_DISPLAY					2
+
+#define	DC_CMD_SIGNAL_RAISE			0x033
+#define	DC_CMD_DISPLAY_POWER_CONTROL		0x036
+#define	 PM1_ENABLE					(1 << 18)
+#define	 PM0_ENABLE					(1 << 16)
+#define	 PW4_ENABLE					(1 <<  8)
+#define	 PW3_ENABLE					(1 <<  6)
+#define	 PW2_ENABLE					(1 <<  4)
+#define	 PW1_ENABLE					(1 <<  2)
+#define	 PW0_ENABLE					(1 <<  0)
+
+#define	DC_CMD_INT_STATUS			0x037
+#define	DC_CMD_INT_MASK				0x038
+#define	DC_CMD_INT_ENABLE			0x039
+#define	DC_CMD_INT_TYPE				0x03a
+#define	DC_CMD_INT_POLARITY			0x03b
+#define	 WIN_T_UF_INT					(1 << 25)
+#define	 WIN_D_UF_INT					(1 << 24)
+#define	 HC_UF_INT					(1 << 23)
+#define	 CMU_LUT_CONFLICT_INT				(1 << 22)
+#define	 WIN_C_OF_INT					(1 << 16)
+#define	 WIN_B_OF_INT					(1 << 15)
+#define	 WIN_A_OF_INT					(1 << 14)
+#define	 SSF_INT					(1 << 13)
+#define	 MSF_INT					(1 << 12)
+#define	 WIN_C_UF_INT					(1 << 10)
+#define	 WIN_B_UF_INT					(1 << 9)
+#define	 WIN_A_UF_INT					(1 << 8)
+#define	 SPI_BUSY_INT					(1 << 6)
+#define	 V_PULSE2_INT					(1 << 5)
+#define	 V_PULSE3_INT					(1 << 4)
+#define	 HBLANK_INT					(1 << 3)
+#define	 VBLANK_INT					(1 << 2)
+#define	 FRAME_END_INT					(1 << 1)
+
+#define	DC_CMD_STATE_ACCESS			0x040
+#define	 WRITE_MUX					(1 << 2)
+#define	 READ_MUX					(1 << 0)
+
+#define	DC_CMD_STATE_CONTROL			0x041
+#define	 NC_HOST_TRIG					(1 << 24)
+#define	 CURSOR_UPDATE					(1 << 15)
+#define	 WIN_C_UPDATE					(1 << 11)
+#define	 WIN_B_UPDATE					(1 << 10)
+#define	 WIN_A_UPDATE					(1 <<  9)
+#define	  WIN_UPDATE(x)					(1 <<  (9 + (x)))
+#define	 GENERAL_UPDATE					(1 <<  8)
+#define	 CURSOR_ACT_REQ					(1 <<  7)
+#define	 WIN_D_ACT_REQ					(1 <<  4)
+#define	 WIN_C_ACT_REQ					(1 <<  3)
+#define	 WIN_B_ACT_REQ					(1 <<  2)
+#define	 WIN_A_ACT_REQ					(1 <<  1)
+#define	 WIN_ACT_REQ(x)					(1 <<  (1 + (x)))
+#define	 GENERAL_ACT_REQ				(1 <<  0)
+
+#define	DC_CMD_DISPLAY_WINDOW_HEADER		0x042
+#define	 WINDOW_D_SELECT				(1 << 7)
+#define	 WINDOW_C_SELECT				(1 << 6)
+#define	 WINDOW_B_SELECT				(1 << 5)
+#define	 WINDOW_A_SELECT				(1 << 4)
+#define	 WINDOW_SELECT(x)				(1 << (4 + (x)))
+
+#define	DC_CMD_REG_ACT_CONTROL			0x043
+#define	DC_CMD_WIN_D_INCR_SYNCPT		0x04c
+#define	DC_CMD_WIN_D_INCR_SYNCPT_CNTRL		0x04d
+#define	DC_CMD_WIN_D_INCR_SYNCPT_ERROR		0x04e
+
+/* ---------------------------- DC COM ------------------------------------- */
+
+/* --------------------------- DC DISP ------------------------------------- */
+
+#define	DC_DISP_DISP_SIGNAL_OPTIONS0		0x400
+#define	 M1_ENABLE					(1 << 26)
+#define	 M0_ENABLE					(1 << 24)
+#define	 V_PULSE2_ENABLE				(1 << 18)
+#define	 V_PULSE1_ENABLE				(1 << 16)
+#define	 V_PULSE0_ENABLE				(1 << 14)
+#define	 H_PULSE2_ENABLE				(1 << 12)
+#define	 H_PULSE1_ENABLE				(1 << 10)
+#define	 H_PULSE0_ENABLE				(1 <<  8)
+
+#define	DC_DISP_DISP_SIGNAL_OPTIONS1		0x401
+
+#define	DC_DISP_DISP_WIN_OPTIONS		0x402
+#define	 HDMI_ENABLE					(1 << 30)
+#define	 DSI_ENABLE					(1 << 29)
+#define	 SOR1_TIMING_CYA				(1 << 27)
+#define	 SOR1_ENABLE					(1 << 26)
+#define	 SOR_ENABLE					(1 << 25)
+#define	 CURSOR_ENABLE					(1 << 16)
+
+#define	DC_DISP_DISP_TIMING_OPTIONS		0x405
+#define	 VSYNC_H_POSITION(x)				(((x) & 0xfff) << 0)
+
+#define	DC_DISP_REF_TO_SYNC			0x406
+#define	DC_DISP_SYNC_WIDTH			0x407
+#define	DC_DISP_BACK_PORCH			0x408
+#define	DC_DISP_DISP_ACTIVE			0x409
+#define	DC_DISP_FRONT_PORCH			0x40a
+#define	DC_DISP_H_PULSE0_CONTROL		0x40b
+#define	DC_DISP_H_PULSE0_POSITION_A		0x40c
+#define	DC_DISP_H_PULSE0_POSITION_B		0x40d
+#define	DC_DISP_H_PULSE0_POSITION_C		0x40e
+#define	DC_DISP_H_PULSE0_POSITION_D		0x40f
+#define	DC_DISP_H_PULSE1_CONTROL		0x410
+#define	DC_DISP_H_PULSE1_POSITION_A		0x411
+#define	DC_DISP_H_PULSE1_POSITION_B		0x412
+#define	DC_DISP_H_PULSE1_POSITION_C		0x413
+#define	DC_DISP_H_PULSE1_POSITION_D		0x414
+#define	DC_DISP_H_PULSE2_CONTROL		0x415
+#define	DC_DISP_H_PULSE2_POSITION_A		0x416
+#define	DC_DISP_H_PULSE2_POSITION_B		0x417
+#define	DC_DISP_H_PULSE2_POSITION_C		0x418
+#define	DC_DISP_H_PULSE2_POSITION_D		0x419
+#define	DC_DISP_V_PULSE0_CONTROL		0x41a
+#define	DC_DISP_V_PULSE0_POSITION_A		0x41b
+#define	DC_DISP_V_PULSE0_POSITION_B		0x41c
+#define	DC_DISP_V_PULSE0_POSITION_C		0x41d
+#define	DC_DISP_V_PULSE1_CONTROL		0x41e
+#define	DC_DISP_V_PULSE1_POSITION_A		0x41f
+#define	DC_DISP_V_PULSE1_POSITION_B		0x420
+#define	DC_DISP_V_PULSE1_POSITION_C		0x421
+#define	DC_DISP_V_PULSE2_CONTROL		0x422
+#define	DC_DISP_V_PULSE2_POSITION_A		0x423
+#define	DC_DISP_V_PULSE3_CONTROL		0x424
+#define	 PULSE_CONTROL_LAST(x)				(((x) & 0x7f) << 8)
+#define	  LAST_START_A						0
+#define	  LAST_END_A						1
+#define	  LAST_START_B						2
+#define	  LAST_END_B						3
+#define	  LAST_START_C						4
+#define	  LAST_END_C						5
+#define	  LAST_START_D						6
+#define	  LAST_END_D						7
+#define	 PULSE_CONTROL_QUAL(x)				(((x) & 0x3) << 8)
+#define	  QUAL_ALWAYS						0
+#define	  QUAL_VACTIVE						2
+#define	  QUAL_VACTIVE1						3
+#define	 PULSE_POLARITY					(1 << 4)
+#define	 PULSE_MODE					(1 << 3)
+
+#define	DC_DISP_V_PULSE3_POSITION_A		0x425
+#define	 PULSE_END(x)					(((x) & 0xfff) << 16)
+#define	 PULSE_START(x)					(((x) & 0xfff) <<  0)
+
+
+#define	DC_DISP_DISP_CLOCK_CONTROL		0x42e
+#define	 PIXEL_CLK_DIVIDER(x)				(((x) & 0xf) <<  8)
+#define	  PCD1							 0
+#define	  PCD1H							 1
+#define	  PCD2							 2
+#define	  PCD3							 3
+#define	  PCD4							 4
+#define	  PCD6							 5
+#define	  PCD8							 6
+#define	  PCD9							 7
+#define	  PCD12							 8
+#define	  PCD16							 9
+#define	  PCD18							10
+#define	  PCD24							11
+#define	  PCD13							12
+#define	 SHIFT_CLK_DIVIDER(x)				((x) & 0xff)
+
+#define	DC_DISP_DISP_INTERFACE_CONTROL		0x42f
+#define	 DISP_ORDER_BLUE_RED				( 1 << 9)
+#define	 DISP_ALIGNMENT_LSB				( 1 << 8)
+#define	 DISP_DATA_FORMAT(x)				(((x) & 0xf) <<  8)
+#define	  DF1P1C						 0
+#define	  DF1P2C24B						 1
+#define	  DF1P2C18B						 2
+#define	  DF1P2C16B						 3
+#define	  DF1S							 4
+#define	  DF2S							 5
+#define	  DF3S							 6
+#define	  DFSPI							 7
+#define	  DF1P3C24B						 8
+#define	  DF2P1C18B						 9
+#define	  DFDUAL1P1C18B						10
+
+#define	DC_DISP_DISP_COLOR_CONTROL		0x430
+#define	 NON_BASE_COLOR					(1 << 18)
+#define	 BLANK_COLOR					(1 << 17)
+#define	 DISP_COLOR_SWAP				(1 << 16)
+#define	 ORD_DITHER_ROTATION(x)				(((x) & 0x3) << 12)
+#define	 DITHER_CONTROL(x)				(((x) & 0x3) <<  8)
+#define	  DITHER_DISABLE					0
+#define	  DITHER_ORDERED					2
+#define	  DITHER_TEMPORAL					3
+#define	 BASE_COLOR_SIZE(x)				(((x) & 0xF) <<  0)
+#define	  SIZE_BASE666						0
+#define	  SIZE_BASE111						1
+#define	  SIZE_BASE222						2
+#define	  SIZE_BASE333						3
+#define	  SIZE_BASE444						4
+#define	  SIZE_BASE555						5
+#define	  SIZE_BASE565						6
+#define	  SIZE_BASE332						7
+#define	  SIZE_BASE888						8
+
+#define	DC_DISP_CURSOR_START_ADDR		0x43e
+#define	 CURSOR_CLIP(x)					(((x) & 0x3) << 28)
+#define	  CC_DISPLAY						0
+#define	  CC_WA							1
+#define	  CC_WB							2
+#define	  CC_WC							3
+#define	 CURSOR_SIZE(x)					(((x) & 0x3) << 24)
+#define	  C32x32						0
+#define	  C64x64						1
+#define	  C128x128						2
+#define	  C256x256						3
+#define	 CURSOR_START_ADDR(x)				(((x) >> 10) & 0x3FFFFF)
+
+#define	DC_DISP_CURSOR_POSITION			0x440
+#define	 CURSOR_POSITION(h, v)		((((h) & 0x3fff) <<  0) |	\
+					 (((v) & 0x3fff) << 16))
+#define	DC_DISP_CURSOR_UNDERFLOW_CTRL		0x4eb
+#define	DC_DISP_BLEND_CURSOR_CONTROL		0x4f1
+#define	 CURSOR_MODE_SELECT				(1 << 24)
+#define	 CURSOR_DST_BLEND_FACTOR_SELECT(x)		(((x) & 0x3) << 16)
+#define	  DST_BLEND_ZERO					0
+#define	  DST_BLEND_K1						1
+#define	  DST_NEG_K1_TIMES_SRC					2
+#define	 CURSOR_SRC_BLEND_FACTOR_SELECT(x)		(((x) & 0x3) <<  8)
+#define	  SRC_BLEND_K1						0
+#define	  SRC_BLEND_K1_TIMES_SRC				1
+#define	 CURSOR_ALPHA(x)				(((x) & 0xFF) << 0)
+
+#define	DC_DISP_CURSOR_UFLOW_DBG_PIXEL		0x4f3
+#define	 CURSOR_UFLOW_CYA				(1 << 7)
+#define	 CURSOR_UFLOW_CTRL_DBG_MODE			(1 << 0)
+/* --------------------------- DC WIN ------------------------------------- */
+
+#define	DC_WINC_COLOR_PALETTE			0x500
+#define	DC_WINC_CSC_YOF				0x611
+#define	DC_WINC_CSC_KYRGB			0x612
+#define	DC_WINC_CSC_KUR				0x613
+#define	DC_WINC_CSC_KVR				0x614
+#define	DC_WINC_CSC_KUG				0x615
+#define	DC_WINC_CSC_KVG				0x616
+#define	DC_WINC_CSC_KUB				0x617
+#define	DC_WINC_CSC_KVB				0x618
+
+#define	DC_WINC_WIN_OPTIONS			0x700
+#define	 H_FILTER_MODE					(1U << 31)
+#define	 WIN_ENABLE					(1 << 30)
+#define	 INTERLACE_ENABLE				(1 << 23)
+#define	 YUV_RANGE_EXPAND				(1 << 22)
+#define	 DV_ENABLE					(1 << 20)
+#define	 CSC_ENABLE					(1 << 18)
+#define	 CP_ENABLE					(1 << 16)
+#define	 V_FILTER_UV_ALIGN				(1 << 14)
+#define	 V_FILTER_OPTIMIZE				(1 << 12)
+#define	 V_FILTER_ENABLE				(1 << 10)
+#define	 H_FILTER_ENABLE				(1 <<  8)
+#define	 COLOR_EXPAND					(1 <<  6)
+#define	 SCAN_COLUMN					(1 <<  4)
+#define	 V_DIRECTION					(1 <<  2)
+#define	 H_DIRECTION					(1 <<  0)
+
+#define	DC_WIN_BYTE_SWAP			0x701
+#define	 BYTE_SWAP(x)					(((x) & 0x7) << 0)
+#define	  NOSWAP						0
+#define	  SWAP2							1
+#define	  SWAP4							2
+#define	  SWAP4HW						3
+#define	  SWAP02						4
+#define	  SWAPLEFT						5
+
+#define	DC_WIN_COLOR_DEPTH			0x703
+#define	WIN_COLOR_DEPTH_P8					 3
+#define	WIN_COLOR_DEPTH_B4G4R4A4				 4
+#define	WIN_COLOR_DEPTH_B5G5R5A					 5
+#define	WIN_COLOR_DEPTH_B5G6R5					 6
+#define	WIN_COLOR_DEPTH_AB5G5R5					 7
+#define	WIN_COLOR_DEPTH_B8G8R8A8				12
+#define	WIN_COLOR_DEPTH_R8G8B8A8				13
+#define	WIN_COLOR_DEPTH_YCbCr422				16
+#define	WIN_COLOR_DEPTH_YUV422					17
+#define	WIN_COLOR_DEPTH_YCbCr420P				18
+#define	WIN_COLOR_DEPTH_YUV420P					19
+#define	WIN_COLOR_DEPTH_YCbCr422P				20
+#define	WIN_COLOR_DEPTH_YUV422P					21
+#define	WIN_COLOR_DEPTH_YCbCr422R				22
+#define	WIN_COLOR_DEPTH_YUV422R					23
+#define	WIN_COLOR_DEPTH_YCbCr422RA				24
+#define	WIN_COLOR_DEPTH_YUV422RA				25
+
+#define	DC_WIN_POSITION				0x704
+#define	 WIN_POSITION(h, v)		((((h) & 0x1fff) <<  0) |	\
+					 (((v) & 0x1fff) << 16))
+
+#define	DC_WIN_SIZE				0x705
+#define	 WIN_SIZE(h, v)			((((h) & 0x1fff) <<  0) |	\
+					 (((v) & 0x1fff) << 16))
+
+#define	DC_WIN_PRESCALED_SIZE			0x706
+#define	 WIN_PRESCALED_SIZE(h, v)	((((h) & 0x7fff) <<  0) |	\
+					 (((v) & 0x1fff) << 16))
+
+
+#define	DC_WIN_H_INITIAL_DDA			0x707
+#define	DC_WIN_V_INITIAL_DDA			0x708
+#define	DC_WIN_DDA_INCREMENT			0x709
+#define	 WIN_DDA_INCREMENT(h, v)	((((h) & 0xffff) <<  0) |	\
+					 (((v) & 0xffff) << 16))
+#define	DC_WIN_LINE_STRIDE			0x70a
+
+/* -------------------------- DC WINBUF ------------------------------------ */
+
+#define	DC_WINBUF_START_ADDR			0x800
+#define	DC_WINBUF_START_ADDR_NS			0x801
+#define	DC_WINBUF_START_ADDR_U			0x802
+#define	DC_WINBUF_START_ADDR_U_NS		0x803
+#define	DC_WINBUF_START_ADDR_V			0x804
+#define	DC_WINBUF_START_ADDR_V_NS		0x805
+#define	DC_WINBUF_ADDR_H_OFFSET			0x806
+#define	DC_WINBUF_ADDR_H_OFFSET_NS		0x807
+#define	DC_WINBUF_ADDR_V_OFFSET			0x808
+#define	DC_WINBUF_ADDR_V_OFFSET_NS		0x809
+#define	DC_WINBUF_UFLOW_STATUS			0x80a
+#define	DC_WINBUF_SURFACE_KIND			0x80b
+#define	 SURFACE_KIND_BLOCK_HEIGHT(x) 			(((x) & 0x7) << 4)
+#define	 SURFACE_KIND_PITCH				0
+#define	 SURFACE_KIND_TILED				1
+#define	 SURFACE_KIND_BL_16B2				2
+#define	DC_WINBUF_SURFACE_WEIGHT		0x80c
+#define	DC_WINBUF_START_ADDR_HI			0x80d
+#define	DC_WINBUF_START_ADDR_HI_NS		0x80e
+#define	DC_WINBUF_START_ADDR_U_HI		0x80f
+#define	DC_WINBUF_START_ADDR_U_HI_NS		0x810
+#define	DC_WINBUF_START_ADDR_V_HI		0x811
+#define	DC_WINBUF_START_ADDR_V_HI_NS		0x812
+#define	DC_WINBUF_UFLOW_CTRL			0x824
+#define	 UFLOW_CTR_ENABLE				(1 << 0)
+#define	DC_WINBUF_UFLOW_DBG_PIXEL		0x825
+
+#endif /* _TEGRA_DC_REG_H_ */


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_dc_reg.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_drm.h
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_drm.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_drm.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,126 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright 1992-2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_drm.h 310600 2016-12-26 14:36:05Z mmel $
+ */
+#ifndef _TEGRA_DRM_H_
+#define _TEGRA_DRM_H_
+
+#include <dev/gpio/gpiobusvar.h>
+
+struct tegra_bo {
+ 	struct drm_gem_object	gem_obj;
+	/* mapped memory buffer */
+	vm_paddr_t		pbase;
+	vm_offset_t		vbase;
+	size_t			npages;
+	vm_page_t 		*m;
+	vm_object_t		cdev_pager;
+};
+
+struct tegra_plane {
+	struct drm_plane	drm_plane;
+	int			index;		/* Window index */
+};
+
+struct tegra_fb {
+	struct drm_framebuffer	drm_fb;
+	struct drm_fb_helper	fb_helper;
+	struct tegra_bo		**planes;	/* Attached planes */
+	int			nplanes;
+
+	/* Surface and display geometry */
+	bool			block_linear;	/* Surface_kind */
+	uint32_t		block_height;
+	int			rotation; 	/* In degrees */
+	bool			flip_x;		/* Inverted X-axis */
+	bool			flip_y;		/* Inverted Y-axis */
+};
+
+struct tegra_crtc {
+	struct drm_crtc 	drm_crtc;
+	device_t		dev;
+	int			nvidia_head;
+	vm_paddr_t		cursor_pbase;	/* Cursor buffer */
+	vm_offset_t		cursor_vbase;
+};
+
+struct tegra_drm_encoder {
+	device_t 		dev;
+
+	void 			*panel;		/* XXX For LVDS panel */
+	device_t  		ddc;
+	struct edid 		*edid;
+
+	gpio_pin_t		gpio_hpd;
+
+	struct drm_encoder 	encoder;
+	struct drm_connector 	connector;
+	int			(*setup_clock)(struct tegra_drm_encoder *output,
+				    clk_t clk, uint64_t pclk);
+};
+
+struct tegra_drm {
+	struct drm_device 	drm_dev;
+	struct tegra_fb 	*fb;		/* Prime framebuffer */
+	int			pitch_align;
+};
+
+/* tegra_drm_subr.c */
+int tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node);
+int tegra_drm_encoder_init(struct tegra_drm_encoder *output,
+    struct tegra_drm *drm);
+int tegra_drm_encoder_exit(struct tegra_drm_encoder *output,
+    struct tegra_drm *drm);
+enum drm_connector_status tegra_drm_connector_detect(
+    struct drm_connector *connector, bool force);
+int tegra_drm_connector_get_modes(struct drm_connector *connector);
+struct drm_encoder *tegra_drm_connector_best_encoder(
+    struct drm_connector *connector);
+
+/* tegra_dc.c */
+void tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc,
+    struct drm_file *file);
+void tegra_dc_enable_vblank(struct drm_crtc *drm_crtc);
+void tegra_dc_disable_vblank(struct drm_crtc *drm_crtc);
+int tegra_dc_get_pipe(struct drm_crtc *drm_crtc);
+
+/* tegra_fb.c */
+struct fb_info *tegra_drm_fb_getinfo(struct drm_device *drm);
+struct tegra_bo *tegra_fb_get_plane(struct tegra_fb *fb, int idx);
+int tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file,
+    struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res);
+int tegra_drm_fb_init(struct drm_device *drm);
+void tegra_drm_fb_destroy(struct drm_device *drm);
+
+
+/* tegra_bo.c */
+struct tegra_bo;
+int tegra_bo_create(struct drm_device *drm, size_t size,
+    struct tegra_bo **res_bo);
+void tegra_bo_driver_register(struct drm_driver *drm_drv);
+
+#endif /* _TEGRA_DRM_H_ */


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_drm.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_drm_if.m
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_drm_if.m	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_drm_if.m	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,69 @@
+/* $MidnightBSD$ */
+#-
+# Copyright (c) 2015 Michal Meloun
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_drm_if.m 310600 2016-12-26 14:36:05Z mmel $
+#
+
+
+INTERFACE tegra_drm;
+HEADER {
+	struct tegra_drm;
+};
+
+
+/**
+ * Register client to host1x
+ */
+METHOD int register_client{
+	device_t		host1x;
+	device_t		client;
+};
+
+/**
+ * Deregister client to host1x
+ */
+METHOD int deregister_client{
+	device_t		host1x;
+	device_t		client;
+};
+
+/**
+ * Call client init method
+ */
+METHOD int init_client{
+	device_t		client;
+	device_t		host1x;
+	struct tegra_drm 	*drm;
+};
+
+/**
+ * Call client exit method
+ */
+METHOD int exit_client{
+	device_t		client;
+	device_t		host1x;
+	struct tegra_drm 	*drm;
+};


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_drm_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,178 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_drm_subr.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_edid.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+#include <gnu/dts/include/dt-bindings/gpio/gpio.h>
+
+int
+tegra_drm_connector_get_modes(struct drm_connector *connector)
+{
+	struct tegra_drm_encoder *output;
+	struct edid *edid = NULL;
+	int rv;
+
+	output = container_of(connector, struct tegra_drm_encoder,
+	     connector);
+
+	/* Panel is first */
+	if (output->panel != NULL) {
+		/* XXX panel parsing */
+		return (0);
+	}
+
+	/* static EDID is second*/
+	edid = output->edid;
+
+	/* EDID from monitor is last */
+	if (edid == NULL)
+		edid = drm_get_edid(connector, output->ddc);
+
+	if (edid == NULL)
+		return (0);
+
+	/* Process EDID */
+	drm_mode_connector_update_edid_property(connector, edid);
+	rv = drm_add_edid_modes(connector, edid);
+	drm_edid_to_eld(connector, edid);
+	return (rv);
+}
+
+struct drm_encoder *
+tegra_drm_connector_best_encoder(struct drm_connector *connector)
+{
+	struct tegra_drm_encoder *output;
+
+	output = container_of(connector, struct tegra_drm_encoder,
+	     connector);
+
+	return &(output->encoder);
+}
+
+enum drm_connector_status
+tegra_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct tegra_drm_encoder *output;
+	bool active;
+	int rv;
+
+	output = container_of(connector, struct tegra_drm_encoder,
+	     connector);
+	if (output->gpio_hpd == NULL) {
+		return ((output->panel != NULL) ?
+		    connector_status_connected:
+		    connector_status_disconnected);
+	}
+
+	rv = gpio_pin_is_active(output->gpio_hpd, &active);
+	if (rv  != 0) {
+		device_printf(output->dev, " GPIO read failed: %d\n", rv);
+		return (connector_status_unknown);
+	}
+
+	return (active ?
+	    connector_status_connected : connector_status_disconnected);
+}
+
+int
+tegra_drm_encoder_attach(struct tegra_drm_encoder *output, phandle_t node)
+{
+	int rv;
+	phandle_t ddc;
+
+	/* XXX parse output panel here */
+
+	rv = OF_getencprop_alloc(node, "nvidia,edid", 1,
+	    (void **)&output->edid);
+
+	/* EDID exist but have invalid size */
+	if ((rv >= 0) && (rv != sizeof(struct edid))) {
+		device_printf(output->dev,
+		    "Malformed \"nvidia,edid\" property\n");
+		if (output->edid != NULL)
+			free(output->edid, M_OFWPROP);
+		return (ENXIO);
+	}
+
+	gpio_pin_get_by_ofw_property(output->dev, node, "nvidia,hpd-gpio",
+	    &output->gpio_hpd);
+	ddc = 0;
+	OF_getencprop(node, "nvidia,ddc-i2c-bus", &ddc, sizeof(ddc));
+	if (ddc > 0)
+		output->ddc = OF_device_from_xref(ddc);
+	if ((output->edid == NULL) && (output->ddc == NULL))
+		return (ENXIO);
+
+	if (output->gpio_hpd != NULL) {
+		output->connector.polled =
+//		    DRM_CONNECTOR_POLL_HPD;
+		    DRM_CONNECTOR_POLL_DISCONNECT |
+		    DRM_CONNECTOR_POLL_CONNECT;
+	}
+
+	return (0);
+}
+
+int tegra_drm_encoder_init(struct tegra_drm_encoder *output,
+    struct tegra_drm *drm)
+{
+
+	if (output->panel) {
+		/* attach panel */
+	}
+	return (0);
+}
+
+int tegra_drm_encoder_exit(struct tegra_drm_encoder *output,
+    struct tegra_drm *drm)
+{
+
+	if (output->panel) {
+		/* detach panel */
+	}
+	return (0);
+}
\ No newline at end of file


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_drm_subr.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_fb.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_fb.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_fb.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,339 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_fb.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+static void
+fb_destroy(struct drm_framebuffer *drm_fb)
+{
+	struct tegra_fb *fb;
+	struct tegra_bo *bo;
+	unsigned int i;
+
+	fb = container_of(drm_fb, struct tegra_fb, drm_fb);
+	for (i = 0; i < fb->nplanes; i++) {
+		bo = fb->planes[i];
+		if (bo != NULL)
+			drm_gem_object_unreference_unlocked(&bo->gem_obj);
+	}
+
+	drm_framebuffer_cleanup(drm_fb);
+	free(fb->planes, DRM_MEM_DRIVER);
+}
+
+static int
+fb_create_handle(struct drm_framebuffer *drm_fb, struct drm_file *file,
+ unsigned int *handle)
+{
+	struct tegra_fb *fb;
+	int rv;
+
+	fb = container_of(drm_fb, struct tegra_fb, drm_fb);
+	rv = drm_gem_handle_create(file, &fb->planes[0]->gem_obj, handle);
+	return (rv);
+}
+
+/* XXX Probably not needed */
+static int
+fb_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv,
+unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips)
+{
+
+	return (0);
+}
+
+static const struct drm_framebuffer_funcs fb_funcs = {
+	.destroy = fb_destroy,
+	.create_handle = fb_create_handle,
+	.dirty = fb_dirty,
+};
+
+static int
+fb_alloc(struct drm_device *drm, struct drm_mode_fb_cmd2 *mode_cmd,
+    struct tegra_bo **planes, int num_planes, struct tegra_fb **res_fb)
+{
+	struct tegra_fb *fb;
+	int i;
+	int rv;
+
+	fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
+	fb->planes = malloc(num_planes * sizeof(*fb->planes), DRM_MEM_DRIVER,
+	    M_WAITOK | M_ZERO);
+	fb->nplanes = num_planes;
+
+	drm_helper_mode_fill_fb_struct(&fb->drm_fb, mode_cmd);
+	for (i = 0; i < fb->nplanes; i++)
+		fb->planes[i] = planes[i];
+	rv = drm_framebuffer_init(drm, &fb->drm_fb, &fb_funcs);
+	if (rv < 0) {
+		device_printf(drm->dev,
+		    "Cannot initialize frame buffer %d\n", rv);
+		free(fb->planes, DRM_MEM_DRIVER);
+		return (rv);
+	}
+	*res_fb = fb;
+	return (0);
+}
+
+static int
+tegra_fb_probe(struct drm_fb_helper *helper,
+    struct drm_fb_helper_surface_size *sizes)
+{
+	u_int bpp, size;
+	struct tegra_drm *drm;
+	struct tegra_fb *fb;
+	struct fb_info *info;
+	struct tegra_bo *bo;
+	struct drm_mode_fb_cmd2 mode_cmd;
+	struct drm_device *drm_dev;
+	int rv;
+
+	if (helper->fb != NULL)
+		return (0);
+
+	DRM_DEBUG_KMS("surface: %d x %d (bpp: %d)\n", sizes->surface_width,
+	    sizes->surface_height, sizes->surface_bpp);
+
+	drm_dev = helper->dev;
+	fb = container_of(helper, struct tegra_fb, fb_helper);
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+	bpp = (sizes->surface_bpp + 7) / 8;
+
+	/* Create mode_cmd */
+	memset(&mode_cmd, 0, sizeof(mode_cmd));
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pitches[0] = roundup(sizes->surface_width * bpp,
+	    drm->pitch_align);
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+	    sizes->surface_depth);
+	size = mode_cmd.pitches[0] * mode_cmd.height;
+
+	DRM_LOCK(drm_dev);
+	rv = tegra_bo_create(drm_dev, size, &bo);
+	DRM_UNLOCK(drm_dev);
+	if (rv != 0)
+		return (rv);
+
+	info = framebuffer_alloc();
+	if (info == NULL) {
+		device_printf(drm_dev->dev,
+		    "Cannot allocate DRM framebuffer info.\n");
+		rv = -ENOMEM;
+		goto err_object;
+	}
+
+	rv = fb_alloc(drm_dev, &mode_cmd,  &bo, 1, &fb);
+	if (rv != 0) {
+		device_printf(drm_dev->dev,
+		     "Cannot allocate DRM framebuffer.\n");
+		goto err_fb;
+	}
+	helper->fb = &fb->drm_fb;
+	helper->fbdev = info;
+
+	/* Fill FB info */
+	info->fb_vbase = bo->vbase;
+	info->fb_pbase = bo->pbase;
+	info->fb_size = size;
+	info->fb_bpp = sizes->surface_bpp;
+	drm_fb_helper_fill_fix(info, fb->drm_fb.pitches[0], fb->drm_fb.depth);
+	drm_fb_helper_fill_var(info, helper, fb->drm_fb.width,
+	    fb->drm_fb.height);
+
+	DRM_DEBUG_KMS("allocated %dx%d (s %dbits) fb size: %d, bo %p\n",
+		      fb->drm_fb.width, fb->drm_fb.height, fb->drm_fb.depth,
+		      size, bo);
+	return (1);
+err_fb:
+	drm_gem_object_unreference_unlocked(&bo->gem_obj);
+	framebuffer_release(info);
+err_object:
+	drm_gem_object_release(&bo->gem_obj);
+	return (rv);
+}
+
+static struct drm_fb_helper_funcs fb_helper_funcs = {
+	.fb_probe = tegra_fb_probe,
+};
+
+/*
+ *	Exported functions
+ */
+struct fb_info *
+tegra_drm_fb_getinfo(struct drm_device *drm_dev)
+{
+	struct tegra_fb *fb;
+	struct tegra_drm *drm;
+
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+	fb = drm->fb;
+	if (fb == NULL)
+		return (NULL);
+	return (fb->fb_helper.fbdev);
+}
+
+struct tegra_bo *
+tegra_fb_get_plane(struct tegra_fb *fb, int idx)
+{
+
+	if (idx >= drm_format_num_planes(fb->drm_fb.pixel_format))
+		return (NULL);
+	if (idx >= fb->nplanes)
+		return (NULL);
+	return (fb->planes[idx]);
+}
+
+int
+tegra_drm_fb_init(struct drm_device *drm_dev)
+{
+	struct tegra_fb *fb;
+	struct tegra_drm *drm;
+	int rv;
+
+	drm = drm_dev->dev_private;
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+	fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
+	drm->fb = fb;
+
+	fb->fb_helper.funcs = &fb_helper_funcs;
+	rv = drm_fb_helper_init(drm_dev, &fb->fb_helper,
+	    drm_dev->mode_config.num_crtc, drm_dev->mode_config.num_connector);
+	if (rv != 0) {
+		device_printf(drm_dev->dev,
+		    "Cannot initialize frame buffer %d\n", rv);
+		return (rv);
+	}
+
+	rv = drm_fb_helper_single_add_all_connectors(&fb->fb_helper);
+	if (rv != 0) {
+		device_printf(drm_dev->dev, "Cannot add all connectors: %d\n",
+		    rv);
+		goto err_fini;
+	}
+
+	rv = drm_fb_helper_initial_config(&fb->fb_helper, 32);
+	if (rv != 0) {
+		device_printf(drm_dev->dev,
+		    "Cannot set initial config: %d\n", rv);
+		goto err_fini;
+	}
+	/* XXXX Setup initial mode for FB */
+	/* drm_fb_helper_set_par(fb->fb_helper.fbdev); */
+	return 0;
+
+err_fini:
+	drm_fb_helper_fini(&fb->fb_helper);
+	return (rv);
+}
+
+int
+tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file,
+    struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res)
+{
+	int hsub, vsub, i;
+	int width, height, size, bpp;
+	struct tegra_bo *planes[4];
+	struct drm_gem_object *gem_obj;
+	struct tegra_fb *fb;
+	int rv, nplanes;
+
+	hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
+	vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
+
+	nplanes = drm_format_num_planes(cmd->pixel_format);
+	for (i = 0; i < nplanes; i++) {
+		width = cmd->width;
+		height = cmd->height;
+		if (i != 0) {
+			width /= hsub;
+			height /= vsub;
+		}
+		gem_obj = drm_gem_object_lookup(drm, file, cmd->handles[i]);
+		if (gem_obj == NULL) {
+			rv = -ENXIO;
+			goto fail;
+		}
+
+		bpp = drm_format_plane_cpp(cmd->pixel_format, i);
+		size = (height - 1) * cmd->pitches[i] +
+		    width * bpp + cmd->offsets[i];
+		if (gem_obj->size < size) {
+			rv = -EINVAL;
+			goto fail;
+		}
+		planes[i] = container_of(gem_obj, struct tegra_bo, gem_obj);
+	}
+
+	rv = fb_alloc(drm, cmd, planes, nplanes, &fb);
+	if (rv != 0)
+		goto fail;
+
+	*fb_res = &fb->drm_fb;
+	return (0);
+
+fail:
+	while (i--)
+		drm_gem_object_unreference_unlocked(&planes[i]->gem_obj);
+	return (rv);
+}
+
+void
+tegra_drm_fb_destroy(struct drm_device *drm_dev)
+{
+	struct fb_info *info;
+	struct tegra_fb *fb;
+	struct tegra_drm *drm;
+
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+	fb = drm->fb;
+	if (fb == NULL)
+		return;
+	info = fb->fb_helper.fbdev;
+	drm_framebuffer_remove(&fb->drm_fb);
+	framebuffer_release(info);
+	drm_fb_helper_fini(&fb->fb_helper);
+	drm_framebuffer_cleanup(&fb->drm_fb);
+	free(fb, DRM_MEM_DRIVER);
+	drm->fb = NULL;
+}


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_fb.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_hdmi.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_hdmi.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_hdmi.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1327 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_hdmi.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+#include <arm/nvidia/drm2/tegra_hdmi_reg.h>
+#include <arm/nvidia/drm2/tegra_dc_reg.h>
+#include <arm/nvidia/drm2/hdmi.h>
+
+#include "tegra_dc_if.h"
+#include "tegra_drm_if.h"
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, 4 * (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, 4 * (_r))
+
+/* HDA stream format verb. */
+#define AC_FMT_CHAN_GET(x)		(((x) >>  0) & 0xf)
+#define AC_FMT_CHAN_BITS_GET(x)		(((x) >>  4) & 0x7)
+#define AC_FMT_DIV_GET(x)		(((x) >>  8) & 0x7)
+#define AC_FMT_MUL_GET(x)		(((x) >> 11) & 0x7)
+#define AC_FMT_BASE_44K			(1 << 14)
+#define AC_FMT_TYPE_NON_PCM		(1 << 15)
+
+#define	HDMI_REKEY_DEFAULT		56
+#define HDMI_ELD_BUFFER_SIZE		96
+
+#define	HDMI_DC_CLOCK_MULTIPIER		2
+
+struct audio_reg {
+	uint32_t	audio_clk;
+	bus_size_t	acr_reg;
+	bus_size_t	nval_reg;
+	bus_size_t	aval_reg;
+};
+
+static const struct audio_reg audio_regs[] =
+{
+	{
+		.audio_clk = 32000,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320,
+	},
+	{
+		.audio_clk = 44100,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441,
+	},
+	{
+		.audio_clk = 88200,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882,
+	},
+	{
+		.audio_clk = 176400,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764,
+	},
+	{
+		.audio_clk = 48000,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480,
+	},
+	{
+		.audio_clk = 96000,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960,
+	},
+	{
+		.audio_clk = 192000,
+		.acr_reg = HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW,
+		.nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920,
+		.aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920,
+	},
+};
+
+struct tmds_config {
+	uint32_t pclk;
+	uint32_t pll0;
+	uint32_t pll1;
+	uint32_t drive_c;
+	uint32_t pe_c;
+	uint32_t peak_c;
+	uint32_t pad_ctls;
+};
+
+static const struct tmds_config tegra124_tmds_config[] =
+{
+	{	/* 480p/576p / 25.2MHz/27MHz */
+		.pclk = 27000000,
+		.pll0 = 0x01003010,
+		.pll1 = 0x00301B00,
+		.drive_c = 0x1F1F1F1F,
+		.pe_c = 0x00000000,
+		.peak_c = 0x03030303,
+		.pad_ctls = 0x800034BB,
+	},
+	{	/* 720p/1080i / 74.25MHz  */
+		.pclk = 74250000,
+		.pll0 = 0x01003110,
+		.pll1 = 0x00301500,
+		.drive_c = 0x2C2C2C2C,
+		.pe_c = 0x00000000,
+		.peak_c = 0x07070707,
+		.pad_ctls = 0x800034BB,
+	},
+	{	 /* 1080p / 148.5MHz */
+		.pclk = 148500000,
+		.pll0 = 0x01003310,
+		.pll1 = 0x00301500,
+		.drive_c = 0x33333333,
+		.pe_c = 0x00000000,
+		.peak_c = 0x0C0C0C0C,
+		.pad_ctls = 0x800034BB,
+	},
+	{	/* 2216p / 297MHz  */
+		.pclk = UINT_MAX,
+		.pll0 = 0x01003F10,
+		.pll1 = 0x00300F00,
+		.drive_c = 0x37373737,
+		.pe_c = 0x00000000,
+		.peak_c = 0x17171717,
+		.pad_ctls = 0x800036BB,
+	},
+};
+
+
+struct hdmi_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+	struct resource		*irq_res;
+	void			*irq_ih;
+
+	clk_t			clk_parent;
+	clk_t			clk_hdmi;
+	hwreset_t		hwreset_hdmi;
+	regulator_t		supply_hdmi;
+	regulator_t		supply_pll;
+	regulator_t		supply_vdd;
+
+	uint64_t		pclk;
+	boolean_t 		hdmi_mode;
+
+	int			audio_src_type;
+	int			audio_freq;
+	int			audio_chans;
+
+	struct tegra_drm 	*drm;
+	struct tegra_drm_encoder output;
+
+	const struct tmds_config *tmds_config;
+	int			n_tmds_configs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-hdmi",	1},
+	{NULL,				0},
+};
+
+/* These functions have been copied from newer version of drm_edid.c */
+/* ELD Header Block */
+#define DRM_ELD_HEADER_BLOCK_SIZE	4
+#define DRM_ELD_BASELINE_ELD_LEN	2       /* in dwords! */
+static int drm_eld_size(const uint8_t *eld)
+{
+	return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
+}
+
+static int
+drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
+    struct drm_display_mode *mode)
+{
+	int rv;
+
+	if (!frame || !mode)
+		return -EINVAL;
+
+	rv = hdmi_avi_infoframe_init(frame);
+	if (rv < 0)
+		return rv;
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		frame->pixel_repeat = 1;
+
+	frame->video_code = drm_match_cea_mode(mode);
+
+	frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
+#ifdef FREEBSD_NOTYET
+	/*
+	 * Populate picture aspect ratio from either
+	 * user input (if specified) or from the CEA mode list.
+	*/
+	if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 ||
+	    mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9)
+		frame->picture_aspect = mode->picture_aspect_ratio;
+	else if (frame->video_code > 0)
+		frame->picture_aspect = drm_get_cea_aspect_ratio(
+		    frame->video_code);
+#endif
+
+	frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
+	frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
+
+	return 0;
+}
+/* --------------------------------------------------------------------- */
+
+static int
+hdmi_setup_clock(struct tegra_drm_encoder *output, clk_t clk, uint64_t pclk)
+{
+	struct hdmi_softc *sc;
+	uint64_t freq;
+	int rv;
+
+	sc = device_get_softc(output->dev);
+
+	/* Disable consumers clock for while. */
+	rv = clk_disable(sc->clk_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot disable 'hdmi' clock\n");
+		return (rv);
+	}
+	rv = clk_disable(clk);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot disable display clock\n");
+		return (rv);
+	}
+
+	/* Set frequency  for Display Controller PLL. */
+	freq = HDMI_DC_CLOCK_MULTIPIER * pclk;
+	rv = clk_set_freq(sc->clk_parent, freq, 0);
+	if (rv != 0) {
+		device_printf(output->dev,
+		    "Cannot set display pixel frequency\n");
+		return (rv);
+	}
+
+	/* Reparent display controller */
+	rv = clk_set_parent_by_clk(clk, sc->clk_parent);
+	if (rv != 0) {
+		device_printf(output->dev, "Cannot set parent clock\n");
+		return (rv);
+
+	}
+	rv = clk_set_freq(clk, freq, 0);
+	if (rv != 0) {
+		device_printf(output->dev,
+		    "Cannot set display controller frequency\n");
+		return (rv);
+	}
+	rv = clk_set_freq(sc->clk_hdmi, pclk, 0);
+	if (rv != 0) {
+		device_printf(output->dev,
+		    "Cannot set display controller frequency\n");
+		return (rv);
+	}
+
+	/* And reenable consumers clock. */
+	rv = clk_enable(clk);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable display clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'hdmi' clock\n");
+		return (rv);
+	}
+
+	rv = clk_get_freq(clk, &freq);
+	if (rv != 0) {
+		device_printf(output->dev,
+		    "Cannot get display controller frequency\n");
+		return (rv);
+	}
+
+	DRM_DEBUG_KMS("DC frequency: %llu\n", freq);
+
+	return (0);
+}
+
+/* -------------------------------------------------------------------
+ *
+ *	Infoframes.
+ *
+ */
+static void
+avi_setup_infoframe(struct hdmi_softc *sc, struct drm_display_mode *mode)
+{
+	struct hdmi_avi_infoframe frame;
+	uint8_t buf[17], *hdr, *pb;;
+	ssize_t rv;
+
+	rv = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+	if (rv < 0) {
+		device_printf(sc->dev, "Cannot setup AVI infoframe: %zd\n", rv);
+		return;
+	}
+	rv = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
+	if (rv < 0) {
+		device_printf(sc->dev, "Cannot pack AVI infoframe: %zd\n", rv);
+		return;
+	}
+	hdr = buf + 0;
+	pb = buf + 3;
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER,
+	    (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0));
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW,
+	    (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0));
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH,
+	    (pb[6] << 16) | (pb[5] << 8) | (pb[4] << 0));
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW,
+	    (pb[10] << 24) |(pb[9] << 16) | (pb[8] << 8) | (pb[7] << 0));
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH,
+	    (pb[13] << 16) | (pb[12] << 8) | (pb[11] << 0));
+
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL,
+	   AVI_INFOFRAME_CTRL_ENABLE);
+}
+
+static void
+audio_setup_infoframe(struct hdmi_softc *sc)
+{
+	struct hdmi_audio_infoframe frame;
+	uint8_t buf[14], *hdr, *pb;
+	ssize_t rv;
+
+
+	rv = hdmi_audio_infoframe_init(&frame);
+	frame.channels = sc->audio_chans;
+	rv = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf));
+	if (rv < 0) {
+		device_printf(sc->dev, "Cannot pack audio infoframe\n");
+		return;
+	}
+	hdr = buf + 0;
+	pb = buf + 3;
+	WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER,
+	    (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0));
+	WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW,
+	    (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0));
+	WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH,
+	    (pb[5] << 8) | (pb[4] << 0));
+
+	WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL,
+	   AUDIO_INFOFRAME_CTRL_ENABLE);
+}
+
+/* -------------------------------------------------------------------
+ *
+ *	Audio
+ *
+ */
+static void
+init_hda_eld(struct hdmi_softc *sc)
+{
+	size_t size;
+	int i ;
+	uint32_t val;
+
+	size = drm_eld_size(sc->output.connector.eld);
+	for (i = 0; i < HDMI_ELD_BUFFER_SIZE; i++) {
+		val = i << 8;
+		if (i < size)
+			val |= sc->output.connector.eld[i];
+		WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR, val);
+	}
+	WR4(sc,HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE,
+	    SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT);
+}
+
+static int
+get_audio_regs(int freq, bus_size_t *acr_reg, bus_size_t *nval_reg,
+    bus_size_t *aval_reg)
+{
+	int i;
+	const struct audio_reg *reg;
+
+	for (i = 0; i < nitems(audio_regs) ; i++) {
+		reg = audio_regs + i;
+		if (reg->audio_clk == freq) {
+			if (acr_reg != NULL)
+				*acr_reg = reg->acr_reg;
+			if (nval_reg != NULL)
+				*nval_reg = reg->nval_reg;
+			if (aval_reg != NULL)
+				*aval_reg = reg->aval_reg;
+			return (0);
+		}
+	}
+	return (ERANGE);
+}
+
+#define FR_BITS 16
+#define TO_FFP(x) (((int64_t)(x)) << FR_BITS)
+#define TO_INT(x) ((int)((x) >> FR_BITS))
+static int
+get_hda_cts_n(uint32_t audio_freq_hz, uint32_t pixclk_freq_hz,
+    uint32_t *best_cts, uint32_t *best_n, uint32_t *best_a)
+{
+	int min_n;
+	int max_n;
+	int ideal_n;
+	int n;
+	int cts;
+	int aval;
+	int64_t err_f;
+	int64_t min_err_f;
+	int64_t cts_f;
+	int64_t aval_f;
+	int64_t half_f;		/* constant 0.5 */
+	bool better_n;
+
+	/*
+	 * All floats are in fixed I48.16 format.
+	 *
+	 * Ideal ACR interval is 1000 hz (1 ms);
+	 * acceptable is 300 hz .. 1500 hz
+	 */
+	min_n = 128 * audio_freq_hz / 1500;
+	max_n = 128 * audio_freq_hz / 300;
+	ideal_n = 128 * audio_freq_hz / 1000;
+	min_err_f = TO_FFP(100);
+	half_f = TO_FFP(1) / 2;
+
+	*best_n = 0;
+	*best_cts = 0;
+	*best_a = 0;
+
+	for (n = min_n; n <= max_n; n++) {
+		cts_f = TO_FFP(pixclk_freq_hz);
+		cts_f *= n;
+		cts_f /= 128 * audio_freq_hz;
+		cts = TO_INT(cts_f + half_f);		/* round */
+		err_f = cts_f - TO_FFP(cts);
+		if (err_f < 0)
+			err_f = -err_f;
+		aval_f = TO_FFP(24000000);
+		aval_f *= n;
+		aval_f /= 128 * audio_freq_hz;
+		aval = TO_INT(aval_f);			/* truncate */
+
+		better_n = abs(n - ideal_n) < abs((int)(*best_n) - ideal_n);
+		if (TO_FFP(aval) == aval_f &&
+		    (err_f < min_err_f || (err_f == min_err_f && better_n))) {
+
+			min_err_f = err_f;
+			*best_n = (uint32_t)n;
+			*best_cts = (uint32_t)cts;
+			*best_a = (uint32_t)aval;
+
+			if (err_f == 0 && n == ideal_n)
+				break;
+		}
+	}
+	return (0);
+}
+#undef FR_BITS
+#undef TO_FFP
+#undef TO_INT
+
+static int
+audio_setup(struct hdmi_softc *sc)
+{
+	uint32_t val;
+	uint32_t audio_n;
+	uint32_t audio_cts;
+	uint32_t audio_aval;
+	uint64_t hdmi_freq;
+	bus_size_t aval_reg;
+	int rv;
+
+	if (!sc->hdmi_mode)
+		return (ENOTSUP);
+	rv  = get_audio_regs(sc->audio_freq, NULL, NULL, &aval_reg);
+	if (rv != 0) {
+		device_printf(sc->dev, "Unsupported audio frequency.\n");
+		return (rv);
+	}
+
+	rv = clk_get_freq(sc->clk_hdmi, &hdmi_freq);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get hdmi frequency: %d\n", rv);
+		return (rv);
+	}
+
+	rv = get_hda_cts_n(sc->audio_freq, hdmi_freq, &audio_cts, &audio_n,
+	    &audio_aval);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot compute audio coefs: %d\n", rv);
+		return (rv);
+	}
+
+	/* Audio infoframe. */
+	audio_setup_infoframe(sc);
+	/* Setup audio source */
+	WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0,
+	    SOR_AUDIO_CNTRL0_SOURCE_SELECT(sc->audio_src_type) |
+	    SOR_AUDIO_CNTRL0_INJECT_NULLSMPL);
+
+	val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
+	val |= SOR_AUDIO_SPARE0_HBR_ENABLE;
+	WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0, val);
+
+	WR4(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL, 0);
+
+	WR4(sc, HDMI_NV_PDISP_AUDIO_N,
+	    AUDIO_N_RESETF |
+	    AUDIO_N_GENERATE_ALTERNATE |
+	    AUDIO_N_VALUE(audio_n - 1));
+
+	WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH,
+	    ACR_SUBPACK_N(audio_n) | ACR_ENABLE);
+
+	WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW,
+	    ACR_SUBPACK_CTS(audio_cts));
+
+	WR4(sc, HDMI_NV_PDISP_HDMI_SPARE,
+	    SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1));
+
+	val = RD4(sc, HDMI_NV_PDISP_AUDIO_N);
+	val &= ~AUDIO_N_RESETF;
+	WR4(sc, HDMI_NV_PDISP_AUDIO_N, val);
+
+	WR4(sc, aval_reg, audio_aval);
+
+	return (0);
+}
+
+static void
+audio_disable(struct hdmi_softc *sc) {
+	uint32_t val;
+
+	/* Disable audio */
+	val = RD4(sc,  HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
+	val &= ~GENERIC_CTRL_AUDIO;
+	WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val);
+
+	/* Disable audio infoframes */
+	val = RD4(sc,  HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
+	val &= ~AUDIO_INFOFRAME_CTRL_ENABLE;
+	WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val);
+}
+
+static void
+audio_enable(struct hdmi_softc *sc) {
+	uint32_t val;
+
+	if (!sc->hdmi_mode)
+		audio_disable(sc);
+
+	/* Enable audio infoframes */
+	val = RD4(sc,  HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
+	val |= AUDIO_INFOFRAME_CTRL_ENABLE;
+	WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val);
+
+	/* Enable audio */
+	val = RD4(sc,  HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
+	val |= GENERIC_CTRL_AUDIO;
+	WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val);
+}
+
+/* -------------------------------------------------------------------
+ *
+ *    HDMI.
+ *
+ */
+ /* Process format change notification from HDA */
+static void
+hda_intr(struct hdmi_softc *sc)
+{
+	uint32_t val;
+	int rv;
+
+	if (!sc->hdmi_mode)
+		return;
+
+	val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
+	if ((val & (1 << 30)) == 0) {
+		audio_disable(sc);
+		return;
+	}
+
+	/* XXX Move this to any header */
+	/* Keep in sync with HDA */
+	sc->audio_freq =  val & 0x00FFFFFF;
+	sc->audio_chans = (val >> 24) & 0x0f;
+	DRM_DEBUG_KMS("%d channel(s) at %dHz\n", sc->audio_chans,
+	    sc->audio_freq);
+
+	rv = audio_setup(sc);
+	if (rv != 0) {
+		audio_disable(sc);
+		return;
+	}
+
+	audio_enable(sc);
+}
+
+static void
+tmds_init(struct hdmi_softc *sc, const struct tmds_config *tmds)
+{
+
+	WR4(sc, HDMI_NV_PDISP_SOR_PLL0, tmds->pll0);
+	WR4(sc, HDMI_NV_PDISP_SOR_PLL1, tmds->pll1);
+	WR4(sc, HDMI_NV_PDISP_PE_CURRENT, tmds->pe_c);
+	WR4(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, tmds->drive_c);
+	WR4(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT, tmds->peak_c);
+	WR4(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0, tmds->pad_ctls);
+}
+
+static int
+hdmi_sor_start(struct hdmi_softc *sc, struct drm_display_mode *mode)
+{
+	int i;
+	uint32_t val;
+
+	/* Enable TMDS macro */
+	val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+	val &= ~SOR_PLL0_PWR;
+	val &= ~SOR_PLL0_VCOPD;
+	val &= ~SOR_PLL0_PULLDOWN;
+	WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+	DELAY(10);
+
+	val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+	val &= ~SOR_PLL0_PDBG;
+	WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+
+	WR4(sc, HDMI_NV_PDISP_SOR_PWR, SOR_PWR_SETTING_NEW);
+	WR4(sc, HDMI_NV_PDISP_SOR_PWR, 0);
+
+	/* Wait until SOR is ready */
+	for (i = 1000; i > 0; i--) {
+		val = RD4(sc, HDMI_NV_PDISP_SOR_PWR);
+		if ((val & SOR_PWR_SETTING_NEW) == 0)
+			break;
+		DELAY(10);
+	}
+	if (i <= 0) {
+		device_printf(sc->dev, "Timeouted while enabling SOR power.\n");
+		return (ETIMEDOUT);
+	}
+
+	val = SOR_STATE2_ASY_OWNER(ASY_OWNER_HEAD0) |
+	    SOR_STATE2_ASY_SUBOWNER(SUBOWNER_BOTH) |
+	    SOR_STATE2_ASY_CRCMODE(ASY_CRCMODE_COMPLETE) |
+	    SOR_STATE2_ASY_PROTOCOL(ASY_PROTOCOL_SINGLE_TMDS_A);
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		val |= SOR_STATE2_ASY_HSYNCPOL_NEG;
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		val |= SOR_STATE2_ASY_VSYNCPOL_NEG;
+	WR4(sc, HDMI_NV_PDISP_SOR_STATE2, val);
+
+	WR4(sc, HDMI_NV_PDISP_SOR_STATE1, SOR_STATE1_ASY_ORMODE_NORMAL |
+	    SOR_STATE1_ASY_HEAD_OPMODE(ASY_HEAD_OPMODE_AWAKE));
+
+	WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0);
+	WR4(sc, HDMI_NV_PDISP_SOR_STATE0, SOR_STATE0_UPDATE);
+
+	val = RD4(sc, HDMI_NV_PDISP_SOR_STATE1);
+	val |= SOR_STATE1_ATTACHED;
+	WR4(sc, HDMI_NV_PDISP_SOR_STATE1, val);
+
+	WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0);
+
+	return 0;
+}
+
+static int
+hdmi_disable(struct hdmi_softc *sc)
+{
+	struct tegra_crtc *crtc;
+	device_t dc;
+	uint32_t val;
+
+	dc = NULL;
+	if (sc->output.encoder.crtc != NULL) {
+		crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc,
+		     drm_crtc);
+		dc = crtc->dev;
+	}
+
+	if (dc != NULL) {
+		TEGRA_DC_HDMI_ENABLE(dc, false);
+		TEGRA_DC_DISPLAY_ENABLE(dc, false);
+	}
+	audio_disable(sc);
+	val = RD4(sc,  HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
+	val &= ~AVI_INFOFRAME_CTRL_ENABLE;
+	WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, val);
+
+	/* Disable interrupts */
+	WR4(sc, HDMI_NV_PDISP_INT_ENABLE, 0);
+	WR4(sc, HDMI_NV_PDISP_INT_MASK, 0);
+
+	return (0);
+}
+
+static int
+hdmi_enable(struct hdmi_softc *sc)
+{
+	uint64_t freq;
+	struct drm_display_mode *mode;
+	struct tegra_crtc *crtc;
+	uint32_t val, h_sync_width, h_back_porch, h_front_porch, h_pulse_start;
+	uint32_t h_max_ac_packet, div8_2;
+	device_t dc;
+	int i, rv;
+
+	mode = &sc->output.encoder.crtc->mode;
+	crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc,
+	    drm_crtc);
+	dc = crtc->dev;
+
+	/* Compute all timings first. */
+	sc->pclk = mode->clock * 1000;
+	h_sync_width = mode->hsync_end - mode->hsync_start;
+	h_back_porch = mode->htotal - mode->hsync_end;
+	h_front_porch = mode->hsync_start - mode->hdisplay;
+	h_pulse_start = 1 + h_sync_width + h_back_porch - 10;
+	h_max_ac_packet = (h_sync_width + h_back_porch + h_front_porch -
+	    HDMI_REKEY_DEFAULT - 18) / 32;
+
+	/* Check if HDMI device is connected and detected. */
+	if (sc->output.connector.edid_blob_ptr == NULL) {
+		sc->hdmi_mode = false;
+	} else {
+		sc->hdmi_mode = drm_detect_hdmi_monitor(
+		    (struct edid *)sc->output.connector.edid_blob_ptr->data);
+	}
+
+	/* Get exact HDMI pixel frequency. */
+	rv = clk_get_freq(sc->clk_hdmi, &freq);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'hdmi' clock frequency\n");
+		    return (rv);
+	}
+	DRM_DEBUG_KMS("HDMI frequency: %llu Hz\n", freq);
+
+	/* Wakeup SOR power */
+	val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+	val &= ~SOR_PLL0_PDBG;
+	WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+	DELAY(10);
+
+	val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0);
+	val &= ~SOR_PLL0_PWR;
+	WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val);
+
+	/* Setup timings */
+	TEGRA_DC_SETUP_TIMING(dc, h_pulse_start);
+	WR4(sc, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW,
+	    VSYNC_WINDOW_START(0x200) | VSYNC_WINDOW_END(0x210) |
+	    VSYNC_WINDOW_ENABLE);
+
+	/* Setup video source and adjust video range */
+	val = 0;
+	if  (crtc->nvidia_head != 0)
+		HDMI_SRC_DISPLAYB;
+	if ((mode->hdisplay != 640) || (mode->vdisplay != 480))
+		val |= ARM_VIDEO_RANGE_LIMITED;
+	WR4(sc, HDMI_NV_PDISP_INPUT_CONTROL, val);
+
+	/* Program SOR reference clock - it uses 8.2 fractional divisor */
+	div8_2 = (freq * 4) / 1000000;
+	val = SOR_REFCLK_DIV_INT(div8_2 >> 2) | SOR_REFCLK_DIV_FRAC(div8_2);
+	WR4(sc, HDMI_NV_PDISP_SOR_REFCLK, val);
+
+	/* Setup audio */
+	if (sc->hdmi_mode) {
+		rv = audio_setup(sc);
+		if (rv != 0)
+			sc->hdmi_mode = false;
+	}
+
+	/* Init HDA ELD */
+	init_hda_eld(sc);
+	val = HDMI_CTRL_REKEY(HDMI_REKEY_DEFAULT);
+	val |= HDMI_CTRL_MAX_AC_PACKET(h_max_ac_packet);
+	if (sc->hdmi_mode)
+		val |= HDMI_CTRL_ENABLE;
+	WR4(sc, HDMI_NV_PDISP_HDMI_CTRL, val);
+
+	/* Setup TMDS */
+	for (i = 0; i < sc->n_tmds_configs; i++) {
+		if (sc->pclk <= sc->tmds_config[i].pclk) {
+			tmds_init(sc, sc->tmds_config + i);
+			break;
+		}
+	}
+
+	/* Program sequencer. */
+	WR4(sc, HDMI_NV_PDISP_SOR_SEQ_CTL,
+	    SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) |
+	    SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8));
+
+	val = SOR_SEQ_INST_WAIT_TIME(1) |
+	    SOR_SEQ_INST_WAIT_UNITS(WAIT_UNITS_VSYNC) |
+	    SOR_SEQ_INST_HALT |
+	    SOR_SEQ_INST_DRIVE_PWM_OUT_LO;
+	WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(0), val);
+	WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(8), val);
+
+	val = RD4(sc,HDMI_NV_PDISP_SOR_CSTM);
+	val &= ~SOR_CSTM_LVDS_ENABLE;
+	val &= ~SOR_CSTM_ROTCLK(~0);
+	val |= SOR_CSTM_ROTCLK(2);
+	val &= ~SOR_CSTM_MODE(~0);
+	val |= SOR_CSTM_MODE(CSTM_MODE_TMDS);
+	val |= SOR_CSTM_PLLDIV;
+	WR4(sc, HDMI_NV_PDISP_SOR_CSTM, val);
+
+	TEGRA_DC_DISPLAY_ENABLE(dc, false);
+
+	rv = hdmi_sor_start(sc, mode);
+	if (rv != 0)
+		return (rv);
+
+	TEGRA_DC_HDMI_ENABLE(dc, true);
+	TEGRA_DC_DISPLAY_ENABLE(dc, true);
+
+	/* Enable HDA codec interrupt */
+	WR4(sc, HDMI_NV_PDISP_INT_MASK, INT_CODEC_SCRATCH0);
+	WR4(sc, HDMI_NV_PDISP_INT_ENABLE, INT_CODEC_SCRATCH0);
+
+	if (sc->hdmi_mode) {
+		avi_setup_infoframe(sc, mode);
+		audio_enable(sc);
+	}
+
+	return (0);
+}
+
+/* -------------------------------------------------------------------
+ *
+ *	DRM Interface.
+ *
+ */
+static enum drm_mode_status
+hdmi_connector_mode_valid(struct drm_connector *connector,
+    struct drm_display_mode *mode)
+{
+	struct tegra_drm_encoder *output;
+	struct hdmi_softc *sc;
+	int rv;
+	uint64_t freq;
+
+	output = container_of(connector, struct tegra_drm_encoder,
+	     connector);
+	sc = device_get_softc(output->dev);
+
+	freq = HDMI_DC_CLOCK_MULTIPIER * mode->clock * 1000;
+	rv = clk_test_freq(sc->clk_parent, freq, 0);
+	DRM_DEBUG_KMS("Test HDMI frequency: %u kHz, rv: %d\n", mode->clock, rv);
+	if (rv != 0)
+		return (MODE_NOCLOCK);
+
+	return (MODE_OK);
+}
+
+
+static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
+	.get_modes = tegra_drm_connector_get_modes,
+	.mode_valid = hdmi_connector_mode_valid,
+	.best_encoder = tegra_drm_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs hdmi_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = tegra_drm_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+};
+
+static const struct drm_encoder_funcs hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static void
+hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+
+	/* Empty function. */
+}
+
+static bool
+hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+    const struct drm_display_mode *mode,
+    struct drm_display_mode *adjusted)
+{
+
+	return (true);
+}
+
+static void
+hdmi_encoder_prepare(struct drm_encoder *encoder)
+{
+
+	/* Empty function. */
+}
+
+static void
+hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+
+	/* Empty function. */
+}
+
+static void
+hdmi_encoder_mode_set(struct drm_encoder *encoder,
+    struct drm_display_mode *mode, struct drm_display_mode *adjusted)
+{
+	struct tegra_drm_encoder *output;
+	struct hdmi_softc *sc;
+	int rv;
+
+	output = container_of(encoder, struct tegra_drm_encoder, encoder);
+	sc = device_get_softc(output->dev);
+	rv = hdmi_enable(sc);
+	if (rv != 0)
+		device_printf(sc->dev, "Cannot enable HDMI port\n");
+
+}
+
+static void
+hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct tegra_drm_encoder *output;
+	struct hdmi_softc *sc;
+	int rv;
+
+	output = container_of(encoder, struct tegra_drm_encoder, encoder);
+	sc = device_get_softc(output->dev);
+	if (sc == NULL)
+		return;
+	rv = hdmi_disable(sc);
+	if (rv != 0)
+		device_printf(sc->dev, "Cannot disable HDMI port\n");
+}
+
+static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = {
+	.dpms = hdmi_encoder_dpms,
+	.mode_fixup = hdmi_encoder_mode_fixup,
+	.prepare = hdmi_encoder_prepare,
+	.commit = hdmi_encoder_commit,
+	.mode_set = hdmi_encoder_mode_set,
+	.disable = hdmi_encoder_disable,
+};
+
+/* -------------------------------------------------------------------
+ *
+ *	Bus and infrastructure.
+ *
+ */
+static int
+hdmi_init_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+	struct hdmi_softc *sc;
+	phandle_t node;
+	int rv;
+
+	sc = device_get_softc(dev);
+	node = ofw_bus_get_node(sc->dev);
+	sc->drm = drm;
+	sc->output.setup_clock = &hdmi_setup_clock;
+
+	rv = tegra_drm_encoder_attach(&sc->output, node);
+	if (rv != 0) {
+		device_printf(dev, "Cannot attach output connector\n");
+		return(ENXIO);
+	}
+
+	/* Connect this encoder + connector  to DRM. */
+	drm_connector_init(&drm->drm_dev, &sc->output.connector,
+	   &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_connector_helper_add(&sc->output.connector,
+	    &hdmi_connector_helper_funcs);
+
+	sc->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+	drm_encoder_init(&drm->drm_dev, &sc->output.encoder,
+	    &hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS);
+
+	drm_encoder_helper_add(&sc->output.encoder, &hdmi_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&sc->output.connector,
+	  &sc->output.encoder);
+
+	rv = tegra_drm_encoder_init(&sc->output, drm);
+	if (rv < 0) {
+		device_printf(sc->dev, "Unable to init HDMI output\n");
+		return (rv);
+	}
+	sc->output.encoder.possible_crtcs = 0x3;
+	return (0);
+}
+
+static int
+hdmi_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm)
+{
+	struct hdmi_softc *sc;
+
+	sc = device_get_softc(dev);
+	tegra_drm_encoder_exit(&sc->output, drm);
+	return (0);
+}
+
+static int
+get_fdt_resources(struct hdmi_softc *sc, phandle_t node)
+{
+	int rv;
+
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "hdmi-supply",
+	    &sc->supply_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'hdmi' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev,0,  "pll-supply",
+	    &sc->supply_pll);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pll' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "vdd-supply",
+	    &sc->supply_vdd);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'vdd' regulator\n");
+		return (ENXIO);
+	}
+
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->hwreset_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'hdmi' reset\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'parent' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->clk_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'hdmi' clock\n");
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
+static int
+enable_fdt_resources(struct hdmi_softc *sc)
+{
+	int rv;
+
+
+	rv = clk_set_parent_by_clk(sc->clk_hdmi, sc->clk_parent);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot set parent for 'hdmi' clock\n");
+		return (rv);
+	}
+
+	/* 594 MHz is arbitrarily selected value */
+	rv = clk_set_freq(sc->clk_parent, 594000000, 0);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot set frequency for 'hdmi' parent clock\n");
+		return (rv);
+	}
+	rv = clk_set_freq(sc->clk_hdmi, 594000000 / 4, 0);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot set frequency for 'hdmi' parent clock\n");
+		return (rv);
+	}
+
+	rv = regulator_enable(sc->supply_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable  'hdmi' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_pll);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable  'pll' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_vdd);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable  'vdd' regulator\n");
+		return (rv);
+	}
+
+	rv = clk_enable(sc->clk_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'hdmi' clock\n");
+		return (rv);
+	}
+
+	rv = hwreset_deassert(sc->hwreset_hdmi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot unreset  'hdmi' reset\n");
+		return (rv);
+	}
+	return (0);
+}
+
+static void
+hdmi_intr(void *arg)
+{
+	struct hdmi_softc *sc;
+	uint32_t status;
+
+	sc = arg;
+
+	/* Confirm interrupt */
+	status = RD4(sc, HDMI_NV_PDISP_INT_STATUS);
+	WR4(sc, HDMI_NV_PDISP_INT_STATUS, status);
+
+	/* process audio verb from HDA */
+	if (status & INT_CODEC_SCRATCH0)
+		hda_intr(sc);
+}
+
+static int
+hdmi_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra HDMI");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+hdmi_attach(device_t dev)
+{
+	struct hdmi_softc *sc;
+	phandle_t node;
+	int rid, rv;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->output.dev = sc->dev;
+	node = ofw_bus_get_node(sc->dev);
+
+	sc->audio_src_type = SOURCE_SELECT_AUTO;
+	sc->audio_freq = 44100;
+	sc->audio_chans = 2;
+	sc->hdmi_mode = false;
+
+	sc->tmds_config = tegra124_tmds_config;
+	sc->n_tmds_configs = nitems(tegra124_tmds_config);
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		goto fail;
+	}
+
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate IRQ resources\n");
+		goto fail;
+	}
+
+	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, hdmi_intr, sc, &sc->irq_ih);
+	if (rv != 0) {
+		device_printf(dev,
+		    "WARNING: unable to register interrupt handler\n");
+		goto fail;
+	}
+
+	rv = get_fdt_resources(sc, node);
+	if (rv != 0) {
+		device_printf(dev, "Cannot parse FDT resources\n");
+		goto fail;
+	}
+	rv = enable_fdt_resources(sc);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable FDT resources\n");
+		goto fail;
+	}
+
+	rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+	if (rv != 0) {
+		device_printf(dev, "Cannot register DRM device\n");
+		goto fail;
+	}
+	return (bus_generic_attach(dev));
+
+fail:
+	TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	if (sc->clk_parent != NULL)
+		clk_release(sc->clk_parent);
+	if (sc->clk_hdmi != NULL)
+		clk_release(sc->clk_hdmi);
+	if (sc->hwreset_hdmi != NULL)
+		hwreset_release(sc->hwreset_hdmi);
+	if (sc->supply_hdmi != NULL)
+		regulator_release(sc->supply_hdmi);
+	if (sc->supply_pll != NULL)
+		regulator_release(sc->supply_pll);
+	if (sc->supply_vdd != NULL)
+		regulator_release(sc->supply_vdd);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	return (ENXIO);
+}
+
+static int
+hdmi_detach(device_t dev)
+{
+	struct hdmi_softc *sc;
+	sc = device_get_softc(dev);
+
+	TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
+
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	if (sc->clk_parent != NULL)
+		clk_release(sc->clk_parent);
+	if (sc->clk_hdmi != NULL)
+		clk_release(sc->clk_hdmi);
+	if (sc->hwreset_hdmi != NULL)
+		hwreset_release(sc->hwreset_hdmi);
+	if (sc->supply_hdmi != NULL)
+		regulator_release(sc->supply_hdmi);
+	if (sc->supply_pll != NULL)
+		regulator_release(sc->supply_pll);
+	if (sc->supply_vdd != NULL)
+		regulator_release(sc->supply_vdd);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_hdmi_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,			hdmi_probe),
+	DEVMETHOD(device_attach,		hdmi_attach),
+	DEVMETHOD(device_detach,		hdmi_detach),
+
+	/* tegra drm interface */
+	DEVMETHOD(tegra_drm_init_client,	hdmi_init_client),
+	DEVMETHOD(tegra_drm_exit_client,	hdmi_exit_client),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_hdmi_devclass;
+DEFINE_CLASS_0(tegra_hdmi, tegra_hdmi_driver, tegra_hdmi_methods,
+    sizeof(struct hdmi_softc));
+DRIVER_MODULE(tegra_hdmi, host1x, tegra_hdmi_driver,
+tegra_hdmi_devclass, 0, 0);


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_hdmi.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,286 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright 1992-2016 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_hdmi_reg.h 310600 2016-12-26 14:36:05Z mmel $
+ */
+#ifndef _TEGRA_HDMI_REG_H_
+#define	_TEGRA_HDMI_REG_H_
+
+/*
+ * !!! WARNING !!!
+ * Tegra manual uses registers index (and not register addreses).
+ * We follow the TRM notation and index is converted to offset in
+ * WR4 / RD4 macros
+ */
+#define	HDMI_NV_PDISP_SOR_STATE0		0x001
+#define	 SOR_STATE0_UPDATE				(1 << 0)
+
+#define	HDMI_NV_PDISP_SOR_STATE1		0x002
+#define	 SOR_STATE1_ATTACHED				(1 << 3)
+#define	 SOR_STATE1_ASY_ORMODE_NORMAL			(1 << 2)
+#define	 SOR_STATE1_ASY_HEAD_OPMODE(x)			(((x) & 0x3) << 0)
+#define	  ASY_HEAD_OPMODE_SLEEP					0
+#define	  ASY_HEAD_OPMODE_SNOOZE				1
+#define	  ASY_HEAD_OPMODE_AWAKE					2
+
+#define	HDMI_NV_PDISP_SOR_STATE2		0x003
+#define	 SOR_STATE2_ASY_DEPOL_NEG			(1 << 14)
+#define	 SOR_STATE2_ASY_VSYNCPOL_NEG			(1 << 13)
+#define	 SOR_STATE2_ASY_HSYNCPOL_NEG			(1 << 12)
+#define	 SOR_STATE2_ASY_PROTOCOL(x)			(((x) & 0xf) << 8)
+#define	  ASY_PROTOCOL_SINGLE_TMDS_A				1
+#define	  ASY_PROTOCOL_CUSTOM					15
+#define	 SOR_STATE2_ASY_CRCMODE(x)			(((x) & 0x3) <<  6)
+#define	  ASY_CRCMODE_ACTIVE					0
+#define	  ASY_CRCMODE_COMPLETE					1
+#define	  ASY_CRCMODE_NON_ACTIVE				2
+#define	 SOR_STATE2_ASY_SUBOWNER(x)			(((x) & 0x3) <<  4)
+#define	  ASY_SUBOWNER_NONE					0
+#define	  ASY_SUBOWNER_SUBHEAD0					1
+#define	  ASY_SUBOWNER_SUBHEAD1					2
+#define	  SUBOWNER_BOTH						3
+#define	 SOR_STATE2_ASY_OWNER(x)			(((x) & 0x3) <<  0)
+#define	  ASY_OWNER_NONE					0
+#define	  ASY_OWNER_HEAD0					1
+
+#define	HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL	0x01e
+#define	 AUDIO_INFOFRAME_CTRL_ENABLE			(1 << 0)
+#define	HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS 0x01f
+#define	HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER 0x020
+#define	HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW 0x021
+#define	HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH 0x022
+#define	 INFOFRAME_HEADER_LEN(x)			(((x) & 0x0f) << 16)
+#define	 INFOFRAME_HEADER_VERSION(x)			(((x) & 0xff) <<  8)
+#define	 INFOFRAME_HEADER_TYPE(x)			(((x) & 0xff) <<  0)
+
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL	0x023
+#define	 AVI_INFOFRAME_CTRL_ENABLE			(1 << 0)
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS	0x024
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER	0x025
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW  0x026
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH 0x027
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW  0x028
+#define	HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH 0x029
+
+#define	HDMI_NV_PDISP_HDMI_GENERIC_CTRL		0x02a
+#define	 GENERIC_CTRL_AUDIO				(1 << 16)
+#define	 GENERIC_CTRL_HBLANK				(1 << 12)
+#define	 GENERIC_CTRL_SINGLE				(1 <<  8)
+#define	 GENERIC_CTRL_OTHER				(1 <<  4)
+#define	 GENERIC_CTRL_ENABLE				(1 <<  0)
+#define	HDMI_NV_PDISP_HDMI_GENERIC_STATUS	0x02b
+#define	HDMI_NV_PDISP_HDMI_GENERIC_HEADER	0x02c
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW	 0x02d
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH 0x02e
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW	 0x02f
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH 0x030
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW	 0x031
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH 0x032
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW	 0x033
+#define	HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH 0x034
+
+#define	HDMI_NV_PDISP_HDMI_ACR_CTRL		0x035
+#define	HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW	 0x036
+#define	HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH 0x037
+#define	HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW	 0x038
+#define	HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH 0x039
+#define	HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW	 0x03a
+#define	HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH 0x03b
+#define	HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW	 0x03c
+#define	HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH 0x03d
+#define	HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW	 0x03e
+#define	HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH 0x03f
+#define	HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW	 0x040
+#define	HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH 0x041
+#define	HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW	 0x042
+#define	HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH 0x043
+#define	 ACR_ENABLE					(1U << 31)
+#define	 ACR_SUBPACK_CTS(x)				(((x) & 0xffffff) << 8)
+#define	 ACR_SUBPACK_N(x)				(((x) & 0xffffff) << 0)
+
+#define	HDMI_NV_PDISP_HDMI_CTRL			0x044
+#define	 HDMI_CTRL_ENABLE				(1 << 30)
+#define	 HDMI_CTRL_CA_SELECT				(1 << 28)
+#define	 HDMI_CTRL_SS_SELECT				(1 << 27)
+#define	 HDMI_CTRL_SF_SELECT				(1 << 26)
+#define	 HDMI_CTRL_CC_SELECT				(1 << 25)
+#define	 HDMI_CTRL_CT_SELECT				(1 << 24)
+#define	 HDMI_CTRL_MAX_AC_PACKET(x)			(((x) & 0x1f) << 16)
+#define	 HDMI_CTRL_SAMPLE_FLAT				(1 << 12)
+#define	 HDMI_CTRL_AUDIO_LAYOUT_SELECT			(1 << 10)
+#define	 HDMI_CTRL_AUDIO_LAYOUT				(1 <<  8)
+#define	 HDMI_CTRL_REKEY(x)				(((x) & 0x7f) <<  0)
+
+#define	HDMI_NV_PDISP_HDMI_VSYNC_WINDOW		0x046
+#define	 VSYNC_WINDOW_ENABLE				(1U << 31)
+#define	 VSYNC_WINDOW_START(x)				(((x) & 0x3ff) << 16)
+#define	 VSYNC_WINDOW_END(x)				(((x) & 0x3ff) <<  0)
+
+#define	HDMI_NV_PDISP_HDMI_SPARE		0x04f
+#define	 SPARE_ACR_PRIORITY				(1U << 31)
+#define	 SPARE_CTS_RESET_VAL(x)				(((x) & 0x7) << 16)
+#define	 SPARE_SUPRESS_SP_B				(1 << 2)
+#define	 SPARE_FORCE_SW_CTS				(1 << 1)
+#define	 SPARE_HW_CTS					(1 << 0)
+
+#define	HDMI_NV_PDISP_SOR_PWR			0x055
+#define	 SOR_PWR_SETTING_NEW				(1U << 31)
+#define	 SOR_PWR_SAFE_STATE_PU				(1 << 16)
+#define	 SOR_PWR_NORMAL_START_ALT			(1 <<  1)
+#define	 SOR_PWR_NORMAL_STATE_PU			(1 <<  0)
+
+#define	HDMI_NV_PDISP_SOR_PLL0			0x057
+#define	 SOR_PLL0_TX_REG_LOAD(x)			(((x) & 0xf) << 28)
+#define	 SOR_PLL0_ICHPMP(x)				(((x) & 0xf) << 24)
+#define	 SOR_PLL0_FILTER(x)				(((x) & 0xf) << 16)
+#define	 SOR_PLL0_BG_V17_S(x)				(((x) & 0xf) << 12)
+#define	 SOR_PLL0_VCOCAP(x)				(((x) & 0xf) <<  8)
+#define	 SOR_PLL0_PULLDOWN				(1 << 5)
+#define	 SOR_PLL0_RESISTORSEL				(1 << 4)
+#define	 SOR_PLL0_PDPORT				(1 << 3)
+#define	 SOR_PLL0_VCOPD					(1 << 2)
+#define	 SOR_PLL0_PDBG					(1 << 1)
+#define	 SOR_PLL0_PWR					(1 << 0)
+
+#define	HDMI_NV_PDISP_SOR_PLL1			0x058
+#define	 SOR_PLL1_S_D_PIN_PE				(1 << 30)
+#define	 SOR_PLL1_HALF_FULL_PE				(1 << 29)
+#define	 SOR_PLL1_PE_EN					(1 << 28)
+#define	 SOR_PLL1_LOADADJ(x)				(((x) & 0xf) << 20)
+#define	 SOR_PLL1_TMDS_TERMADJ(x)			(((x) & 0xf) <<  9)
+#define	 SOR_PLL1_TMDS_TERM				(1 << 8)
+
+#define	HDMI_NV_PDISP_SOR_CSTM			0x05a
+#define	 SOR_CSTM_ROTAT(x)				(((x) & 0xf) << 28)
+#define	 SOR_CSTM_ROTCLK(x)				(((x) & 0xf) << 24)
+#define	 SOR_CSTM_PLLDIV				(1 << 21)
+#define	 SOR_CSTM_BALANCED				(1 << 19)
+#define	 SOR_CSTM_NEW_MODE				(1 << 18)
+#define	 SOR_CSTM_DUP_SYNC				(1 << 17)
+#define	 SOR_CSTM_LVDS_ENABLE				(1 << 16)
+#define	 SOR_CSTM_LINKACTB				(1 << 15)
+#define	 SOR_CSTM_LINKACTA				(1 << 14)
+#define	 SOR_CSTM_MODE(x)				(((x) & 0x3) << 12)
+#define	  CSTM_MODE_LVDS					0
+#define	  CSTM_MODE_TMDS					1
+
+#define	HDMI_NV_PDISP_SOR_SEQ_CTL		0x05f
+#define	 SOR_SEQ_SWITCH					(1 << 30)
+#define	 SOR_SEQ_STATUS					(1 << 28)
+#define	 SOR_SEQ_PC(x)					(((x) & 0xf) << 16)
+#define	 SOR_SEQ_PD_PC_ALT(x)				(((x) & 0xf) << 12)
+#define	 SOR_SEQ_PD_PC(x)				(((x) & 0xf) <<  8)
+#define	 SOR_SEQ_PU_PC_ALT(x)				(((x) & 0xf) <<  4)
+#define	 SOR_SEQ_PU_PC(x)				(((x) & 0xf) <<  0)
+
+#define	HDMI_NV_PDISP_SOR_SEQ_INST(x)		(0x060 + (x))
+#define	 SOR_SEQ_INST_PLL_PULLDOWN			(1U << 31)
+#define	 SOR_SEQ_INST_POWERDOWN_MACRO			(1 << 30)
+#define	 SOR_SEQ_INST_ASSERT_PLL_RESETV			(1 << 29)
+#define	 SOR_SEQ_INST_BLANK_V				(1 << 28)
+#define	 SOR_SEQ_INST_BLANK_H				(1 << 27)
+#define	 SOR_SEQ_INST_BLANK_DE				(1 << 26)
+#define	 SOR_SEQ_INST_BLACK_DATA			(1 << 25)
+#define	 SOR_SEQ_INST_TRISTATE_IOS			(1 << 24)
+#define	 SOR_SEQ_INST_DRIVE_PWM_OUT_LO			(1 << 23)
+#define	 SOR_SEQ_INST_PIN_B_HIGH			(1 << 22)
+#define	 SOR_SEQ_INST_PIN_A_HIGH			(1 << 21)
+#define	 SOR_SEQ_INST_HALT				(1 << 15)
+#define	 SOR_SEQ_INST_WAIT_UNITS(x)			(((x) & 0x3) << 12)
+#define	  WAIT_UNITS_US						0
+#define	  WAIT_UNITS_MS						1
+#define	  WAIT_UNITS_VSYNC					2
+#define	 SOR_SEQ_INST_WAIT_TIME(x)			(((x) & 0x3ff) << 0)
+
+#define	HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT	0x07e
+
+
+#define	HDMI_NV_PDISP_AUDIO_N			0x08c
+#define	 AUDIO_N_LOOKUP					(1 << 28)
+#define	 AUDIO_N_GENERATE_ALTERNATE			(1 << 24)
+#define	 AUDIO_N_RESETF					(1 << 20)
+#define	 AUDIO_N_VALUE(x)				(((x) & 0xfffff) << 0)
+
+#define	HDMI_NV_PDISP_SOR_REFCLK		0x095
+#define	 SOR_REFCLK_DIV_INT(x)				(((x) & 0xff) << 8)
+#define	 SOR_REFCLK_DIV_FRAC(x)				(((x) & 0x03) << 6)
+
+#define	HDMI_NV_PDISP_INPUT_CONTROL		0x097
+#define	 ARM_VIDEO_RANGE_LIMITED			(1 << 1)
+#define	 HDMI_SRC_DISPLAYB				(1 << 0)
+
+#define	HDMI_NV_PDISP_PE_CURRENT		0x099
+#define	HDMI_NV_PDISP_SOR_AUDIO_CNTRL0		0x0ac
+#define	 SOR_AUDIO_CNTRL0_INJECT_NULLSMPL		(1 << 29)
+#define	 SOR_AUDIO_CNTRL0_SOURCE_SELECT(x)		(((x) & 0x03) << 20)
+#define	  SOURCE_SELECT_AUTO					0
+#define	  SOURCE_SELECT_SPDIF					1
+#define	  SOURCE_SELECT_HDAL					2
+#define	 SOR_AUDIO_CNTRL0_AFIFO_FLUSH			(1 << 12)
+
+#define	HDMI_NV_PDISP_SOR_AUDIO_SPARE0		0x0ae
+#define	 SOR_AUDIO_SPARE0_HBR_ENABLE			(1 << 27)
+
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320	0x0af
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441	0x0b0
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882	0x0b1
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764	0x0b2
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480	0x0b3
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960	0x0b4
+#define	HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920	0x0b5
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH0	0x0b6
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH1	0x0b7
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH2	0x0b8
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_SCRATCH3	0x0b9
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0x0ba
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1 0x0bb
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR	0x0bc
+#define	HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE	0x0bd
+#define	 SOR_AUDIO_HDA_PRESENSE_VALID			(1 << 1)
+#define	 SOR_AUDIO_HDA_PRESENSE_PRESENT			(1 << 0)
+
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320	0x0bf
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441	0x0c0
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882	0x0c1
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764	0x0c2
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480	0x0c3
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960	0x0c4
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920	0x0c5
+#define	HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT	0x0c6
+
+#define	HDMI_NV_PDISP_INT_STATUS		0x0cc
+#define	 INT_SCRATCH					(1 << 3)
+#define	 INT_CP_REQUEST					(1 << 2)
+#define	 INT_CODEC_SCRATCH1				(1 << 1)
+#define	 INT_CODEC_SCRATCH0				(1 << 0)
+
+#define	HDMI_NV_PDISP_INT_MASK			0x0cd
+#define	HDMI_NV_PDISP_INT_ENABLE		0x0ce
+#define	HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT	0x0d1
+#define	HDMI_NV_PDISP_SOR_PAD_CTLS0		0x0d2
+
+
+#endif /* _TEGRA_HDMI_REG_H_ */


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_hdmi_reg.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/drm2/tegra_host1x.c
===================================================================
--- trunk/sys/arm/nvidia/drm2/tegra_host1x.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/drm2/tegra_host1x.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,651 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/drm2/tegra_host1x.c 310600 2016-12-26 14:36:05Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/sx.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/drm2/drmP.h>
+#include <dev/drm2/drm_crtc_helper.h>
+#include <dev/drm2/drm_fb_helper.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/drm2/tegra_drm.h>
+
+#include "fb_if.h"
+#include "tegra_drm_if.h"
+
+#define	WR4(_sc, _r, _v)	bus_rite_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+#define	LOCK(_sc)		sx_xlock(&(_sc)->lock)
+#define	UNLOCK(_sc)		sx_xunlock(&(_sc)->lock)
+#define	SLEEP(_sc, timeout)	sx_sleep(sc, &sc->lock, 0, "host1x", timeout);
+#define	LOCK_INIT(_sc)		sx_init(&_sc->lock, "host1x")
+#define	LOCK_DESTROY(_sc)	sx_destroy(&_sc->lock)
+#define	ASSERT_LOCKED(_sc)	sx_assert(&_sc->lock, SA_LOCKED)
+#define	ASSERT_UNLOCKED(_sc)	sx_assert(&_sc->lock, SA_UNLOCKED)
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-host1x",	1},
+	{NULL,				0}
+};
+
+#define DRIVER_NAME "tegra"
+#define DRIVER_DESC "NVIDIA Tegra TK1"
+#define DRIVER_DATE "20151101"
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+struct client_info;
+TAILQ_HEAD(client_list, client_info);
+typedef struct client_list client_list_t;
+
+struct client_info {
+	TAILQ_ENTRY(client_info) list_e;
+	device_t client;
+	int 	activated;
+};
+
+struct host1x_softc {
+	struct simplebus_softc	simplebus_sc;	/* must be first */
+	device_t		dev;
+	struct sx		lock;
+	int 			attach_done;
+
+	struct resource		*mem_res;
+	struct resource		*syncpt_irq_res;
+	void			*syncpt_irq_h;
+	struct resource		*gen_irq_res;
+	void			*gen_irq_h;
+
+	clk_t			clk;
+	hwreset_t			reset;
+	struct intr_config_hook	irq_hook;
+
+	int			drm_inited;
+	client_list_t		clients;
+
+	struct tegra_drm 	*tegra_drm;
+};
+
+
+static void
+host1x_output_poll_changed(struct drm_device *drm_dev)
+{
+	struct tegra_drm *drm;
+
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+	if (drm->fb != NULL)
+		drm_fb_helper_hotplug_event(&drm->fb->fb_helper);
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = tegra_drm_fb_create,
+	.output_poll_changed = host1x_output_poll_changed,
+};
+
+
+static int
+host1x_drm_init(struct host1x_softc *sc)
+{
+	struct client_info *entry;
+	int rv;
+
+	LOCK(sc);
+
+	TAILQ_FOREACH(entry, &sc->clients, list_e) {
+		if (entry->activated)
+			continue;
+		rv = TEGRA_DRM_INIT_CLIENT(entry->client, sc->dev,
+		    sc->tegra_drm);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot init DRM client %s: %d\n",
+			    device_get_name(entry->client), rv);
+			return (rv);
+		}
+		entry->activated = 1;
+	}
+	UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+host1x_drm_exit(struct host1x_softc *sc)
+{
+	struct client_info *entry;
+	int rv;
+#ifdef FREEBSD_NOTYET
+	struct drm_device *dev, *tmp;
+#endif
+	LOCK(sc);
+	if (!sc->drm_inited) {
+		UNLOCK(sc);
+		return (0);
+	}
+	TAILQ_FOREACH_REVERSE(entry, &sc->clients, client_list, list_e) {
+		if (!entry->activated)
+			continue;
+		rv = TEGRA_DRM_EXIT_CLIENT(entry->client, sc->dev,
+		    sc->tegra_drm);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot exit DRM client %s: %d\n",
+			    device_get_name(entry->client), rv);
+		}
+		entry->activated = 0;
+	}
+
+#ifdef FREEBSD_NOTYET
+	list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
+		drm_put_dev(dev);
+#endif
+	sc->drm_inited = 0;
+	UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+host1x_drm_load(struct drm_device *drm_dev, unsigned long flags)
+{
+	struct host1x_softc *sc;
+	int rv;
+
+	sc = device_get_softc(drm_dev->dev);
+
+	drm_mode_config_init(drm_dev);
+	drm_dev->mode_config.min_width = 32;
+	drm_dev->mode_config.min_height = 32;
+	drm_dev->mode_config.max_width = 4096;
+	drm_dev->mode_config.max_height = 4096;
+	drm_dev->mode_config.funcs = &mode_config_funcs;
+
+	rv = host1x_drm_init(sc);
+	if (rv != 0)
+		goto fail_host1x;
+
+	drm_dev->irq_enabled = true;
+	drm_dev->max_vblank_count = 0xffffffff;
+	drm_dev->vblank_disable_allowed = true;
+
+	rv = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+	if (rv != 0)
+		goto fail_vblank;
+
+	drm_mode_config_reset(drm_dev);
+
+	rv = tegra_drm_fb_init(drm_dev);
+	if (rv != 0)
+		goto fail_fb;
+	drm_kms_helper_poll_init(drm_dev);
+
+	return (0);
+
+fail_fb:
+	tegra_drm_fb_destroy(drm_dev);
+	drm_vblank_cleanup(drm_dev);
+fail_vblank:
+	host1x_drm_exit(sc);
+fail_host1x:
+	drm_mode_config_cleanup(drm_dev);
+
+	return (rv);
+}
+
+static int
+host1x_drm_unload(struct drm_device *drm_dev)
+{
+	struct host1x_softc *sc;
+	int rv;
+
+	sc = device_get_softc(drm_dev->dev);
+
+	drm_kms_helper_poll_fini(drm_dev);
+	tegra_drm_fb_destroy(drm_dev);
+	drm_mode_config_cleanup(drm_dev);
+
+	rv = host1x_drm_exit(sc);
+	if (rv < 0)
+		return (rv);
+	return (0);
+}
+
+static int
+host1x_drm_open(struct drm_device *drm_dev, struct drm_file *filp)
+{
+
+	return (0);
+}
+
+static void
+tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
+		tegra_dc_cancel_page_flip(crtc, file);
+}
+
+static void
+host1x_drm_lastclose(struct drm_device *drm_dev)
+{
+
+	struct tegra_drm *drm;
+
+	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
+	if (drm->fb  != NULL)
+		drm_fb_helper_restore_fbdev_mode(&drm->fb->fb_helper);
+}
+
+static int
+host1x_drm_enable_vblank(struct drm_device *drm_dev, int pipe)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+		if (pipe == tegra_dc_get_pipe(crtc)) {
+			tegra_dc_enable_vblank(crtc);
+			return (0);
+		}
+	}
+	return (-ENODEV);
+}
+
+static void
+host1x_drm_disable_vblank(struct drm_device *drm_dev, int pipe)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+		if (pipe == tegra_dc_get_pipe(crtc)) {
+			tegra_dc_disable_vblank(crtc);
+			return;
+		}
+	}
+}
+
+
+static struct drm_ioctl_desc host1x_drm_ioctls[] = {
+};
+
+
+struct drm_driver tegra_drm_driver = {
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.load = host1x_drm_load,
+	.unload = host1x_drm_unload,
+	.open = host1x_drm_open,
+	.preclose = tegra_drm_preclose,
+	.lastclose = host1x_drm_lastclose,
+
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = host1x_drm_enable_vblank,
+	.disable_vblank = host1x_drm_disable_vblank,
+
+	/* Fields filled by tegra_bo_driver_register()
+	.gem_free_object
+	.gem_pager_ops
+	.dumb_create
+	.dumb_map_offset
+	.dumb_destroy
+	*/
+	.ioctls = host1x_drm_ioctls,
+	.num_ioctls = nitems(host1x_drm_ioctls),
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+/*
+ * ----------------- Device methods -------------------------
+ */
+static void
+host1x_irq_hook(void *arg)
+{
+	struct host1x_softc *sc;
+	int rv;
+
+	sc = arg;
+	config_intrhook_disestablish(&sc->irq_hook);
+
+	tegra_bo_driver_register(&tegra_drm_driver);
+	rv = drm_get_platform_dev(sc->dev, &sc->tegra_drm->drm_dev,
+	    &tegra_drm_driver);
+	if (rv != 0) {
+		device_printf(sc->dev, "drm_get_platform_dev(): %d\n", rv);
+		return;
+	}
+
+	sc->drm_inited = 1;
+}
+
+static struct fb_info *
+host1x_fb_helper_getinfo(device_t dev)
+{
+	struct host1x_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->tegra_drm == NULL)
+		return (NULL);
+	return (tegra_drm_fb_getinfo(&sc->tegra_drm->drm_dev));
+}
+
+static int
+host1x_register_client(device_t dev, device_t client)
+{
+	struct host1x_softc *sc;
+	struct client_info *entry;
+
+	sc = device_get_softc(dev);
+
+	entry = malloc(sizeof(struct client_info), M_DEVBUF, M_WAITOK | M_ZERO);
+	entry->client = client;
+	entry->activated = 0;
+
+	LOCK(sc);
+	TAILQ_INSERT_TAIL(&sc->clients, entry, list_e);
+	UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+host1x_deregister_client(device_t dev, device_t client)
+{
+	struct host1x_softc *sc;
+	struct client_info *entry;
+
+	sc = device_get_softc(dev);
+
+	LOCK(sc);
+	TAILQ_FOREACH(entry, &sc->clients, list_e) {
+		if (entry->client == client) {
+			if (entry->activated)
+				panic("Tegra DRM: Attempt to deregister "
+				    "activated client");
+			TAILQ_REMOVE(&sc->clients, entry, list_e);
+			free(entry, M_DEVBUF);
+			UNLOCK(sc);
+			return (0);
+		}
+	}
+	UNLOCK(sc);
+
+	return (0);
+}
+
+static void
+host1x_gen_intr(void *arg)
+{
+	struct host1x_softc *sc;
+
+	sc = (struct host1x_softc *)arg;
+	LOCK(sc);
+	UNLOCK(sc);
+}
+
+static void
+host1x_syncpt_intr(void *arg)
+{
+	struct host1x_softc *sc;
+
+	sc = (struct host1x_softc *)arg;
+	LOCK(sc);
+	UNLOCK(sc);
+}
+
+static void
+host1x_new_pass(device_t dev)
+{
+	struct host1x_softc *sc;
+	int rv, rid;
+	phandle_t node;
+
+	/*
+	 * We attach during BUS_PASS_BUS (because we must overcome simplebus),
+	 * but some of our FDT resources are not ready until BUS_PASS_DEFAULT
+	 */
+	sc = device_get_softc(dev);
+	if (sc->attach_done || bus_current_pass < BUS_PASS_DEFAULT) {
+		bus_generic_new_pass(dev);
+		return;
+	}
+
+	sc->attach_done = 1;
+	node = ofw_bus_get_node(dev);
+
+	/* Allocate our IRQ resource. */
+	rid = 0;
+	sc->syncpt_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->syncpt_irq_res == NULL) {
+		device_printf(dev, "Cannot allocate interrupt.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+	rid = 1;
+	sc->gen_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->gen_irq_res == NULL) {
+		device_printf(dev, "Cannot allocate interrupt.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* FDT resources */
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "host1x", &sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get fuse reset\n");
+		goto fail;
+	}
+	rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get i2c clock: %d\n", rv);
+		goto fail;
+	}
+
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock: %d\n", rv);
+		goto fail;
+	}
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot clear reset\n");
+		goto fail;
+	}
+
+	/* Setup  interrupts */
+	rv = bus_setup_intr(dev, sc->gen_irq_res,
+	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_gen_intr,
+	    sc, &sc->gen_irq_h);
+	if (rv) {
+		device_printf(dev, "Cannot setup gen interrupt.\n");
+		goto fail;
+	}
+
+	rv = bus_setup_intr(dev, sc->syncpt_irq_res,
+	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_syncpt_intr,
+	    sc, &sc->syncpt_irq_h);
+	if (rv) {
+		device_printf(dev, "Cannot setup syncpt interrupt.\n");
+		goto fail;
+	}
+
+	simplebus_init(dev, 0);
+	for (node = OF_child(node); node > 0; node = OF_peer(node))
+	    simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+
+	sc->irq_hook.ich_func = host1x_irq_hook;
+	sc->irq_hook.ich_arg = sc;
+	config_intrhook_establish(&sc->irq_hook);
+	bus_generic_new_pass(dev);
+	return;
+
+fail:
+	device_detach(dev);
+	return;
+}
+
+static int
+host1x_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+host1x_attach(device_t dev)
+{
+	int rv, rid;
+	struct host1x_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->tegra_drm = malloc(sizeof(struct tegra_drm), DRM_MEM_DRIVER,
+	    M_WAITOK | M_ZERO);
+
+	/* crosslink together all worlds */
+	sc->dev = dev;
+	sc->tegra_drm->drm_dev.dev_private = &sc->tegra_drm;
+	sc->tegra_drm->drm_dev.dev = dev;
+
+	TAILQ_INIT(&sc->clients);
+
+	LOCK_INIT(sc);
+
+	/* Get the memory resource for the register mapping. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot map registers.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->tegra_drm != NULL)
+		free(sc->tegra_drm, DRM_MEM_DRIVER);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+	return (rv);
+}
+
+static int
+host1x_detach(device_t dev)
+{
+	struct host1x_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	host1x_drm_exit(sc);
+
+	if (sc->gen_irq_h != NULL)
+		bus_teardown_intr(dev, sc->gen_irq_res, sc->gen_irq_h);
+	if (sc->tegra_drm != NULL)
+		free(sc->tegra_drm, DRM_MEM_DRIVER);
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (sc->reset != NULL)
+		hwreset_release(sc->reset);
+	if (sc->syncpt_irq_h != NULL)
+		bus_teardown_intr(dev, sc->syncpt_irq_res, sc->syncpt_irq_h);
+	if (sc->gen_irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 1, sc->gen_irq_res);
+	if (sc->syncpt_irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->syncpt_irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t host1x_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		host1x_probe),
+	DEVMETHOD(device_attach,	host1x_attach),
+	DEVMETHOD(device_detach,	host1x_detach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_new_pass,		host1x_new_pass),
+
+	/* Framebuffer service methods */
+	DEVMETHOD(fb_getinfo,           host1x_fb_helper_getinfo),
+
+	/* tegra drm interface */
+	DEVMETHOD(tegra_drm_register_client,	host1x_register_client),
+	DEVMETHOD(tegra_drm_deregister_client,	host1x_deregister_client),
+
+	DEVMETHOD_END
+};
+
+static devclass_t host1x_devclass;
+DEFINE_CLASS_1(host1x, host1x_driver, host1x_methods,
+    sizeof(struct host1x_softc), simplebus_driver);
+EARLY_DRIVER_MODULE(host1x, simplebus, host1x_driver,
+    host1x_devclass, 0, 0,  BUS_PASS_BUS);
+
+/* Bindings for fbd device. */
+extern devclass_t fbd_devclass;
+extern driver_t fbd_driver;
+DRIVER_MODULE(fbd, host1x, fbd_driver, fbd_devclass, 0, 0);
+


Property changes on: trunk/sys/arm/nvidia/drm2/tegra_host1x.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/files.tegra124
===================================================================
--- trunk/sys/arm/nvidia/tegra124/files.tegra124	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/files.tegra124	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,72 @@
+# $MidnightBSD$
+
+#
+# Standard ARM support.
+#
+kern/kern_clocksource.c			standard
+
+#
+# Standard tegra124 devices and support.
+#
+arm/nvidia/tegra124/tegra124_machdep.c	standard
+arm/nvidia/tegra124/tegra124_mp.c	optional	smp
+arm/nvidia/tegra124/tegra124_car.c	standard
+arm/nvidia/tegra124/tegra124_clk_pll.c	standard
+arm/nvidia/tegra124/tegra124_clk_per.c	standard
+arm/nvidia/tegra124/tegra124_clk_super.c standard
+arm/nvidia/tegra124/tegra124_xusbpadctl.c standard
+arm/nvidia/tegra124/tegra124_pmc.c	standard
+arm/nvidia/tegra124/tegra124_cpufreq.c	standard
+arm/nvidia/tegra124/tegra124_coretemp.c	standard
+arm/nvidia/tegra_usbphy.c		standard
+arm/nvidia/tegra_pinmux.c		standard
+arm/nvidia/tegra_uart.c			optional	uart
+arm/nvidia/tegra_sdhci.c		optional	sdhci
+arm/nvidia/tegra_gpio.c			optional	gpio
+arm/nvidia/tegra_ehci.c			optional	ehci
+arm/nvidia/tegra_xhci.c			optional	xhci
+arm/nvidia/tegra_ahci.c			optional	ahci
+arm/nvidia/tegra_pcie.c			optional	pci
+arm/nvidia/tegra_i2c.c			optional	iic
+arm/nvidia/tegra_rtc.c			standard
+arm/nvidia/tegra_abpmisc.c		standard
+arm/nvidia/tegra_efuse.c		standard
+arm/nvidia/tegra_soctherm_if.m		standard
+arm/nvidia/tegra_soctherm.c		standard
+arm/nvidia/tegra_lic.c			standard
+arm/nvidia/tegra_mc.c			standard
+#arm/nvidia/tegra_hda.c			optional	snd_hda
+arm/nvidia/drm2/hdmi.c			optional	drm2
+arm/nvidia/drm2/tegra_drm_if.m		optional	drm2
+arm/nvidia/drm2/tegra_drm_subr.c	optional	drm2
+arm/nvidia/drm2/tegra_host1x.c		optional	drm2
+arm/nvidia/drm2/tegra_hdmi.c		optional	drm2
+arm/nvidia/drm2/tegra_dc_if.m		optional	drm2
+arm/nvidia/drm2/tegra_dc.c		optional	drm2
+arm/nvidia/drm2/tegra_fb.c		optional	drm2
+arm/nvidia/drm2/tegra_bo.c		optional	drm2
+#
+# Firmware
+#
+tegra124_xusb_fw.c			optional tegra124_xusb_fw	\
+	dependency	"$S/arm/nvidia/tegra124/files.tegra124"		\
+	compile-with	"${AWK} -f $S/tools/fw_stub.awk tegra124_xusb.fw:tegra124_xusb_fw -mtegra124_xusb_fw -c${.TARGET}" \
+	no-implicit-rule before-depend local				\
+	clean		"tegra124_xusb_fw.c"
+tegra124_xusb.fwo			optional tegra124_xusb_fw	\
+	dependency	"tegra124_xusb.fw"				\
+	compile-with	"${NORMAL_FWO}"					\
+	no-implicit-rule						\
+	clean		"tegra124_xusb.fwo"
+tegra124_xusb.fw			optional tegra124_xusb_fw	\
+	dependency	"$S/contrib/dev/nvidia/tegra124_xusb.bin.uu"	\
+	compile-with	"${NORMAL_FW}"					\
+	no-obj no-implicit-rule						\
+	clean		"tegra124_xusb.fw"
+#
+# Temporary/to be moved stuff
+#
+arm/nvidia/as3722.c			optional	iic
+arm/nvidia/as3722_regulators.c		optional	iic
+arm/nvidia/as3722_rtc.c			optional	iic
+arm/nvidia/as3722_gpio.c		optional	iic


Property changes on: trunk/sys/arm/nvidia/tegra124/files.tegra124
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/std.tegra124
===================================================================
--- trunk/sys/arm/nvidia/tegra124/std.tegra124	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/std.tegra124	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,11 @@
+# $MidnightBSD$
+cpu 		CPU_CORTEXA
+machine		arm	armv6
+makeoptions	CONF_CFLAGS="-march=armv7a"
+
+options		INTRNG
+
+options		IPI_IRQ_START=0
+options		IPI_IRQ_END=15
+
+files "../nvidia/tegra124/files.tegra124"


Property changes on: trunk/sys/arm/nvidia/tegra124/std.tegra124
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_car.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_car.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_car.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,610 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_car.c 317013 2017-04-16 08:21:14Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/clk/clk_mux.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+#include "tegra124_car.h"
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-car",	1},
+	{NULL,		 	0},
+};
+
+#define	PLIST(x) static const char *x[]
+
+/* Pure multiplexer. */
+#define	MUX(_id, cname, plists, o, s, w)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = plists,					\
+	.clkdef.parent_cnt = nitems(plists),				\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.offset = o,							\
+	.shift  = s,							\
+	.width = w,							\
+}
+
+/* Fractional divider (7.1). */
+#define	DIV7_1(_id, cname, plist, o, s)					\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){plist},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags =  CLK_NODE_STATIC_STRINGS,			\
+	.offset = o,							\
+	.i_shift = (s) + 1,						\
+	.i_width = 7,							\
+	.f_shift = s,							\
+	.f_width = 1,							\
+}
+
+/* Integer divider. */
+#define	DIV(_id, cname, plist, o, s, w, f)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){plist},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags =  CLK_NODE_STATIC_STRINGS,			\
+	.offset = o,							\
+	.i_shift = s,							\
+	.i_width = w,							\
+	.div_flags = f,							\
+}
+
+/* Gate in PLL block. */
+#define	GATE_PLL(_id, cname, plist, o, s)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){plist},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.offset = o,							\
+	.shift = s,							\
+	.mask = 3,							\
+	.on_value = 3,							\
+	.off_value = 0,							\
+}
+
+/* Standard gate. */
+#define	GATE(_id, cname, plist, o, s)					\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){plist},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.offset = o,							\
+	.shift = s,							\
+	.mask = 1,							\
+	.on_value = 1,							\
+	.off_value = 0,							\
+}
+
+/* Inverted gate. */
+#define	GATE_INV(_id, cname, plist, o, s)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){plist},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.offset = o,							\
+	.shift = s,							\
+	.mask = 1,							\
+	.on_value = 0,							\
+	.off_value = 1,							\
+}
+
+/* Fixed rate clock. */
+#define	FRATE(_id, cname, _freq)					\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = NULL,					\
+	.clkdef.parent_cnt = 0,						\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.freq = _freq,							\
+}
+
+/* Fixed rate multipier/divider. */
+#define	FACT(_id, cname, pname, _mult, _div)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){pname},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.mult = _mult,							\
+	.div = _div,							\
+}
+
+static uint32_t osc_freqs[16] = {
+	 [0] =  13000000,
+	 [1] =  16800000,
+	 [4] =  19200000,
+	 [5] =  38400000,
+	 [8] =  12000000,
+	 [9] =  48000000,
+	[12] = 260000000,
+};
+
+
+/* Parent lists. */
+PLIST(mux_pll_srcs) = {"osc_div_clk", NULL, "pllP_out0", NULL}; /* FIXME */
+PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"};
+PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"};
+PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"};
+PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60"};
+PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"};
+
+
+/* Clocks ajusted online. */
+static struct clk_fixed_def fixed_clk_m =
+	FRATE(TEGRA124_CLK_CLK_M, "clk_m", 12000000);
+static struct clk_fixed_def fixed_osc_div_clk =
+	FACT(0, "osc_div_clk", "clk_m", 1, 1);
+
+static struct clk_fixed_def tegra124_fixed_clks[] = {
+	/* Core clocks. */
+	FRATE(0, "clk_s", 32768),
+	FACT(0, "clk_m_div2", "clk_m", 1, 2),
+	FACT(0, "clk_m_div4", "clk_m", 1, 3),
+	FACT(0, "pllU_60", "pllU_out", 1, 8),
+	FACT(0, "pllU_48", "pllU_out", 1, 10),
+	FACT(0, "pllU_12", "pllU_out", 1, 40),
+	FACT(TEGRA124_CLK_PLL_D_OUT0, "pllD_out0", "pllD_out", 1, 2),
+	FACT(TEGRA124_CLK_PLL_D2_OUT0, "pllD2_out0", "pllD2_out", 1, 1),
+	FACT(0, "pllX_out0", "pllX_out", 1, 2),
+	FACT(0, "pllC_UD", "pllC_out0", 1, 1),
+	FACT(0, "pllM_UD", "pllM_out0", 1, 1),
+
+	/* Audio clocks. */
+	FRATE(0, "audio0", 10000000),
+	FRATE(0, "audio1", 10000000),
+	FRATE(0, "audio2", 10000000),
+	FRATE(0, "audio3", 10000000),
+	FRATE(0, "audio4", 10000000),
+	FRATE(0, "ext_vimclk", 10000000),
+
+	/* XUSB */
+	FACT(TEGRA124_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2),
+
+};
+
+
+static struct clk_mux_def tegra124_mux_clks[] = {
+	/* Core clocks. */
+	MUX(0, "pllD2_src", mux_pll_srcs, PLLD2_BASE, 25, 2),
+	MUX(0, "pllDP_src", mux_pll_srcs, PLLDP_BASE, 25, 2),
+	MUX(0, "pllC4_src", mux_pll_srcs, PLLC4_BASE, 25, 2),
+	MUX(0, "pllE_src1", mux_plle_src1, PLLE_AUX, 2, 1),
+	MUX(0, "pllE_src",  mux_plle_src, PLLE_AUX, 28, 1),
+
+	/* Base peripheral clocks. */
+	MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1),
+	MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1),
+
+	/* USB. */
+	MUX(TEGRA124_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 1),
+	MUX(0, "xusb_ss_mux", mux_xusb_ss, CLK_SOURCE_XUSB_SS, 24, 1),
+
+};
+
+
+static struct clk_gate_def tegra124_gate_clks[] = {
+	/* Core clocks. */
+	GATE_PLL(0, "pllC_out1", "pllC_out1_div", PLLC_OUT, 0),
+	GATE_PLL(0, "pllM_out1", "pllM_out1_div", PLLM_OUT, 0),
+	GATE_PLL(TEGRA124_CLK_PLL_U_480M, "pllU_480", "pllU_out", PLLU_BASE, 22),
+	GATE_PLL(0, "pllP_outX0", "pllP_outX0_div", PLLP_RESHIFT, 0),
+	GATE_PLL(0, "pllP_out1", "pllP_out1_div", PLLP_OUTA, 0),
+	GATE_PLL(0, "pllP_out2", "pllP_out2_div", PLLP_OUTA, 16),
+	GATE_PLL(0, "pllP_out3", "pllP_out3_div", PLLP_OUTB, 0),
+	GATE_PLL(0, "pllP_out4", "pllP_out4_div", PLLP_OUTB, 16),
+	GATE_PLL(0, "pllP_out5", "pllP_out5_div", PLLP_OUTC, 16),
+	GATE_PLL(0, "pllA_out0", "pllA_out1_div", PLLA_OUT, 0),
+
+	/* Base peripheral clocks. */
+	GATE(TEGRA124_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0),
+	GATE(TEGRA124_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1),
+	GATE_INV(TEGRA124_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7),
+	GATE_INV(TEGRA124_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3),
+};
+
+static struct clk_div_def tegra124_div_clks[] = {
+	/* Core clocks. */
+	DIV7_1(0, "pllC_out1_div", "pllC_out0", PLLC_OUT, 2),
+	DIV7_1(0, "pllM_out1_div", "pllM_out0", PLLM_OUT, 8),
+	DIV7_1(0, "pllP_outX0_div", "pllP_out0", PLLP_RESHIFT, 2),
+	DIV7_1(0, "pllP_out1_div", "pllP_out0", PLLP_OUTA, 8),
+	DIV7_1(0, "pllP_out2_div", "pllP_out0", PLLP_OUTA, 24),
+	DIV7_1(0, "pllP_out3_div", "pllP_out0", PLLP_OUTB, 8),
+	DIV7_1(0, "pllP_out4_div", "pllP_out0", PLLP_OUTB, 24),
+	DIV7_1(0, "pllP_out5_div", "pllP_out0", PLLP_OUTC, 24),
+	DIV7_1(0, "pllA_out1_div", "pllA_out", PLLA_OUT, 8),
+
+	/* Base peripheral clocks. */
+	DIV(0, "hclk_div", "sclk", CLK_SYSTEM_RATE, 4, 2, 0),
+	DIV(0, "pclk_div", "hclk", CLK_SYSTEM_RATE, 0, 2, 0),
+};
+
+/* Initial setup table. */
+static struct  tegra124_init_item clk_init_table[] = {
+	/* clock, partent, frequency, enable */
+	{"uarta", "pllP_out0", 408000000, 0},
+	{"uartb", "pllP_out0", 408000000, 0},
+	{"uartc", "pllP_out0", 408000000, 0},
+	{"uartd", "pllP_out0", 408000000, 0},
+	{"pllA_out", NULL, 282240000, 1},
+	{"pllA_out0", NULL, 11289600, 1},
+	{"extperiph1", "pllA_out0", 0, 1},
+	{"i2s0", "pllA_out0", 11289600, 0},
+	{"i2s1", "pllA_out0", 11289600, 0},
+	{"i2s2", "pllA_out0", 11289600, 0},
+	{"i2s3", "pllA_out0", 11289600, 0},
+	{"i2s4", "pllA_out0", 11289600, 0},
+	{"vde", "pllP_out0", 0, 0},
+	{"host1x", "pllP_out0", 136000000, 1},
+	{"sclk", "pllP_out2", 102000000, 1},
+	{"dvfs_soc", "pllP_out0", 51000000, 1},
+	{"dvfs_ref", "pllP_out0", 51000000, 1},
+	{"pllC_out0", NULL, 600000000, 0},
+	{"pllC_out1", NULL, 100000000, 0},
+	{"spi4", "pllP_out0", 12000000, 1},
+	{"tsec", "pllC3_out0", 0, 0},
+	{"msenc", "pllC3_out0", 0, 0},
+	{"pllREFE_out", NULL, 672000000, 0},
+	{"pc_xusb_ss", "pllU_480", 120000000, 0},
+	{"xusb_ss", "pc_xusb_ss", 120000000, 0},
+	{"pc_xusb_fs", "pllU_48", 48000000, 0},
+	{"xusb_hs", "pllU_60", 60000000, 0},
+	{"pc_xusb_falcon", "pllREFE_out", 224000000, 0},
+	{"xusb_core_host", "pllREFE_out", 112000000, 0},
+	{"sata", "pllP_out0", 102000000, 0},
+	{"sata_oob", "pllP_out0", 204000000, 0},
+	{"sata_cold", NULL, 0, 1},
+	{"emc", NULL, 0, 1},
+	{"mselect", NULL, 0, 1},
+	{"csite", NULL, 0, 1},
+	{"tsensor", "clk_m", 400000, 0},
+
+	/* tegra124 only*/
+	{"soc_therm", "pllP_out0", 51000000, 0},
+	{"cclk_g", NULL, 0, 1},
+	{"hda", "pllP_out0", 102000000, 0},
+	{"hda2codec_2x", "pllP_out0", 48000000, 0},
+};
+
+static void
+init_divs(struct tegra124_car_softc *sc, struct clk_div_def *clks, int nclks)
+{
+	int i, rv;
+
+	for (i = 0; i < nclks; i++) {
+		rv = clknode_div_register(sc->clkdom, clks + i);
+		if (rv != 0)
+			panic("clk_div_register failed");
+	}
+}
+
+static void
+init_gates(struct tegra124_car_softc *sc, struct clk_gate_def *clks, int nclks)
+{
+	int i, rv;
+
+
+	for (i = 0; i < nclks; i++) {
+		rv = clknode_gate_register(sc->clkdom, clks + i);
+		if (rv != 0)
+			panic("clk_gate_register failed");
+	}
+}
+
+static void
+init_muxes(struct tegra124_car_softc *sc, struct clk_mux_def *clks, int nclks)
+{
+	int i, rv;
+
+
+	for (i = 0; i < nclks; i++) {
+		rv = clknode_mux_register(sc->clkdom, clks + i);
+		if (rv != 0)
+			panic("clk_mux_register failed");
+	}
+}
+
+static void
+init_fixeds(struct tegra124_car_softc *sc, struct clk_fixed_def *clks,
+    int nclks)
+{
+	int i, rv;
+	uint32_t val;
+	int osc_idx;
+
+	CLKDEV_READ_4(sc->dev, OSC_CTRL, &val);
+	osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
+	fixed_clk_m.freq = osc_freqs[osc_idx];
+	if (fixed_clk_m.freq == 0)
+		panic("Undefined input frequency");
+	rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m);
+	if (rv != 0) panic("clk_fixed_register failed");
+
+	val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3;
+	fixed_osc_div_clk.div = 1 << val;
+	rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div_clk);
+	if (rv != 0) panic("clk_fixed_register failed");
+
+	for (i = 0; i < nclks; i++) {
+		rv = clknode_fixed_register(sc->clkdom, clks + i);
+		if (rv != 0)
+			panic("clk_fixed_register failed");
+	}
+}
+
+static void
+postinit_clock(struct tegra124_car_softc *sc)
+{
+	int i;
+	struct tegra124_init_item *tbl;
+	struct clknode *clknode;
+	int rv;
+
+	for (i = 0; i < nitems(clk_init_table); i++) {
+		tbl = &clk_init_table[i];
+
+		clknode =  clknode_find_by_name(tbl->name);
+		if (clknode == NULL) {
+			device_printf(sc->dev, "Cannot find clock %s\n",
+			    tbl->name);
+			continue;
+		}
+		if (tbl->parent != NULL) {
+			rv = clknode_set_parent_by_name(clknode, tbl->parent);
+			if (rv != 0) {
+				device_printf(sc->dev,
+				    "Cannot set parent for %s (to %s): %d\n",
+				    tbl->name, tbl->parent, rv);
+				continue;
+			}
+		}
+		if (tbl->frequency != 0) {
+			rv = clknode_set_freq(clknode, tbl->frequency, 0 , 9999);
+			if (rv != 0) {
+				device_printf(sc->dev,
+				    "Cannot set frequency for %s: %d\n",
+				    tbl->name, rv);
+				continue;
+			}
+		}
+		if (tbl->enable!= 0) {
+			rv = clknode_enable(clknode);
+			if (rv != 0) {
+				device_printf(sc->dev,
+				    "Cannot enable %s: %d\n", tbl->name, rv);
+				continue;
+			}
+		}
+	}
+}
+
+static void
+register_clocks(device_t dev)
+{
+	struct tegra124_car_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->clkdom = clkdom_create(dev);
+	if (sc->clkdom == NULL)
+		panic("clkdom == NULL");
+
+	tegra124_init_plls(sc);
+	init_fixeds(sc, tegra124_fixed_clks, nitems(tegra124_fixed_clks));
+	init_muxes(sc, tegra124_mux_clks, nitems(tegra124_mux_clks));
+	init_divs(sc, tegra124_div_clks, nitems(tegra124_div_clks));
+	init_gates(sc, tegra124_gate_clks, nitems(tegra124_gate_clks));
+	tegra124_periph_clock(sc);
+	tegra124_super_mux_clock(sc);
+	clkdom_finit(sc->clkdom);
+	clkdom_xlock(sc->clkdom);
+	postinit_clock(sc);
+	clkdom_unlock(sc->clkdom);
+	if (bootverbose)
+		clkdom_dump(sc->clkdom);
+}
+
+static int
+tegra124_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+	struct tegra124_car_softc *sc;
+
+	sc = device_get_softc(dev);
+	*val = bus_read_4(sc->mem_res, addr);
+	return (0);
+}
+
+static int
+tegra124_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+	struct tegra124_car_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_write_4(sc->mem_res, addr, val);
+	return (0);
+}
+
+static int
+tegra124_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,
+    uint32_t set_mask)
+{
+	struct tegra124_car_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+	reg = bus_read_4(sc->mem_res, addr);
+	reg &= ~clear_mask;
+	reg |= set_mask;
+	bus_write_4(sc->mem_res, addr, reg);
+	return (0);
+}
+
+static void
+tegra124_car_clkdev_device_lock(device_t dev)
+{
+	struct tegra124_car_softc *sc;
+
+	sc = device_get_softc(dev);
+	mtx_lock(&sc->mtx);
+}
+
+static void
+tegra124_car_clkdev_device_unlock(device_t dev)
+{
+	struct tegra124_car_softc *sc;
+
+	sc = device_get_softc(dev);
+	mtx_unlock(&sc->mtx);
+}
+
+static int
+tegra124_car_detach(device_t dev)
+{
+
+	device_printf(dev, "Error: Clock driver cannot be detached\n");
+	return (EBUSY);
+}
+
+static int
+tegra124_car_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+		device_set_desc(dev, "Tegra Clock Driver");
+		return (BUS_PROBE_DEFAULT);
+	}
+
+	return (ENXIO);
+}
+
+static int
+tegra124_car_attach(device_t dev)
+{
+	struct tegra124_car_softc *sc = device_get_softc(dev);
+	int rid, rv;
+
+	sc->dev = dev;
+
+	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+	sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+	/* Resource setup. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (!sc->mem_res) {
+		device_printf(dev, "cannot allocate memory resource\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	register_clocks(dev);
+	hwreset_register_ofw_provider(dev);
+	return (0);
+
+fail:
+	if (sc->mem_res)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	return (rv);
+}
+
+static int
+tegra124_car_hwreset_assert(device_t dev, intptr_t id, bool value)
+{
+	struct tegra124_car_softc *sc = device_get_softc(dev);
+
+	return (tegra124_hwreset_by_idx(sc, id, value));
+}
+
+static device_method_t tegra124_car_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra124_car_probe),
+	DEVMETHOD(device_attach,	tegra124_car_attach),
+	DEVMETHOD(device_detach,	tegra124_car_detach),
+
+	/* Clkdev  interface*/
+	DEVMETHOD(clkdev_read_4,	tegra124_car_clkdev_read_4),
+	DEVMETHOD(clkdev_write_4,	tegra124_car_clkdev_write_4),
+	DEVMETHOD(clkdev_modify_4,	tegra124_car_clkdev_modify_4),
+	DEVMETHOD(clkdev_device_lock,	tegra124_car_clkdev_device_lock),
+	DEVMETHOD(clkdev_device_unlock,	tegra124_car_clkdev_device_unlock),
+
+	/* Reset interface */
+	DEVMETHOD(hwreset_assert,	tegra124_car_hwreset_assert),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra124_car_devclass;
+static DEFINE_CLASS_0(car, tegra124_car_driver, tegra124_car_methods,
+    sizeof(struct tegra124_car_softc));
+EARLY_DRIVER_MODULE(tegra124_car, simplebus, tegra124_car_driver,
+    tegra124_car_devclass, NULL, NULL, BUS_PASS_TIMER);


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_car.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_car.h
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_car.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_car.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,338 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_car.h 296936 2016-03-16 13:01:48Z mmel $
+ */
+
+#ifndef _TEGRA124_CAR_
+#define	_TEGRA124_CAR_
+
+#include "clkdev_if.h"
+
+#define	RD4(sc, reg, val)	CLKDEV_READ_4((sc)->clkdev, reg, val)
+#define	WR4(sc, reg, val)	CLKDEV_WRITE_4((sc)->clkdev, reg, val)
+#define	MD4(sc, reg, mask, set)	CLKDEV_MODIFY_4((sc)->clkdev, reg, mask, set)
+#define	DEVICE_LOCK(sc)		CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define	DEVICE_UNLOCK(sc)	CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+#define	RST_DEVICES_L			0x004
+#define	RST_DEVICES_H			0x008
+#define	RST_DEVICES_U			0x00C
+#define	CLK_OUT_ENB_L			0x010
+#define	CLK_OUT_ENB_H			0x014
+#define	CLK_OUT_ENB_U			0x018
+#define	CCLK_BURST_POLICY		0x020
+#define	SUPER_CCLK_DIVIDER		0x024
+#define	SCLK_BURST_POLICY		0x028
+#define	SUPER_SCLK_DIVIDER		0x02c
+#define	CLK_SYSTEM_RATE			0x030
+
+#define	OSC_CTRL			0x050
+ #define	OSC_CTRL_OSC_FREQ_SHIFT		28
+ #define	OSC_CTRL_PLL_REF_DIV_SHIFT		26
+
+#define	PLLE_SS_CNTL 			0x068
+#define	 PLLE_SS_CNTL_SSCINCINTRV_MASK		(0x3f << 24)
+#define	 PLLE_SS_CNTL_SSCINCINTRV_VAL 		(0x20 << 24)
+#define	 PLLE_SS_CNTL_SSCINC_MASK 		(0xff << 16)
+#define	 PLLE_SS_CNTL_SSCINC_VAL 		(0x1 << 16)
+#define	 PLLE_SS_CNTL_SSCINVERT 		(1 << 15)
+#define	 PLLE_SS_CNTL_SSCCENTER 		(1 << 14)
+#define	 PLLE_SS_CNTL_SSCBYP 			(1 << 12)
+#define	 PLLE_SS_CNTL_INTERP_RESET 		(1 << 11)
+#define	 PLLE_SS_CNTL_BYPASS_SS 		(1 << 10)
+#define	 PLLE_SS_CNTL_SSCMAX_MASK		0x1ff
+#define	 PLLE_SS_CNTL_SSCMAX_VAL 		0x25
+#define	 PLLE_SS_CNTL_DISABLE 			(PLLE_SS_CNTL_BYPASS_SS |    \
+						 PLLE_SS_CNTL_INTERP_RESET | \
+						 PLLE_SS_CNTL_SSCBYP)
+#define	 PLLE_SS_CNTL_COEFFICIENTS_MASK 	(PLLE_SS_CNTL_SSCMAX_MASK |  \
+						 PLLE_SS_CNTL_SSCINC_MASK |  \
+						 PLLE_SS_CNTL_SSCINCINTRV_MASK)
+#define	 PLLE_SS_CNTL_COEFFICIENTS_VAL 		(PLLE_SS_CNTL_SSCMAX_VAL |   \
+						 PLLE_SS_CNTL_SSCINC_VAL |   \
+						 PLLE_SS_CNTL_SSCINCINTRV_VAL)
+
+#define	PLLC_BASE			0x080
+#define	PLLC_OUT			0x084
+#define	PLLC_MISC2			0x088
+#define	PLLC_MISC			0x08c
+#define	PLLM_BASE			0x090
+#define	PLLM_OUT			0x094
+#define	PLLM_MISC			0x09c
+#define	PLLP_BASE			0x0a0
+#define	PLLP_MISC			0x0ac
+#define	PLLP_OUTA			0x0a4
+#define	PLLP_OUTB			0x0a8
+#define	PLLA_BASE			0x0b0
+#define	PLLA_OUT			0x0b4
+#define	PLLA_MISC			0x0bc
+#define	PLLU_BASE			0x0c0
+#define	PLLU_MISC			0x0cc
+#define	PLLD_BASE			0x0d0
+#define	PLLD_MISC			0x0dc
+#define	PLLX_BASE			0x0e0
+#define	PLLX_MISC			0x0e4
+#define	PLLE_BASE			0x0e8
+#define	 PLLE_BASE_LOCK_OVERRIDE		(1 << 29)
+#define	 PLLE_BASE_DIVCML_SHIFT 		24
+#define	 PLLE_BASE_DIVCML_MASK 			0xf
+
+#define	PLLE_MISC			0x0ec
+#define	 PLLE_MISC_SETUP_BASE_SHIFT 		16
+#define	 PLLE_MISC_SETUP_BASE_MASK 		(0xffff << PLLE_MISC_SETUP_BASE_SHIFT)
+#define	 PLLE_MISC_READY 			(1 << 15)
+#define	 PLLE_MISC_IDDQ_SWCTL			(1 << 14)
+#define	 PLLE_MISC_IDDQ_OVERRIDE_VALUE		(1 << 13)
+#define	 PLLE_MISC_LOCK 			(1 << 11)
+#define	 PLLE_MISC_REF_ENABLE 			(1 << 10)
+#define	 PLLE_MISC_LOCK_ENABLE 			(1 << 9)
+#define	 PLLE_MISC_PTS 				(1 << 8)
+#define	 PLLE_MISC_VREG_BG_CTRL_SHIFT		4
+#define	 PLLE_MISC_VREG_BG_CTRL_MASK		(3 << PLLE_MISC_VREG_BG_CTRL_SHIFT)
+#define	 PLLE_MISC_VREG_CTRL_SHIFT		2
+#define	 PLLE_MISC_VREG_CTRL_MASK		(2 << PLLE_MISC_VREG_CTRL_SHIFT)
+
+#define	CLK_SOURCE_I2S1			0x100
+#define	CLK_SOURCE_I2S2			0x104
+#define	CLK_SOURCE_SPDIF_OUT		0x108
+#define	CLK_SOURCE_SPDIF_IN		0x10c
+#define	CLK_SOURCE_PWM			0x110
+#define	CLK_SOURCE_SPI2			0x118
+#define	CLK_SOURCE_SPI3			0x11c
+#define	CLK_SOURCE_I2C1			0x124
+#define	CLK_SOURCE_I2C5			0x128
+#define	CLK_SOURCE_SPI1			0x134
+#define	CLK_SOURCE_DISP1		0x138
+#define	CLK_SOURCE_DISP2		0x13c
+#define	CLK_SOURCE_ISP			0x144
+#define	CLK_SOURCE_VI			0x148
+#define	CLK_SOURCE_SDMMC1		0x150
+#define	CLK_SOURCE_SDMMC2		0x154
+#define	CLK_SOURCE_SDMMC4		0x164
+#define	CLK_SOURCE_VFIR			0x168
+#define	CLK_SOURCE_HSI			0x174
+#define	CLK_SOURCE_UARTA		0x178
+#define	CLK_SOURCE_UARTB		0x17c
+#define	CLK_SOURCE_HOST1X		0x180
+#define	CLK_SOURCE_HDMI			0x18c
+#define	CLK_SOURCE_I2C2			0x198
+#define	CLK_SOURCE_EMC			0x19c
+#define	CLK_SOURCE_UARTC		0x1a0
+#define	CLK_SOURCE_VI_SENSOR		0x1a8
+#define	CLK_SOURCE_SPI4			0x1b4
+#define	CLK_SOURCE_I2C3			0x1b8
+#define	CLK_SOURCE_SDMMC3		0x1bc
+#define	CLK_SOURCE_UARTD		0x1c0
+#define	CLK_SOURCE_VDE			0x1c8
+#define	CLK_SOURCE_OWR			0x1cc
+#define	CLK_SOURCE_NOR			0x1d0
+#define	CLK_SOURCE_CSITE		0x1d4
+#define	CLK_SOURCE_I2S0			0x1d8
+#define	CLK_SOURCE_DTV			0x1dc
+#define	CLK_SOURCE_MSENC		0x1f0
+#define	CLK_SOURCE_TSEC			0x1f4
+#define	CLK_SOURCE_SPARE2		0x1f8
+
+#define	CLK_OUT_ENB_X			0x280
+#define	RST_DEVICES_X			0x28C
+
+#define	RST_DEVICES_V			0x358
+#define	RST_DEVICES_W			0x35C
+#define	CLK_OUT_ENB_V			0x360
+#define	CLK_OUT_ENB_W			0x364
+#define	CCLKG_BURST_POLICY		0x368
+#define	SUPER_CCLKG_DIVIDER		0x36C
+#define	CCLKLP_BURST_POLICY		0x370
+#define	SUPER_CCLKLP_DIVIDER		0x374
+
+#define	CLK_SOURCE_MSELECT		0x3b4
+#define	CLK_SOURCE_TSENSOR		0x3b8
+#define	CLK_SOURCE_I2S3			0x3bc
+#define	CLK_SOURCE_I2S4			0x3c0
+#define	CLK_SOURCE_I2C4			0x3c4
+#define	CLK_SOURCE_SPI5			0x3c8
+#define	CLK_SOURCE_SPI6			0x3cc
+#define	CLK_SOURCE_AUDIO		0x3d0
+#define	CLK_SOURCE_DAM0			0x3d8
+#define	CLK_SOURCE_DAM1			0x3dc
+#define	CLK_SOURCE_DAM2			0x3e0
+#define	CLK_SOURCE_HDA2CODEC_2X		0x3e4
+#define	CLK_SOURCE_ACTMON		0x3e8
+#define	CLK_SOURCE_EXTPERIPH1		0x3ec
+#define	CLK_SOURCE_EXTPERIPH2		0x3f0
+#define	CLK_SOURCE_EXTPERIPH3		0x3f4
+#define	CLK_SOURCE_I2C_SLOW		0x3fc
+
+#define	CLK_SOURCE_SYS			0x400
+#define	CLK_SOURCE_SOR0			0x414
+#define	CLK_SOURCE_SATA_OOB		0x420
+#define	CLK_SOURCE_SATA			0x424
+#define	CLK_SOURCE_HDA			0x428
+#define	UTMIP_PLL_CFG0			0x480
+#define	UTMIP_PLL_CFG1			0x484
+#define	 UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP		(1 << 17)
+#define	 UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN		(1 << 16)
+#define	 UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP	(1 << 15)
+#define	 UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN	(1 << 14)
+#define	 UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN 	(1 << 12)
+#define	 UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x)		(((x) & 0x1f) << 6)
+#define	 UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x)		(((x) & 0xfff) << 0)
+
+#define	UTMIP_PLL_CFG2			0x488
+#define	 UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x)		(((x) & 0x3f) << 18)
+#define	 UTMIP_PLL_CFG2_STABLE_COUNT(x)			(((x) & 0xffff) << 6)
+#define	 UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN 	(1 << 4)
+#define	 UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN 	(1 << 2)
+#define	 UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN	(1 << 0)
+
+#define	PLLE_AUX			0x48c
+#define	 PLLE_AUX_PLLRE_SEL				(1 << 28)
+#define	 PLLE_AUX_SEQ_START_STATE 			(1 << 25)
+#define	 PLLE_AUX_SEQ_ENABLE				(1 << 24)
+#define	 PLLE_AUX_SS_SWCTL				(1 << 6)
+#define	 PLLE_AUX_ENABLE_SWCTL				(1 << 4)
+#define	 PLLE_AUX_USE_LOCKDET				(1 << 3)
+#define	 PLLE_AUX_PLLP_SEL				(1 << 2)
+
+#define	SATA_PLL_CFG0			0x490
+#define	SATA_PLL_CFG0_SEQ_START_STATE			(1 << 25)
+#define	SATA_PLL_CFG0_SEQ_ENABLE			(1 << 24)
+#define	SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE		(1 << 7)
+#define	SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE		(1 << 6)
+#define	SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE		(1 << 5)
+#define	SATA_PLL_CFG0_SEQ_IN_SWCTL			(1 << 4)
+#define	SATA_PLL_CFG0_PADPLL_USE_LOCKDET		(1 << 2)
+#define	SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE	(1 << 1)
+#define	SATA_PLL_CFG0_PADPLL_RESET_SWCTL		(1 << 0)
+
+#define	SATA_PLL_CFG1			0x494
+#define	PCIE_PLL_CFG0			0x498
+#define	PCIE_PLL_CFG0_SEQ_START_STATE			(1 << 25)
+#define	PCIE_PLL_CFG0_SEQ_ENABLE			(1 << 24)
+
+#define	PLLD2_BASE			0x4b8
+#define	PLLD2_MISC			0x4bc
+#define	UTMIP_PLL_CFG3			0x4c0
+#define	PLLRE_BASE			0x4c4
+#define	PLLRE_MISC			0x4c8
+#define	PLLC2_BASE			0x4e8
+#define	PLLC2_MISC			0x4ec
+#define	PLLC3_BASE			0x4fc
+
+#define	PLLC3_MISC			0x500
+#define	PLLX_MISC2			0x514
+#define	PLLX_MISC2			0x514
+#define	PLLX_MISC3			0x518
+#define	 PLLX_MISC3_DYNRAMP_STEPB_MASK		0xFF
+#define	 PLLX_MISC3_DYNRAMP_STEPB_SHIFT		24
+#define	 PLLX_MISC3_DYNRAMP_STEPA_MASK		0xFF
+#define	 PLLX_MISC3_DYNRAMP_STEPA_SHIFT		16
+#define	 PLLX_MISC3_NDIV_NEW_MASK		0xFF
+#define	 PLLX_MISC3_NDIV_NEW_SHIFT		8
+#define	 PLLX_MISC3_EN_FSTLCK			(1 << 5)
+#define	 PLLX_MISC3_LOCK_OVERRIDE		(1 << 4)
+#define	 PLLX_MISC3_PLL_FREQLOCK		(1 << 3)
+#define	 PLLX_MISC3_DYNRAMP_DONE		(1 << 2)
+#define	 PLLX_MISC3_CLAMP_NDIV			(1 << 1)
+#define	 PLLX_MISC3_EN_DYNRAMP			(1 << 0)
+#define	XUSBIO_PLL_CFG0			0x51c
+#define	 XUSBIO_PLL_CFG0_SEQ_START_STATE		(1 << 25)
+#define	 XUSBIO_PLL_CFG0_SEQ_ENABLE			(1 << 24)
+#define	 XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET		(1 << 6)
+#define	 XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL		(1 << 2)
+#define	 XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL		(1 << 0)
+
+#define	PLLP_RESHIFT			0x528
+#define	UTMIPLL_HW_PWRDN_CFG0		0x52c
+#define	 UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE		(1 << 25)
+#define	 UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE		(1 << 24)
+#define	 UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET		(1 << 6)
+#define	 UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE	(1 << 5)
+#define	 UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL		(1 << 4)
+#define	 UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL		(1 << 2)
+#define	 UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE		(1 << 1)
+#define	 UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL		(1 << 0)
+
+#define	PLLDP_BASE			0x590
+#define	PLLDP_MISC			0x594
+#define	PLLC4_BASE			0x5a4
+#define	PLLC4_MISC			0x5a8
+
+#define	CLK_SOURCE_XUSB_CORE_HOST	0x600
+#define	CLK_SOURCE_XUSB_FALCON		0x604
+#define	CLK_SOURCE_XUSB_FS		0x608
+#define	CLK_SOURCE_XUSB_CORE_DEV	0x60c
+#define	CLK_SOURCE_XUSB_SS		0x610
+#define	CLK_SOURCE_CILAB		0x614
+#define	CLK_SOURCE_CILCD		0x618
+#define	CLK_SOURCE_CILE			0x61c
+#define	CLK_SOURCE_DSIA_LP		0x620
+#define	CLK_SOURCE_DSIB_LP		0x624
+#define	CLK_SOURCE_ENTROPY		0x628
+#define	CLK_SOURCE_DVFS_REF		0x62c
+#define	CLK_SOURCE_DVFS_SOC		0x630
+#define	CLK_SOURCE_TRACECLKIN		0x634
+#define	CLK_SOURCE_ADX			0x638
+#define	CLK_SOURCE_AMX			0x63c
+#define	CLK_SOURCE_EMC_LATENCY		0x640
+#define	CLK_SOURCE_SOC_THERM		0x644
+#define	CLK_SOURCE_VI_SENSOR2		0x658
+#define	CLK_SOURCE_I2C6			0x65c
+#define	CLK_SOURCE_EMC_DLL		0x664
+#define	CLK_SOURCE_HDMI_AUDIO		0x668
+#define	CLK_SOURCE_CLK72MHZ		0x66c
+#define	CLK_SOURCE_ADX1			0x670
+#define	CLK_SOURCE_AMX1			0x674
+#define	CLK_SOURCE_VIC			0x678
+#define	PLLP_OUTC			0x67c
+#define	PLLP_MISC1			0x680
+
+
+struct tegra124_car_softc {
+	device_t		dev;
+	struct resource *	mem_res;
+	struct mtx		mtx;
+	struct clkdom 		*clkdom;
+	int			type;
+};
+
+struct tegra124_init_item {
+	char 		*name;
+	char 		*parent;
+	uint64_t	frequency;
+	int 		enable;
+};
+
+void tegra124_init_plls(struct tegra124_car_softc *sc);
+
+void tegra124_periph_clock(struct tegra124_car_softc *sc);
+void tegra124_super_mux_clock(struct tegra124_car_softc *sc);
+
+int tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx,
+    bool reset);
+
+#endif /*_TEGRA124_CAR_*/
\ No newline at end of file


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_car.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,829 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_clk_per.c 317013 2017-04-16 08:21:14Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h>
+#include "tegra124_car.h"
+
+/* The TEGRA124_CLK_XUSB_GATE is missing in current
+ * DT bindings, define it localy
+ */
+#ifdef TEGRA124_CLK_XUSB_GATE
+#error "TEGRA124_CLK_XUSB_GATE is now defined, revisit XUSB code!"
+#else
+#define TEGRA124_CLK_XUSB_GATE 143
+#endif
+
+/* Bits in base register. */
+#define	PERLCK_AMUX_MASK	0x0F
+#define	PERLCK_AMUX_SHIFT	16
+#define	PERLCK_AMUX_DIS		(1 << 20)
+#define	PERLCK_UDIV_DIS		(1 << 24)
+#define	PERLCK_ENA_MASK		(1 << 28)
+#define	PERLCK_MUX_SHIFT	29
+#define	PERLCK_MUX_MASK		0x07
+
+
+struct periph_def {
+	struct clknode_init_def	clkdef;
+	uint32_t		base_reg;
+	uint32_t		div_width;
+	uint32_t		div_mask;
+	uint32_t		div_f_width;
+	uint32_t		div_f_mask;
+	uint32_t		flags;
+};
+
+struct pgate_def {
+	struct clknode_init_def	clkdef;
+	uint32_t		idx;
+	uint32_t		flags;
+};
+#define	PLIST(x) static const char *x[]
+
+#define	GATE(_id, cname, plist, _idx)					\
+{									\
+	.clkdef.id = TEGRA124_CLK_##_id,				\
+	.clkdef.name = cname,						\
+	.clkdef.parent_names = (const char *[]){plist},			\
+	.clkdef.parent_cnt = 1,						\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.idx = _idx,							\
+	.flags = 0,							\
+}
+
+/* Sources for multiplexors. */
+PLIST(mux_a_N_audio_N_p_N_clkm) =
+    {"pllA_out0", NULL, "audio",  NULL,
+     "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio0_N_p_N_clkm) =
+    {"pllA_out0", NULL, "audio0", NULL,
+     "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio1_N_p_N_clkm) =
+    {"pllA_out0", NULL, "audio1", NULL,
+     "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio2_N_p_N_clkm) =
+    {"pllA_out0", NULL, "audio2", NULL,
+     "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio3_N_p_N_clkm) =
+    {"pllA_out0", NULL, "audio3", NULL,
+     "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_N_audio4_N_p_N_clkm) =
+    {"pllA_out0", NULL, "audio4", NULL,
+     "pllP_out0", NULL, "clk_m"};
+PLIST(mux_a_clks_p_clkm_e) =
+    {"pllA_out0", "clk_s", "pllP_out0",
+     "clk_m", "pllE_out0"};
+PLIST(mux_a_c2_c_c3_p_N_clkm) =
+    {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllP_out0", NULL, "clk_m"};
+
+PLIST(mux_m_c_p_a_c2_c3) =
+    {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0",
+     "pllC2_out0", "pllC3_out0"};
+PLIST(mux_m_c_p_a_c2_c3_clkm) =
+    {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0",
+     "pllC2_out0", "pllC3_out0", "clk_m"};
+PLIST(mux_m_c_p_a_c2_c3_clkm_c4) =
+    {"pllM_out0", "pllC_out0", "pllP_out0", "pllA_out0",
+     "pllC2_out0", "pllC3_out0", "clk_m", "pllC4_out0"};
+PLIST(mux_m_c_p_clkm_mud_c2_c3) =
+    {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
+     "pllM_UD", "pllC2_out0", "pllC3_out0"};
+PLIST(mux_m_c_p_clkm_mud_c2_c3_cud) =
+    {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
+     "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"};
+
+PLIST(mux_m_c2_c_c3_p_N_a) =
+    {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllP_out0", NULL, "pllA_out0"};
+PLIST(mux_m_c2_c_c3_p_N_a_c4) =
+    {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     NULL, "pllA_out0", "pllC4_out0"};
+
+PLIST(mux_p_N_c_N_N_N_clkm) =
+    {"pllP_out0", NULL, "pllC_out0", NULL,
+     NULL, NULL, "clk_m"};
+PLIST(mux_p_N_c_N_m_N_clkm) =
+    {"pllP_out0", NULL, "pllC_out0", NULL,
+     "pllM_out0", NULL, "clk_m"};
+PLIST(mux_p_c_c2_clkm) =
+    {"pllP_out0", "pllC_out0", "pllC2_out0", "clk_m"};
+PLIST(mux_p_c2_c_c3_m) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllM_out0"};
+PLIST(mux_p_c2_c_c3_m_N_clkm) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllM_out0", NULL, "clk_m"};
+PLIST(mux_p_c2_c_c3_m_e_clkm) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllM_out0", "pllE_out0", "clk_m"};
+PLIST(mux_p_c2_c_c3_m_a_clkm) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllM_out0", "pllA_out0", "clk_m"};
+PLIST(mux_p_c2_c_c3_m_clks_clkm) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "pllM_out0", "clk_s", "clk_m"};
+PLIST(mux_p_c2_c_c3_clks_N_clkm) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "clk_s", NULL, "clk_m"};
+PLIST(mux_p_c2_c_c3_clkm_N_clks) =
+    {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+     "clk_m", NULL, "clk_s"};
+PLIST(mux_p_clkm_clks_E) =
+    {"pllP_out0", "clk_m", "clk_s", "pllE_out0"};
+PLIST(mux_p_m_d_a_c_d2_clkm) =
+    {"pllP_out0", "pllM_out0", "pllD_out0", "pllA_out0",
+     "pllC_out0", "pllD2_out0", "clk_m"};
+
+PLIST(mux_clkm_N_u48_N_p_N_u480) =
+    {"clk_m", NULL, "pllU_48", NULL,
+     "pllP_out0", NULL, "pllU_480"};
+PLIST(mux_clkm_p_c2_c_c3_refre) =
+    {"clk_m", "pllP_out0", "pllC2_out0", "pllC_out0",
+     "pllC3_out0", "pllREFE_out"};
+PLIST(mux_clkm_refe_clks_u480_c_c2_c3_oscdiv) =
+    {"clk_m", "pllREFE_out", "clk_s", "pllU_480",
+     "pllC_out0", "pllC2_out0", "pllC3_out0", "osc_div_clk"};
+
+PLIST(mux_sep_audio) =
+   {"pllA_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
+    "pllP_out0", NULL, "clk_m", NULL,
+    "spdif_in", "i2s0", "i2s1", "i2s2",
+    "i2s4", "pllA_out0", "ext_vimclk"};
+
+static uint32_t clk_enable_reg[] = {
+	CLK_OUT_ENB_L,
+	CLK_OUT_ENB_H,
+	CLK_OUT_ENB_U,
+	CLK_OUT_ENB_V,
+	CLK_OUT_ENB_W,
+	CLK_OUT_ENB_X,
+};
+
+static uint32_t clk_reset_reg[] = {
+	RST_DEVICES_L,
+	RST_DEVICES_H,
+	RST_DEVICES_U,
+	RST_DEVICES_V,
+	RST_DEVICES_W,
+	RST_DEVICES_X,
+};
+
+#define	L(n)  ((0 * 32) + (n))
+#define	H(n)  ((1 * 32) + (n))
+#define	U(n)  ((2 * 32) + (n))
+#define	V(n)  ((3 * 32) + (n))
+#define	W(n)  ((4 * 32) + (n))
+#define	X(n)  ((5 * 32) + (n))
+
+static struct pgate_def pgate_def[] = {
+	/* bank L ->  0-31 */
+	/* GATE(CPU, "cpu", "clk_m", L(0)), */
+	GATE(ISPB, "ispb", "clk_m", L(3)),
+	GATE(RTC, "rtc", "clk_s", L(4)),
+	GATE(TIMER, "timer", "clk_m", L(5)),
+	GATE(UARTA, "uarta", "pc_uarta" , L(6)),
+	GATE(UARTB, "uartb", "pc_uartb", L(7)),
+	GATE(VFIR, "vfir", "pc_vfir", L(7)),
+	/* GATE(GPIO, "gpio", "clk_m", L(8)), */
+	GATE(SDMMC2, "sdmmc2", "pc_sdmmc2", L(9)),
+	GATE(SPDIF_OUT, "spdif_out", "pc_spdif_out", L(10)),
+	GATE(SPDIF_IN, "spdif_in", "pc_spdif_in", L(10)),
+	GATE(I2S1, "i2s1", "pc_i2s1", L(11)),
+	GATE(I2C1, "i2c1", "pc_i2c1", L(12)),
+	GATE(SDMMC1, "sdmmc1", "pc_sdmmc1", L(14)),
+	GATE(SDMMC4, "sdmmc4", "pc_sdmmc4", L(15)),
+	GATE(PWM, "pwm", "pc_pwm", L(17)),
+	GATE(I2S2, "i2s2", "pc_i2s2", L(18)),
+	GATE(VI, "vi", "pc_vi", L(20)),
+	GATE(USBD, "usbd", "clk_m", L(22)),
+	GATE(ISP, "isp", "pc_isp", L(23)),
+	GATE(DISP2, "disp2", "pc_disp2", L(26)),
+	GATE(DISP1, "disp1", "pc_disp1", L(27)),
+	GATE(HOST1X, "host1x", "pc_host1x", L(28)),
+	GATE(VCP, "vcp", "clk_m", L(29)),
+	GATE(I2S0, "i2s0", "pc_i2s0", L(30)),
+	/* GATE(CACHE2, "ccache2", "clk_m", L(31)), */
+
+	/* bank H -> 32-63 */
+	GATE(MC, "mem", "clk_m", H(0)),
+	/* GATE(AHBDMA, "ahbdma", "clk_m", H(1)), */
+	GATE(APBDMA, "apbdma", "clk_m", H(2)),
+	GATE(KBC, "kbc", "clk_s", H(4)),
+	/* GATE(STAT_MON, "stat_mon", "clk_s", H(5)), */
+	/* GATE(PMC, "pmc", "clk_s", H(6)), */
+	GATE(FUSE, "fuse", "clk_m", H(7)),
+	GATE(KFUSE, "kfuse", "clk_m", H(8)),
+	GATE(SBC1, "spi1", "pc_spi1", H(9)),
+	GATE(NOR, "snor", "pc_snor", H(10)),
+	/* GATE(JTAG2TBC, "jtag2tbc", "clk_m", H(11)), */
+	GATE(SBC2, "spi2", "pc_spi2", H(12)),
+	GATE(SBC3, "spi3", "pc_spi3", H(14)),
+	GATE(I2C5, "i2c5", "pc_i2c5", H(15)),
+	GATE(DSIA, "dsia", "dsia_mux", H(16)),
+	GATE(MIPI, "hsi", "pc_hsi", H(18)),
+	GATE(HDMI, "hdmi", "pc_hdmi", H(19)),
+	GATE(CSI, "csi", "pllP_out3", H(20)),
+	GATE(I2C2, "i2c2", "pc_i2c2", H(22)),
+	GATE(UARTC, "uartc", "pc_uartc", H(23)),
+	GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)),
+	GATE(EMC, "emc", "pc_emc_2x", H(25)),
+	GATE(USB2, "usb2", "clk_m", H(26)),
+	GATE(USB3, "usb3", "clk_m", H(27)),
+	GATE(VDE, "vde", "pc_vde", H(29)),
+	GATE(BSEA, "bsea", "clk_m", H(30)),
+	GATE(BSEV, "bsev", "clk_m", H(31)),
+
+	/* bank U  -> 64-95 */
+	GATE(UARTD, "uartd", "pc_uartd", U(1)),
+	GATE(I2C3, "i2c3", "pc_i2c3", U(3)),
+	GATE(SBC4, "spi4", "pc_spi4", U(4)),
+	GATE(SDMMC3, "sdmmc3", "pc_sdmmc3", U(5)),
+	GATE(PCIE, "pcie", "clk_m", U(6)),
+	GATE(OWR, "owr", "pc_owr", U(7)),
+	GATE(AFI, "afi", "clk_m", U(8)),
+	GATE(CSITE, "csite", "pc_csite", U(9)),
+	/* GATE(AVPUCQ, "avpucq", clk_m, U(11)), */
+	GATE(TRACE, "traceclkin", "pc_traceclkin", U(13)),
+	GATE(SOC_THERM, "soc_therm", "pc_soc_therm", U(14)),
+	GATE(DTV, "dtv", "clk_m", U(15)),
+	GATE(I2CSLOW, "i2c_slow", "pc_i2c_slow", U(17)),
+	GATE(DSIB, "dsib", "dsib_mux", U(18)),
+	GATE(TSEC, "tsec", "pc_tsec", U(19)),
+	/* GATE(IRAMA, "irama", "clk_m", U(20)), */
+	/* GATE(IRAMB, "iramb", "clk_m", U(21)), */
+	/* GATE(IRAMC, "iramc", "clk_m", U(22)), */
+	/* GATE(IRAMD, "iramd", "clk_m", U(23)), */
+	/* GATE(CRAM2, "cram2", "clk_m", U(24)), */
+	GATE(XUSB_HOST, "xusb_core_host", "pc_xusb_core_host", U(25)),
+	/* GATE(M_DOUBLER, "m_doubler", "clk_m", U(26)), */
+	GATE(MSENC, "msenc", "pc_msenc", U(27)),
+	GATE(CSUS, "sus_out", "clk_m", U(28)),
+	/* GATE(DEVD2_OUT, "devd2_out", "clk_m", U(29)), */
+	/* GATE(DEVD1_OUT, "devd1_out", "clk_m", U(30)), */
+	GATE(XUSB_DEV, "xusb_core_dev", "pc_xusb_core_dev", U(31)),
+
+	/* bank V  -> 96-127 */
+	/* GATE(CPUG, "cpug", "clk_m", V(0)), */
+	/* GATE(CPULP, "cpuLP", "clk_m", V(1)), */
+	GATE(MSELECT, "mselect", "pc_mselect", V(3)),
+	GATE(TSENSOR, "tsensor", "pc_tsensor", V(4)),
+	GATE(I2S3, "i2s3", "pc_i2s3", V(5)),
+	GATE(I2S4, "i2s4", "pc_i2s4", V(6)),
+	GATE(I2C4, "i2c4", "pc_i2c4", V(7)),
+	GATE(SBC5, "spi5", "pc_spi5", V(8)),
+	GATE(SBC6, "spi6", "pc_spi6", V(9)),
+	GATE(D_AUDIO, "audio", "pc_audio", V(10)),
+	GATE(APBIF, "apbif", "clk_m", V(11)),
+	GATE(DAM0, "dam0", "pc_dam0", V(12)),
+	GATE(DAM1, "dam1", "pc_dam1", V(13)),
+	GATE(DAM2, "dam2",  "pc_dam2", V(14)),
+	GATE(HDA2CODEC_2X, "hda2codec_2x", "pc_hda2codec_2x", V(15)),
+	/* GATE(ATOMICS, "atomics", "clk_m", V(16)), */
+	/* GATE(SPDIF_DOUBLER, "spdif_doubler", "clk_m", V(22)), */
+	GATE(ACTMON, "actmon", "pc_actmon", V(23)),
+	GATE(EXTERN1, "extperiph1", "pc_extperiph1", V(24)),
+	GATE(EXTERN2, "extperiph2", "pc_extperiph2", V(25)),
+	GATE(EXTERN3, "extperiph3", "pc_extperiph3", V(26)),
+	GATE(SATA_OOB, "sata_oob", "pc_sata_oob", V(27)),
+	GATE(SATA, "sata", "pc_sata", V(28)),
+	GATE(HDA, "hda", "pc_hda", V(29)),
+
+	/* bank W   -> 128-159*/
+	GATE(HDA2HDMI, "hda2hdmi", "clk_m", W(0)),
+	GATE(SATA_COLD, "sata_cold", "clk_m", W(1)), /* Reset only */
+	/* GATE(PCIERX0, "pcierx0", "clk_m", W(2)), */
+	/* GATE(PCIERX1, "pcierx1", "clk_m", W(3)), */
+	/* GATE(PCIERX2, "pcierx2", "clk_m", W(4)), */
+	/* GATE(PCIERX3, "pcierx3", "clk_m", W(5)), */
+	/* GATE(PCIERX4, "pcierx4", "clk_m", W(6)), */
+	/* GATE(PCIERX5, "pcierx5", "clk_m", W(7)), */
+	/* GATE(CEC, "cec", "clk_m", W(8)), */
+	/* GATE(PCIE2_IOBIST, "pcie2_iobist", "clk_m", W(9)), */
+	/* GATE(EMC_IOBIST, "emc_iobist", "clk_m", W(10)), */
+	/* GATE(HDMI_IOBIST, "hdmi_iobist", "clk_m", W(11)), */
+	/* GATE(SATA_IOBIST, "sata_iobist", "clk_m", W(12)), */
+	/* GATE(MIPI_IOBIST, "mipi_iobist", "clk_m", W(13)), */
+	GATE(XUSB_GATE, "xusb_gate", "clk_m", W(15)),
+	GATE(CILAB, "cilab", "pc_cilab", W(16)),
+	GATE(CILCD, "cilcd", "pc_cilcd", W(17)),
+	GATE(CILE, "cile", "pc_cile", W(18)),
+	GATE(DSIALP, "dsia_lp", "pc_dsia_lp", W(19)),
+	GATE(DSIBLP, "dsib_lp", "pc_dsib_lp", W(20)),
+	GATE(ENTROPY, "entropy", "pc_entropy", W(21)),
+	GATE(AMX, "amx", "pc_amx", W(25)),
+	GATE(ADX, "adx", "pc_adx", W(26)),
+	GATE(DFLL_REF, "dvfs_ref", "pc_dvfs_ref", W(27)),
+	GATE(DFLL_SOC, "dvfs_soc", "pc_dvfs_soc",  W(27)),
+	GATE(XUSB_SS, "xusb_ss", "xusb_ss_mux", W(28)),
+	/* GATE(EMC_LATENCY, "emc_latency", "pc_emc_latency", W(29)), */
+
+	/* bank X -> 160-191*/
+	/* GATE(SPARE, "spare", "clk_m", X(0)), */
+	/* GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), */
+	/* GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), */
+	GATE(I2C6, "i2c6", "pc_i2c6", X(6)),
+	GATE(VIM2_CLK, "vim2_clk", "clk_m", X(11)),
+	/* GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), */
+	GATE(HDMI_AUDIO, "hdmi_audio", "pc_hdmi_audio", X(16)),
+	GATE(CLK72MHZ, "clk72mhz", "pc_clk72mhz", X(17)),
+	GATE(VIC03, "vic", "pc_vic", X(18)),
+	GATE(ADX1, "adx1", "pc_adx1", X(20)),
+	GATE(DPAUX, "dpaux", "clk_m", X(21)),
+	GATE(SOR0_LVDS, "sor0", "pc_sor0", X(22)),
+	GATE(GPU, "gpu", "osc_div_clk", X(24)),
+	GATE(AMX1, "amx1", "pc_amx1", X(26)),
+};
+
+/* Peripheral clock clock */
+#define	DCF_HAVE_MUX		0x0100 /* Block with multipexor */
+#define	DCF_HAVE_ENA		0x0200 /* Block with enable bit */
+#define	DCF_HAVE_DIV		0x0400 /* Block with divider */
+
+/* Mark block with additional bits / functionality. */
+#define	DCF_IS_MASK		0x00FF
+#define	DCF_IS_UART		0x0001
+#define	DCF_IS_VI		0x0002
+#define	DCF_IS_HOST1X		0x0003
+#define	DCF_IS_XUSB_SS		0x0004
+#define	DCF_IS_EMC_DLL		0x0005
+#define	DCF_IS_SATA		0x0006
+#define	DCF_IS_VIC		0x0007
+#define	DCF_IS_AUDIO		0x0008
+#define	DCF_IS_SOR0		0x0009
+#define	DCF_IS_EMC		0x000A
+
+/* Basic pheripheral clock */
+#define	PER_CLK(_id, cn, pl, r, diw, fiw, f)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cn,						\
+	.clkdef.parent_names = pl,					\
+	.clkdef.parent_cnt = nitems(pl),				\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
+	.base_reg = r,							\
+	.div_width = diw,						\
+	.div_f_width = fiw,						\
+	.flags = f,							\
+}
+
+/* Mux with fractional 8.1 divider. */
+#define	CLK_8_1(id, cn, pl, r,  f)					\
+	PER_CLK(id, cn, pl, r,  8, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV)
+
+/* Mux with fractional 16.1 divider. */
+#define	CLK16_1(id, cn, pl, r,  f)					\
+	PER_CLK(id, cn, pl, r,  16, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV)
+/* Mux with integer 16bits divider. */
+#define	CLK16_0(id, cn, pl, r,  f)					\
+	PER_CLK(id, cn, pl, r,  16, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV)
+/* Mux wihout divider. */
+#define	CLK_0_0(id, cn, pl, r,  f)					\
+	PER_CLK(id, cn, pl, r,  0, 0, (f) | DCF_HAVE_MUX)
+
+static struct periph_def periph_def[] = {
+	CLK_8_1(0, "pc_i2s1", mux_a_N_audio1_N_p_N_clkm, CLK_SOURCE_I2S1, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_i2s2", mux_a_N_audio2_N_p_N_clkm, CLK_SOURCE_I2S2, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_spdif_out", mux_a_N_audio_N_p_N_clkm, CLK_SOURCE_SPDIF_OUT, 0),
+	CLK_8_1(0, "pc_spdif_in", mux_p_c2_c_c3_m, CLK_SOURCE_SPDIF_IN, 0),
+	CLK_8_1(0, "pc_pwm", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_PWM, 0),
+	CLK_8_1(0, "pc_spi2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI2, 0),
+	CLK_8_1(0, "pc_spi3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI3, 0),
+	CLK16_0(0, "pc_i2c5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C5, 0),
+	CLK16_0(0, "pc_i2c1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C1, 0),
+	CLK_8_1(0, "pc_spi1", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI1, 0),
+	CLK_0_0(0, "pc_disp1", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP1, 0),
+	CLK_0_0(0, "pc_disp2", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_DISP2, 0),
+	CLK_8_1(0, "pc_isp", mux_m_c_p_a_c2_c3_clkm_c4, CLK_SOURCE_ISP, 0),
+	CLK_8_1(0, "pc_vi", mux_m_c2_c_c3_p_N_a_c4, CLK_SOURCE_VI, DCF_IS_VI),
+	CLK_8_1(0, "pc_sdmmc1", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC1, 0),
+	CLK_8_1(0, "pc_sdmmc2", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC2, 0),
+	CLK_8_1(0, "pc_sdmmc4", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC4, 0),
+	CLK_8_1(0, "pc_vfir", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VFIR, 0),
+	CLK_8_1(0, "pc_hsi", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HSI, 0),
+	CLK16_1(0, "pc_uarta", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTA, DCF_IS_UART),
+	CLK16_1(0, "pc_uartb", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTB, DCF_IS_UART),
+	CLK_8_1(0, "pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, DCF_IS_HOST1X),
+	CLK_8_1(0, "pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0),
+	CLK16_0(0, "pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0),
+	CLK_8_1(0, "pc_emc_2x", mux_m_c_p_clkm_mud_c2_c3_cud, CLK_SOURCE_EMC, DCF_IS_EMC),
+	CLK16_1(0, "pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, DCF_IS_UART),
+	CLK_8_1(0, "pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0),
+	CLK_8_1(0, "pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0),
+	CLK16_0(0, "pc_i2c3", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C3, 0),
+	CLK_8_1(0, "pc_sdmmc3", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC3, 0),
+	CLK16_1(0, "pc_uartd", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTD, DCF_IS_UART),
+	CLK_8_1(0, "pc_vde", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_VDE, 0),
+	CLK_8_1(0, "pc_owr", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_OWR, 0),
+	CLK_8_1(0, "pc_snor", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_NOR, 0),
+	CLK_8_1(0, "pc_csite", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_CSITE, 0),
+	CLK_8_1(0, "pc_i2s0", mux_a_N_audio0_N_p_N_clkm, CLK_SOURCE_I2S0, 0),
+/* DTV xxx */
+	CLK_8_1(0, "pc_msenc", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_MSENC, 0),
+	CLK_8_1(0, "pc_tsec", mux_p_c2_c_c3_m_a_clkm, CLK_SOURCE_TSEC, 0),
+/* SPARE2 */
+
+
+	CLK_8_1(0, "pc_mselect", mux_p_c2_c_c3_m_clks_clkm, CLK_SOURCE_MSELECT, 0),
+	CLK_8_1(0, "pc_tsensor", mux_p_c2_c_c3_clkm_N_clks, CLK_SOURCE_TSENSOR, 0),
+	CLK_8_1(0, "pc_i2s3", mux_a_N_audio3_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_i2s4", mux_a_N_audio4_N_p_N_clkm, CLK_SOURCE_I2S4, DCF_HAVE_ENA),
+	CLK16_0(0, "pc_i2c4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C4, 0),
+	CLK_8_1(0, "pc_spi5", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI5, 0),
+	CLK_8_1(0, "pc_spi6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI6, 0),
+	CLK_8_1(0, "pc_audio", mux_sep_audio, CLK_SOURCE_AUDIO, DCF_IS_AUDIO),
+	CLK_8_1(0, "pc_dam0", mux_sep_audio, CLK_SOURCE_DAM0, DCF_IS_AUDIO),
+	CLK_8_1(0, "pc_dam1", mux_sep_audio, CLK_SOURCE_DAM1, DCF_IS_AUDIO),
+	CLK_8_1(0, "pc_dam2",  mux_sep_audio, CLK_SOURCE_DAM2, DCF_IS_AUDIO),
+	CLK_8_1(0, "pc_hda2codec_2x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA2CODEC_2X, 0),
+	CLK_8_1(0, "pc_actmon", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_ACTMON, 0),
+	CLK_8_1(0, "pc_extperiph1", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH1, 0),
+	CLK_8_1(0, "pc_extperiph2", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH2,  0),
+	CLK_8_1(0, "pc_extperiph3", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH3, 0),
+	CLK_8_1(0, "pc_i2c_slow", mux_p_c2_c_c3_clks_N_clkm, CLK_SOURCE_I2C_SLOW, 0),
+/* SYS */
+	CLK_8_1(0, "pc_sor0", mux_p_m_d_a_c_d2_clkm,  CLK_SOURCE_SOR0, DCF_IS_SOR0),
+	CLK_8_1(0, "pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0),
+	CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, DCF_IS_SATA),
+	CLK_8_1(0, "pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0),
+	CLK_8_1(TEGRA124_CLK_XUSB_HOST_SRC,
+		   "pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_HOST, 0),
+	CLK_8_1(TEGRA124_CLK_XUSB_FALCON_SRC,
+		   "pc_xusb_falcon", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_FALCON, 0),
+	CLK_8_1(TEGRA124_CLK_XUSB_FS_SRC,
+		   "pc_xusb_fs", mux_clkm_N_u48_N_p_N_u480, CLK_SOURCE_XUSB_FS, 0),
+	CLK_8_1(TEGRA124_CLK_XUSB_DEV_SRC,
+		   "pc_xusb_core_dev", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_DEV, 0),
+	CLK_8_1(TEGRA124_CLK_XUSB_SS_SRC,
+		   "pc_xusb_ss", mux_clkm_refe_clks_u480_c_c2_c3_oscdiv, CLK_SOURCE_XUSB_SS, DCF_IS_XUSB_SS),
+	CLK_8_1(0, "pc_cilab", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILAB, 0),
+	CLK_8_1(0, "pc_cilcd", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILCD, 0),
+	CLK_8_1(0, "pc_cile", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_CILE, 0),
+	CLK_8_1(0, "pc_dsia_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIA_LP, 0),
+	CLK_8_1(0, "pc_dsib_lp", mux_p_N_c_N_N_N_clkm, CLK_SOURCE_DSIB_LP, 0),
+	CLK_8_1(0, "pc_entropy", mux_p_clkm_clks_E, CLK_SOURCE_ENTROPY, 0),
+	CLK_8_1(0, "pc_dvfs_ref", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_REF, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_dvfs_soc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_DVFS_SOC, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_traceclkin", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_TRACECLKIN, 0),
+	CLK_8_1(0, "pc_adx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_amx", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_emc_latency", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_LATENCY, 0),
+	CLK_8_1(0, "pc_soc_therm", mux_m_c_p_a_c2_c3, CLK_SOURCE_SOC_THERM, 0),
+	CLK_8_1(0, "pc_vi_sensor2", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR2, 0),
+	CLK16_0(0, "pc_i2c6", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C6, 0),
+	CLK_8_1(0, "pc_emc_dll", mux_m_c_p_clkm_mud_c2_c3, CLK_SOURCE_EMC_DLL, DCF_IS_EMC_DLL),
+	CLK_8_1(0, "pc_hdmi_audio", mux_p_c_c2_clkm, CLK_SOURCE_HDMI_AUDIO, 0),
+	CLK_8_1(0, "pc_clk72mhz", mux_p_c_c2_clkm, CLK_SOURCE_CLK72MHZ, 0),
+	CLK_8_1(0, "pc_adx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_ADX1, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_amx1", mux_a_c2_c_c3_p_N_clkm, CLK_SOURCE_AMX1, DCF_HAVE_ENA),
+	CLK_8_1(0, "pc_vic", mux_m_c_p_a_c2_c3_clkm, CLK_SOURCE_VIC, DCF_IS_VIC),
+};
+
+static int periph_init(struct clknode *clk, device_t dev);
+static int periph_recalc(struct clknode *clk, uint64_t *freq);
+static int periph_set_freq(struct clknode *clk, uint64_t fin,
+    uint64_t *fout, int flags, int *stop);
+static int periph_set_mux(struct clknode *clk, int idx);
+
+struct periph_sc {
+	device_t		clkdev;
+	uint32_t		base_reg;
+	uint32_t		div_shift;
+	uint32_t		div_width;
+	uint32_t		div_mask;
+	uint32_t		div_f_width;
+	uint32_t		div_f_mask;
+	uint32_t		flags;
+
+	uint32_t		divider;
+	int 			mux;
+};
+
+static clknode_method_t periph_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		periph_init),
+	CLKNODEMETHOD(clknode_recalc_freq,	periph_recalc),
+	CLKNODEMETHOD(clknode_set_freq,		periph_set_freq),
+	CLKNODEMETHOD(clknode_set_mux, 		periph_set_mux),
+	CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_periph, tegra124_periph_class, periph_methods,
+   sizeof(struct periph_sc), clknode_class);
+
+static int
+periph_init(struct clknode *clk, device_t dev)
+{
+	struct periph_sc *sc;
+	uint32_t reg;
+	sc = clknode_get_softc(clk);
+
+	DEVICE_LOCK(sc);
+	if (sc->flags & DCF_HAVE_ENA)
+		MD4(sc, sc->base_reg, PERLCK_ENA_MASK, PERLCK_ENA_MASK);
+
+	RD4(sc, sc->base_reg, &reg);
+	DEVICE_UNLOCK(sc);
+
+	/* Stnadard mux. */
+	if (sc->flags & DCF_HAVE_MUX)
+		sc->mux = (reg >> PERLCK_MUX_SHIFT) & PERLCK_MUX_MASK;
+	else
+		sc->mux = 0;
+	if (sc->flags & DCF_HAVE_DIV)
+		sc->divider = (reg & sc->div_mask) + 2;
+	else
+		sc->divider = 1;
+	if ((sc->flags & DCF_IS_MASK) == DCF_IS_UART) {
+		if (!(reg & PERLCK_UDIV_DIS))
+			sc->divider = 2;
+	}
+
+	/* AUDIO MUX */
+	if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) {
+		if (!(reg & PERLCK_AMUX_DIS) && (sc->mux == 7)) {
+			sc->mux = 8 +
+			    ((reg >> PERLCK_AMUX_SHIFT) & PERLCK_MUX_MASK);
+		}
+	}
+	clknode_init_parent_idx(clk, sc->mux);
+	return(0);
+}
+
+static int
+periph_set_mux(struct clknode *clk, int idx)
+{
+	struct periph_sc *sc;
+	uint32_t reg;
+
+
+	sc = clknode_get_softc(clk);
+	if (!(sc->flags & DCF_HAVE_MUX))
+		return (ENXIO);
+
+	sc->mux = idx;
+	DEVICE_LOCK(sc);
+	RD4(sc, sc->base_reg, &reg);
+	reg &= ~(PERLCK_MUX_MASK << PERLCK_MUX_SHIFT);
+	if ((sc->flags & DCF_IS_MASK) == DCF_IS_AUDIO) {
+		reg &= ~PERLCK_AMUX_DIS;
+		reg &= ~(PERLCK_MUX_MASK << PERLCK_AMUX_SHIFT);
+
+		if (idx <= 7) {
+			reg |= idx << PERLCK_MUX_SHIFT;
+		} else {
+			reg |= 7 << PERLCK_MUX_SHIFT;
+			reg |= (idx - 8) << PERLCK_AMUX_SHIFT;
+		}
+	} else {
+		reg |= idx << PERLCK_MUX_SHIFT;
+	}
+	WR4(sc, sc->base_reg, reg);
+	DEVICE_UNLOCK(sc);
+
+	return(0);
+}
+
+static int
+periph_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct periph_sc *sc;
+	uint32_t reg;
+
+	sc = clknode_get_softc(clk);
+
+	if (sc->flags & DCF_HAVE_DIV) {
+		DEVICE_LOCK(sc);
+		RD4(sc, sc->base_reg, &reg);
+		DEVICE_UNLOCK(sc);
+		*freq = (*freq << sc->div_f_width) / sc->divider;
+	}
+	return (0);
+}
+
+static int
+periph_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+   int flags, int *stop)
+{
+	struct periph_sc *sc;
+	uint64_t tmp, divider;
+
+	sc = clknode_get_softc(clk);
+	if (!(sc->flags & DCF_HAVE_DIV)) {
+		*stop = 0;
+		return (0);
+	}
+
+	tmp = fin << sc->div_f_width;
+	divider = tmp / *fout;
+	if ((tmp % *fout) != 0)
+		divider++;
+
+	if (divider < (1 << sc->div_f_width))
+		 divider = 1 << (sc->div_f_width - 1);
+
+	if (flags & CLK_SET_DRYRUN) {
+		if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+		    (*fout != (tmp / divider)))
+			return (ERANGE);
+	} else {
+		DEVICE_LOCK(sc);
+		MD4(sc, sc->base_reg, sc->div_mask,
+		    (divider - (1 << sc->div_f_width)));
+		DEVICE_UNLOCK(sc);
+		sc->divider = divider;
+	}
+	*fout = tmp / divider;
+	*stop = 1;
+	return (0);
+}
+
+static int
+periph_register(struct clkdom *clkdom, struct periph_def *clkdef)
+{
+	struct clknode *clk;
+	struct periph_sc *sc;
+
+	clk = clknode_create(clkdom, &tegra124_periph_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->clkdev = clknode_get_device(clk);
+	sc->base_reg = clkdef->base_reg;
+	sc->div_width = clkdef->div_width;
+	sc->div_mask = (1 <<clkdef->div_width) - 1;
+	sc->div_f_width = clkdef->div_f_width;
+	sc->div_f_mask = (1 <<clkdef->div_f_width) - 1;
+	sc->flags = clkdef->flags;
+
+	clknode_register(clkdom, clk);
+	return (0);
+}
+
+/* -------------------------------------------------------------------------- */
+static int pgate_init(struct clknode *clk, device_t dev);
+static int pgate_set_gate(struct clknode *clk, bool enable);
+
+struct pgate_sc {
+	device_t		clkdev;
+	uint32_t		idx;
+	uint32_t		flags;
+	uint32_t		enabled;
+
+};
+
+static clknode_method_t pgate_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		pgate_init),
+	CLKNODEMETHOD(clknode_set_gate,		pgate_set_gate),
+	CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_pgate, tegra124_pgate_class, pgate_methods,
+   sizeof(struct pgate_sc), clknode_class);
+
+static uint32_t
+get_enable_reg(int idx)
+{
+	KASSERT(idx / 32 < nitems(clk_enable_reg),
+	    ("Invalid clock index for enable: %d", idx));
+	return (clk_enable_reg[idx / 32]);
+}
+
+static uint32_t
+get_reset_reg(int idx)
+{
+	KASSERT(idx / 32 < nitems(clk_reset_reg),
+	    ("Invalid clock index for reset: %d", idx));
+	return (clk_reset_reg[idx / 32]);
+}
+
+static int
+pgate_init(struct clknode *clk, device_t dev)
+{
+	struct pgate_sc *sc;
+	uint32_t ena_reg, rst_reg, mask;
+
+	sc = clknode_get_softc(clk);
+	mask = 1 << (sc->idx % 32);
+
+	DEVICE_LOCK(sc);
+	RD4(sc, get_enable_reg(sc->idx), &ena_reg);
+	RD4(sc, get_reset_reg(sc->idx), &rst_reg);
+	DEVICE_UNLOCK(sc);
+
+	sc->enabled = ena_reg & mask ? 1 : 0;
+	clknode_init_parent_idx(clk, 0);
+
+	return(0);
+}
+
+static int
+pgate_set_gate(struct clknode *clk, bool enable)
+{
+	struct pgate_sc *sc;
+	uint32_t reg, mask, base_reg;
+
+	sc = clknode_get_softc(clk);
+	mask = 1 << (sc->idx % 32);
+	sc->enabled = enable;
+	base_reg = get_enable_reg(sc->idx);
+
+	DEVICE_LOCK(sc);
+	MD4(sc, base_reg, mask, enable ? mask : 0);
+	RD4(sc, base_reg, &reg);
+	DEVICE_UNLOCK(sc);
+
+	DELAY(2);
+	return(0);
+}
+
+int
+tegra124_hwreset_by_idx(struct tegra124_car_softc *sc, intptr_t idx, bool reset)
+{
+	uint32_t reg, mask, reset_reg;
+
+	mask = 1 << (idx % 32);
+	reset_reg = get_reset_reg(idx);
+
+	CLKDEV_DEVICE_LOCK(sc->dev);
+	CLKDEV_MODIFY_4(sc->dev, reset_reg, mask, reset ? mask : 0);
+	CLKDEV_READ_4(sc->dev, reset_reg, &reg);
+	CLKDEV_DEVICE_UNLOCK(sc->dev);
+
+	return(0);
+}
+
+static int
+pgate_register(struct clkdom *clkdom, struct pgate_def *clkdef)
+{
+	struct clknode *clk;
+	struct pgate_sc *sc;
+
+	clk = clknode_create(clkdom, &tegra124_pgate_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->clkdev = clknode_get_device(clk);
+	sc->idx = clkdef->idx;
+	sc->flags = clkdef->flags;
+
+	clknode_register(clkdom, clk);
+	return (0);
+}
+
+void
+tegra124_periph_clock(struct tegra124_car_softc *sc)
+{
+	int i, rv;
+
+	for (i = 0; i <  nitems(periph_def); i++) {
+		rv = periph_register(sc->clkdom, &periph_def[i]);
+		if (rv != 0)
+			panic("tegra124_periph_register failed");
+	}
+	for (i = 0; i <  nitems(pgate_def); i++) {
+		rv = pgate_register(sc->clkdom, &pgate_def[i]);
+		if (rv != 0)
+			panic("tegra124_pgate_register failed");
+	}
+
+}


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_clk_per.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1148 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_clk_pll.c 317013 2017-04-16 08:21:14Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h>
+#include "tegra124_car.h"
+
+/* #define TEGRA_PLL_DEBUG */
+#ifdef TEGRA_PLL_DEBUG
+#define dprintf(...) printf(__VA_ARGS__)
+#else
+#define dprintf(...)
+#endif
+
+/* All PLLs. */
+enum pll_type {
+	PLL_M,
+	PLL_X,
+	PLL_C,
+	PLL_C2,
+	PLL_C3,
+	PLL_C4,
+	PLL_P,
+	PLL_A,
+	PLL_U,
+	PLL_D,
+	PLL_D2,
+	PLL_DP,
+	PLL_E,
+	PLL_REFE};
+
+/* Common base register bits. */
+#define	PLL_BASE_BYPASS		(1U << 31)
+#define	PLL_BASE_ENABLE		(1  << 30)
+#define	PLL_BASE_REFDISABLE	(1  << 29)
+#define	PLL_BASE_LOCK		(1  << 27)
+#define	PLL_BASE_DIVM_SHIFT	0
+#define	PLL_BASE_DIVN_SHIFT	8
+
+#define	PLLRE_MISC_LOCK		(1 << 24)
+
+#define	PLL_MISC_LOCK_ENABLE	(1 << 18)
+#define	PLLC_MISC_LOCK_ENABLE	(1 << 24)
+#define	PLLDU_MISC_LOCK_ENABLE	(1 << 22)
+#define	PLLRE_MISC_LOCK_ENABLE	(1 << 30)
+#define	PLLSS_MISC_LOCK_ENABLE	(1 << 30)
+
+#define	PLLC_IDDQ_BIT		26
+#define	PLLX_IDDQ_BIT		3
+#define	PLLRE_IDDQ_BIT		16
+#define	PLLSS_IDDQ_BIT		19
+
+#define	PLL_LOCK_TIMEOUT	5000
+
+/* Post divider <-> register value mapping. */
+struct pdiv_table {
+	uint32_t divider;	/* real divider */
+	uint32_t value;		/* register value */
+};
+
+/* Bits definition of M, N and P fields. */
+struct mnp_bits {
+	uint32_t	m_width;
+	uint32_t	n_width;
+	uint32_t	p_width;
+	uint32_t	p_shift;
+};
+
+struct clk_pll_def {
+	struct clknode_init_def	clkdef;
+	enum pll_type		type;
+	uint32_t		base_reg;
+	uint32_t		misc_reg;
+	uint32_t		lock_mask;
+	uint32_t		lock_enable;
+	uint32_t		iddq_reg;
+	uint32_t		iddq_mask;
+	uint32_t		flags;
+	struct pdiv_table 	*pdiv_table;
+	struct mnp_bits		mnp_bits;
+};
+
+#define	PLL(_id, cname, pname)					\
+	.clkdef.id = _id,					\
+	.clkdef.name = cname,					\
+	.clkdef.parent_names = (const char *[]){pname},		\
+	.clkdef.parent_cnt = 1,				\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS
+
+/* Tegra K1 PLLs
+ PLLM: Clock source for EMC 2x clock
+ PLLX: Clock source for the fast CPU cluster and the shadow CPU
+ PLLC: Clock source for general use
+ PLLC2: Clock source for engine scaling
+ PLLC3: Clock source for engine scaling
+ PLLC4: Clock source for ISP/VI units
+ PLLP: Clock source for most peripherals
+ PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz)
+ PLLU: Clock source for USB PHY, provides 12/60/480 MHz
+ PLLD: Clock sources for the DSI and display subsystem
+ PLLD2: Clock sources for the DSI and display subsystem
+ refPLLe:
+ PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum)
+ PLLDP: Clock source for eDP/LVDS (spread spectrum)
+
+ DFLLCPU: DFLL clock source for the fast CPU cluster
+ GPCPLL: Clock source for the GPU
+*/
+
+static struct pdiv_table pllm_map[] = {
+	{1, 0},
+	{2, 1},
+	{0, 0}
+};
+
+static struct pdiv_table pllxc_map[] = {
+	{ 1,  0},
+	{ 2,  1},
+	{ 3,  2},
+	{ 4,  3},
+	{ 5,  4},
+	{ 6,  5},
+	{ 8,  6},
+	{10,  7},
+	{12,  8},
+	{16,  9},
+	{12, 10},
+	{16, 11},
+	{20, 12},
+	{24, 13},
+	{32, 14},
+	{ 0,  0}
+};
+
+static struct pdiv_table pllc_map[] = {
+	{ 1, 0},
+	{ 2, 1},
+	{ 3, 2},
+	{ 4, 3},
+	{ 6, 4},
+	{ 8, 5},
+	{12, 6},
+	{16, 7},
+	{ 0,  0}
+};
+
+static struct pdiv_table pll12g_ssd_esd_map[] = {
+	{ 1,  0},
+	{ 2,  1},
+	{ 3,  2},
+	{ 4,  3},
+	{ 5,  4},
+	{ 6,  5},
+	{ 8,  6},
+	{10,  7},
+	{12,  8},
+	{16,  9},
+	{12, 10},
+	{16, 11},
+	{20, 12},
+	{24, 13},
+	{32, 14},
+	{ 0,  0}
+};
+
+static struct pdiv_table pllu_map[] = {
+	{1, 1},
+	{2, 0},
+	{0, 0}
+};
+
+static struct pdiv_table pllrefe_map[] = {
+	{1, 0},
+	{2, 1},
+	{3, 2},
+	{4, 3},
+	{5, 4},
+	{6, 5},
+	{0, 0},
+};
+
+static struct clk_pll_def pll_clks[] = {
+/* PLLM: 880 MHz Clock source for EMC 2x clock */
+	{
+		PLL(TEGRA124_CLK_PLL_M, "pllM_out0", "osc_div_clk"),
+		.type = PLL_M,
+		.base_reg = PLLM_BASE,
+		.misc_reg = PLLM_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.pdiv_table = pllm_map,
+		.mnp_bits = {8, 8, 1, 20},
+	},
+/* PLLX: 1GHz Clock source for the fast CPU cluster and the shadow CPU */
+	{
+		PLL(TEGRA124_CLK_PLL_X, "pllX_out", "osc_div_clk"),
+		.type = PLL_X,
+		.base_reg = PLLX_BASE,
+		.misc_reg = PLLX_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.iddq_reg = PLLX_MISC3,
+		.iddq_mask = 1 << PLLX_IDDQ_BIT,
+		.pdiv_table = pllxc_map,
+		.mnp_bits = {8, 8, 4, 20},
+	},
+/* PLLC: 600 MHz Clock source for general use */
+	{
+		PLL(TEGRA124_CLK_PLL_C, "pllC_out0", "osc_div_clk"),
+		.type = PLL_C,
+		.base_reg = PLLC_BASE,
+		.misc_reg = PLLC_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLLC_MISC_LOCK_ENABLE,
+		.iddq_reg = PLLC_MISC,
+		.iddq_mask = 1 << PLLC_IDDQ_BIT,
+		.pdiv_table = pllc_map,
+		.mnp_bits = {8, 8, 4, 20},
+	},
+/* PLLC2: 600 MHz Clock source for engine scaling */
+	{
+		PLL(TEGRA124_CLK_PLL_C2, "pllC2_out0", "osc_div_clk"),
+		.type = PLL_C2,
+		.base_reg = PLLC2_BASE,
+		.misc_reg = PLLC2_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.pdiv_table = pllc_map,
+		.mnp_bits = {2, 8, 3, 20},
+	},
+/* PLLC3: 600 MHz Clock source for engine scaling */
+	{
+		PLL(TEGRA124_CLK_PLL_C3, "pllC3_out0", "osc_div_clk"),
+		.type = PLL_C3,
+		.base_reg = PLLC3_BASE,
+		.misc_reg = PLLC3_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.pdiv_table = pllc_map,
+		.mnp_bits = {2, 8, 3, 20},
+	},
+/* PLLC4: 600 MHz Clock source for ISP/VI units */
+	{
+		PLL(TEGRA124_CLK_PLL_C4, "pllC4_out0", "pllC4_src"),
+		.type = PLL_C4,
+		.base_reg = PLLC4_BASE,
+		.misc_reg = PLLC4_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLLSS_MISC_LOCK_ENABLE,
+		.iddq_reg = PLLC4_BASE,
+		.iddq_mask = 1 << PLLSS_IDDQ_BIT,
+		.pdiv_table = pll12g_ssd_esd_map,
+		.mnp_bits = {8, 8, 4, 20},
+	},
+/* PLLP: 408 MHz Clock source for most peripherals */
+	{
+		PLL(TEGRA124_CLK_PLL_P, "pllP_out0", "osc_div_clk"),
+		.type = PLL_P,
+		.base_reg = PLLP_BASE,
+		.misc_reg = PLLP_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.mnp_bits = {5, 10, 3,  20},
+	},
+/* PLLA: Audio clock sources: (11.2896 MHz, 12.288 MHz, 24.576 MHz) */
+	{
+		PLL(TEGRA124_CLK_PLL_A, "pllA_out", "pllP_out1"),
+		.type = PLL_A,
+		.base_reg = PLLA_BASE,
+		.misc_reg = PLLA_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.mnp_bits = {5, 10, 3,  20},
+	},
+/* PLLU: 480 MHz Clock source for USB PHY, provides 12/60/480 MHz */
+	{
+		PLL(TEGRA124_CLK_PLL_U, "pllU_out", "osc_div_clk"),
+		.type = PLL_U,
+		.base_reg = PLLU_BASE,
+		.misc_reg = PLLU_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLLDU_MISC_LOCK_ENABLE,
+		.pdiv_table = pllu_map,
+		.mnp_bits = {5, 10, 1, 20},
+	},
+/* PLLD: 600 MHz Clock sources for the DSI and display subsystem */
+	{
+		PLL(TEGRA124_CLK_PLL_D, "pllD_out", "osc_div_clk"),
+		.type = PLL_D,
+		.base_reg = PLLD_BASE,
+		.misc_reg = PLLD_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLL_MISC_LOCK_ENABLE,
+		.mnp_bits = {5, 11, 3, 20},
+	},
+/* PLLD2: 600 MHz Clock sources for the DSI and display subsystem */
+	{
+		PLL(TEGRA124_CLK_PLL_D2, "pllD2_out", "pllD2_src"),
+		.type = PLL_D2,
+		.base_reg = PLLD2_BASE,
+		.misc_reg = PLLD2_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLLSS_MISC_LOCK_ENABLE,
+		.iddq_reg = PLLD2_BASE,
+		.iddq_mask =  1 << PLLSS_IDDQ_BIT,
+		.pdiv_table = pll12g_ssd_esd_map,
+		.mnp_bits = {8, 8, 4, 20},
+	},
+/* refPLLe:  */
+	{
+		PLL(0, "pllREFE_out", "osc_div_clk"),
+		.type = PLL_REFE,
+		.base_reg = PLLRE_BASE,
+		.misc_reg = PLLRE_MISC,
+		.lock_mask = PLLRE_MISC_LOCK,
+		.lock_enable = PLLRE_MISC_LOCK_ENABLE,
+		.iddq_reg = PLLRE_MISC,
+		.iddq_mask = 1 << PLLRE_IDDQ_BIT,
+		.pdiv_table = pllrefe_map,
+		.mnp_bits = {8, 8, 4, 16},
+	},
+/* PLLE: generate the 100 MHz reference clock for USB 3.0 (spread spectrum) */
+	{
+		PLL(TEGRA124_CLK_PLL_E, "pllE_out0", "pllE_src"),
+		.type = PLL_E,
+		.base_reg = PLLE_BASE,
+		.misc_reg = PLLE_MISC,
+		.lock_mask = PLLE_MISC_LOCK,
+		.lock_enable = PLLE_MISC_LOCK_ENABLE,
+		.mnp_bits = {8, 8, 4, 24},
+	},
+/* PLLDP: 600 MHz Clock source for eDP/LVDS (spread spectrum) */
+	{
+		PLL(0, "pllDP_out0", "pllDP_src"),
+		.type = PLL_DP,
+		.base_reg = PLLDP_BASE,
+		.misc_reg = PLLDP_MISC,
+		.lock_mask = PLL_BASE_LOCK,
+		.lock_enable = PLLSS_MISC_LOCK_ENABLE,
+		.iddq_reg = PLLDP_BASE,
+		.iddq_mask =  1 << PLLSS_IDDQ_BIT,
+		.pdiv_table = pll12g_ssd_esd_map,
+		.mnp_bits = {8, 8, 4, 20},
+	},
+};
+
+static int tegra124_pll_init(struct clknode *clk, device_t dev);
+static int tegra124_pll_set_gate(struct clknode *clk, bool enable);
+static int tegra124_pll_recalc(struct clknode *clk, uint64_t *freq);
+static int tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin,
+    uint64_t *fout, int flags, int *stop);
+struct pll_sc {
+	device_t		clkdev;
+	enum pll_type		type;
+	uint32_t		base_reg;
+	uint32_t		misc_reg;
+	uint32_t		lock_mask;
+	uint32_t		lock_enable;
+	uint32_t		iddq_reg;
+	uint32_t		iddq_mask;
+	uint32_t		flags;
+	struct pdiv_table 	*pdiv_table;
+	struct mnp_bits		mnp_bits;
+};
+
+static clknode_method_t tegra124_pll_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		tegra124_pll_init),
+	CLKNODEMETHOD(clknode_set_gate,		tegra124_pll_set_gate),
+	CLKNODEMETHOD(clknode_recalc_freq,	tegra124_pll_recalc),
+	CLKNODEMETHOD(clknode_set_freq,		tegra124_pll_set_freq),
+	CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_pll, tegra124_pll_class, tegra124_pll_methods,
+   sizeof(struct pll_sc), clknode_class);
+
+static int
+pll_enable(struct pll_sc *sc)
+{
+	uint32_t reg;
+
+
+	RD4(sc, sc->base_reg, &reg);
+	if (sc->type != PLL_E)
+		reg &= ~PLL_BASE_BYPASS;
+	reg |= PLL_BASE_ENABLE;
+	WR4(sc, sc->base_reg, reg);
+	return (0);
+}
+
+static int
+pll_disable(struct pll_sc *sc)
+{
+	uint32_t reg;
+
+	RD4(sc, sc->base_reg, &reg);
+	if (sc->type != PLL_E)
+		reg |= PLL_BASE_BYPASS;
+	reg &= ~PLL_BASE_ENABLE;
+	WR4(sc, sc->base_reg, reg);
+	return (0);
+}
+
+static uint32_t
+pdiv_to_reg(struct pll_sc *sc, uint32_t p_div)
+{
+	struct pdiv_table *tbl;
+
+	tbl = sc->pdiv_table;
+	if (tbl == NULL)
+		return (ffs(p_div) - 1);
+
+	while (tbl->divider != 0) {
+		if (p_div <= tbl->divider)
+			return (tbl->value);
+		tbl++;
+	}
+	return (0xFFFFFFFF);
+}
+
+static uint32_t
+reg_to_pdiv(struct pll_sc *sc, uint32_t reg)
+{
+	struct pdiv_table *tbl;
+
+	tbl = sc->pdiv_table;
+	if (tbl == NULL)
+		return (1 << reg);
+
+	while (tbl->divider) {
+		if (reg == tbl->value)
+			return (tbl->divider);
+		tbl++;
+	}
+	return (0);
+}
+
+static uint32_t
+get_masked(uint32_t val, uint32_t shift, uint32_t width)
+{
+
+	return ((val >> shift) & ((1 << width) - 1));
+}
+
+static uint32_t
+set_masked(uint32_t val, uint32_t v, uint32_t shift, uint32_t width)
+{
+
+	val &= ~(((1 << width) - 1) << shift);
+	val |= (v & ((1 << width) - 1)) << shift;
+	return (val);
+}
+
+static void
+get_divisors(struct pll_sc *sc, uint32_t *m, uint32_t *n, uint32_t *p)
+{
+	uint32_t val;
+	struct mnp_bits *mnp_bits;
+
+	mnp_bits = &sc->mnp_bits;
+	RD4(sc, sc->base_reg, &val);
+	*m = get_masked(val, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+	*n = get_masked(val, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+	*p = get_masked(val, mnp_bits->p_shift, mnp_bits->p_width);
+}
+
+static uint32_t
+set_divisors(struct pll_sc *sc, uint32_t val, uint32_t m, uint32_t n,
+    uint32_t p)
+{
+	struct mnp_bits *mnp_bits;
+
+	mnp_bits = &sc->mnp_bits;
+	val = set_masked(val, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+	val = set_masked(val, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+	val = set_masked(val, p, mnp_bits->p_shift, mnp_bits->p_width);
+	return (val);
+}
+
+static bool
+is_locked(struct pll_sc *sc)
+{
+	uint32_t reg;
+
+	switch (sc->type) {
+	case PLL_REFE:
+		RD4(sc, sc->misc_reg, &reg);
+		reg &=  PLLRE_MISC_LOCK;
+		break;
+
+	case PLL_E:
+		RD4(sc, sc->misc_reg, &reg);
+		reg &= PLLE_MISC_LOCK;
+		break;
+
+	default:
+		RD4(sc, sc->base_reg, &reg);
+		reg &= PLL_BASE_LOCK;
+		break;
+	}
+	return (reg != 0);
+}
+
+static int
+wait_for_lock(struct pll_sc *sc)
+{
+	int i;
+
+	for (i = PLL_LOCK_TIMEOUT / 10; i > 0; i--) {
+		if (is_locked(sc))
+			break;
+		DELAY(10);
+	}
+	if (i <= 0) {
+		printf("PLL lock timeout\n");
+		return (ETIMEDOUT);
+	}
+	return (0);
+}
+
+static int
+plle_enable(struct pll_sc *sc)
+{
+	uint32_t reg;
+	int rv;
+	struct mnp_bits *mnp_bits;
+	uint32_t pll_m = 1;
+	uint32_t pll_n = 200;
+	uint32_t pll_p = 13;
+	uint32_t pll_cml = 13;
+
+	mnp_bits = &sc->mnp_bits;
+
+
+	/* Disable lock override. */
+	RD4(sc, sc->base_reg, &reg);
+	reg &= ~PLLE_BASE_LOCK_OVERRIDE;
+	WR4(sc, sc->base_reg, reg);
+
+	RD4(sc, PLLE_AUX, &reg);
+	reg |= PLLE_AUX_ENABLE_SWCTL;
+	reg &= ~PLLE_AUX_SEQ_ENABLE;
+	WR4(sc, PLLE_AUX, reg);
+	DELAY(10);
+
+	RD4(sc, sc->misc_reg, &reg);
+	reg |= PLLE_MISC_LOCK_ENABLE;
+	reg |= PLLE_MISC_IDDQ_SWCTL;
+	reg &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE;
+	reg |= PLLE_MISC_PTS;
+	reg |= PLLE_MISC_VREG_BG_CTRL_MASK;
+	reg |= PLLE_MISC_VREG_CTRL_MASK;
+	WR4(sc, sc->misc_reg, reg);
+	DELAY(10);
+
+	RD4(sc, PLLE_SS_CNTL, &reg);
+	reg |= PLLE_SS_CNTL_DISABLE;
+	WR4(sc, PLLE_SS_CNTL, reg);
+
+	RD4(sc, sc->base_reg, &reg);
+	reg = set_divisors(sc, reg, pll_m, pll_n, pll_p);
+	reg &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT);
+	reg |= pll_cml << PLLE_BASE_DIVCML_SHIFT;
+	WR4(sc, sc->base_reg, reg);
+	DELAY(10);
+
+	pll_enable(sc);
+	rv = wait_for_lock(sc);
+	if (rv != 0)
+		return (rv);
+
+	RD4(sc, PLLE_SS_CNTL, &reg);
+	reg &= ~PLLE_SS_CNTL_SSCCENTER;
+	reg &= ~PLLE_SS_CNTL_SSCINVERT;
+	reg &= ~PLLE_SS_CNTL_COEFFICIENTS_MASK;
+	reg |= PLLE_SS_CNTL_COEFFICIENTS_VAL;
+	WR4(sc, PLLE_SS_CNTL, reg);
+	reg &= ~PLLE_SS_CNTL_SSCBYP;
+	reg &= ~PLLE_SS_CNTL_BYPASS_SS;
+	WR4(sc, PLLE_SS_CNTL, reg);
+	DELAY(10);
+
+	reg &= ~PLLE_SS_CNTL_INTERP_RESET;
+	WR4(sc, PLLE_SS_CNTL, reg);
+	DELAY(10);
+
+	/* HW control of brick pll. */
+	RD4(sc, sc->misc_reg, &reg);
+	reg &= ~PLLE_MISC_IDDQ_SWCTL;
+	WR4(sc, sc->misc_reg, reg);
+
+	RD4(sc, PLLE_AUX, &reg);
+	reg |= PLLE_AUX_USE_LOCKDET;
+	reg |= PLLE_AUX_SEQ_START_STATE;
+	reg &= ~PLLE_AUX_ENABLE_SWCTL;
+	reg &= ~PLLE_AUX_SS_SWCTL;
+	WR4(sc, PLLE_AUX, reg);
+	reg |= PLLE_AUX_SEQ_START_STATE;
+	DELAY(10);
+	reg |= PLLE_AUX_SEQ_ENABLE;
+	WR4(sc, PLLE_AUX, reg);
+
+	RD4(sc, XUSBIO_PLL_CFG0, &reg);
+	reg |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET;
+	reg |= XUSBIO_PLL_CFG0_SEQ_START_STATE;
+	reg &= ~XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL;
+	reg &= ~XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL;
+	WR4(sc, XUSBIO_PLL_CFG0, reg);
+	DELAY(10);
+
+	reg |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
+	WR4(sc, XUSBIO_PLL_CFG0, reg);
+
+
+	/* Enable HW control and unreset SATA PLL. */
+	RD4(sc, SATA_PLL_CFG0, &reg);
+	reg &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL;
+	reg &= ~SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE;
+	reg |=  SATA_PLL_CFG0_PADPLL_USE_LOCKDET;
+	reg &= ~SATA_PLL_CFG0_SEQ_IN_SWCTL;
+	reg &= ~SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE;
+	reg &= ~SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE;
+	reg &= ~SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE;
+	reg &= ~SATA_PLL_CFG0_SEQ_ENABLE;
+	reg |=  SATA_PLL_CFG0_SEQ_START_STATE;
+	WR4(sc, SATA_PLL_CFG0, reg);
+	DELAY(10);
+	reg |= SATA_PLL_CFG0_SEQ_ENABLE;
+	WR4(sc, SATA_PLL_CFG0, reg);
+
+	/* Enable HW control of PCIe PLL. */
+	RD4(sc, PCIE_PLL_CFG0, &reg);
+	reg |= PCIE_PLL_CFG0_SEQ_ENABLE;
+	WR4(sc, PCIE_PLL_CFG0, reg);
+
+	return (0);
+}
+
+static int
+tegra124_pll_set_gate(struct clknode *clknode, bool enable)
+{
+	int rv;
+	struct pll_sc *sc;
+
+	sc = clknode_get_softc(clknode);
+	if (enable == 0) {
+		rv = pll_disable(sc);
+		return(rv);
+	}
+
+	if (sc->type == PLL_E)
+		rv = plle_enable(sc);
+	else
+		rv = pll_enable(sc);
+	return (rv);
+}
+
+static int
+pll_set_std(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags,
+    uint32_t m, uint32_t n, uint32_t p)
+{
+	uint32_t reg;
+	struct mnp_bits *mnp_bits;
+	int rv;
+
+	mnp_bits = &sc->mnp_bits;
+	if (m >= (1 << mnp_bits->m_width))
+		return (ERANGE);
+	if (n >= (1 << mnp_bits->n_width))
+		return (ERANGE);
+	if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width))
+		return (ERANGE);
+
+	if (flags & CLK_SET_DRYRUN) {
+		if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+		    (*fout != (((fin / m) * n) /p)))
+			return (ERANGE);
+
+		*fout = ((fin / m) * n) /p;
+
+		return (0);
+	}
+
+	pll_disable(sc);
+
+	/* take pll out of IDDQ */
+	if (sc->iddq_reg != 0)
+		MD4(sc, sc->iddq_reg, sc->iddq_mask, 0);
+
+	RD4(sc, sc->base_reg, &reg);
+	reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+	reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+	reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift,
+	    mnp_bits->p_width);
+	WR4(sc, sc->base_reg, reg);
+
+	/* Enable PLL. */
+	RD4(sc, sc->base_reg, &reg);
+	reg |= PLL_BASE_ENABLE;
+	WR4(sc, sc->base_reg, reg);
+
+	/* Enable lock detection. */
+	RD4(sc, sc->misc_reg, &reg);
+	reg |= sc->lock_enable;
+	WR4(sc, sc->misc_reg, reg);
+
+	rv = wait_for_lock(sc);
+	if (rv != 0) {
+		/* Disable PLL */
+		RD4(sc, sc->base_reg, &reg);
+		reg &= ~PLL_BASE_ENABLE;
+		WR4(sc, sc->base_reg, reg);
+		return (rv);
+	}
+	RD4(sc, sc->misc_reg, &reg);
+
+	pll_enable(sc);
+	*fout = ((fin / m) * n) / p;
+	return 0;
+}
+
+static int
+plla_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+	uint32_t m, n, p;
+
+	p = 1;
+	m = 5;
+	n = (*fout * p * m + fin / 2)/ fin;
+	dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+	return (pll_set_std(sc,  fin, fout, flags, m, n, p));
+}
+
+static int
+pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+	uint32_t m, n, p;
+
+	p = 2;
+	m = 1;
+	n = (*fout * p * m + fin / 2)/ fin;
+	dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+	return (pll_set_std( sc, fin, fout, flags, m, n, p));
+}
+
+/*
+ * PLLD2 is used as source for pixel clock for HDMI.
+ * We must be able to set it frequency very flexibly and
+ * precisely (within 5% tolerance limit allowed by HDMI specs).
+ *
+ * For this reason, it is necessary to search the full state space.
+ * Fortunately, thanks to early cycle terminations, performance
+ * is within acceptable limits.
+ */
+#define	PLLD2_PFD_MIN		  12000000 	/*  12 MHz */
+#define	PLLD2_PFD_MAX		  38000000	/*  38 MHz */
+#define	PLLD2_VCO_MIN	  	 600000000	/* 600 MHz */
+#define	PLLD2_VCO_MAX		1200000000	/* 1.2 GHz */
+
+static int
+plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+	uint32_t m, n, p;
+	uint32_t best_m, best_n, best_p;
+	uint64_t vco, pfd;
+	int64_t err, best_err;
+	struct mnp_bits *mnp_bits;
+	struct pdiv_table *tbl;
+	int p_idx, rv;
+
+	mnp_bits = &sc->mnp_bits;
+	tbl = sc->pdiv_table;
+	best_err = INT64_MAX;
+
+	for (p_idx = 0; tbl[p_idx].divider != 0; p_idx++) {
+		p = tbl[p_idx].divider;
+
+		/* Check constraints */
+		vco = *fout * p;
+		if (vco < PLLD2_VCO_MIN)
+			continue;
+		if (vco > PLLD2_VCO_MAX)
+			break;
+
+		for (m = 1; m < (1 << mnp_bits->m_width); m++) {
+			n = (*fout * p * m + fin / 2) / fin;
+
+			/* Check constraints */
+			if (n == 0)
+				continue;
+			if (n >= (1 << mnp_bits->n_width))
+				break;
+			vco = (fin * n) / m;
+			if (vco > PLLD2_VCO_MAX || vco < PLLD2_VCO_MIN)
+				continue;
+			pfd = fin / m;
+			if (pfd > PLLD2_PFD_MAX || vco < PLLD2_PFD_MIN)
+				continue;
+
+			/* Constraints passed, save best result */
+			err = *fout - vco / p;
+			if (err < 0)
+				err = -err;
+			if (err < best_err) {
+				best_err = err;
+				best_p = p;
+				best_m = m;
+				best_n = n;
+			}
+			if (err == 0)
+				goto done;
+		}
+	}
+done:
+	/*
+	 * HDMI specification allows 5% pixel clock tolerance,
+	 * we will by a slightly stricter
+	 */
+	if (best_err > ((*fout * 100) / 4))
+		return (ERANGE);
+
+	if (flags & CLK_SET_DRYRUN)
+		return (0);
+	rv = pll_set_std(sc, fin, fout, flags, best_m, best_n, best_p);
+	/* XXXX Panic for rv == ERANGE ? */
+	return (rv);
+}
+
+static int
+pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+	uint32_t m, n, p;
+
+	m = 1;
+	p = 1;
+	n = *fout * p * m / fin;
+	dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+	return (pll_set_std(sc, fin, fout, flags, m, n, p));
+}
+
+static int
+pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
+{
+	uint32_t reg;
+	uint32_t m, n, p;
+	struct mnp_bits *mnp_bits;
+	int rv;
+
+	mnp_bits = &sc->mnp_bits;
+
+	p = 1;
+	m = 1;
+	n = (*fout * p * m + fin / 2)/ fin;
+	dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
+
+	if (m >= (1 << mnp_bits->m_width))
+		return (ERANGE);
+	if (n >= (1 << mnp_bits->n_width))
+		return (ERANGE);
+	if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width))
+		return (ERANGE);
+
+	if (flags & CLK_SET_DRYRUN) {
+		if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+		    (*fout != (((fin / m) * n) /p)))
+			return (ERANGE);
+		*fout = ((fin / m) * n) /p;
+		return (0);
+	}
+
+	/* PLLX doesn't have bypass, disable it first. */
+	RD4(sc, sc->base_reg, &reg);
+	reg &= ~PLL_BASE_ENABLE;
+	WR4(sc, sc->base_reg, reg);
+
+	/* Set PLL. */
+	RD4(sc, sc->base_reg, &reg);
+	reg = set_masked(reg, m, PLL_BASE_DIVM_SHIFT, mnp_bits->m_width);
+	reg = set_masked(reg, n, PLL_BASE_DIVN_SHIFT, mnp_bits->n_width);
+	reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift,
+	    mnp_bits->p_width);
+	WR4(sc, sc->base_reg, reg);
+	RD4(sc, sc->base_reg, &reg);
+	DELAY(100);
+
+	/* Enable lock detection. */
+	RD4(sc, sc->misc_reg, &reg);
+	reg |= sc->lock_enable;
+	WR4(sc, sc->misc_reg, reg);
+
+	/* Enable PLL. */
+	RD4(sc, sc->base_reg, &reg);
+	reg |= PLL_BASE_ENABLE;
+	WR4(sc, sc->base_reg, reg);
+
+	rv = wait_for_lock(sc);
+	if (rv != 0) {
+		/* Disable PLL */
+		RD4(sc, sc->base_reg, &reg);
+		reg &= ~PLL_BASE_ENABLE;
+		WR4(sc, sc->base_reg, reg);
+		return (rv);
+	}
+	RD4(sc, sc->misc_reg, &reg);
+
+	*fout = ((fin / m) * n) / p;
+	return (0);
+}
+
+static int
+tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout,
+    int flags, int *stop)
+{
+	*stop = 1;
+	int rv;
+	struct pll_sc *sc;
+
+	sc = clknode_get_softc(clknode);
+	dprintf("%s: %s requested freq: %llu, input freq: %llu\n", __func__,
+	   clknode_get_name(clknode), *fout, fin);
+	switch (sc->type) {
+	case PLL_A:
+		rv = plla_set_freq(sc, fin, fout, flags);
+		break;
+	case PLL_C:
+		rv = pllc_set_freq(sc, fin, fout, flags);
+		break;
+	case PLL_D2:
+		rv = plld2_set_freq(sc, fin, fout, flags);
+		break;
+
+	case PLL_REFE:
+		rv = pllrefe_set_freq(sc, fin, fout, flags);
+		break;
+
+	case PLL_X:
+		rv = pllx_set_freq(sc, fin, fout, flags);
+		break;
+
+	case PLL_U:
+		if (*fout == 480000000)  /* PLLU is fixed to 480 MHz */
+			rv = 0;
+		else
+			rv = ERANGE;
+		break;
+	default:
+		rv = ENXIO;
+		break;
+	}
+
+	return (rv);
+}
+
+
+static int
+tegra124_pll_init(struct clknode *clk, device_t dev)
+{
+	struct pll_sc *sc;
+	uint32_t reg;
+
+	sc = clknode_get_softc(clk);
+
+	/* If PLL is enabled, enable lock detect too. */
+	RD4(sc, sc->base_reg, &reg);
+	if (reg & PLL_BASE_ENABLE) {
+		RD4(sc, sc->misc_reg, &reg);
+		reg |= sc->lock_enable;
+		WR4(sc, sc->misc_reg, reg);
+	}
+	if (sc->type == PLL_REFE) {
+		RD4(sc, sc->misc_reg, &reg);
+		reg &= ~(1 << 29);	/* Diasble lock override */
+		WR4(sc, sc->misc_reg, reg);
+	}
+
+	clknode_init_parent_idx(clk, 0);
+	return(0);
+}
+
+static int
+tegra124_pll_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct pll_sc *sc;
+	uint32_t m, n, p, pr;
+	uint32_t reg, misc_reg;
+	int locked;
+
+	sc = clknode_get_softc(clk);
+
+	RD4(sc, sc->base_reg, &reg);
+	RD4(sc, sc->misc_reg, &misc_reg);
+
+	get_divisors(sc, &m, &n, &pr);
+	if (sc->type != PLL_E)
+		p = reg_to_pdiv(sc, pr);
+	else
+		p = 2 * (pr - 1);
+	locked = is_locked(sc);
+
+	dprintf("%s: %s (0x%08x, 0x%08x) - m: %d, n: %d, p: %d (%d): "
+	    "e: %d, r: %d, o: %d - %s\n", __func__,
+	    clknode_get_name(clk), reg, misc_reg, m, n, p, pr,
+	    (reg >> 30) & 1, (reg >> 29) & 1, (reg >> 28) & 1,
+	    locked ? "locked" : "unlocked");
+
+	if ((m == 0) || (n == 0) || (p == 0)) {
+		*freq = 0;
+		return (EINVAL);
+	}
+	*freq = ((*freq / m) * n) / p;
+	return (0);
+}
+
+static int
+pll_register(struct clkdom *clkdom, struct clk_pll_def *clkdef)
+{
+	struct clknode *clk;
+	struct pll_sc *sc;
+
+	clk = clknode_create(clkdom, &tegra124_pll_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (ENXIO);
+
+	sc = clknode_get_softc(clk);
+	sc->clkdev = clknode_get_device(clk);
+	sc->type = clkdef->type;
+	sc->base_reg = clkdef->base_reg;
+	sc->misc_reg = clkdef->misc_reg;
+	sc->lock_mask = clkdef->lock_mask;
+	sc->lock_enable = clkdef->lock_enable;
+	sc->iddq_reg = clkdef->iddq_reg;
+	sc->iddq_mask = clkdef->iddq_mask;
+	sc->flags = clkdef->flags;
+	sc->pdiv_table = clkdef->pdiv_table;
+	sc->mnp_bits = clkdef->mnp_bits;
+	clknode_register(clkdom, clk);
+	return (0);
+}
+
+static void config_utmi_pll(struct tegra124_car_softc *sc)
+{
+	uint32_t reg;
+	/*
+	 * XXX Simplified UTMIP settings for 12MHz base clock.
+	 */
+#define	ENABLE_DELAY_COUNT 	0x02
+#define	STABLE_COUNT		0x2F
+#define	ACTIVE_DELAY_COUNT	0x04
+#define	XTAL_FREQ_COUNT		0x76
+
+	CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG2, &reg);
+	reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+	reg |= UTMIP_PLL_CFG2_STABLE_COUNT(STABLE_COUNT);
+	reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+	reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(ACTIVE_DELAY_COUNT);
+	reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+	reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+	reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+	CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG2, reg);
+
+	CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, &reg);
+	reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+	reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(ENABLE_DELAY_COUNT);
+	reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+	reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(XTAL_FREQ_COUNT);
+	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+	reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP;
+	reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+	CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg);
+
+	/* Prepare UTMIP requencer. */
+	CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, &reg);
+	reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET;
+	reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL;
+	reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE;
+	CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg);
+
+	/* Powerup UTMIP. */
+	CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, &reg);
+	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
+	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+	CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg);
+	DELAY(10);
+
+	/* SW override for UTMIPLL */
+	CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, &reg);
+	reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL;
+	reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+	CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg);
+	DELAY(10);
+
+	/* HW control of UTMIPLL. */
+	CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, &reg);
+	reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE;
+	CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg);
+}
+
+void
+tegra124_init_plls(struct tegra124_car_softc *sc)
+{
+	int i, rv;
+
+	for (i = 0; i < nitems(pll_clks); i++) {
+		rv = pll_register(sc->clkdom, pll_clks + i);
+		if (rv != 0)
+			panic("pll_register failed");
+	}
+	config_utmi_pll(sc);
+
+}


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_clk_pll.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,266 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_clk_super.c 297576 2016-04-05 09:20:52Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h>
+#include "tegra124_car.h"
+
+
+/* Flags */
+#define	SMF_HAVE_DIVIDER_2	1
+
+struct super_mux_def {
+	struct clknode_init_def	clkdef;
+	uint32_t		base_reg;
+	uint32_t		flags;
+	int			src_pllx;
+	int			src_div2;
+};
+
+#define	PLIST(x) static const char *x[]
+#define	SM(_id, cn, pl, r, x, d, f)				\
+{									\
+	.clkdef.id = _id,						\
+	.clkdef.name = cn,						\
+	.clkdef.parent_names = pl,					\
+	.clkdef.parent_cnt = nitems(pl),				\
+	.clkdef.flags = CLK_NODE_STATIC_STRINGS,				\
+	.base_reg = r,							\
+	.src_pllx = x,							\
+	.src_div2 = d,							\
+	.flags = f,							\
+}
+
+PLIST(cclk_g_parents) = {
+	"clk_m", "pllC_out0", "clk_s", "pllM_out0",
+	"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
+	"pllX_out", NULL, NULL, NULL,
+	NULL, NULL, NULL,NULL, // "dfllCPU_out0"
+};
+
+PLIST(cclk_lp_parents) = {
+	"clk_m", "pllC_out0", "clk_s", "pllM_out0",
+	"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
+	"pllX_out", NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL,
+	"pllX_out0"
+};
+
+PLIST(sclk_parents) = {
+	"clk_m", "pllC_out1", "pllP_out4", "pllP_out0",
+	"pllP_out2", "pllC_out0", "clk_s", "pllM_out1",
+};
+
+static struct super_mux_def super_mux_def[] = {
+ SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0),
+ SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2),
+ SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0),
+};
+
+static int super_mux_init(struct clknode *clk, device_t dev);
+static int super_mux_set_mux(struct clknode *clk, int idx);
+
+struct super_mux_sc {
+	device_t		clkdev;
+	uint32_t		base_reg;
+	int			src_pllx;
+	int			src_div2;
+	uint32_t		flags;
+
+	int 			mux;
+};
+
+static clknode_method_t super_mux_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		super_mux_init),
+	CLKNODEMETHOD(clknode_set_mux, 		super_mux_set_mux),
+	CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods,
+   sizeof(struct super_mux_sc), clknode_class);
+
+/* Mux status. */
+#define	SUPER_MUX_STATE_STDBY		0
+#define	SUPER_MUX_STATE_IDLE		1
+#define	SUPER_MUX_STATE_RUN		2
+#define	SUPER_MUX_STATE_IRQ		3
+#define	SUPER_MUX_STATE_FIQ		4
+
+/* Mux register bits. */
+#define	SUPER_MUX_STATE_BIT_SHIFT	28
+#define	SUPER_MUX_STATE_BIT_MASK	0xF
+/* State is Priority encoded */
+#define	SUPER_MUX_STATE_BIT_STDBY	0x00
+#define	SUPER_MUX_STATE_BIT_IDLE	0x01
+#define	SUPER_MUX_STATE_BIT_RUN		0x02
+#define	SUPER_MUX_STATE_BIT_IRQ		0x04
+#define	SUPER_MUX_STATE_BIT_FIQ		0x08
+
+#define	SUPER_MUX_MUX_WIDTH		4
+#define	SUPER_MUX_LP_DIV2_BYPASS	(1 << 16)
+
+static uint32_t
+super_mux_get_state(uint32_t reg)
+{
+	reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;
+	if (reg & SUPER_MUX_STATE_BIT_FIQ)
+		 return (SUPER_MUX_STATE_FIQ);
+	if (reg & SUPER_MUX_STATE_BIT_IRQ)
+		 return (SUPER_MUX_STATE_IRQ);
+	if (reg & SUPER_MUX_STATE_BIT_RUN)
+		 return (SUPER_MUX_STATE_RUN);
+	if (reg & SUPER_MUX_STATE_BIT_IDLE)
+		 return (SUPER_MUX_STATE_IDLE);
+	return (SUPER_MUX_STATE_STDBY);
+}
+
+static int
+super_mux_init(struct clknode *clk, device_t dev)
+{
+	struct super_mux_sc *sc;
+	uint32_t reg;
+	int shift, state;
+
+	sc = clknode_get_softc(clk);
+
+	DEVICE_LOCK(sc);
+	RD4(sc, sc->base_reg, &reg);
+	DEVICE_UNLOCK(sc);
+	state = super_mux_get_state(reg);
+
+	if ((state != SUPER_MUX_STATE_RUN) &&
+	    (state != SUPER_MUX_STATE_IDLE)) {
+		panic("Unexpected super mux state: %u", state);
+	}
+
+	shift = state * SUPER_MUX_MUX_WIDTH;
+
+	sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);
+
+	/*
+	 * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set
+	 * and source mux is set to PLLX.
+	 */
+	if (sc->flags & SMF_HAVE_DIVIDER_2) {
+		if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) &&
+		    (sc->mux == sc->src_pllx))
+		sc->mux = sc->src_div2;
+	}
+	clknode_init_parent_idx(clk, sc->mux);
+
+	return(0);
+}
+
+static int
+super_mux_set_mux(struct clknode *clk, int idx)
+{
+
+	struct super_mux_sc *sc;
+	int shift, state;
+	uint32_t reg, dummy;
+
+	sc = clknode_get_softc(clk);
+
+	DEVICE_LOCK(sc);
+	RD4(sc, sc->base_reg, &reg);
+	state = super_mux_get_state(reg);
+
+	if ((state != SUPER_MUX_STATE_RUN) &&
+	    (state != SUPER_MUX_STATE_IDLE)) {
+		panic("Unexpected super mux state: %u", state);
+	}
+	shift = (state - 1) * SUPER_MUX_MUX_WIDTH;
+	sc->mux = idx;
+	if (sc->flags & SMF_HAVE_DIVIDER_2) {
+		if (idx == sc->src_div2) {
+			idx = sc->src_pllx;
+			reg &= ~SUPER_MUX_LP_DIV2_BYPASS;
+			WR4(sc, sc->base_reg, reg);
+			RD4(sc, sc->base_reg, &dummy);
+		} else if (idx == sc->src_pllx) {
+			reg = SUPER_MUX_LP_DIV2_BYPASS;
+			WR4(sc, sc->base_reg, reg);
+			RD4(sc, sc->base_reg, &dummy);
+		}
+	}
+	reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);
+	reg |= idx << shift;
+
+	WR4(sc, sc->base_reg, reg);
+	RD4(sc, sc->base_reg, &dummy);
+	DEVICE_UNLOCK(sc);
+
+	return(0);
+}
+
+static int
+super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)
+{
+	struct clknode *clk;
+	struct super_mux_sc *sc;
+
+	clk = clknode_create(clkdom, &tegra124_super_mux_class,
+	    &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->clkdev = clknode_get_device(clk);
+	sc->base_reg = clkdef->base_reg;
+	sc->src_pllx = clkdef->src_pllx;
+	sc->src_div2 = clkdef->src_div2;
+	sc->flags = clkdef->flags;
+
+	clknode_register(clkdom, clk);
+	return (0);
+}
+
+void
+tegra124_super_mux_clock(struct tegra124_car_softc *sc)
+{
+	int i, rv;
+
+	for (i = 0; i <  nitems(super_mux_def); i++) {
+		rv = super_mux_register(sc->clkdom, &super_mux_def[i]);
+		if (rv != 0)
+			panic("super_mux_register failed");
+	}
+
+}


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_clk_super.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,273 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c 308374 2016-11-06 15:25:46Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "tegra_soctherm_if.h"
+
+
+enum therm_info {
+	CORETEMP_TEMP,
+	CORETEMP_DELTA,
+	CORETEMP_RESOLUTION,
+	CORETEMP_TJMAX,
+};
+
+struct tegra124_coretemp_softc {
+	device_t		dev;
+	int			overheat_log;
+	int			core_max_temp;
+	int			cpu_id;
+	device_t		tsens_dev;
+	intptr_t		tsens_id;
+};
+
+static int
+coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	device_t dev;
+	int val, temp, rv;
+	struct tegra124_coretemp_softc *sc;
+	enum therm_info type;
+	char stemp[16];
+
+
+	dev = (device_t) arg1;
+	sc = device_get_softc(dev);
+	type = arg2;
+
+
+	rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev,
+	     sc->tsens_id, &temp);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot read temperature sensor %d:  %d\n",
+		    sc->tsens_id, rv);
+		return (rv);
+	}
+
+	switch (type) {
+	case CORETEMP_TEMP:
+		val = temp / 100;
+		val +=  2731;
+		break;
+	case CORETEMP_DELTA:
+		val = (sc->core_max_temp - temp) / 1000;
+		break;
+	case CORETEMP_RESOLUTION:
+		val = 1;
+		break;
+	case CORETEMP_TJMAX:
+		val = sc->core_max_temp / 100;
+		val +=  2731;
+		break;
+	}
+
+
+	if ((temp > sc->core_max_temp)  && !sc->overheat_log) {
+		sc->overheat_log = 1;
+
+		/*
+		 * Check for Critical Temperature Status and Critical
+		 * Temperature Log.  It doesn't really matter if the
+		 * current temperature is invalid because the "Critical
+		 * Temperature Log" bit will tell us if the Critical
+		 * Temperature has * been reached in past. It's not
+		 * directly related to the current temperature.
+		 *
+		 * If we reach a critical level, allow devctl(4)
+		 * to catch this and shutdown the system.
+		 */
+		device_printf(dev, "critical temperature detected, "
+		    "suggest system shutdown\n");
+		snprintf(stemp, sizeof(stemp), "%d", val);
+		devctl_notify("coretemp", "Thermal", stemp,
+		    "notify=0xcc");
+	} else {
+		sc->overheat_log = 0;
+	}
+
+	return (sysctl_handle_int(oidp, 0, val, req));
+}
+
+static int
+tegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc)
+{
+	int rv, ncells;
+	phandle_t node, xnode;
+	pcell_t *cells;
+
+	node = OF_peer(0);
+	node = ofw_bus_find_child(node, "thermal-zones");
+	if (node <= 0) {
+		device_printf(sc->dev, "Cannot find 'thermal-zones'.\n");
+		return (ENXIO);
+	}
+
+	node = ofw_bus_find_child(node, "cpu");
+	if (node <= 0) {
+		device_printf(sc->dev, "Cannot find 'cpu'\n");
+		return (ENXIO);
+	}
+	rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors",
+	    "#thermal-sensor-cells", 0, &xnode, &ncells, &cells);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot parse 'thermal-sensors' property.\n");
+		return (ENXIO);
+	}
+	if (ncells != 1) {
+		device_printf(sc->dev,
+		    "Invalid format of 'thermal-sensors' property(%d).\n",
+		    ncells);
+		return (ENXIO);
+	}
+
+	sc->tsens_id = 0x100 + sc->cpu_id; //cells[0];
+	OF_prop_free(cells);
+
+	sc->tsens_dev = OF_device_from_xref(xnode);
+	if (sc->tsens_dev == NULL) {
+		device_printf(sc->dev,
+		    "Cannot find thermal sensors device.");
+		return (ENXIO);
+	}
+	return (0);
+}
+
+static void
+tegra124_coretemp_identify(driver_t *driver, device_t parent)
+{
+	phandle_t root;
+
+	root = OF_finddevice("/");
+	if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))
+		return;
+	if (device_find_child(parent, "tegra124_coretemp", -1) != NULL)
+		return;
+	if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL)
+		device_printf(parent, "add child failed\n");
+}
+
+static int
+tegra124_coretemp_probe(device_t dev)
+{
+
+	device_set_desc(dev, "CPU Thermal Sensor");
+	return (0);
+}
+
+static int
+tegra124_coretemp_attach(device_t dev)
+{
+	struct tegra124_coretemp_softc *sc;
+	device_t pdev;
+	struct sysctl_oid *oid;
+	struct sysctl_ctx_list *ctx;
+	int rv;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->cpu_id = device_get_unit(dev);
+	sc->core_max_temp = 102000;
+	pdev = device_get_parent(dev);
+
+	rv = tegra124_coretemp_ofw_parse(sc);
+	if (rv != 0)
+		return (rv);
+
+	ctx = device_get_sysctl_ctx(dev);
+
+	oid = SYSCTL_ADD_NODE(ctx,
+	    SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO,
+	    "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information");
+
+	/*
+	 * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp.
+	 */
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)),
+	    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+	    dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK",
+	    "Current temperature");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta",
+	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA,
+	    coretemp_get_val_sysctl, "I",
+	    "Delta between TCC activation and current temperature");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution",
+	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION,
+	    coretemp_get_val_sysctl, "I",
+	    "Resolution of CPU thermal sensor");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax",
+	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX,
+	    coretemp_get_val_sysctl, "IK",
+	    "TCC activation temperature");
+
+	return (0);
+}
+
+static int
+tegra124_coretemp_detach(device_t dev)
+{
+	struct tegra124_coretemp_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (0);
+}
+
+static device_method_t tegra124_coretemp_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	tegra124_coretemp_identify),
+	DEVMETHOD(device_probe,		tegra124_coretemp_probe),
+	DEVMETHOD(device_attach,	tegra124_coretemp_attach),
+	DEVMETHOD(device_detach,	tegra124_coretemp_detach),
+
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra124_coretemp_devclass;
+static DEFINE_CLASS_0(tegra124_coretemp, tegra124_coretemp_driver,
+    tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc));
+DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver,
+    tegra124_coretemp_devclass, NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_coretemp.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,599 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c 308374 2016-11-06 15:25:46Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#include "cpufreq_if.h"
+
+#define	XXX
+
+/* CPU voltage table entry */
+struct speedo_entry {
+	uint64_t		freq; 	/* Frequency point */
+	int			c0; 	/* Coeeficient values for */
+	int			c1;	/* quadratic equation: */
+	int 			c2;	/* c2 * speedo^2 + c1 * speedo + c0 */
+};
+
+struct cpu_volt_def {
+	int			min_uvolt;	/* Min allowed CPU voltage */
+	int			max_uvolt;	/* Max allowed CPU voltage */
+	int 			step_uvolt; 	/* Step of CPU voltage */
+	int			speedo_scale;	/* Scaling factor for cvt */
+	int			speedo_nitems;	/* Size of speedo table */
+	struct speedo_entry	*speedo_tbl;	/* CPU voltage table */
+};
+
+struct cpu_speed_point {
+	uint64_t		freq;		/* Frequecy */
+	int			uvolt;		/* Requested voltage */
+};
+
+static struct speedo_entry tegra124_speedo_dpll_tbl[] =
+{
+	{ 204000000ULL,	1112619, -29295, 402},
+	{ 306000000ULL,	1150460, -30585, 402},
+	{ 408000000ULL,	1190122, -31865, 402},
+	{ 510000000ULL,	1231606, -33155, 402},
+	{ 612000000ULL,	1274912, -34435, 402},
+	{ 714000000ULL,	1320040, -35725, 402},
+	{ 816000000ULL,	1366990, -37005, 402},
+	{ 918000000ULL,	1415762, -38295, 402},
+	{1020000000ULL,	1466355, -39575, 402},
+	{1122000000ULL,	1518771, -40865, 402},
+	{1224000000ULL,	1573009, -42145, 402},
+	{1326000000ULL,	1629068, -43435, 402},
+	{1428000000ULL,	1686950, -44715, 402},
+	{1530000000ULL,	1746653, -46005, 402},
+	{1632000000ULL,	1808179, -47285, 402},
+	{1734000000ULL,	1871526, -48575, 402},
+	{1836000000ULL,	1936696, -49855, 402},
+	{1938000000ULL,	2003687, -51145, 402},
+	{2014500000ULL,	2054787, -52095, 402},
+	{2116500000ULL,	2124957, -53385, 402},
+	{2218500000ULL,	2196950, -54665, 402},
+	{2320500000ULL,	2270765, -55955, 402},
+	{2320500000ULL,	2270765, -55955, 402},
+	{2422500000ULL,	2346401, -57235, 402},
+	{2524500000ULL,	2437299, -58535, 402},
+};
+
+static struct cpu_volt_def tegra124_cpu_volt_dpll_def =
+{
+	.min_uvolt =  900000,		/* 0.9 V */
+	.max_uvolt = 1260000,		/* 1.26 */
+	.step_uvolt =  10000,		/* 10 mV */
+	.speedo_scale = 100,
+	.speedo_nitems = nitems(tegra124_speedo_dpll_tbl),
+	.speedo_tbl = tegra124_speedo_dpll_tbl,
+};
+
+static struct speedo_entry tegra124_speedo_pllx_tbl[] =
+{
+	{ 204000000ULL,	 800000, 0, 0},
+	{ 306000000ULL,	 800000, 0, 0},
+	{ 408000000ULL,	 800000, 0, 0},
+	{ 510000000ULL,	 800000, 0, 0},
+	{ 612000000ULL,	 800000, 0, 0},
+	{ 714000000ULL,	 800000, 0, 0},
+	{ 816000000ULL,	 820000, 0, 0},
+	{ 918000000ULL,	 840000, 0, 0},
+	{1020000000ULL,	 880000, 0, 0},
+	{1122000000ULL,	 900000, 0, 0},
+	{1224000000ULL,	 930000, 0, 0},
+	{1326000000ULL,	 960000, 0, 0},
+	{1428000000ULL,	 990000, 0, 0},
+	{1530000000ULL,	1020000, 0, 0},
+	{1632000000ULL,	1070000, 0, 0},
+	{1734000000ULL,	1100000, 0, 0},
+	{1836000000ULL,	1140000, 0, 0},
+	{1938000000ULL,	1180000, 0, 0},
+	{2014500000ULL,	1220000, 0, 0},
+	{2116500000ULL,	1260000, 0, 0},
+	{2218500000ULL,	1310000, 0, 0},
+	{2320500000ULL,	1360000, 0, 0},
+	{2397000000ULL,	1400000, 0, 0},
+	{2499000000ULL,	1400000, 0, 0},
+};
+
+
+static struct cpu_volt_def tegra124_cpu_volt_pllx_def =
+{
+	.min_uvolt = 1000000,		/* XXX 0.9 V doesn't work on all boards */
+	.max_uvolt = 1260000,		/* 1.26 */
+	.step_uvolt =  10000,		/* 10 mV */
+	.speedo_scale = 100,
+	.speedo_nitems = nitems(tegra124_speedo_pllx_tbl),
+	.speedo_tbl = tegra124_speedo_pllx_tbl,
+};
+
+static uint64_t cpu_freq_tbl[] = {
+	 204000000ULL,
+	 306000000ULL,
+	 408000000ULL,
+	 510000000ULL,
+	 612000000ULL,
+	 714000000ULL,
+	 816000000ULL,
+	 918000000ULL,
+	1020000000ULL,
+	1122000000ULL,
+	1224000000ULL,
+	1326000000ULL,
+	1428000000ULL,
+	1530000000ULL,
+	1632000000ULL,
+	1734000000ULL,
+	1836000000ULL,
+	1938000000ULL,
+	2014000000ULL,
+	2116000000ULL,
+	2218000000ULL,
+	2320000000ULL,
+	2422000000ULL,
+	2524000000ULL,
+};
+
+static uint64_t cpu_max_freq[] = {
+	2014500000ULL,
+	2320500000ULL,
+	2116500000ULL,
+	2524500000ULL,
+};
+
+struct tegra124_cpufreq_softc {
+	device_t		dev;
+	phandle_t		node;
+
+	regulator_t		supply_vdd_cpu;
+	clk_t			clk_cpu_g;
+	clk_t			clk_cpu_lp;
+	clk_t			clk_pll_x;
+	clk_t			clk_pll_p;
+	clk_t			clk_dfll;
+
+	int 			process_id;
+	int 			speedo_id;
+	int 			speedo_value;
+
+	uint64_t		cpu_max_freq;
+	struct cpu_volt_def	*cpu_def;
+	struct cpu_speed_point	*speed_points;
+	int			nspeed_points;
+
+	struct cpu_speed_point	*act_speed_point;
+
+	int			latency;
+};
+
+static int cpufreq_lowest_freq = 1;
+TUNABLE_INT("hw.tegra124.cpufreq.lowest_freq", &cpufreq_lowest_freq);
+
+#define	DIV_ROUND_CLOSEST(val, div)	(((val) + ((div) / 2)) / (div))
+
+#define	ROUND_UP(val, div)	roundup(val, div)
+#define	ROUND_DOWN(val, div)	rounddown(val, div)
+
+/*
+ * Compute requesetd voltage for given frequency and SoC process variations,
+ * - compute base voltage from speedo value using speedo table
+ * - round up voltage to next regulator step
+ * - clamp it to regulator limits
+ */
+static int
+freq_to_voltage(struct tegra124_cpufreq_softc *sc, uint64_t freq)
+{
+	int uv, scale, min_uvolt, max_uvolt, step_uvolt;
+	struct speedo_entry *ent;
+	int i;
+
+	/* Get speedo entry with higher frequency */
+	ent = NULL;
+	for (i = 0; i < sc->cpu_def->speedo_nitems; i++) {
+		if (sc->cpu_def->speedo_tbl[i].freq >= freq) {
+			ent = &sc->cpu_def->speedo_tbl[i];
+			break;
+		}
+	}
+	if (ent == NULL)
+		ent = &sc->cpu_def->speedo_tbl[sc->cpu_def->speedo_nitems - 1];
+	scale = sc->cpu_def->speedo_scale;
+
+
+	/* uV = (c2 * speedo / scale + c1) * speedo / scale + c0) */
+	uv = DIV_ROUND_CLOSEST(ent->c2 * sc->speedo_value, scale);
+	uv = DIV_ROUND_CLOSEST((uv + ent->c1) * sc->speedo_value, scale) +
+	    ent->c0;
+	step_uvolt = sc->cpu_def->step_uvolt;
+	/* Round up it to next regulator step */
+	uv = ROUND_UP(uv, step_uvolt);
+
+	/* Clamp result */
+	min_uvolt = ROUND_UP(sc->cpu_def->min_uvolt, step_uvolt);
+	max_uvolt = ROUND_DOWN(sc->cpu_def->max_uvolt, step_uvolt);
+	if (uv < min_uvolt)
+		uv =  min_uvolt;
+	if (uv > max_uvolt)
+		uv =  max_uvolt;
+	return (uv);
+
+}
+
+static void
+build_speed_points(struct tegra124_cpufreq_softc *sc) {
+	int i;
+
+	sc->nspeed_points = nitems(cpu_freq_tbl);
+	sc->speed_points = malloc(sizeof(struct cpu_speed_point) *
+	    sc->nspeed_points, M_DEVBUF, M_NOWAIT);
+	for (i = 0; i < sc->nspeed_points; i++) {
+		sc->speed_points[i].freq = cpu_freq_tbl[i];
+		sc->speed_points[i].uvolt = freq_to_voltage(sc,
+		    cpu_freq_tbl[i]);
+	}
+}
+
+static struct cpu_speed_point *
+get_speed_point(struct tegra124_cpufreq_softc *sc, uint64_t freq)
+{
+	int i;
+
+	if (sc->speed_points[0].freq >= freq)
+		return (sc->speed_points + 0);
+
+	for (i = 0; i < sc->nspeed_points - 1; i++) {
+		if (sc->speed_points[i + 1].freq > freq)
+			return (sc->speed_points + i);
+	}
+
+	return (sc->speed_points + sc->nspeed_points - 1);
+}
+
+static int
+tegra124_cpufreq_settings(device_t dev, struct cf_setting *sets, int *count)
+{
+	struct tegra124_cpufreq_softc *sc;
+	int i, j, max_cnt;
+
+	if (sets == NULL || count == NULL)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * (*count));
+
+	max_cnt = min(sc->nspeed_points, *count);
+	for (i = 0, j = sc->nspeed_points - 1; j >= 0; j--) {
+		if (sc->cpu_max_freq < sc->speed_points[j].freq)
+			continue;
+		sets[i].freq = sc->speed_points[j].freq / 1000000;
+		sets[i].volts = sc->speed_points[j].uvolt / 1000;
+		sets[i].lat = sc->latency;
+		sets[i].dev = dev;
+		i++;
+	}
+	*count = i;
+
+	return (0);
+}
+
+static int
+set_cpu_freq(struct tegra124_cpufreq_softc *sc, uint64_t freq)
+{
+	struct cpu_speed_point *point;
+	int rv;
+
+	point = get_speed_point(sc, freq);
+
+	if (sc->act_speed_point->uvolt < point->uvolt) {
+		/* set cpu voltage */
+		rv = regulator_set_voltage(sc->supply_vdd_cpu,
+		    point->uvolt, point->uvolt);
+		DELAY(10000);
+		if (rv != 0)
+			return (rv);
+	}
+
+	/* Switch supermux to PLLP first */
+	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_p);
+	if (rv != 0) {
+		device_printf(sc->dev, "Can't set parent to PLLP\n");
+		return (rv);
+	}
+
+	/* Set PLLX frequency */
+	rv = clk_set_freq(sc->clk_pll_x, point->freq, CLK_SET_ROUND_DOWN);
+	if (rv != 0) {
+		device_printf(sc->dev, "Can't set CPU clock frequency\n");
+		return (rv);
+	}
+
+	rv = clk_set_parent_by_clk(sc->clk_cpu_g, sc->clk_pll_x);
+	if (rv != 0) {
+		device_printf(sc->dev, "Can't set parent to PLLX\n");
+		return (rv);
+	}
+
+	if (sc->act_speed_point->uvolt > point->uvolt) {
+		/* set cpu voltage */
+		rv = regulator_set_voltage(sc->supply_vdd_cpu,
+		    point->uvolt, point->uvolt);
+		if (rv != 0)
+			return (rv);
+	}
+
+	sc->act_speed_point = point;
+
+	return (0);
+}
+
+static int
+tegra124_cpufreq_set(device_t dev, const struct cf_setting *cf)
+{
+	struct tegra124_cpufreq_softc *sc;
+	uint64_t freq;
+	int rv;
+
+	if (cf == NULL || cf->freq < 0)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	freq = cf->freq;
+	if (freq < cpufreq_lowest_freq)
+		freq = cpufreq_lowest_freq;
+	freq *= 1000000;
+	if (freq >= sc->cpu_max_freq)
+		freq = sc->cpu_max_freq;
+	rv = set_cpu_freq(sc, freq);
+
+	return (rv);
+}
+
+static int
+tegra124_cpufreq_get(device_t dev, struct cf_setting *cf)
+{
+	struct tegra124_cpufreq_softc *sc;
+
+	if (cf == NULL)
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
+	cf->dev = NULL;
+	cf->freq = sc->act_speed_point->freq / 1000000;
+	cf->volts = sc->act_speed_point->uvolt / 1000;
+	/* Transition latency in us. */
+	cf->lat = sc->latency;
+	/* Driver providing this setting. */
+	cf->dev = dev;
+
+	return (0);
+}
+
+
+static int
+tegra124_cpufreq_type(device_t dev, int *type)
+{
+
+	if (type == NULL)
+		return (EINVAL);
+	*type = CPUFREQ_TYPE_ABSOLUTE;
+
+	return (0);
+}
+
+static int
+get_fdt_resources(struct tegra124_cpufreq_softc *sc, phandle_t node)
+{
+	int rv;
+	device_t parent_dev;
+
+	parent_dev =  device_get_parent(sc->dev);
+	rv = regulator_get_by_ofw_property(parent_dev, 0, "vdd-cpu-supply",
+	    &sc->supply_vdd_cpu);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'vdd-cpu' regulator\n");
+		return (rv);
+	}
+
+	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_g", &sc->clk_cpu_g);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'cpu_g' clock: %d\n", rv);
+		return (ENXIO);
+	}
+
+	rv = clk_get_by_ofw_name(parent_dev, 0, "cpu_lp", &sc->clk_cpu_lp);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'cpu_lp' clock\n");
+		return (ENXIO);
+	}
+
+	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_x", &sc->clk_pll_x);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pll_x' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(parent_dev, 0, "pll_p", &sc->clk_pll_p);
+	if (rv != 0) {
+		device_printf(parent_dev, "Cannot get 'pll_p' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(parent_dev, 0, "dfll", &sc->clk_dfll);
+	if (rv != 0) {
+		/* XXX DPLL is not implemented yet */
+/*
+		device_printf(sc->dev, "Cannot get 'dfll' clock\n");
+		return (ENXIO);
+*/
+	}
+	return (0);
+}
+
+static void
+tegra124_cpufreq_identify(driver_t *driver, device_t parent)
+{
+	phandle_t root;
+
+	root = OF_finddevice("/");
+	if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))
+		return;
+
+	if (device_get_unit(parent) != 0)
+		return;
+	if (device_find_child(parent, "tegra124_cpufreq", -1) != NULL)
+		return;
+	if (BUS_ADD_CHILD(parent, 0, "tegra124_cpufreq", -1) == NULL)
+		device_printf(parent, "add child failed\n");
+}
+
+static int
+tegra124_cpufreq_probe(device_t dev)
+{
+
+	device_set_desc(dev, "CPU Frequency Control");
+
+	return (0);
+}
+
+static int
+tegra124_cpufreq_attach(device_t dev)
+{
+	struct tegra124_cpufreq_softc *sc;
+	uint64_t freq;
+	int rv;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->node = ofw_bus_get_node(device_get_parent(dev));
+
+	sc->process_id = tegra_sku_info.cpu_process_id;
+	sc->speedo_id = tegra_sku_info.cpu_speedo_id;
+	sc->speedo_value = tegra_sku_info.cpu_speedo_value;
+
+	/* Tegra 124 */
+	/* XXX DPLL is not implemented yet */
+	if (1)
+		sc->cpu_def = &tegra124_cpu_volt_pllx_def;
+	else
+		sc->cpu_def = &tegra124_cpu_volt_dpll_def;
+
+
+	rv = get_fdt_resources(sc, sc->node);
+	if (rv !=  0) {
+		return (rv);
+	}
+
+	build_speed_points(sc);
+
+	rv = clk_get_freq(sc->clk_cpu_g, &freq);
+	if (rv != 0) {
+		device_printf(dev, "Can't get CPU clock frequency\n");
+		return (rv);
+	}
+	if (sc->speedo_id < nitems(cpu_max_freq))
+		sc->cpu_max_freq = cpu_max_freq[sc->speedo_id];
+	else
+		sc->cpu_max_freq = cpu_max_freq[0];
+	sc->act_speed_point = get_speed_point(sc, freq);
+
+	/* Set safe startup CPU frequency. */
+	rv = set_cpu_freq(sc, 1632000000);
+	if (rv != 0) {
+		device_printf(dev, "Can't set initial CPU clock frequency\n");
+		return (rv);
+	}
+
+	/* This device is controlled by cpufreq(4). */
+	cpufreq_register(dev);
+
+	return (0);
+}
+
+static int
+tegra124_cpufreq_detach(device_t dev)
+{
+	struct tegra124_cpufreq_softc *sc;
+
+	sc = device_get_softc(dev);
+	cpufreq_unregister(dev);
+
+	if (sc->supply_vdd_cpu != NULL)
+		regulator_release(sc->supply_vdd_cpu);
+
+	if (sc->clk_cpu_g != NULL)
+		clk_release(sc->clk_cpu_g);
+	if (sc->clk_cpu_lp != NULL)
+		clk_release(sc->clk_cpu_lp);
+	if (sc->clk_pll_x != NULL)
+		clk_release(sc->clk_pll_x);
+	if (sc->clk_pll_p != NULL)
+		clk_release(sc->clk_pll_p);
+	if (sc->clk_dfll != NULL)
+		clk_release(sc->clk_dfll);
+	return (0);
+}
+
+static device_method_t tegra124_cpufreq_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	tegra124_cpufreq_identify),
+	DEVMETHOD(device_probe,		tegra124_cpufreq_probe),
+	DEVMETHOD(device_attach,	tegra124_cpufreq_attach),
+	DEVMETHOD(device_detach,	tegra124_cpufreq_detach),
+
+	/* cpufreq interface */
+	DEVMETHOD(cpufreq_drv_set,	tegra124_cpufreq_set),
+	DEVMETHOD(cpufreq_drv_get,	tegra124_cpufreq_get),
+	DEVMETHOD(cpufreq_drv_settings,	tegra124_cpufreq_settings),
+	DEVMETHOD(cpufreq_drv_type,	tegra124_cpufreq_type),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra124_cpufreq_devclass;
+static DEFINE_CLASS_0(tegra124_cpufreq, tegra124_cpufreq_driver,
+    tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc));
+DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver,
+    tegra124_cpufreq_devclass, NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_cpufreq.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,157 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_machdep.c 331893 2018-04-02 23:19:07Z gonzo $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/reboot.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+
+#include <arm/nvidia/tegra124/tegra124_mp.h>
+
+#include "platform_if.h"
+
+#define	PMC_PHYSBASE		0x7000e400
+#define	PMC_SIZE		0x400
+#define	PMC_CONTROL_REG		0x0
+#define	PMC_SCRATCH0		0x50
+#define	 PMC_SCRATCH0_MODE_RECOVERY	(1 << 31)
+#define	 PMC_SCRATCH0_MODE_BOOTLOADER	(1 << 30)
+#define	 PMC_SCRATCH0_MODE_RCM		(1 << 1)
+#define	 PMC_SCRATCH0_MODE_MASK		(PMC_SCRATCH0_MODE_RECOVERY | \
+					PMC_SCRATCH0_MODE_BOOTLOADER | \
+					PMC_SCRATCH0_MODE_RCM)
+
+static vm_offset_t
+tegra124_lastaddr(platform_t plat)
+{
+
+	return (devmap_lastaddr());
+}
+
+static int
+tegra124_attach(platform_t plat)
+{
+
+	return (0);
+}
+
+static void
+tegra124_late_init(platform_t plat)
+{
+
+}
+
+/*
+ * Set up static device mappings.
+ *
+ */
+static int
+tegra124_devmap_init(platform_t plat)
+{
+
+	devmap_add_entry(0x70000000, 0x01000000);
+	return (0);
+}
+
+static void
+tegra124_cpu_reset(platform_t plat)
+{
+	bus_space_handle_t pmc;
+	uint32_t reg;
+
+	printf("Resetting...\n");
+	bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc);
+
+	reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0);
+	reg &= PMC_SCRATCH0_MODE_MASK;
+	bus_space_write_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0,
+	   reg | PMC_SCRATCH0_MODE_BOOTLOADER); 	/* boot to bootloader */
+	bus_space_read_4(fdtbus_bs_tag, pmc, PMC_SCRATCH0);
+
+	reg = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG);
+	spinlock_enter();
+	dsb();
+	bus_space_write_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG, reg | 0x10);
+	bus_space_read_4(fdtbus_bs_tag, pmc, PMC_CONTROL_REG);
+	while(1)
+		;
+
+}
+
+/*
+ * Early putc routine for EARLY_PRINTF support.  To use, add to kernel config:
+ *   option SOCDEV_PA=0x70000000
+ *   option SOCDEV_VA=0x70000000
+ *   option EARLY_PRINTF
+ */
+#ifdef EARLY_PRINTF
+static void
+tegra124_early_putc(int c)
+{
+
+	volatile uint32_t * UART_STAT_REG = (uint32_t *)(0x70006314);
+	volatile uint32_t * UART_TX_REG   = (uint32_t *)(0x70006300);
+	const uint32_t      UART_TXRDY    = (1 << 6);
+	while ((*UART_STAT_REG & UART_TXRDY) == 0)
+		continue;
+	*UART_TX_REG = c;
+}
+early_putc_t *early_putc = tegra124_early_putc;
+#endif
+
+static platform_method_t tegra124_methods[] = {
+	PLATFORMMETHOD(platform_attach,		tegra124_attach),
+	PLATFORMMETHOD(platform_lastaddr,	tegra124_lastaddr),
+	PLATFORMMETHOD(platform_devmap_init,	tegra124_devmap_init),
+	PLATFORMMETHOD(platform_late_init,	tegra124_late_init),
+	PLATFORMMETHOD(platform_cpu_reset,	tegra124_cpu_reset),
+
+#ifdef SMP
+	PLATFORMMETHOD(platform_mp_start_ap,	tegra124_mp_start_ap),
+	PLATFORMMETHOD(platform_mp_setmaxid,	tegra124_mp_setmaxid),
+#endif
+	PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(tegra124, "Nvidia Jetson-TK1", 0, "nvidia,jetson-tk1", 120);


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_machdep.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_mp.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_mp.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_mp.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,129 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_mp.c 307344 2016-10-15 08:27:54Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/fdt.h>
+#include <machine/smp.h>
+#include <machine/platformvar.h>
+#include <machine/pmap.h>
+
+#include <arm/nvidia/tegra124/tegra124_mp.h>
+
+#define	PMC_PHYSBASE			0x7000e400
+#define	PMC_SIZE			0x400
+#define	PMC_CONTROL_REG			0x0
+#define	PMC_PWRGATE_TOGGLE		0x30
+#define	 PCM_PWRGATE_TOGGLE_START	(1 << 8)
+#define	PMC_PWRGATE_STATUS		0x38
+
+#define	TEGRA_EXCEPTION_VECTORS_BASE	0x6000F000 /* exception vectors */
+#define	TEGRA_EXCEPTION_VECTORS_SIZE	1024
+#define	 TEGRA_EXCEPTION_VECTOR_ENTRY	0x100
+
+void
+tegra124_mp_setmaxid(platform_t plat)
+{
+	int ncpu;
+
+	/* If we've already set the global vars don't bother to do it again. */
+	if (mp_ncpus != 0)
+		return;
+
+	/* Read current CP15 Cache Size ID Register */
+	ncpu = cp15_l2ctlr_get();
+	ncpu = CPUV7_L2CTLR_NPROC(ncpu);
+
+	mp_ncpus = ncpu;
+	mp_maxid = ncpu - 1;
+}
+
+void
+tegra124_mp_start_ap(platform_t plat)
+{
+	bus_space_handle_t pmc;
+	bus_space_handle_t exvec;
+	int i;
+	uint32_t val;
+	uint32_t mask;
+
+	if (bus_space_map(fdtbus_bs_tag, PMC_PHYSBASE, PMC_SIZE, 0, &pmc) != 0)
+		panic("Couldn't map the PMC\n");
+	if (bus_space_map(fdtbus_bs_tag, TEGRA_EXCEPTION_VECTORS_BASE,
+	    TEGRA_EXCEPTION_VECTORS_SIZE, 0, &exvec) != 0)
+		panic("Couldn't map the exception vectors\n");
+
+	bus_space_write_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY,
+			  pmap_kextract((vm_offset_t)mpentry));
+	bus_space_read_4(fdtbus_bs_tag, exvec , TEGRA_EXCEPTION_VECTOR_ENTRY);
+
+
+	/* Wait until POWERGATE is ready (max 20 APB cycles). */
+	do {
+		val = bus_space_read_4(fdtbus_bs_tag, pmc,
+		    PMC_PWRGATE_TOGGLE);
+	} while ((val & PCM_PWRGATE_TOGGLE_START) != 0);
+
+	for (i = 1; i < mp_ncpus; i++) {
+		val = bus_space_read_4(fdtbus_bs_tag, pmc, PMC_PWRGATE_STATUS);
+		mask = 1 << (i + 8);	/* cpu mask */
+		if ((val & mask) == 0) {
+			/* Wait until POWERGATE is ready (max 20 APB cycles). */
+			do {
+				val = bus_space_read_4(fdtbus_bs_tag, pmc,
+				PMC_PWRGATE_TOGGLE);
+			} while ((val & PCM_PWRGATE_TOGGLE_START) != 0);
+			bus_space_write_4(fdtbus_bs_tag, pmc,
+			    PMC_PWRGATE_TOGGLE,
+			    PCM_PWRGATE_TOGGLE_START | (8 + i));
+
+			/* Wait until CPU is powered */
+			do {
+				val = bus_space_read_4(fdtbus_bs_tag, pmc,
+				    PMC_PWRGATE_STATUS);
+			} while ((val & mask) == 0);
+		}
+
+	}
+	dsb();
+	sev();
+	bus_space_unmap(fdtbus_bs_tag, pmc, PMC_SIZE);
+	bus_space_unmap(fdtbus_bs_tag, exvec, TEGRA_EXCEPTION_VECTORS_SIZE);
+}


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_mp.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_mp.h
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_mp.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_mp.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,36 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_mp.h 296936 2016-03-16 13:01:48Z mmel $
+ */
+
+#ifndef _TEGRA124_MP_H_
+#define	_TEGRA124_MP_H_
+
+void tegra124_mp_setmaxid(platform_t plat);
+void tegra124_mp_start_ap(platform_t plat);
+
+#endif /*_TEGRA124_MP_H_*/
\ No newline at end of file


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_mp.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,563 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_pmc.c 308335 2016-11-05 10:56:32Z mmel $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_pmc.h>
+
+#define	PMC_CNTRL			0x000
+#define	 PMC_CNTRL_CPUPWRGOOD_SEL_MASK		(0x3 << 20)
+#define	 PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT		20
+#define	 PMC_CNTRL_CPUPWRGOOD_EN		(1 << 19)
+#define	 PMC_CNTRL_FUSE_OVERRIDE		(1 << 18)
+#define	 PMC_CNTRL_INTR_POLARITY		(1 << 17)
+#define	 PMC_CNTRL_CPU_PWRREQ_OE		(1 << 16)
+#define	 PMC_CNTRL_CPU_PWRREQ_POLARITY		(1 << 15)
+#define	 PMC_CNTRL_SIDE_EFFECT_LP0		(1 << 14)
+#define	 PMC_CNTRL_AOINIT			(1 << 13)
+#define	 PMC_CNTRL_PWRGATE_DIS			(1 << 12)
+#define	 PMC_CNTRL_SYSCLK_OE			(1 << 11)
+#define	 PMC_CNTRL_SYSCLK_POLARITY		(1 << 10)
+#define	 PMC_CNTRL_PWRREQ_OE			(1 <<  9)
+#define	 PMC_CNTRL_PWRREQ_POLARITY		(1 <<  8)
+#define	 PMC_CNTRL_BLINK_EN			(1 <<  7)
+#define	 PMC_CNTRL_GLITCHDET_DIS		(1 <<  6)
+#define	 PMC_CNTRL_LATCHWAKE_EN			(1 <<  5)
+#define	 PMC_CNTRL_MAIN_RST			(1 <<  4)
+#define	 PMC_CNTRL_KBC_RST			(1 <<  3)
+#define	 PMC_CNTRL_RTC_RST			(1 <<  2)
+#define	 PMC_CNTRL_RTC_CLK_DIS			(1 <<  1)
+#define	 PMC_CNTRL_KBC_CLK_DIS			(1 <<  0)
+
+#define	PMC_DPD_SAMPLE			0x020
+
+#define	PMC_CLAMP_STATUS		0x02C
+#define	  PMC_CLAMP_STATUS_PARTID(x)		(1 << ((x) & 0x1F))
+
+#define	PMC_PWRGATE_TOGGLE		0x030
+#define	 PMC_PWRGATE_TOGGLE_START		(1 << 8)
+#define	 PMC_PWRGATE_TOGGLE_PARTID(x)		(((x) & 0x1F) << 0)
+
+#define	PMC_REMOVE_CLAMPING_CMD		0x034
+#define	  PMC_REMOVE_CLAMPING_CMD_PARTID(x)	(1 << ((x) & 0x1F))
+
+#define	PMC_PWRGATE_STATUS		0x038
+#define	PMC_PWRGATE_STATUS_PARTID(x)		(1 << ((x) & 0x1F))
+
+#define	PMC_SCRATCH0			0x050
+#define	 PMC_SCRATCH0_MODE_RECOVERY		(1 << 31)
+#define	 PMC_SCRATCH0_MODE_BOOTLOADER		(1 << 30)
+#define	 PMC_SCRATCH0_MODE_RCM			(1 << 1)
+#define	 PMC_SCRATCH0_MODE_MASK			(PMC_SCRATCH0_MODE_RECOVERY | \
+						PMC_SCRATCH0_MODE_BOOTLOADER | \
+						PMC_SCRATCH0_MODE_RCM)
+
+#define	PMC_CPUPWRGOOD_TIMER		0x0c8
+#define	PMC_CPUPWROFF_TIMER		0x0cc
+
+#define	PMC_SCRATCH41			0x140
+
+#define	PMC_SENSOR_CTRL			0x1b0
+#define	PMC_SENSOR_CTRL_BLOCK_SCRATCH_WRITE	(1 << 2)
+#define	PMC_SENSOR_CTRL_ENABLE_RST		(1 << 1)
+#define	PMC_SENSOR_CTRL_ENABLE_PG		(1 << 0)
+
+#define	PMC_IO_DPD_REQ			0x1b8
+#define	 PMC_IO_DPD_REQ_CODE_IDLE		(0 << 30)
+#define	 PMC_IO_DPD_REQ_CODE_OFF		(1 << 30)
+#define	 PMC_IO_DPD_REQ_CODE_ON			(2 << 30)
+#define	 PMC_IO_DPD_REQ_CODE_MASK		(3 << 30)
+
+#define	PMC_IO_DPD_STATUS		0x1bc
+#define	 PMC_IO_DPD_STATUS_HDMI			(1 << 28)
+#define	PMC_IO_DPD2_REQ			0x1c0
+#define	PMC_IO_DPD2_STATUS		0x1c4
+#define	 PMC_IO_DPD2_STATUS_HV			(1 << 6)
+#define	PMC_SEL_DPD_TIM			0x1c8
+
+#define	PMC_SCRATCH54			0x258
+#define	PMC_SCRATCH54_DATA_SHIFT		8
+#define	PMC_SCRATCH54_ADDR_SHIFT		0
+
+#define	PMC_SCRATCH55			0x25c
+#define	PMC_SCRATCH55_RST_ENABLE		(1 << 31)
+#define	PMC_SCRATCH55_CNTRL_TYPE		(1 << 30)
+#define	PMC_SCRATCH55_CNTRL_ID_SHIFT		27
+#define	PMC_SCRATCH55_CNTRL_ID_MASK		0x07
+#define	PMC_SCRATCH55_PINMUX_SHIFT		24
+#define	PMC_SCRATCH55_PINMUX_MASK		0x07
+#define	PMC_SCRATCH55_CHECKSUM_SHIFT		16
+#define	PMC_SCRATCH55_CHECKSUM_MASK		0xFF
+#define	PMC_SCRATCH55_16BITOP			(1 << 15)
+#define	PMC_SCRATCH55_I2CSLV1_SHIFT		0
+#define	PMC_SCRATCH55_I2CSLV1_MASK		0x7F
+
+#define	PMC_GPU_RG_CNTRL		0x2d4
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+#define	PMC_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	PMC_UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	PMC_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx, 			\
+	    device_get_nameunit(_sc->dev), "tegra124_pmc", MTX_DEF)
+#define	PMC_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->mtx);
+#define	PMC_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED);
+#define	PMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED);
+
+struct tegra124_pmc_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+	clk_t			clk;
+	struct mtx		mtx;
+
+	uint32_t		rate;
+	enum tegra_suspend_mode suspend_mode;
+	uint32_t		cpu_good_time;
+	uint32_t		cpu_off_time;
+	uint32_t		core_osc_time;
+	uint32_t		core_pmu_time;
+	uint32_t		core_off_time;
+	int			corereq_high;
+	int			sysclkreq_high;
+	int			combined_req;
+	int			cpu_pwr_good_en;
+	uint32_t		lp0_vec_phys;
+	uint32_t		lp0_vec_size;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-pmc",		1},
+	{NULL,				0},
+};
+
+static struct tegra124_pmc_softc *pmc_sc;
+
+static inline struct tegra124_pmc_softc *
+tegra124_pmc_get_sc(void)
+{
+	if (pmc_sc == NULL)
+		panic("To early call to Tegra PMC driver.\n");
+	return (pmc_sc);
+}
+
+static int
+tegra124_pmc_set_powergate(struct tegra124_pmc_softc *sc,
+    enum tegra_powergate_id id, int ena)
+{
+	uint32_t reg;
+	int i;
+
+	PMC_LOCK(sc);
+
+	reg = RD4(sc, PMC_PWRGATE_STATUS) & PMC_PWRGATE_STATUS_PARTID(id);
+	if (((reg != 0) && ena) || ((reg == 0) && !ena)) {
+		PMC_UNLOCK(sc);
+		return (0);
+	}
+
+	for (i = 100; i > 0; i--) {
+		reg = RD4(sc, PMC_PWRGATE_TOGGLE);
+		if ((reg & PMC_PWRGATE_TOGGLE_START) == 0)
+			break;
+		DELAY(1);
+	}
+	if (i <= 0)
+		device_printf(sc->dev,
+		    "Timeout when waiting for TOGGLE_START\n");
+
+	WR4(sc, PMC_PWRGATE_TOGGLE,
+	    PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID(id));
+
+	for (i = 100; i > 0; i--) {
+		reg = RD4(sc, PMC_PWRGATE_TOGGLE);
+		if ((reg & PMC_PWRGATE_TOGGLE_START) == 0)
+			break;
+		DELAY(1);
+	}
+	if (i <= 0)
+		device_printf(sc->dev,
+		    "Timeout when waiting for TOGGLE_START\n");
+		PMC_UNLOCK(sc);
+	return (0);
+}
+
+int
+tegra_powergate_remove_clamping(enum tegra_powergate_id  id)
+{
+	struct tegra124_pmc_softc *sc;
+	uint32_t reg;
+	enum tegra_powergate_id swid;
+	int i;
+
+	sc = tegra124_pmc_get_sc();
+
+	if (id == TEGRA_POWERGATE_3D) {
+		WR4(sc, PMC_GPU_RG_CNTRL, 0);
+		return (0);
+	}
+
+	reg = RD4(sc, PMC_PWRGATE_STATUS);
+	if ((reg & PMC_PWRGATE_STATUS_PARTID(id)) == 0)
+		panic("Attempt to remove clamping for unpowered partition.\n");
+
+	if (id == TEGRA_POWERGATE_PCX)
+		swid = TEGRA_POWERGATE_VDE;
+	else if (id == TEGRA_POWERGATE_VDE)
+		swid = TEGRA_POWERGATE_PCX;
+	else
+		swid = id;
+	WR4(sc, PMC_REMOVE_CLAMPING_CMD, PMC_REMOVE_CLAMPING_CMD_PARTID(swid));
+
+	for (i = 100; i > 0; i--) {
+		reg = RD4(sc, PMC_REMOVE_CLAMPING_CMD);
+		if ((reg & PMC_REMOVE_CLAMPING_CMD_PARTID(swid)) == 0)
+			break;
+		DELAY(1);
+	}
+	if (i <= 0)
+		device_printf(sc->dev, "Timeout when remove clamping\n");
+
+	reg = RD4(sc, PMC_CLAMP_STATUS);
+	if ((reg & PMC_CLAMP_STATUS_PARTID(id)) != 0)
+		panic("Cannot remove clamping\n");
+
+	return (0);
+}
+
+int
+tegra_powergate_is_powered(enum tegra_powergate_id id)
+{
+	struct tegra124_pmc_softc *sc;
+	uint32_t reg;
+
+	sc = tegra124_pmc_get_sc();
+
+	reg = RD4(sc, PMC_PWRGATE_STATUS);
+	return ((reg & PMC_PWRGATE_STATUS_PARTID(id)) ? 1 : 0);
+}
+
+int
+tegra_powergate_power_on(enum tegra_powergate_id id)
+{
+	struct tegra124_pmc_softc *sc;
+	int rv, i;
+
+	sc = tegra124_pmc_get_sc();
+
+	rv = tegra124_pmc_set_powergate(sc, id, 1);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot set powergate: %d\n", id);
+		return (rv);
+	}
+
+	for (i = 100; i > 0; i--) {
+		if (tegra_powergate_is_powered(id))
+			break;
+		DELAY(1);
+	}
+	if (i <= 0)
+		device_printf(sc->dev, "Timeout when waiting on power up\n");
+
+	return (rv);
+}
+
+int
+tegra_powergate_power_off(enum tegra_powergate_id id)
+{
+	struct tegra124_pmc_softc *sc;
+	int rv, i;
+
+	sc = tegra124_pmc_get_sc();
+
+	rv = tegra124_pmc_set_powergate(sc, id, 0);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot set powergate: %d\n", id);
+		return (rv);
+	}
+	for (i = 100; i > 0; i--) {
+		if (!tegra_powergate_is_powered(id))
+			break;
+		DELAY(1);
+	}
+	if (i <= 0)
+		device_printf(sc->dev, "Timeout when waiting on power off\n");
+
+	return (rv);
+}
+
+int
+tegra_powergate_sequence_power_up(enum tegra_powergate_id id, clk_t clk,
+    hwreset_t rst)
+{
+	struct tegra124_pmc_softc *sc;
+	int rv;
+
+	sc = tegra124_pmc_get_sc();
+
+	rv = hwreset_assert(rst);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert reset\n");
+		return (rv);
+	}
+
+	rv = clk_stop(clk);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot stop clock\n");
+		goto clk_fail;
+	}
+
+	rv = tegra_powergate_power_on(id);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot power on powergate\n");
+		goto clk_fail;
+	}
+
+	rv = clk_enable(clk);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable clock\n");
+		goto clk_fail;
+	}
+	DELAY(20);
+
+	rv = tegra_powergate_remove_clamping(id);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot remove clamping\n");
+		goto fail;
+	}
+	rv = hwreset_deassert(rst);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot unreset reset\n");
+		goto fail;
+	}
+	return 0;
+
+fail:
+	clk_disable(clk);
+clk_fail:
+	hwreset_assert(rst);
+	tegra_powergate_power_off(id);
+	return (rv);
+}
+
+static int
+tegra124_pmc_parse_fdt(struct tegra124_pmc_softc *sc, phandle_t node)
+{
+	int rv;
+	uint32_t tmp;
+	uint32_t tmparr[2];
+
+	rv = OF_getencprop(node, "nvidia,suspend-mode", &tmp, sizeof(tmp));
+	if (rv > 0) {
+		switch (tmp) {
+		case 0:
+			sc->suspend_mode = TEGRA_SUSPEND_LP0;
+			break;
+
+		case 1:
+			sc->suspend_mode = TEGRA_SUSPEND_LP1;
+			break;
+
+		case 2:
+			sc->suspend_mode = TEGRA_SUSPEND_LP2;
+			break;
+
+		default:
+			sc->suspend_mode = TEGRA_SUSPEND_NONE;
+			break;
+		}
+	}
+
+	rv = OF_getencprop(node, "nvidia,cpu-pwr-good-time", &tmp, sizeof(tmp));
+	if (rv > 0) {
+		sc->cpu_good_time = tmp;
+		sc->suspend_mode = TEGRA_SUSPEND_NONE;
+	}
+
+	rv = OF_getencprop(node, "nvidia,cpu-pwr-off-time", &tmp, sizeof(tmp));
+	if (rv > 0) {
+		sc->cpu_off_time = tmp;
+		sc->suspend_mode = TEGRA_SUSPEND_NONE;
+	}
+
+	rv = OF_getencprop(node, "nvidia,core-pwr-good-time", tmparr,
+	    sizeof(tmparr));
+	if (rv == sizeof(tmparr)) {
+		sc->core_osc_time = tmparr[0];
+		sc->core_pmu_time = tmparr[1];
+		sc->suspend_mode = TEGRA_SUSPEND_NONE;
+	}
+
+	rv = OF_getencprop(node, "nvidia,core-pwr-off-time", &tmp, sizeof(tmp));
+	if (rv > 0) {
+		sc->core_off_time = tmp;
+		sc->suspend_mode = TEGRA_SUSPEND_NONE;
+	}
+
+	sc->corereq_high =
+	    OF_hasprop(node, "nvidia,core-power-req-active-high");
+	sc->sysclkreq_high =
+	    OF_hasprop(node, "nvidia,sys-clock-req-active-high");
+	sc->combined_req =
+	    OF_hasprop(node, "nvidia,combined-power-req");
+	sc->cpu_pwr_good_en =
+	    OF_hasprop(node, "nvidia,cpu-pwr-good-en");
+
+	rv = OF_getencprop(node, "nvidia,lp0-vec", tmparr, sizeof(tmparr));
+	if (rv == sizeof(tmparr)) {
+
+		sc->lp0_vec_phys = tmparr[0];
+		sc->core_pmu_time = tmparr[1];
+		sc->lp0_vec_size = TEGRA_SUSPEND_NONE;
+		if (sc->suspend_mode == TEGRA_SUSPEND_LP0)
+			sc->suspend_mode = TEGRA_SUSPEND_LP1;
+	}
+	return 0;
+}
+
+static int
+tegra124_pmc_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra PMC");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra124_pmc_detach(device_t dev)
+{
+
+	/* This device is always present. */
+	return (EBUSY);
+}
+
+static int
+tegra124_pmc_attach(device_t dev)
+{
+	struct tegra124_pmc_softc *sc;
+	int rid, rv;
+	uint32_t reg;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+
+	rv = tegra124_pmc_parse_fdt(sc, node);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot parse FDT data\n");
+		return (rv);
+	}
+
+	rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get \"pclk\" clock\n");
+		return (ENXIO);
+	}
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	PMC_LOCK_INIT(sc);
+
+	/* Enable CPU power request. */
+	reg = RD4(sc, PMC_CNTRL);
+	reg |= PMC_CNTRL_CPU_PWRREQ_OE;
+	WR4(sc, PMC_CNTRL, reg);
+
+	/* Set sysclk output polarity */
+	reg = RD4(sc, PMC_CNTRL);
+	if (sc->sysclkreq_high)
+		reg &= ~PMC_CNTRL_SYSCLK_POLARITY;
+	else
+		reg |= PMC_CNTRL_SYSCLK_POLARITY;
+	WR4(sc, PMC_CNTRL, reg);
+
+	/* Enable sysclk request. */
+	reg = RD4(sc, PMC_CNTRL);
+	reg |= PMC_CNTRL_SYSCLK_OE;
+	WR4(sc, PMC_CNTRL, reg);
+
+	/*
+	 * Remove HDMI from deep power down mode.
+	 * XXX mote this to HDMI driver
+	 */
+	reg = RD4(sc, PMC_IO_DPD_STATUS);
+	reg &= ~ PMC_IO_DPD_STATUS_HDMI;
+	WR4(sc, PMC_IO_DPD_STATUS, reg);
+
+	reg = RD4(sc, PMC_IO_DPD2_STATUS);
+	reg &= ~ PMC_IO_DPD2_STATUS_HV;
+	WR4(sc, PMC_IO_DPD2_STATUS, reg);
+
+	if (pmc_sc != NULL)
+		panic("tegra124_pmc: double driver attach");
+	pmc_sc = sc;
+	return (0);
+}
+
+static device_method_t tegra124_pmc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra124_pmc_probe),
+	DEVMETHOD(device_attach,	tegra124_pmc_attach),
+	DEVMETHOD(device_detach,	tegra124_pmc_detach),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra124_pmc_devclass;
+static DEFINE_CLASS_0(pmc, tegra124_pmc_driver, tegra124_pmc_methods,
+    sizeof(struct tegra124_pmc_softc));
+EARLY_DRIVER_MODULE(tegra124_pmc, simplebus, tegra124_pmc_driver,
+    tegra124_pmc_devclass, NULL, NULL, 70);


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_pmc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c
===================================================================
--- trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1213 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c 332025 2018-04-04 13:23:06Z mmel $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#include <gnu/dts/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
+
+#include "phydev_if.h"
+
+/* FUSE calibration data. */
+#define	FUSE_XUSB_CALIB				0x0F0
+#define	  FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x)		(((x) >> 15) & 0x3F);
+#define	  FUSE_XUSB_CALIB_HS_IREF_CAP(x)		(((x) >> 13) & 0x03);
+#define	  FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x)		(((x) >> 11) & 0x03);
+#define	  FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x)		(((x) >>  7) & 0x0F);
+#define	  FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x)		(((x) >>  0) & 0x3F);
+
+
+/* Registers. */
+#define	XUSB_PADCTL_USB2_PAD_MUX		0x004
+
+#define	XUSB_PADCTL_USB2_PORT_CAP		0x008
+#define	 USB2_PORT_CAP_ULPI_PORT_INTERNAL		(1 << 25)
+#define	 USB2_PORT_CAP_ULPI_PORT_CAP			(1 << 24)
+#define	 USB2_PORT_CAP_PORT_REVERSE_ID(p)		(1 << (3 + (p) * 4))
+#define	 USB2_PORT_CAP_PORT_INTERNAL(p)			(1 << (2 + (p) * 4))
+#define	 USB2_PORT_CAP_PORT_CAP(p, x)			(((x) & 3) << ((p) * 4))
+#define	  USB2_PORT_CAP_PORT_CAP_OTG			0x3
+#define	  USB2_PORT_CAP_PORT_CAP_DEVICE			0x2
+#define	  USB2_PORT_CAP_PORT_CAP_HOST			0x1
+#define	  USB2_PORT_CAP_PORT_CAP_DISABLED		0x0
+
+#define	XUSB_PADCTL_SS_PORT_MAP			0x014
+#define	 SS_PORT_MAP_PORT_INTERNAL(p)			(1 << (3 + (p) * 4))
+#define	 SS_PORT_MAP_PORT_MAP(p, x)			(((x) & 7) << ((p) * 4))
+
+#define	XUSB_PADCTL_ELPG_PROGRAM		0x01C
+#define	 ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN		(1 << 26)
+#define	 ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY	(1 << 25)
+#define	 ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN		(1 << 24)
+#define	 ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) 		(1 << (18 + (x) * 4))
+#define	 ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) 	(1 << (17 + (x) * 4))
+#define	 ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x)		(1 << (16 + (x) * 4))
+
+#define	XUSB_PADCTL_IOPHY_PLL_P0_CTL1		0x040
+#define	 IOPHY_PLL_P0_CTL1_PLL0_LOCKDET			(1 << 19)
+#define	 IOPHY_PLL_P0_CTL1_REFCLK_SEL(x)		(((x) & 0xF) << 12)
+#define	 IOPHY_PLL_P0_CTL1_PLL_RST			(1 << 1)
+
+#define	XUSB_PADCTL_IOPHY_PLL_P0_CTL2		0x044
+#define	 IOPHY_PLL_P0_CTL2_REFCLKBUF_EN			(1 << 6)
+#define	 IOPHY_PLL_P0_CTL2_TXCLKREF_EN			(1 << 5)
+#define	 IOPHY_PLL_P0_CTL2_TXCLKREF_SEL			(1 << 4)
+
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(x) 	(0x058 + (x) * 4)
+#define	 IOPHY_USB3_PAD_CTL2_CDR_CNTL(x)		(((x) & 0x00FF) <<  4)
+#define	 IOPHY_USB3_PAD_CTL2_RX_EQ(x)			(((x) & 0xFFFF) <<  8)
+#define	 IOPHY_USB3_PAD_CTL2_RX_WANDER(x)		(((x) & 0x000F) <<  4)
+#define	 IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x)		(((x) & 0x0003) <<  2)
+#define	 IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x)		(((x) & 0x0003) <<  0)
+
+
+#define	XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x)	(0x068 + (x) * 4)
+
+#define	XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) 	(0x0A0 + (x) * 4)
+#define	 USB2_OTG_PAD_CTL0_LSBIAS_SEL			(1 << 23)
+#define	 USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD		(1 << 22)
+#define	 USB2_OTG_PAD_CTL0_PD_ZI			(1 << 21)
+#define	 USB2_OTG_PAD_CTL0_PD2				(1 << 20)
+#define	 USB2_OTG_PAD_CTL0_PD				(1 << 19)
+#define	 USB2_OTG_PAD_CTL0_TERM_EN			(1 << 18)
+#define	 USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x)		(((x) & 0x03) << 16)
+#define	 USB2_OTG_PAD_CTL0_LS_RSLEW(x)			(((x) & 0x03) << 14)
+#define	 USB2_OTG_PAD_CTL0_FS_SLEW(x)			(((x) & 0x03) << 12)
+#define	 USB2_OTG_PAD_CTL0_HS_SLEW(x)			(((x) & 0x3F) <<  6)
+#define	 USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x)		(((x) & 0x3F) <<  0)
+
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) 	(0x0AC + (x) * 4)
+#define	 USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x)		(((x) & 0x3) << 11)
+#define	 USB2_OTG_PAD_CTL1_HS_IREF_CAP(x)		(((x) & 0x3) <<  9)
+#define	 USB2_OTG_PAD_CTL1_SPARE(x)			(((x) & 0x3) <<  7)
+#define	 USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x)		(((x) & 0xF) <<  3)
+#define	 USB2_OTG_PAD_CTL1_PD_DR			(1 <<  2)
+#define	 USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP	(1 <<  1)
+#define	 USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP	(1 <<  0)
+
+#define	XUSB_PADCTL_USB2_BIAS_PAD_CTL0		0x0B8
+#define	 USB2_BIAS_PAD_CTL0_ADJRPU(x)			(((x) & 0x7) << 14)
+#define	 USB2_BIAS_PAD_CTL0_PD_TRK			(1 << 13)
+#define	 USB2_BIAS_PAD_CTL0_PD				(1 << 12)
+#define	 USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x)		(((x) & 0x3) <<  9)
+#define	 USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x)		(((x) & 0x3) <<  7)
+#define	 USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x)		(((x) & 0x3) <<  5)
+#define	 USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x)		(((x) & 0x7) <<  2)
+#define	 USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x)		(((x) & 0x3) <<  0)
+
+#define	XUSB_PADCTL_HSIC_PAD0_CTL0		0x0C8
+#define	 HSIC_PAD0_CTL0_HSIC_OPT(x)			(((x) & 0xF) << 16)
+#define	 HSIC_PAD0_CTL0_TX_SLEWN(x)			(((x) & 0xF) << 12)
+#define	 HSIC_PAD0_CTL0_TX_SLEWP(x)			(((x) & 0xF) <<  8)
+#define	 HSIC_PAD0_CTL0_TX_RTUNEN(x)			(((x) & 0xF) <<  4)
+#define	 HSIC_PAD0_CTL0_TX_RTUNEP(x)			(((x) & 0xF) <<  0)
+
+#define	XUSB_PADCTL_USB3_PAD_MUX		0x134
+#define	 USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) 		(1 << (1 + (x)))
+#define	 USB3_PAD_MUX_SATA_IDDQ_DISABLE 		(1 << 6)
+
+
+#define	XUSB_PADCTL_IOPHY_PLL_S0_CTL1		0x138
+#define	 IOPHY_PLL_S0_CTL1_PLL1_LOCKDET			(1 << 27)
+#define	 IOPHY_PLL_S0_CTL1_PLL1_MODE			(1 << 24)
+#define	 IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD			(1 << 3)
+#define	 IOPHY_PLL_S0_CTL1_PLL_RST_L			(1 << 1)
+#define	 IOPHY_PLL_S0_CTL1_PLL_IDDQ			(1 << 0)
+
+#define	XUSB_PADCTL_IOPHY_PLL_S0_CTL2		0x13C
+#define	XUSB_PADCTL_IOPHY_PLL_S0_CTL3		0x140
+#define	XUSB_PADCTL_IOPHY_PLL_S0_CTL4		0x144
+
+#define	XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1	0x148
+#define	 IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD		(1 << 1)
+#define	 IOPHY_MISC_PAD_S0_CTL1_IDDQ			(1 << 0)
+
+#define	XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2	0x14C
+#define	XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3	0x150
+#define	XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4	0x154
+#define	XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5	0x158
+#define	XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6	0x15C
+
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+
+struct padctl_softc {
+	device_t	dev;
+	struct resource	*mem_res;
+	hwreset_t	rst;
+	int		phy_ena_cnt;
+
+	/* Fuses calibration data */
+	uint32_t	hs_curr_level_0;
+	uint32_t	hs_curr_level_123;
+	uint32_t	hs_iref_cap;
+	uint32_t	hs_term_range_adj;
+	uint32_t	hs_squelch_level;
+
+	uint32_t	hs_curr_level_offset;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-xusb-padctl",	1},
+	{NULL,				0},
+};
+
+/* Ports. */
+enum padctl_port_type {
+	PADCTL_PORT_USB2,
+	PADCTL_PORT_ULPI,
+	PADCTL_PORT_HSIC,
+	PADCTL_PORT_USB3,
+};
+
+struct padctl_lane;
+struct padctl_port {
+	enum padctl_port_type	type;
+	const char		*name;
+	const char		*base_name;
+	int			idx;
+	int			(*init)(struct padctl_softc *sc,
+				    struct padctl_port *port);
+
+	/* Runtime data. */
+	bool			enabled;
+	regulator_t		supply_vbus;	/* USB2, USB3 */
+	bool			internal;	/* ULPI, USB2, USB3 */
+	uint32_t		companion;	/* USB3 */
+	struct padctl_lane	*lane;
+};
+
+static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port);
+
+#define	PORT(t, n, p, i) {						\
+	.type = t,							\
+	.name = n "-" #p,						\
+	.base_name = n,							\
+	.idx = p,							\
+	.init = i,							\
+}
+static struct padctl_port ports_tbl[] = {
+	PORT(PADCTL_PORT_USB2, "usb2", 0, NULL),
+	PORT(PADCTL_PORT_USB2, "usb2", 1, NULL),
+	PORT(PADCTL_PORT_USB2, "usb2", 2, NULL),
+	PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL),
+	PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL),
+	PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL),
+	PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init),
+	PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init),
+};
+
+/* Pads - a group of lannes. */
+enum padctl_pad_type {
+	PADCTL_PAD_USB2,
+	PADCTL_PAD_ULPI,
+	PADCTL_PAD_HSIC,
+	PADCTL_PAD_PCIE,
+	PADCTL_PAD_SATA,
+};
+
+struct padctl_lane;
+struct padctl_pad {
+	const char		*name;
+	enum padctl_pad_type	type;
+	int			(*powerup)(struct padctl_softc *sc,
+				    struct padctl_lane *lane);
+	int			(*powerdown)(struct padctl_softc *sc,
+				    struct padctl_lane *lane);
+	/* Runtime data. */
+	bool			enabled;
+	struct padctl_lane	*lanes[8]; 	/* Safe maximum value. */
+	int			nlanes;
+};
+
+static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
+static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
+static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
+static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
+static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
+static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
+
+#define	PAD(n, t, u, d) {						\
+	.name = n,							\
+	.type = t,							\
+	.powerup = u,							\
+	.powerdown = d,							\
+}
+static struct padctl_pad pads_tbl[] = {
+	PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown),
+	PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL),
+	PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL),
+	PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown),
+	PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown),
+};
+
+/* Lanes. */
+static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"};
+static char *usb_mux[] = {"snps", "xusb"};
+static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"};
+
+struct padctl_lane {
+	const char		*name;
+	int			idx;
+	bus_size_t		reg;
+	uint32_t		shift;
+	uint32_t		mask;
+	char			**mux;
+	int			nmux;
+	/* Runtime data. */
+	bool			enabled;
+	struct padctl_pad	*pad;
+	struct padctl_port	*port;
+	int			mux_idx;
+
+};
+
+#define	LANE(n, p, r, s, m, mx) {					\
+	.name = n "-" #p,						\
+	.idx = p,							\
+	.reg = r,							\
+	.shift = s,							\
+	.mask = m,							\
+	.mux = mx,							\
+	.nmux = nitems(mx),						\
+}
+static struct padctl_lane lanes_tbl[] = {
+	LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX,  0, 0x3, otg_mux),
+	LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX,  2, 0x3, otg_mux),
+	LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX,  4, 0x3, otg_mux),
+	LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux),
+	LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux),
+	LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux),
+	LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux),
+	LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux),
+	LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux),
+	LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux),
+	LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux),
+	LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux),
+};
+
+/* Define all possible mappings for USB3 port lanes */
+struct padctl_lane_map {
+	int			port_idx;
+	enum padctl_pad_type	pad_type;
+	int			lane_idx;
+};
+
+#define	LANE_MAP(pi, pt, li) {						\
+	.port_idx = pi,							\
+	.pad_type = pt,							\
+	.lane_idx = li,							\
+}
+static struct padctl_lane_map lane_map_tbl[] = {
+	LANE_MAP(0, PADCTL_PAD_PCIE, 0), 	/* port USB3-0 -> lane PCIE-0 */
+	LANE_MAP(1, PADCTL_PAD_PCIE, 1), 	/* port USB3-1 -> lane PCIE-1 */
+						/* -- or -- */
+	LANE_MAP(1, PADCTL_PAD_SATA, 0), 	/* port USB3-1 -> lane SATA-0 */
+};
+
+ /* Phy class and methods. */
+static int xusbpadctl_phy_enable(struct phynode *phy, bool enable);
+static phynode_method_t xusbpadctl_phynode_methods[] = {
+	PHYNODEMETHOD(phynode_enable,	xusbpadctl_phy_enable),
+	PHYNODEMETHOD_END
+
+};
+DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class,
+    xusbpadctl_phynode_methods, 0, phynode_class);
+
+static struct padctl_port *search_lane_port(struct padctl_softc *sc,
+    struct padctl_lane *lane);
+/* -------------------------------------------------------------------------
+ *
+ *   PHY functions
+ */
+static int
+usb3_port_init(struct padctl_softc *sc, struct padctl_port *port)
+{
+	uint32_t reg;
+
+	reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP);
+	if (port->internal)
+		reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx);
+	else
+		reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx);
+	reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0);
+	reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion);
+	WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx));
+	reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0);
+	reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0);
+	reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0);
+	reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24);
+	reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070);
+	reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF);
+	WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg);
+
+	WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx),
+	    0x002008EE);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx);
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx);
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx);
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	return (0);
+}
+
+static int
+pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+	int i;
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+	reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0);
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
+	reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN;
+	reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN;
+	reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+	reg |= IOPHY_PLL_P0_CTL1_PLL_RST;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
+	DELAY(100);
+
+	for (i = 100; i > 0; i--) {
+		reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+		if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET)
+			break;
+		DELAY(10);
+	}
+	if (i <= 0) {
+		device_printf(sc->dev, "Failed to power up PCIe phy\n");
+		return (ETIMEDOUT);
+	}
+	reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+	reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
+	WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+	return (0);
+}
+
+static int
+pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+
+	reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+	reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
+	WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+	reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
+	DELAY(100);
+
+	return (0);
+
+}
+
+static int
+sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+	int i;
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+	reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+	reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+	WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+	reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+	reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+	reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+	reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+
+	for (i = 100; i >= 0; i--) {
+		reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+		if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET)
+			break;
+		DELAY(100);
+	}
+	if (i <= 0) {
+		device_printf(sc->dev, "Failed to power up SATA phy\n");
+		return (ETIMEDOUT);
+	}
+	reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+	reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
+	WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+	reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE;
+	WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+	return (0);
+}
+
+static int
+sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+
+	reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
+	reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE;
+	WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+	reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+	reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+	reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+	reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+	WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+	reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+	reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+	WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
+	DELAY(100);
+
+	return (0);
+}
+
+static int
+usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+	struct padctl_port *port;
+	int rv;
+
+	port = search_lane_port(sc, lane);
+	if (port == NULL) {
+		device_printf(sc->dev, "Cannot find port for lane: %s\n",
+		    lane->name);
+	}
+	reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+	reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0);
+	reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0);
+	reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level);
+	reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5);
+	WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP);
+	reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0);
+	reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST);
+	WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg);
+
+	reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx));
+	reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0);
+	reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0);
+	reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0);
+	reg &= ~USB2_OTG_PAD_CTL0_PD;
+	reg &= ~USB2_OTG_PAD_CTL0_PD2;
+	reg &= ~USB2_OTG_PAD_CTL0_PD_ZI;
+
+	reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14);
+	if (lane->idx == 0) {
+		reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0);
+		reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3);
+	} else {
+		reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123);
+		reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0);
+	}
+	WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg);
+
+	reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx));
+	reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0);
+	reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0);
+	reg &= ~USB2_OTG_PAD_CTL1_PD_DR;
+	reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP;
+	reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP;
+
+	reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj);
+	reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap);
+	WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg);
+
+	if (port != NULL && port->supply_vbus != NULL) {
+		rv = regulator_enable(port->supply_vbus);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot enable vbus regulator\n");
+			return (rv);
+		}
+	}
+	reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+	reg &= ~USB2_BIAS_PAD_CTL0_PD;
+	WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
+
+	return (0);
+}
+
+static int
+usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+	struct padctl_port *port;
+	int rv;
+
+	port = search_lane_port(sc, lane);
+	if (port == NULL) {
+		device_printf(sc->dev, "Cannot find port for lane: %s\n",
+		    lane->name);
+	}
+	reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+	reg |= USB2_BIAS_PAD_CTL0_PD;
+	WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
+
+	if (port != NULL && port->supply_vbus != NULL) {
+		rv = regulator_enable(port->supply_vbus);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot disable vbus regulator\n");
+			return (rv);
+		}
+	}
+	return (0);
+}
+
+
+static int
+phy_powerup(struct padctl_softc *sc)
+{
+	uint32_t reg;
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	return (0);
+}
+
+static int
+phy_powerdown(struct padctl_softc *sc)
+{
+	uint32_t reg;
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
+	reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+	WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
+	DELAY(100);
+
+	return (0);
+}
+
+static int
+xusbpadctl_phy_enable(struct phynode *phy, bool enable)
+{
+	device_t dev;
+	intptr_t id;
+	struct padctl_softc *sc;
+	struct padctl_lane *lane;
+	struct padctl_pad *pad;
+	int rv;
+
+	dev = phynode_get_device(phy);
+	id = phynode_get_id(phy);
+	sc = device_get_softc(dev);
+
+	if (id < 0 || id >= nitems(lanes_tbl)) {
+		device_printf(dev, "Unknown phy: %d\n", id);
+		return (ENXIO);
+	}
+	lane = lanes_tbl + id;
+	if (!lane->enabled) {
+		device_printf(dev, "Lane is not enabled/configured: %s\n",
+		    lane->name);
+		return (ENXIO);
+	}
+	pad = lane->pad;
+	if (enable) {
+		if (sc->phy_ena_cnt == 0) {
+			rv = phy_powerup(sc);
+			if (rv != 0)
+				return (rv);
+		}
+		sc->phy_ena_cnt++;
+	}
+
+	if (enable)
+		rv = pad->powerup(sc, lane);
+	else
+		rv = pad->powerdown(sc, lane);
+	if (rv != 0)
+		return (rv);
+
+	if (!enable) {
+		 if (sc->phy_ena_cnt == 1) {
+			rv = phy_powerdown(sc);
+			if (rv != 0)
+				return (rv);
+		}
+		sc->phy_ena_cnt--;
+	}
+
+	return (0);
+}
+
+/* -------------------------------------------------------------------------
+ *
+ *   FDT processing
+ */
+static struct padctl_port *
+search_port(struct padctl_softc *sc, char *port_name)
+{
+	int i;
+
+	for (i = 0; i < nitems(ports_tbl); i++) {
+		if (strcmp(port_name, ports_tbl[i].name) == 0)
+			return (&ports_tbl[i]);
+	}
+	return (NULL);
+}
+
+static struct padctl_port *
+search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	int i;
+
+	for (i = 0; i < nitems(ports_tbl); i++) {
+		if (!ports_tbl[i].enabled)
+			continue;
+		if (ports_tbl[i].lane == lane)
+			return (ports_tbl + i);
+	}
+	return (NULL);
+}
+
+static struct padctl_lane *
+search_lane(struct padctl_softc *sc, char *lane_name)
+{
+	int i;
+
+	for (i = 0; i < nitems(lanes_tbl); i++) {
+		if (strcmp(lane_name, lanes_tbl[i].name) == 0)
+			return 	(lanes_tbl + i);
+	}
+	return (NULL);
+}
+
+static struct padctl_lane *
+search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx)
+{
+	int i;
+
+	for (i = 0; i < nitems(lanes_tbl); i++) {
+		if (!lanes_tbl[i].enabled)
+			continue;
+		if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx)
+			return 	(lanes_tbl + i);
+	}
+	return (NULL);
+}
+
+static struct padctl_lane *
+search_usb3_pad_lane(struct padctl_softc *sc, int idx)
+{
+	int i;
+	struct padctl_lane *lane, *tmp;
+
+	lane = NULL;
+	for (i = 0; i < nitems(lane_map_tbl); i++) {
+		if (idx != lane_map_tbl[i].port_idx)
+			continue;
+		tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type,
+		    lane_map_tbl[i].lane_idx);
+		if (tmp == NULL)
+			continue;
+		if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0)
+			continue;
+		if (lane != NULL) {
+			device_printf(sc->dev, "Duplicated mappings found for"
+			 " lanes: %s and %s\n", lane->name, tmp->name);
+			return (NULL);
+		}
+		lane = tmp;
+	}
+	return (lane);
+}
+
+static struct padctl_pad *
+search_pad(struct padctl_softc *sc, char *pad_name)
+{
+	int i;
+
+	for (i = 0; i < nitems(pads_tbl); i++) {
+		if (strcmp(pad_name, pads_tbl[i].name) == 0)
+			return 	(pads_tbl + i);
+	}
+	return (NULL);
+}
+
+static int
+search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name)
+{
+	int i;
+
+	for (i = 0; i < lane->nmux; i++) {
+		if (strcmp(fnc_name, lane->mux[i]) == 0)
+			return 	(i);
+	}
+	return (-1);
+}
+
+static int
+config_lane(struct padctl_softc *sc, struct padctl_lane *lane)
+{
+	uint32_t reg;
+
+	reg = RD4(sc, lane->reg);
+	reg &= ~(lane->mask << lane->shift);
+	reg |=  (lane->mux_idx & lane->mask) << lane->shift;
+	WR4(sc, lane->reg, reg);
+	return (0);
+}
+
+static int
+process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad)
+{
+	struct padctl_lane *lane;
+	struct phynode *phynode;
+	struct phynode_init_def phy_init;
+	char *name;
+	char *function;
+	int rv;
+
+	name = NULL;
+	function = NULL;
+	rv = OF_getprop_alloc(node, "name", 1, (void **)&name);
+	if (rv <= 0) {
+		device_printf(sc->dev, "Cannot read lane name.\n");
+		return (ENXIO);
+	}
+
+	lane = search_lane(sc, name);
+	if (lane == NULL) {
+		device_printf(sc->dev, "Unknown lane: %s\n", name);
+		rv = ENXIO;
+		goto end;
+	}
+
+	/* Read function (mux) settings. */
+	rv = OF_getprop_alloc(node, "nvidia,function", 1, (void **)&function);
+	if (rv <= 0) {
+		device_printf(sc->dev, "Cannot read lane function.\n");
+		rv = ENXIO;
+		goto end;
+	}
+
+	lane->mux_idx = search_mux(sc, lane, function);
+	if (lane->mux_idx == ~0) {
+		device_printf(sc->dev, "Unknown function %s for lane %s\n",
+		    function, name);
+		rv = ENXIO;
+		goto end;
+	}
+
+	rv = config_lane(sc, lane);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot configure lane: %s: %d\n",
+		    name, rv);
+		rv = ENXIO;
+		goto end;
+	}
+	lane->pad = pad;
+	lane->enabled = true;
+	pad->lanes[pad->nlanes++] = lane;
+
+	/* Create and register phy. */
+	bzero(&phy_init, sizeof(phy_init));
+	phy_init.id = lane - lanes_tbl;
+	phy_init.ofw_node = node;
+	phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init);
+	if (phynode == NULL) {
+		device_printf(sc->dev, "Cannot create phy\n");
+		rv = ENXIO;
+		goto end;
+	}
+	if (phynode_register(phynode) == NULL) {
+		device_printf(sc->dev, "Cannot create phy\n");
+		return (ENXIO);
+	}
+
+	rv = 0;
+
+end:
+	if (name != NULL)
+		OF_prop_free(name);
+	if (function != NULL)
+		OF_prop_free(function);
+	return (rv);
+}
+
+static int
+process_pad(struct padctl_softc *sc, phandle_t node)
+{
+	struct padctl_pad *pad;
+	char *name;
+	int rv;
+
+	name = NULL;
+	rv = OF_getprop_alloc(node, "name", 1, (void **)&name);
+	if (rv <= 0) {
+		device_printf(sc->dev, "Cannot read pad name.\n");
+		return (ENXIO);
+	}
+	pad = search_pad(sc, name);
+	if (pad == NULL) {
+		device_printf(sc->dev, "Unknown pad: %s\n", name);
+		rv = ENXIO;
+		goto end;
+	}
+
+	/* Read and process associated lanes. */
+	node = ofw_bus_find_child(node, "lanes");
+	if (node <= 0) {
+		device_printf(sc->dev, "Cannot find regulators subnode\n");
+		rv = ENXIO;
+		goto end;
+	}
+
+	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+		if (!fdt_is_enabled(node))
+			continue;
+
+		rv = process_lane(sc, node, pad);
+		if (rv != 0)
+			goto end;
+	}
+	pad->enabled = true;
+	rv = 0;
+end:
+	if (name != NULL)
+		OF_prop_free(name);
+	return (rv);
+}
+
+static int
+process_port(struct padctl_softc *sc, phandle_t node)
+{
+
+	struct padctl_port *port;
+	char *name;
+	int rv;
+
+	name = NULL;
+	rv = OF_getprop_alloc(node, "name", 1, (void **)&name);
+	if (rv <= 0) {
+		device_printf(sc->dev, "Cannot read port name.\n");
+		return (ENXIO);
+	}
+
+	port = search_port(sc, name);
+	if (port == NULL) {
+		device_printf(sc->dev, "Unknown port: %s\n", name);
+		rv = ENXIO;
+		goto end;
+	}
+
+	if (port->type == PADCTL_PORT_USB3) {
+		rv = OF_getencprop(node,  "nvidia,usb2-companion",
+		   &(port->companion), sizeof(port->companion));
+		if (rv <= 0) {
+			device_printf(sc->dev,
+			    "Missing 'nvidia,usb2-companion' property "
+			    "for port: %s\n", name);
+			rv = ENXIO;
+			goto end;
+		}
+	}
+
+	if (OF_hasprop(node, "vbus-supply")) {
+		rv = regulator_get_by_ofw_property(sc->dev, 0,
+		    "vbus-supply", &port->supply_vbus);
+		if (rv <= 0) {
+			device_printf(sc->dev,
+			    "Cannot get 'vbus-supply' regulator "
+			    "for port: %s\n", name);
+			rv = ENXIO;
+			goto end;
+		}
+	}
+
+	if (OF_hasprop(node, "nvidia,internal"))
+		port->internal = true;
+	/* Find assigned lane */
+	if (port->lane == NULL) {
+		switch(port->type) {
+		/* Routing is fixed for USB2, ULPI AND HSIC. */
+		case PADCTL_PORT_USB2:
+			port->lane = search_pad_lane(sc, PADCTL_PAD_USB2,
+			    port->idx);
+			break;
+		case PADCTL_PORT_ULPI:
+			port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI,
+			    port->idx);
+			break;
+		case PADCTL_PORT_HSIC:
+			port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC,
+			    port->idx);
+			break;
+		case PADCTL_PORT_USB3:
+			port->lane = search_usb3_pad_lane(sc, port->idx);
+			break;
+		}
+	}
+	if (port->lane == NULL) {
+		device_printf(sc->dev, "Cannot find lane for port: %s\n", name);
+		rv = ENXIO;
+		goto end;
+	}
+	port->enabled = true;
+	rv = 0;
+end:
+	if (name != NULL)
+		OF_prop_free(name);
+	return (rv);
+}
+
+static int
+parse_fdt(struct padctl_softc *sc, phandle_t base_node)
+{
+	phandle_t node;
+	int rv;
+
+	rv = 0;
+	node = ofw_bus_find_child(base_node, "pads");
+
+	if (node <= 0) {
+		device_printf(sc->dev, "Cannot find pads subnode.\n");
+		return (ENXIO);
+	}
+	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+		if (!fdt_is_enabled(node))
+			continue;
+		rv = process_pad(sc, node);
+		if (rv != 0)
+			return (rv);
+	}
+
+	node = ofw_bus_find_child(base_node, "ports");
+	if (node <= 0) {
+		device_printf(sc->dev, "Cannot find ports subnode.\n");
+		return (ENXIO);
+	}
+	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+		if (!fdt_is_enabled(node))
+			continue;
+		rv = process_port(sc, node);
+		if (rv != 0)
+			return (rv);
+	}
+
+	return (0);
+}
+
+static void
+load_calibration(struct padctl_softc *sc)
+{
+	uint32_t reg;
+
+	/* All XUSB pad calibrations are packed into single dword.*/
+	reg = tegra_fuse_read_4(FUSE_XUSB_CALIB);
+	sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg);
+	sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg);
+	sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg);
+	sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg);
+	sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg);
+}
+
+/* -------------------------------------------------------------------------
+ *
+ *   BUS functions
+ */
+static int
+xusbpadctl_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra XUSB phy");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+xusbpadctl_detach(device_t dev)
+{
+
+	/* This device is always present. */
+	return (EBUSY);
+}
+
+static int
+xusbpadctl_attach(device_t dev)
+{
+	struct padctl_softc * sc;
+	int i, rid, rv;
+	struct padctl_port *port;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv);
+		return (rv);
+	}
+	rv = hwreset_deassert(sc->rst);
+	if (rv != 0) {
+		device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv);
+		return (rv);
+	}
+
+	load_calibration(sc);
+
+	rv = parse_fdt(sc, node);
+	if (rv != 0) {
+		device_printf(dev, "Cannot parse fdt configuration: %d\n", rv);
+		return (rv);
+	}
+	for (i = 0; i < nitems(ports_tbl); i++) {
+		port = ports_tbl + i;
+		if (!port->enabled)
+			continue;
+		if (port->init == NULL)
+			continue;
+		rv = port->init(sc, port);
+		if (rv != 0) {
+			device_printf(dev, "Cannot init port '%s'\n",
+			    port->name);
+			return (rv);
+		}
+	}
+	return (0);
+}
+
+static device_method_t tegra_xusbpadctl_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,         xusbpadctl_probe),
+	DEVMETHOD(device_attach,        xusbpadctl_attach),
+	DEVMETHOD(device_detach,        xusbpadctl_detach),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_xusbpadctl_devclass;
+static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver,
+    tegra_xusbpadctl_methods, sizeof(struct padctl_softc));
+EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver,
+    tegra_xusbpadctl_devclass, NULL, NULL, 73);


Property changes on: trunk/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_abpmisc.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_abpmisc.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_abpmisc.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,195 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_abpmisc.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * SoC misc configuration and indentification driver.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+#define	PMC_STRAPPING_OPT_A	0  	/* 0x464 */
+
+#define	PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT	4
+#define	PMC_STRAPPING_OPT_A_RAM_CODE_MASK_LONG	\
+	(0xf << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT)
+#define	PMC_STRAPPING_OPT_A_RAM_CODE_MASK_SHORT	\
+	(0x3 << PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT)
+
+
+#define	ABP_RD4(_sc, _r)	bus_read_4((_sc)->abp_misc_res, (_r))
+#define	STR_RD4(_sc, _r)	bus_read_4((_sc)->strap_opt_res, (_r))
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-apbmisc",	1},
+	{NULL,				0}
+};
+
+struct tegra_abpmisc_softc {
+	device_t		dev;
+
+	struct resource		*abp_misc_res;
+	struct resource		*strap_opt_res;
+};
+
+static struct tegra_abpmisc_softc *dev_sc;
+
+static void
+tegra_abpmisc_read_revision(struct tegra_abpmisc_softc *sc)
+{
+	uint32_t id, chip_id, minor_rev;
+	int rev;
+
+	id = ABP_RD4(sc, 4);
+	chip_id = (id >> 8) & 0xff;
+	minor_rev = (id >> 16) & 0xf;
+
+	switch (minor_rev) {
+	case 1:
+		rev = TEGRA_REVISION_A01;
+		break;
+	case 2:
+		rev = TEGRA_REVISION_A02;
+		break;
+	case 3:
+		rev = TEGRA_REVISION_A03;
+		break;
+	case 4:
+		rev = TEGRA_REVISION_A04;
+		break;
+	default:
+		rev = TEGRA_REVISION_UNKNOWN;
+	}
+
+	tegra_sku_info.chip_id = chip_id;
+	tegra_sku_info.revision = rev;
+}
+
+static int
+tegra_abpmisc_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_abpmisc_attach(device_t dev)
+{
+	int rid;
+	struct tegra_abpmisc_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	rid = 0;
+	sc->abp_misc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	if (sc->abp_misc_res == NULL) {
+		device_printf(dev, "Cannot map ABP misc registers.\n");
+		goto fail;
+	}
+
+	rid = 1;
+	sc->strap_opt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->strap_opt_res == NULL) {
+		device_printf(dev, "Cannot map strapping options registers.\n");
+		goto fail;
+	}
+
+	tegra_abpmisc_read_revision(sc);
+
+	/* XXX - Hack - address collision with pinmux. */
+	if (sc->abp_misc_res != NULL) {
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res);
+		sc->abp_misc_res = NULL;
+	}
+
+	dev_sc = sc;
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->abp_misc_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res);
+	if (sc->strap_opt_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res);
+
+	return (ENXIO);
+}
+
+static int
+tegra_abpmisc_detach(device_t dev)
+{
+	struct tegra_abpmisc_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->abp_misc_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->abp_misc_res);
+	if (sc->strap_opt_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->strap_opt_res);
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_abpmisc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_abpmisc_probe),
+	DEVMETHOD(device_attach,	tegra_abpmisc_attach),
+	DEVMETHOD(device_detach,	tegra_abpmisc_detach),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_abpmisc_devclass;
+static DEFINE_CLASS_0(abpmisc, tegra_abpmisc_driver, tegra_abpmisc_methods,
+    sizeof(struct tegra_abpmisc_softc));
+EARLY_DRIVER_MODULE(tegra_abpmisc, simplebus, tegra_abpmisc_driver,
+    tegra_abpmisc_devclass, NULL, NULL, BUS_PASS_TIMER);


Property changes on: trunk/sys/arm/nvidia/tegra_abpmisc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_ahci.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_ahci.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_ahci.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,626 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_ahci.c 332025 2018-04-04 13:23:06Z mmel $");
+
+/*
+ * AHCI driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ahci/ahci.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+#include <arm/nvidia/tegra_pmc.h>
+
+#define	AHCI_WR4(_sc, _r, _v)	bus_write_4((_sc)->ctlr.r_mem, (_r), (_v))
+#define	AHCI_RD4(_sc, _r)	bus_read_4((_sc)->ctlr.r_mem, (_r))
+#define	SATA_WR4(_sc, _r, _v)	bus_write_4((_sc)->sata_mem, (_r), (_v))
+#define	SATA_RD4(_sc, _r)	bus_read_4((_sc)->sata_mem, (_r))
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-ahci", 	1},
+	{NULL,			0}
+};
+
+struct tegra_ahci_sc {
+	struct ahci_controller	ctlr;	/* Must be first */
+	device_t		dev;
+	struct resource		*sata_mem;
+	clk_t			clk_sata;
+	clk_t			clk_sata_oob;
+	clk_t			clk_pll_e;
+	clk_t			clk_cml;
+	hwreset_t		hwreset_sata;
+	hwreset_t		hwreset_sata_oob;
+	hwreset_t		hwreset_sata_cold;
+	regulator_t		supply_hvdd;
+	regulator_t		supply_vddio;
+	regulator_t		supply_avdd;
+	regulator_t		supply_target_5v;
+	regulator_t		supply_target_12v;
+	phy_t			phy;
+};
+
+struct sata_pad_calibration {
+	uint32_t gen1_tx_amp;
+	uint32_t gen1_tx_peak;
+	uint32_t gen2_tx_amp;
+	uint32_t gen2_tx_peak;
+};
+
+static const struct sata_pad_calibration tegra124_pad_calibration[] = {
+	{0x18, 0x04, 0x18, 0x0a},
+	{0x0e, 0x04, 0x14, 0x0a},
+	{0x0e, 0x07, 0x1a, 0x0e},
+	{0x14, 0x0e, 0x1a, 0x0e},
+};
+
+#define	SATA_CONFIGURATION			0x180
+#define	 SATA_CONFIGURATION_EN_FPCI			(1 << 0)
+
+#define	SATA_FPCI_BAR5				0x94
+#define	 SATA_FPCI_BAR5_START_SHIFT			4
+
+#define	SATA_INTR_MASK				0x188
+#define	SATA_INTR_MASK_IP_INT_MASK			(1 << 16)
+
+#define	SCFG_OFFSET				0x1000
+
+#define	T_SATA0_CFG_1				0x04
+#define	 T_SATA0_CFG_1_IO_SPACE				(1 << 0)
+#define	 T_SATA0_CFG_1_MEMORY_SPACE			(1 << 1)
+#define	 T_SATA0_CFG_1_BUS_MASTER			(1 << 2)
+#define	 T_SATA0_CFG_1_SERR				(1 << 8)
+
+#define	T_SATA0_CFG_9				0x24
+#define	 T_SATA0_CFG_9_BASE_ADDRESS_SHIFT		13
+
+#define	T_SATA0_AHCI_HBA_CAP_BKDR		0x300
+#define	T_SATA0_BKDOOR_CC			0x4a4
+#define	T_SATA0_CFG_SATA			0x54c
+#define	 T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN		(1 << 12)
+
+#define	T_SATA0_CFG_MISC			0x550
+#define	T_SATA0_INDEX				0x680
+
+#define	T_SATA0_CHX_PHY_CTRL1_GEN1		0x690
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK	0xff
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT	8
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK		0xff
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT	0
+
+
+#define	T_SATA0_CHX_PHY_CTRL1_GEN2		0x694
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK	0xff
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT	12
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK		0xff
+#define	 T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT	0
+
+#define	T_SATA0_CHX_PHY_CTRL2			0x69c
+#define	 T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1		0x23
+
+#define	T_SATA0_CHX_PHY_CTRL11			0x6d0
+#define	 T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ		(0x2800 << 16)
+
+#define	FUSE_SATA_CALIB				0x124
+#define	FUSE_SATA_CALIB_MASK			0x3
+
+
+#define	SATA_AUX_MISC_CNTL			0x1108
+#define	SATA_AUX_PAD_PLL_CTRL_0			0x1120
+#define	SATA_AUX_PAD_PLL_CTRL_1			0x1124
+#define	SATA_AUX_PAD_PLL_CTRL_2			0x1128
+#define	SATA_AUX_PAD_PLL_CTRL_3			0x112c
+
+#define	T_AHCI_HBA_CCC_PORTS			0x0018
+#define	T_AHCI_HBA_CAP_BKDR			0x00A0
+#define	 T_AHCI_HBA_CAP_BKDR_S64A			(1 << 31)
+#define	 T_AHCI_HBA_CAP_BKDR_SNCQ			(1 << 30)
+#define	 T_AHCI_HBA_CAP_BKDR_SSNTF			(1 << 29)
+#define	 T_AHCI_HBA_CAP_BKDR_SMPS			(1 << 28)
+#define	 T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP		(1 << 27)
+#define	 T_AHCI_HBA_CAP_BKDR_SALP			(1 << 26)
+#define	 T_AHCI_HBA_CAP_BKDR_SAL			(1 << 25)
+#define	 T_AHCI_HBA_CAP_BKDR_SUPP_CLO			(1 << 24)
+#define	 T_AHCI_HBA_CAP_BKDR_INTF_SPD_SUPP(x)		(((x) & 0xF) << 20)
+#define	 T_AHCI_HBA_CAP_BKDR_SUPP_NONZERO_OFFSET	(1 << 19)
+#define	 T_AHCI_HBA_CAP_BKDR_SUPP_AHCI_ONLY		(1 << 18)
+#define	 T_AHCI_HBA_CAP_BKDR_SUPP_PM			(1 << 17)
+#define	 T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING		(1 << 16)
+#define	 T_AHCI_HBA_CAP_BKDR_PIO_MULT_DRQ_BLK		(1 << 15)
+#define	 T_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP		(1 << 14)
+#define	 T_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP		(1 << 13)
+#define	 T_AHCI_HBA_CAP_BKDR_NUM_CMD_SLOTS(x)		(((x) & 0x1F) <<  8)
+#define	 T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING		(1 <<  7)
+#define	 T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP		(1 <<  6)
+#define	 T_AHCI_HBA_CAP_BKDR_EXT_SATA			(1 <<  5)
+#define	 T_AHCI_HBA_CAP_BKDR_NUM_PORTS(x)		(((x) & 0xF) <<  0)
+
+#define	T_AHCI_PORT_BKDR			0x0170
+
+#define	 T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE_VAL(x)	(((x) & 0xFF) << 24)
+#define	 T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE_VAL(x)	(((x) & 0x1F) << 16)
+#define	 T_AHCI_PORT_BKDR_PXDEVSLP_DETO_OVERRIDE	(1 << 15)
+#define	 T_AHCI_PORT_BKDR_PXDEVSLP_MDAT_OVERRIDE	(1 << 14)
+#define	 T_AHCI_PORT_BKDR_PXDEVSLP_DM(x)		(((x) & 0xF) << 10)
+#define	 T_AHCI_PORT_BKDR_PORT_UNCONNECTED		(1 <<  9)
+#define	 T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_CLAMP_THIS_CH	(1 <<  8)
+#define	 T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_UNCLAMP (1 <<  7)
+#define	 T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_TXRXCLK_CLAMP	(1 <<  6)
+#define	 T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_UNCLAMP	(1 <<  5)
+#define	 T_AHCI_PORT_BKDR_CLK_CLAMP_CTRL_DEVCLK_CLAMP	(1 <<  4)
+#define	 T_AHCI_PORT_BKDR_HOTPLUG_CAP			(1 <<  3)
+#define	 T_AHCI_PORT_BKDR_MECH_SWITCH			(1 <<  2)
+#define	 T_AHCI_PORT_BKDR_COLD_PRSN_DET			(1 <<  1)
+#define	 T_AHCI_PORT_BKDR_EXT_SATA_SUPP			(1 <<  0)
+
+static int
+get_fdt_resources(struct tegra_ahci_sc *sc, phandle_t node)
+{
+	int rv;
+
+
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "hvdd-supply",
+	    &sc->supply_hvdd );
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'hvdd' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "vddio-supply",
+	    &sc->supply_vddio);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'vddio' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-supply",
+	    &sc->supply_avdd);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'avdd' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "target-5v-supply",
+	    &sc->supply_target_5v);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'target-5v' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "target-12v-supply",
+	    &sc->supply_target_12v);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'target-12v' regulator\n");
+		return (ENXIO);
+	}
+
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata", &sc->hwreset_sata );
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sata' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata-oob",
+	    &sc->hwreset_sata_oob);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sata oob' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "sata-cold",
+	    &sc->hwreset_sata_cold);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sata cold' reset\n");
+		return (ENXIO);
+	}
+
+	rv = phy_get_by_ofw_name(sc->dev, 0, "sata-0", &sc->phy);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sata' phy\n");
+		return (ENXIO);
+	}
+
+	rv = clk_get_by_ofw_name(sc->dev, 0, "sata", &sc->clk_sata);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sata' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "sata-oob", &sc->clk_sata_oob);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sata oob' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "cml1", &sc->clk_cml);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'cml1' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "pll_e", &sc->clk_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pll_e' clock\n");
+		return (ENXIO);
+	}
+	return (0);
+}
+
+static int
+enable_fdt_resources(struct tegra_ahci_sc *sc)
+{
+	int rv;
+
+	rv = regulator_enable(sc->supply_hvdd);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable  'hvdd' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_vddio);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable  'vddio' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable  'avdd' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_target_5v);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable  'target-5v' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_target_12v);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable  'sc->target-12v' regulator\n");
+		return (rv);
+	}
+
+	/* Stop clocks */
+	clk_stop(sc->clk_sata);
+	clk_stop(sc->clk_sata_oob);
+	tegra_powergate_power_off(TEGRA_POWERGATE_SAX);
+
+	rv = hwreset_assert(sc->hwreset_sata);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert 'sata' reset\n");
+		return (rv);
+	}
+	rv = hwreset_assert(sc->hwreset_sata_oob);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert 'sata oob' reset\n");
+		return (rv);
+	}
+
+	rv = hwreset_assert(sc->hwreset_sata_cold);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert 'sata cold' reset\n");
+		return (rv);
+	}
+	rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SAX,
+	    sc->clk_sata, sc->hwreset_sata);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'SAX' powergate\n");
+		return (rv);
+	}
+
+	rv = clk_enable(sc->clk_sata_oob);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'sata oob' clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_cml);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'cml' clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'pll e' clock\n");
+		return (rv);
+	}
+
+	rv = hwreset_deassert(sc->hwreset_sata_cold);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot unreset 'sata cold' reset\n");
+		return (rv);
+	}
+	rv = hwreset_deassert(sc->hwreset_sata_oob);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot unreset 'sata oob' reset\n");
+		return (rv);
+	}
+
+	rv = phy_enable(sc->phy);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable SATA phy\n");
+		return (rv);
+	}
+
+	return (0);
+}
+
+static int
+tegra_ahci_ctrl_init(struct tegra_ahci_sc *sc)
+{
+	uint32_t val;
+	const struct sata_pad_calibration *calib;
+
+	val = SATA_RD4(sc, SATA_CONFIGURATION);
+	val |= SATA_CONFIGURATION_EN_FPCI;
+	SATA_WR4(sc, SATA_CONFIGURATION, val);
+
+
+	/* Pad calibration. */
+	val = tegra_fuse_read_4(FUSE_SATA_CALIB);
+	calib = tegra124_pad_calibration + (val & FUSE_SATA_CALIB_MASK);
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 1);
+
+	val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1);
+	val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK <<
+	    T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT);
+	val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK <<
+	    T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT);
+	val |= calib->gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT;
+	val |= calib->gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT;
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1, val);
+
+	val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2);
+	val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK <<
+	    T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT);
+	val &= ~(T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK <<
+	    T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT);
+	val |= calib->gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_SHIFT;
+	val |= calib->gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_SHIFT;
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2, val);
+
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11,
+	    T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ);
+
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2,
+	    T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1);
+
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_INDEX, 0);
+
+	/* Set device ID. */
+	val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA);
+	val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN;
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val);
+
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_BKDOOR_CC, 0x01060100);
+
+	val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA);
+	val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN;
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_SATA, val);
+
+	/* Enable IO & memory access, bus master mode */
+	val = SATA_RD4(sc, SCFG_OFFSET + T_SATA0_CFG_1);
+	val |= T_SATA0_CFG_1_IO_SPACE;
+	val |= T_SATA0_CFG_1_MEMORY_SPACE;
+	val |= T_SATA0_CFG_1_BUS_MASTER;
+	val |= T_SATA0_CFG_1_SERR;
+	SATA_WR4(sc, SCFG_OFFSET + T_SATA0_CFG_1, val);
+
+	/* SATA MMIO. */
+	SATA_WR4(sc, SATA_FPCI_BAR5, 0x10000 << SATA_FPCI_BAR5_START_SHIFT);
+	/* AHCI bar */
+	SATA_WR4(sc,  SCFG_OFFSET + T_SATA0_CFG_9,
+	    0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT);
+
+	/* Unmask  interrupts. */
+	val = SATA_RD4(sc, SATA_INTR_MASK);
+	val |= SATA_INTR_MASK_IP_INT_MASK;
+	SATA_WR4(sc, SATA_INTR_MASK, val);
+
+	return (0);
+}
+
+static int
+tegra_ahci_ctlr_reset(device_t dev)
+{
+	struct tegra_ahci_sc *sc;
+	int rv;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+	rv = ahci_ctlr_reset(dev);
+	if (rv != 0)
+		return (0);
+	AHCI_WR4(sc, T_AHCI_HBA_CCC_PORTS, 1);
+
+	/* Overwrite AHCI capabilites. */
+	reg  = AHCI_RD4(sc, T_AHCI_HBA_CAP_BKDR);
+	reg &= ~T_AHCI_HBA_CAP_BKDR_NUM_PORTS(~0);
+	reg |= T_AHCI_HBA_CAP_BKDR_NUM_PORTS(0);
+	reg |= T_AHCI_HBA_CAP_BKDR_EXT_SATA;
+	reg |= T_AHCI_HBA_CAP_BKDR_ENCL_MGMT_SUPP;
+	reg |= T_AHCI_HBA_CAP_BKDR_CMD_CMPL_COALESING;
+	reg |= T_AHCI_HBA_CAP_BKDR_FIS_SWITCHING;
+	reg |= T_AHCI_HBA_CAP_BKDR_SUPP_PM;
+	reg |= T_AHCI_HBA_CAP_BKDR_SUPP_CLO;
+	reg |= T_AHCI_HBA_CAP_BKDR_SUPP_STG_SPUP;
+	AHCI_WR4(sc, T_AHCI_HBA_CAP_BKDR, reg);
+
+	/* Overwrite AHCI portcapabilites. */
+	reg  = AHCI_RD4(sc, T_AHCI_PORT_BKDR);
+	reg |= T_AHCI_PORT_BKDR_COLD_PRSN_DET;
+	reg |= T_AHCI_PORT_BKDR_HOTPLUG_CAP;
+	reg |= T_AHCI_PORT_BKDR_EXT_SATA_SUPP;
+	AHCI_WR4(sc, T_AHCI_PORT_BKDR, reg);
+
+	return (0);
+}
+
+static int
+tegra_ahci_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc_copy(dev, "AHCI SATA controller");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_ahci_attach(device_t dev)
+{
+	struct tegra_ahci_sc *sc;
+	struct ahci_controller *ctlr;
+	phandle_t node;
+	int rv, rid;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	ctlr = &sc->ctlr;
+	node = ofw_bus_get_node(dev);
+
+	ctlr->r_rid = 0;
+	ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+	    &ctlr->r_rid, RF_ACTIVE);
+	if (ctlr->r_mem == NULL)
+		return (ENXIO);
+
+	rid = 1;
+	sc->sata_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+	    &rid, RF_ACTIVE);
+	if (sc->sata_mem == NULL) {
+		rv = ENXIO;
+		goto fail;
+	}
+	rv = get_fdt_resources(sc, node);
+	if (rv != 0) {
+		device_printf(sc->dev, "Failed to allocate FDT resource(s)\n");
+		goto fail;
+	}
+
+	rv = enable_fdt_resources(sc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Failed to enable FDT resource(s)\n");
+		goto fail;
+	}
+	rv = tegra_ahci_ctrl_init(sc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Failed to initialize controller)\n");
+		goto fail;
+	}
+
+	/* Setup controller defaults. */
+	ctlr->msi = 0;
+	ctlr->numirqs = 1;
+	ctlr->ccc = 0;
+
+	/* Reset controller. */
+	rv = tegra_ahci_ctlr_reset(dev);
+	if (rv != 0)
+		goto fail;
+	rv = ahci_attach(dev);
+	return (rv);
+
+fail:
+	/* XXX FDT  stuff */
+	if (sc->sata_mem != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->sata_mem);
+	if (ctlr->r_mem != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid,
+		    ctlr->r_mem);
+	return (rv);
+}
+
+static int
+tegra_ahci_detach(device_t dev)
+{
+
+	ahci_detach(dev);
+	return (0);
+}
+
+static int
+tegra_ahci_suspend(device_t dev)
+{
+	struct tegra_ahci_sc *sc = device_get_softc(dev);
+
+	bus_generic_suspend(dev);
+	/* Disable interupts, so the state change(s) doesn't trigger. */
+	ATA_OUTL(sc->ctlr.r_mem, AHCI_GHC,
+	     ATA_INL(sc->ctlr.r_mem, AHCI_GHC) & (~AHCI_GHC_IE));
+	return (0);
+}
+
+static int
+tegra_ahci_resume(device_t dev)
+{
+	int res;
+
+	if ((res = tegra_ahci_ctlr_reset(dev)) != 0)
+		return (res);
+	ahci_ctlr_setup(dev);
+	return (bus_generic_resume(dev));
+}
+
+static device_method_t tegra_ahci_methods[] = {
+	DEVMETHOD(device_probe,		tegra_ahci_probe),
+	DEVMETHOD(device_attach,	tegra_ahci_attach),
+	DEVMETHOD(device_detach,	tegra_ahci_detach),
+	DEVMETHOD(device_suspend,	tegra_ahci_suspend),
+	DEVMETHOD(device_resume,	tegra_ahci_resume),
+	DEVMETHOD(bus_print_child,	ahci_print_child),
+	DEVMETHOD(bus_alloc_resource,	ahci_alloc_resource),
+	DEVMETHOD(bus_release_resource,	ahci_release_resource),
+	DEVMETHOD(bus_setup_intr,	ahci_setup_intr),
+	DEVMETHOD(bus_teardown_intr,	ahci_teardown_intr),
+	DEVMETHOD(bus_child_location_str, ahci_child_location_str),
+	DEVMETHOD(bus_get_dma_tag,	ahci_get_dma_tag),
+
+	DEVMETHOD_END
+};
+
+static DEFINE_CLASS_0(ahci, tegra_ahci_driver, tegra_ahci_methods,
+    sizeof(struct tegra_ahci_sc));
+DRIVER_MODULE(tegra_ahci, simplebus, tegra_ahci_driver, ahci_devclass,
+    NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/tegra_ahci.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_efuse.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_efuse.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_efuse.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,368 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2015 Michal Meloun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_efuse.c 314506 2017-03-01 19:55:04Z ian $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+
+
+#define	RD4(_sc, _r)	bus_read_4((_sc)->mem_res, (_sc)->fuse_begin + (_r))
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-efuse",	1},
+	{NULL,			0}
+};
+
+struct tegra_efuse_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+
+	int			fuse_begin;
+	clk_t			clk;
+	hwreset_t			reset;
+};
+struct tegra_efuse_softc *dev_sc;
+
+struct tegra_sku_info tegra_sku_info;
+static char *tegra_rev_name[] = {
+	[TEGRA_REVISION_UNKNOWN] = "unknown",
+	[TEGRA_REVISION_A01]     = "A01",
+	[TEGRA_REVISION_A02]     = "A02",
+	[TEGRA_REVISION_A03]     = "A03",
+	[TEGRA_REVISION_A03p]    = "A03 prime",
+	[TEGRA_REVISION_A04]     = "A04",
+};
+
+/* Tegra30 and later */
+#define	FUSE_VENDOR_CODE	0x100
+#define	FUSE_FAB_CODE		0x104
+#define	FUSE_LOT_CODE_0		0x108
+#define	FUSE_LOT_CODE_1		0x10c
+#define	FUSE_WAFER_ID		0x110
+#define	FUSE_X_COORDINATE	0x114
+#define	FUSE_Y_COORDINATE	0x118
+
+/* ---------------------- Tegra 124 specific code & data --------------- */
+#define	TEGRA124_FUSE_BEGIN		0x100
+
+#define	TEGRA124_CPU_PROCESS_CORNERS	2
+#define	TEGRA124_GPU_PROCESS_CORNERS	2
+#define	TEGRA124_SOC_PROCESS_CORNERS	2
+
+#define	TEGRA124_FUSE_SKU_INFO		0x10
+#define	TEGRA124_FUSE_CPU_SPEEDO_0	0x14
+#define	TEGRA124_FUSE_CPU_IDDQ		0x18
+#define	TEGRA124_FUSE_FT_REV		0x28
+#define	TEGRA124_FUSE_CPU_SPEEDO_1	0x2c
+#define	TEGRA124_FUSE_CPU_SPEEDO_2	0x30
+#define	TEGRA124_FUSE_SOC_SPEEDO_0	0x34
+#define	TEGRA124_FUSE_SOC_SPEEDO_1	0x38
+#define	TEGRA124_FUSE_SOC_SPEEDO_2	0x3c
+#define	TEGRA124_FUSE_SOC_IDDQ		0x40
+#define	TEGRA124_FUSE_GPU_IDDQ		0x128
+
+enum {
+	TEGRA124_THRESHOLD_INDEX_0,
+	TEGRA124_THRESHOLD_INDEX_1,
+	TEGRA124_THRESHOLD_INDEX_COUNT,
+};
+
+static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] =
+{
+	{2190,	UINT_MAX},
+	{0,	UINT_MAX},
+};
+
+static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] =
+{
+	{1965,	UINT_MAX},
+	{0,	UINT_MAX},
+};
+
+static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] =
+{
+	{2101,	UINT_MAX},
+	{0,	UINT_MAX},
+};
+
+static void
+tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc,
+    struct tegra_sku_info *sku, int *threshold)
+{
+
+	/* Assign to default */
+	sku->cpu_speedo_id = 0;
+	sku->soc_speedo_id = 0;
+	sku->gpu_speedo_id = 0;
+	*threshold = TEGRA124_THRESHOLD_INDEX_0;
+
+	switch (sku->sku_id) {
+	case 0x00: /* Eng sku */
+	case 0x0F:
+	case 0x23:
+		/* Using the default */
+		break;
+	case 0x83:
+		sku->cpu_speedo_id = 2;
+		break;
+
+	case 0x1F:
+	case 0x87:
+	case 0x27:
+		sku->cpu_speedo_id = 2;
+		sku->soc_speedo_id = 0;
+		sku->gpu_speedo_id = 1;
+		*threshold = TEGRA124_THRESHOLD_INDEX_0;
+		break;
+	case 0x81:
+	case 0x21:
+	case 0x07:
+		sku->cpu_speedo_id = 1;
+		sku->soc_speedo_id = 1;
+		sku->gpu_speedo_id = 1;
+		*threshold = TEGRA124_THRESHOLD_INDEX_1;
+		break;
+	case 0x49:
+	case 0x4A:
+	case 0x48:
+		sku->cpu_speedo_id = 4;
+		sku->soc_speedo_id = 2;
+		sku->gpu_speedo_id = 3;
+		*threshold = TEGRA124_THRESHOLD_INDEX_1;
+		break;
+	default:
+		device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id);
+		break;
+	}
+}
+
+
+static void
+tegra124_init_speedo(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku)
+{
+	int i, threshold;
+
+	sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO);
+	sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ);
+	sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ);
+	sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ);
+	sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0);
+	sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0);
+	sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2);
+
+	if (sku->cpu_speedo_value == 0) {
+		device_printf(sc->dev, "CPU Speedo value is not fused.\n");
+		return;
+	}
+
+	tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold);
+
+	for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) {
+		if (sku->soc_speedo_value <
+			tegra124_soc_process_speedos[threshold][i])
+			break;
+	}
+	sku->soc_process_id = i;
+
+	for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) {
+		if (sku->cpu_speedo_value <
+			tegra124_cpu_process_speedos[threshold][i])
+				break;
+	}
+	sku->cpu_process_id = i;
+
+	for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) {
+		if (sku->gpu_speedo_value <
+			tegra124_gpu_process_speedos[threshold][i])
+			break;
+	}
+	sku->gpu_process_id = i;
+
+}
+
+/* ----------------- End of Tegra 124 specific code & data --------------- */
+
+uint32_t
+tegra_fuse_read_4(int addr) {
+
+	if (dev_sc == NULL)
+		panic("tegra_fuse_read_4 called too early");
+	return (RD4(dev_sc, addr));
+}
+
+
+static void
+tegra_efuse_dump_sku(void)
+{
+	printf(" TEGRA SKU Info:\n");
+	printf("  chip_id: %u\n", tegra_sku_info.chip_id);
+	printf("  sku_id: %u\n", tegra_sku_info.sku_id);
+	printf("  cpu_process_id: %u\n", tegra_sku_info.cpu_process_id);
+	printf("  cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id);
+	printf("  cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value);
+	printf("  cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value);
+	printf("  soc_process_id: %u\n", tegra_sku_info.soc_process_id);
+	printf("  soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id);
+	printf("  soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value);
+	printf("  soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value);
+	printf("  gpu_process_id: %u\n", tegra_sku_info.gpu_process_id);
+	printf("  gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id);
+	printf("  gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value);
+	printf("  gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value);
+	printf("  revision: %s\n", tegra_rev_name[tegra_sku_info.revision]);
+}
+
+static int
+tegra_efuse_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_efuse_attach(device_t dev)
+{
+	int rv, rid;
+	phandle_t node;
+	struct tegra_efuse_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+
+	/* Get the memory resource for the register mapping. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot map registers.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* OFW resources. */
+	rv = clk_get_by_ofw_name(dev, 0, "fuse", &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get fuse clock: %d\n", rv);
+		goto fail;
+	}
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock: %d\n", rv);
+		goto fail;
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "fuse", &sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get fuse reset\n");
+		goto fail;
+	}
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot clear reset\n");
+		goto fail;
+	}
+
+	/* Tegra124 specific init. */
+	sc->fuse_begin = TEGRA124_FUSE_BEGIN;
+	tegra124_init_speedo(sc, &tegra_sku_info);
+
+	dev_sc = sc;
+
+	if (bootverbose)
+		tegra_efuse_dump_sku();
+	return (bus_generic_attach(dev));
+
+fail:
+	dev_sc = NULL;
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (sc->reset != NULL)
+		hwreset_release(sc->reset);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	return (rv);
+}
+
+static int
+tegra_efuse_detach(device_t dev)
+{
+	struct tegra_efuse_softc *sc;
+
+	sc = device_get_softc(dev);
+	dev_sc = NULL;
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (sc->reset != NULL)
+		hwreset_release(sc->reset);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_efuse_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_efuse_probe),
+	DEVMETHOD(device_attach,	tegra_efuse_attach),
+	DEVMETHOD(device_detach,	tegra_efuse_detach),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_efuse_devclass;
+static DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods,
+    sizeof(struct tegra_efuse_softc));
+EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver,
+    tegra_efuse_devclass, NULL, NULL, BUS_PASS_TIMER);


Property changes on: trunk/sys/arm/nvidia/tegra_efuse.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_efuse.h
===================================================================
--- trunk/sys/arm/nvidia/tegra_efuse.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_efuse.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,62 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/tegra_efuse.h 296936 2016-03-16 13:01:48Z mmel $
+ */
+
+#ifndef _TEGRA_EFUSE_H_
+
+enum tegra_revision {
+	TEGRA_REVISION_UNKNOWN = 0,
+	TEGRA_REVISION_A01,
+	TEGRA_REVISION_A02,
+	TEGRA_REVISION_A03,
+	TEGRA_REVISION_A03p,
+	TEGRA_REVISION_A04,
+};
+
+struct tegra_sku_info {
+	u_int chip_id;
+	u_int sku_id;
+	u_int cpu_process_id;
+	u_int cpu_speedo_id;
+	u_int cpu_speedo_value;
+	u_int cpu_iddq_value;
+	u_int soc_process_id;
+	u_int soc_speedo_id;
+	u_int soc_speedo_value;
+	u_int soc_iddq_value;
+	u_int gpu_process_id;
+	u_int gpu_speedo_id;
+	u_int gpu_speedo_value;
+	u_int gpu_iddq_value;
+	enum tegra_revision revision;
+};
+
+extern struct tegra_sku_info tegra_sku_info;
+uint32_t tegra_fuse_read_4(int addr);
+
+#endif /* _TEGRA_EFUSE_H_ */


Property changes on: trunk/sys/arm/nvidia/tegra_efuse.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_ehci.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_ehci.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_ehci.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,319 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_ehci.c 332025 2018-04-04 13:23:06Z mmel $");
+
+/*
+ * EHCI driver for Tegra SoCs.
+ */
+#include "opt_bus.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include "usbdevs.h"
+
+#define	TEGRA_EHCI_REG_OFF	0x100
+#define	TEGRA_EHCI_REG_SIZE	0x100
+
+/* Compatible devices. */
+#define	TEGRA124_EHCI		1
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-ehci",	(uintptr_t)TEGRA124_EHCI},
+	{NULL,		 	0},
+};
+
+struct tegra_ehci_softc {
+	ehci_softc_t	ehci_softc;
+	device_t	dev;
+	struct resource	*ehci_mem_res;	/* EHCI core regs. */
+	struct resource	*ehci_irq_res;	/* EHCI core IRQ. */
+	int		usb_alloc_called;
+	clk_t		clk;
+	phy_t 		phy;
+	hwreset_t 	reset;
+};
+
+static void
+tegra_ehci_post_reset(struct ehci_softc *ehci_softc)
+{
+	uint32_t usbmode;
+
+	/* Force HOST mode. */
+	usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_LPM);
+	usbmode &= ~EHCI_UM_CM;
+	usbmode |= EHCI_UM_CM_HOST;
+	device_printf(ehci_softc->sc_bus.bdev, "set host controller mode\n");
+	EOWRITE4(ehci_softc, EHCI_USBMODE_LPM, usbmode);
+}
+
+static int
+tegra_ehci_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+		device_set_desc(dev, "Nvidia Tegra EHCI controller");
+		return (BUS_PROBE_DEFAULT);
+	}
+	return (ENXIO);
+}
+
+static int
+tegra_ehci_detach(device_t dev)
+{
+	struct tegra_ehci_softc *sc;
+	ehci_softc_t *esc;
+
+	sc = device_get_softc(dev);
+
+	esc = &sc->ehci_softc;
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (esc->sc_bus.bdev != NULL)
+		device_delete_child(dev, esc->sc_bus.bdev);
+	if (esc->sc_flags & EHCI_SCFLG_DONEINIT)
+		ehci_detach(esc);
+	if (esc->sc_intr_hdl != NULL)
+		bus_teardown_intr(dev, esc->sc_irq_res,
+		    esc->sc_intr_hdl);
+	if (sc->ehci_irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0,
+		    sc->ehci_irq_res);
+	if (sc->ehci_mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0,
+		    sc->ehci_mem_res);
+	if (sc->usb_alloc_called)
+		usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc);
+
+	/* During module unload there are lots of children leftover. */
+	device_delete_children(dev);
+
+	return (0);
+}
+
+static int
+tegra_ehci_attach(device_t dev)
+{
+	struct tegra_ehci_softc *sc;
+	ehci_softc_t *esc;
+	int rv, rid;
+	uint64_t freq;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+	esc = &sc->ehci_softc;
+
+	/* Allocate resources. */
+	rid = 0;
+	sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	if (sc->ehci_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	rid = 0;
+	sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->ehci_irq_res == NULL) {
+		device_printf(dev, "Cannot allocate IRQ resources\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	rv = hwreset_get_by_ofw_name(dev, 0, "usb", &sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get reset\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	rv = phy_get_by_ofw_property(sc->dev, 0, "nvidia,phy", &sc->phy);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'nvidia,phy' phy\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get clock\n");
+		goto out;
+	}
+
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock\n");
+		goto out;
+	}
+
+	freq = 0;
+	rv = clk_get_freq(sc->clk, &freq);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get clock frequency\n");
+		goto out;
+	}
+
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot clear reset: %d\n", rv);
+		rv = ENXIO;
+		goto out;
+	}
+
+	rv = phy_enable(sc->phy);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable phy: %d\n", rv);
+		goto out;
+	}
+
+	/* Fill data for EHCI driver. */
+	esc->sc_vendor_get_port_speed = ehci_get_port_speed_hostc;
+	esc->sc_vendor_post_reset = tegra_ehci_post_reset;
+	esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res);
+	esc->sc_bus.parent = dev;
+	esc->sc_bus.devices = esc->sc_devices;
+	esc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+	esc->sc_bus.dma_bits = 32;
+
+	/* Allocate all DMA memory. */
+	rv = usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev),
+	    &ehci_iterate_hw_softc);
+	sc->usb_alloc_called = 1;
+	if (rv != 0) {
+		device_printf(dev, "usb_bus_mem_alloc_all() failed\n");
+		rv = ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Set handle to USB related registers subregion used by
+	 * generic EHCI driver.
+	 */
+	rv = bus_space_subregion(esc->sc_io_tag,
+	    rman_get_bushandle(sc->ehci_mem_res),
+	    TEGRA_EHCI_REG_OFF, TEGRA_EHCI_REG_SIZE, &esc->sc_io_hdl);
+	if (rv != 0) {
+		device_printf(dev, "Could not create USB memory subregion\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	/* Setup interrupt handler. */
+	rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+	    NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl);
+	if (rv != 0) {
+		device_printf(dev, "Could not setup IRQ\n");
+		goto out;
+	}
+
+	/* Add USB bus device. */
+	esc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+	if (esc->sc_bus.bdev == NULL) {
+		device_printf(dev, "Could not add USB device\n");
+		goto out;
+	}
+	device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus);
+
+	esc->sc_id_vendor = USB_VENDOR_FREESCALE;
+	strlcpy(esc->sc_vendor, "Nvidia", sizeof(esc->sc_vendor));
+
+	/* Set flags that affect ehci_init() behavior. */
+	esc->sc_flags |= EHCI_SCFLG_TT;
+	esc->sc_flags |= EHCI_SCFLG_NORESTERM;
+	rv = ehci_init(esc);
+	if (rv != 0) {
+		device_printf(dev, "USB init failed: %d\n",
+		    rv);
+		goto out;
+	}
+	esc->sc_flags |= EHCI_SCFLG_DONEINIT;
+
+	/* Probe the bus. */
+	rv = device_probe_and_attach(esc->sc_bus.bdev);
+	if (rv != 0) {
+		device_printf(dev,
+		    "device_probe_and_attach() failed\n");
+		goto out;
+	}
+	return (0);
+
+out:
+	tegra_ehci_detach(dev);
+	return (rv);
+}
+
+static device_method_t ehci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, tegra_ehci_probe),
+	DEVMETHOD(device_attach, tegra_ehci_attach),
+	DEVMETHOD(device_detach, tegra_ehci_detach),
+	DEVMETHOD(device_suspend, bus_generic_suspend),
+	DEVMETHOD(device_resume, bus_generic_resume),
+	DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+	/* Bus interface */
+	DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+	DEVMETHOD_END
+};
+
+static devclass_t ehci_devclass;
+static DEFINE_CLASS_0(ehci, ehci_driver, ehci_methods,
+    sizeof(struct tegra_ehci_softc));
+DRIVER_MODULE(tegra_ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL);
+MODULE_DEPEND(tegra_ehci, usb, 1, 1, 1);


Property changes on: trunk/sys/arm/nvidia/tegra_ehci.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_gpio.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_gpio.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_gpio.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,893 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_gpio.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * Tegra GPIO driver.
+ */
+#include "opt_platform.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
+#define	GPIO_LOCK_INIT(_sc)	mtx_init(&_sc->mtx, 			\
+	    device_get_nameunit(_sc->dev), "tegra_gpio", MTX_DEF)
+#define	GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
+#define	GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
+#define	GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+#define	GPIO_BANK_OFFS		0x100	/* Bank offset */
+#define	GPIO_NUM_BANKS		8	/* Total number per bank */
+#define	GPIO_REGS_IN_BANK	4	/* Total registers in bank */
+#define	GPIO_PINS_IN_REG	8	/* Total pin in register */
+
+#define	GPIO_BANKNUM(n)		((n) / (GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG))
+#define	GPIO_PORTNUM(n)		(((n) / GPIO_PINS_IN_REG) % GPIO_REGS_IN_BANK)
+#define	GPIO_BIT(n)		((n) % GPIO_PINS_IN_REG)
+
+#define	GPIO_REGNUM(n)		(GPIO_BANKNUM(n) * GPIO_BANK_OFFS + \
+				    GPIO_PORTNUM(n) * 4)
+
+#define	NGPIO	((GPIO_NUM_BANKS * GPIO_REGS_IN_BANK * GPIO_PINS_IN_REG) - 8)
+
+/* Register offsets */
+#define	GPIO_CNF		0x00
+#define	GPIO_OE			0x10
+#define	GPIO_OUT		0x20
+#define	GPIO_IN			0x30
+#define	GPIO_INT_STA		0x40
+#define	GPIO_INT_ENB		0x50
+#define	GPIO_INT_LVL		0x60
+#define  GPIO_INT_LVL_DELTA		(1 << 16)
+#define  GPIO_INT_LVL_EDGE		(1 << 8)
+#define  GPIO_INT_LVL_HIGH		(1 << 0)
+#define  GPIO_INT_LVL_MASK		(GPIO_INT_LVL_DELTA |		\
+					 GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH)
+#define	GPIO_INT_CLR		0x70
+#define	GPIO_MSK_CNF		0x80
+#define	GPIO_MSK_OE		0x90
+#define	GPIO_MSK_OUT		0xA0
+#define	GPIO_MSK_INT_STA	0xC0
+#define	GPIO_MSK_INT_ENB	0xD0
+#define	GPIO_MSK_INT_LVL	0xE0
+
+char *tegra_gpio_port_names[] = {
+	 "A",  "B",  "C",  "D", /* Bank 0 */
+	 "E",  "F",  "G",  "H", /* Bank 1 */
+	 "I",  "J",  "K",  "L", /* Bank 2 */
+	 "M",  "N",  "O",  "P", /* Bank 3 */
+	 "Q",  "R",  "S",  "T", /* Bank 4 */
+	 "U",  "V",  "W",  "X", /* Bank 5 */
+	 "Y",  "Z", "AA", "BB", /* Bank 6 */
+	"CC", "DD", "EE"	/* Bank 7 */
+};
+
+struct tegra_gpio_irqsrc {
+	struct intr_irqsrc	isrc;
+	u_int			irq;
+	uint32_t		cfgreg;
+};
+
+struct tegra_gpio_softc;
+struct tegra_gpio_irq_cookie {
+	struct tegra_gpio_softc	*sc;
+	int			bank_num;
+};
+
+struct tegra_gpio_softc {
+	device_t		dev;
+	device_t		busdev;
+	struct mtx		mtx;
+	struct resource		*mem_res;
+	struct resource		*irq_res[GPIO_NUM_BANKS];
+	void			*irq_ih[GPIO_NUM_BANKS];
+	struct tegra_gpio_irq_cookie irq_cookies[GPIO_NUM_BANKS];
+	int			gpio_npins;
+	struct gpio_pin		gpio_pins[NGPIO];
+	struct tegra_gpio_irqsrc *isrcs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-gpio", 1},
+	{NULL,			0}
+};
+
+/* --------------------------------------------------------------------------
+ *
+ * GPIO
+ *
+ */
+static inline void
+gpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg,
+    struct gpio_pin *pin, uint32_t val)
+{
+	uint32_t tmp;
+	int bit;
+
+	bit = GPIO_BIT(pin->gp_pin);
+	tmp = 0x100 << bit;		/* mask */
+	tmp |= (val & 1) << bit;	/* value */
+	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp);
+}
+
+static inline uint32_t
+gpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
+{
+	int bit;
+	uint32_t val;
+
+	bit = GPIO_BIT(pin->gp_pin);
+	val = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin));
+	return (val >> bit) & 1;
+}
+
+static void
+tegra_gpio_pin_configure(struct tegra_gpio_softc *sc, struct gpio_pin *pin,
+    unsigned int flags)
+{
+
+	if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0)
+		return;
+
+	/* Manage input/output */
+	pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+	if (flags & GPIO_PIN_OUTPUT) {
+		pin->gp_flags |= GPIO_PIN_OUTPUT;
+		gpio_write_masked(sc, GPIO_MSK_OE, pin, 1);
+	} else {
+		pin->gp_flags |= GPIO_PIN_INPUT;
+		gpio_write_masked(sc, GPIO_MSK_OE, pin, 0);
+	}
+}
+
+static device_t
+tegra_gpio_get_bus(device_t dev)
+{
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (sc->busdev);
+}
+
+static int
+tegra_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+	*maxpin = NGPIO - 1;
+	return (0);
+}
+
+static int
+tegra_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	*caps = sc->gpio_pins[pin].gp_caps;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+	struct tegra_gpio_softc *sc;
+	int cnf;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	cnf = gpio_read(sc, GPIO_CNF, &sc->gpio_pins[pin]);
+	if (cnf == 0) {
+		GPIO_UNLOCK(sc);
+		return (ENXIO);
+	}
+	*flags = sc->gpio_pins[pin].gp_flags;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+	struct tegra_gpio_softc *sc;
+	int cnf;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	cnf = gpio_read(sc, GPIO_CNF,  &sc->gpio_pins[pin]);
+	if (cnf == 0) {
+		/* XXX - allow this for while ....
+		GPIO_UNLOCK(sc);
+		return (ENXIO);
+		*/
+		gpio_write_masked(sc, GPIO_MSK_CNF,  &sc->gpio_pins[pin], 1);
+	}
+	tegra_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+	GPIO_LOCK(sc);
+	gpio_write_masked(sc, GPIO_MSK_OUT, &sc->gpio_pins[pin], value);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	*val = gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (pin >= sc->gpio_npins)
+		return (EINVAL);
+
+	GPIO_LOCK(sc);
+	gpio_write_masked(sc, GPIO_MSK_OE, &sc->gpio_pins[pin],
+	     gpio_read(sc, GPIO_IN, &sc->gpio_pins[pin]) ^ 1);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+static inline void
+intr_write_masked(struct tegra_gpio_softc *sc, bus_addr_t reg,
+    struct tegra_gpio_irqsrc *tgi, uint32_t val)
+{
+	uint32_t tmp;
+	int bit;
+
+	bit = GPIO_BIT(tgi->irq);
+	tmp = 0x100 << bit;		/* mask */
+	tmp |= (val & 1) << bit;	/* value */
+	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
+}
+
+static inline void
+intr_write_modify(struct tegra_gpio_softc *sc, bus_addr_t reg,
+    struct tegra_gpio_irqsrc *tgi, uint32_t val, uint32_t mask)
+{
+	uint32_t tmp;
+	int bit;
+
+	bit = GPIO_BIT(tgi->irq);
+	GPIO_LOCK(sc);
+	tmp = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq));
+	tmp &= ~(mask << bit);
+	tmp |= val << bit;
+	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
+	GPIO_UNLOCK(sc);
+}
+
+static inline void
+tegra_gpio_isrc_mask(struct tegra_gpio_softc *sc,
+     struct tegra_gpio_irqsrc *tgi, uint32_t val)
+{
+
+	intr_write_masked(sc, GPIO_MSK_INT_ENB, tgi, val);
+}
+
+static inline void
+tegra_gpio_isrc_eoi(struct tegra_gpio_softc *sc,
+     struct tegra_gpio_irqsrc *tgi)
+{
+
+	intr_write_masked(sc, GPIO_INT_CLR, tgi, 1);
+}
+
+static inline bool
+tegra_gpio_isrc_is_level(struct tegra_gpio_irqsrc *tgi)
+{
+
+	return (tgi->cfgreg & GPIO_INT_LVL_EDGE);
+}
+
+static int
+tegra_gpio_intr(void *arg)
+{
+	u_int irq, i, j, val, basepin;
+	struct tegra_gpio_softc *sc;
+	struct trapframe *tf;
+	struct tegra_gpio_irqsrc *tgi;
+	struct tegra_gpio_irq_cookie *cookie;
+
+	cookie = (struct tegra_gpio_irq_cookie *)arg;
+	sc = cookie->sc;
+	tf = curthread->td_intr_frame;
+
+	for (i = 0; i < GPIO_REGS_IN_BANK; i++) {
+		basepin  = cookie->bank_num * GPIO_REGS_IN_BANK *
+		    GPIO_PINS_IN_REG + i * GPIO_PINS_IN_REG;
+
+		val = bus_read_4(sc->mem_res, GPIO_INT_STA +
+		    GPIO_REGNUM(basepin));
+		val &= bus_read_4(sc->mem_res, GPIO_INT_ENB +
+		    GPIO_REGNUM(basepin));
+		/* Interrupt handling */
+		for (j = 0; j < GPIO_PINS_IN_REG; j++) {
+			if ((val & (1 << j)) == 0)
+				continue;
+			irq = basepin + j;
+			tgi = &sc->isrcs[irq];
+			if (!tegra_gpio_isrc_is_level(tgi))
+				tegra_gpio_isrc_eoi(sc, tgi);
+			if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) {
+				tegra_gpio_isrc_mask(sc, tgi, 0);
+				if (tegra_gpio_isrc_is_level(tgi))
+					tegra_gpio_isrc_eoi(sc, tgi);
+				device_printf(sc->dev,
+				    "Stray irq %u disabled\n", irq);
+			}
+
+		}
+	}
+
+	return (FILTER_HANDLED);
+}
+
+static int
+tegra_gpio_pic_attach(struct tegra_gpio_softc *sc)
+{
+	int error;
+	uint32_t irq;
+	const char *name;
+
+	sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
+	    M_WAITOK | M_ZERO);
+
+	name = device_get_nameunit(sc->dev);
+	for (irq = 0; irq < sc->gpio_npins; irq++) {
+		sc->isrcs[irq].irq = irq;
+		sc->isrcs[irq].cfgreg = 0;
+		error = intr_isrc_register(&sc->isrcs[irq].isrc,
+		    sc->dev, 0, "%s,%u", name, irq);
+		if (error != 0)
+			return (error); /* XXX deregister ISRCs */
+	}
+	if (intr_pic_register(sc->dev,
+	    OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL)
+		return (ENXIO);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pic_detach(struct tegra_gpio_softc *sc)
+{
+
+	/*
+	 *  There has not been established any procedure yet
+	 *  how to detach PIC from living system correctly.
+	 */
+	device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+	return (EBUSY);
+}
+
+
+static void
+tegra_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	tegra_gpio_isrc_mask(sc, tgi, 0);
+}
+
+static void
+tegra_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	tegra_gpio_isrc_mask(sc, tgi, 1);
+}
+
+static int
+tegra_gpio_pic_map_fdt(struct tegra_gpio_softc *sc, u_int ncells,
+    pcell_t *cells, u_int *irqp, uint32_t *regp)
+{
+	uint32_t reg;
+
+	/*
+	 * The first cell is the interrupt number.
+	 * The second cell is used to specify flags:
+	 *	bits[3:0] trigger type and level flags:
+	 *		1 = low-to-high edge triggered.
+	 *		2 = high-to-low edge triggered.
+	 *		4 = active high level-sensitive.
+	 *		8 = active low level-sensitive.
+	 */
+	if (ncells != 2 || cells[0] >= sc->gpio_npins)
+		return (EINVAL);
+
+	/*
+	 * All interrupt types could be set for an interrupt at one moment.
+	 * At least, the combination of 'low-to-high' and 'high-to-low' edge
+	 * triggered interrupt types can make a sense.
+	 */
+	if (cells[1] == 1)
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
+	else if (cells[1] == 2)
+		reg = GPIO_INT_LVL_EDGE;
+	else if (cells[1] == 3)
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
+	else if (cells[1] == 4)
+		reg = GPIO_INT_LVL_HIGH;
+	else if (cells[1] == 8)
+		reg = 0;
+	else
+		return (EINVAL);
+
+	*irqp = cells[0];
+	if (regp != NULL)
+		*regp = reg;
+	return (0);
+}
+
+
+static int
+tegra_gpio_pic_map_gpio(struct tegra_gpio_softc *sc, u_int gpio_pin_num,
+    u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, uint32_t *regp)
+{
+
+	uint32_t reg;
+
+	if (gpio_pin_num >= sc->gpio_npins)
+		return (EINVAL);
+	switch (intr_mode) {
+	case GPIO_INTR_CONFORM:
+	case GPIO_INTR_LEVEL_LOW:
+		reg = 0;
+		break;
+	case GPIO_INTR_LEVEL_HIGH:
+		reg = GPIO_INT_LVL_HIGH;
+		break;
+	case GPIO_INTR_EDGE_RISING:
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
+		break;
+	case GPIO_INTR_EDGE_FALLING:
+		reg = GPIO_INT_LVL_EDGE;
+		break;
+	case GPIO_INTR_EDGE_BOTH:
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
+		break;
+	default:
+		return (EINVAL);
+	}
+	*irqp = gpio_pin_num;
+	if (regp != NULL)
+		*regp = reg;
+	return (0);
+}
+
+static int
+tegra_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+	int rv;
+	u_int irq;
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (data->type == INTR_MAP_DATA_FDT) {
+		struct intr_map_data_fdt *daf;
+
+		daf = (struct intr_map_data_fdt *)data;
+		rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+		    NULL);
+	} else if (data->type == INTR_MAP_DATA_GPIO) {
+		struct intr_map_data_gpio *dag;
+
+		dag = (struct intr_map_data_gpio *)data;
+		rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+		   dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL);
+	} else
+		return (ENOTSUP);
+
+	if (rv == 0)
+		*isrcp = &sc->isrcs[irq].isrc;
+	return (rv);
+}
+
+static void
+tegra_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	if (tegra_gpio_isrc_is_level(tgi))
+		tegra_gpio_isrc_eoi(sc, tgi);
+}
+
+static void
+tegra_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	tegra_gpio_isrc_mask(sc, tgi, 1);
+}
+
+static void
+tegra_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+	tegra_gpio_isrc_mask(sc, tgi, 0);
+	if (tegra_gpio_isrc_is_level(tgi))
+		tegra_gpio_isrc_eoi(sc, tgi);
+}
+
+static int
+tegra_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	u_int irq;
+	uint32_t cfgreg;
+	int rv;
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+	if (data == NULL)
+		return (ENOTSUP);
+
+	/* Get and check config for an interrupt. */
+	if (data->type == INTR_MAP_DATA_FDT) {
+		struct intr_map_data_fdt *daf;
+
+		daf = (struct intr_map_data_fdt *)data;
+		rv = tegra_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq,
+		    &cfgreg);
+	} else if (data->type == INTR_MAP_DATA_GPIO) {
+		struct intr_map_data_gpio *dag;
+
+		dag = (struct intr_map_data_gpio *)data;
+		rv = tegra_gpio_pic_map_gpio(sc, dag->gpio_pin_num,
+		   dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, &cfgreg);
+	} else
+		return (ENOTSUP);
+	if (rv != 0)
+		return (EINVAL);
+
+	/*
+	 * If this is a setup for another handler,
+	 * only check that its configuration match.
+	 */
+	if (isrc->isrc_handlers != 0)
+		return (tgi->cfgreg == cfgreg ? 0 : EINVAL);
+
+	tgi->cfgreg = cfgreg;
+	intr_write_modify(sc, GPIO_INT_LVL, tgi, cfgreg, GPIO_INT_LVL_MASK);
+	tegra_gpio_pic_enable_intr(dev, isrc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+	if (isrc->isrc_handlers == 0)
+		tegra_gpio_isrc_mask(sc, tgi, 0);
+	return (0);
+}
+
+static int
+tegra_gpio_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+		device_set_desc(dev, "Tegra GPIO Controller");
+		return (BUS_PROBE_DEFAULT);
+	}
+
+	return (ENXIO);
+}
+
+/* --------------------------------------------------------------------------
+ *
+ * Bus
+ *
+ */
+static int
+tegra_gpio_detach(device_t dev)
+{
+	struct tegra_gpio_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
+
+	for (i = 0; i < GPIO_NUM_BANKS; i++) {
+		if (sc->irq_ih[i] != NULL)
+			bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
+	}
+
+	if (sc->isrcs != NULL)
+		tegra_gpio_pic_detach(sc);
+
+	gpiobus_detach_bus(dev);
+
+	for (i = 0; i < GPIO_NUM_BANKS; i++) {
+		if (sc->irq_res[i] != NULL)
+			bus_release_resource(dev, SYS_RES_IRQ, 0,
+			    sc->irq_res[i]);
+	}
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	GPIO_LOCK_DESTROY(sc);
+
+	return(0);
+}
+
+static int
+tegra_gpio_attach(device_t dev)
+{
+	struct tegra_gpio_softc *sc;
+	int i, rid;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	GPIO_LOCK_INIT(sc);
+
+	/* Allocate bus_space resources. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		tegra_gpio_detach(dev);
+		return (ENXIO);
+	}
+
+	sc->gpio_npins = NGPIO;
+	for (i = 0; i < sc->gpio_npins; i++) {
+		sc->gpio_pins[i].gp_pin = i;
+		sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+		    GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | 
+		    GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING |
+		    GPIO_INTR_EDGE_BOTH;
+		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d",
+		    tegra_gpio_port_names[ i / GPIO_PINS_IN_REG],
+		    i % GPIO_PINS_IN_REG);
+		sc->gpio_pins[i].gp_flags =
+		    gpio_read(sc, GPIO_OE, &sc->gpio_pins[i]) != 0 ?
+		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
+	}
+
+	/* Init interrupt related registes. */
+	for (i = 0; i < sc->gpio_npins; i += GPIO_PINS_IN_REG) {
+		bus_write_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i), 0);
+		bus_write_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i), 0xFF);
+		bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), 0xFF);
+	}
+
+	/* Allocate interrupts. */
+	for (i = 0; i < GPIO_NUM_BANKS; i++) {
+		sc->irq_cookies[i].sc = sc;
+		sc->irq_cookies[i].bank_num = i;
+		rid = i;
+		sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+		    &rid, RF_ACTIVE);
+		if (sc->irq_res[i] == NULL) {
+			device_printf(dev, "Cannot allocate IRQ resources\n");
+			tegra_gpio_detach(dev);
+			return (ENXIO);
+		}
+		if ((bus_setup_intr(dev, sc->irq_res[i],
+		    INTR_TYPE_MISC | INTR_MPSAFE, tegra_gpio_intr, NULL,
+		    &sc->irq_cookies[i], &sc->irq_ih[i]))) {
+			device_printf(dev,
+			    "WARNING: unable to register interrupt handler\n");
+			tegra_gpio_detach(dev);
+			return (ENXIO);
+		}
+	}
+
+	if (tegra_gpio_pic_attach(sc) != 0) {
+		device_printf(dev, "WARNING: unable to attach PIC\n");
+		tegra_gpio_detach(dev);
+		return (ENXIO);
+	}
+
+	sc->busdev = gpiobus_attach_bus(dev);
+	if (sc->busdev == NULL) {
+		tegra_gpio_detach(dev);
+		return (ENXIO);
+	}
+
+	return (bus_generic_attach(dev));
+}
+
+static int
+tegra_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
+    int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+
+	if (gcells != 2)
+		return (ERANGE);
+	*pin = gpios[0];
+	*flags= gpios[1];
+	return (0);
+}
+
+static phandle_t
+tegra_gpio_get_node(device_t bus, device_t dev)
+{
+
+	/* We only have one child, the GPIO bus, which needs our own node. */
+	return (ofw_bus_get_node(bus));
+}
+
+static device_method_t tegra_gpio_methods[] = {
+	DEVMETHOD(device_probe,		tegra_gpio_probe),
+	DEVMETHOD(device_attach,	tegra_gpio_attach),
+	DEVMETHOD(device_detach,	tegra_gpio_detach),
+
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_intr,	tegra_gpio_pic_disable_intr),
+	DEVMETHOD(pic_enable_intr,	tegra_gpio_pic_enable_intr),
+	DEVMETHOD(pic_map_intr,		tegra_gpio_pic_map_intr),
+	DEVMETHOD(pic_setup_intr,	tegra_gpio_pic_setup_intr),
+	DEVMETHOD(pic_teardown_intr,	tegra_gpio_pic_teardown_intr),
+	DEVMETHOD(pic_post_filter,	tegra_gpio_pic_post_filter),
+	DEVMETHOD(pic_post_ithread,	tegra_gpio_pic_post_ithread),
+	DEVMETHOD(pic_pre_ithread,	tegra_gpio_pic_pre_ithread),
+
+	/* GPIO protocol */
+	DEVMETHOD(gpio_get_bus,		tegra_gpio_get_bus),
+	DEVMETHOD(gpio_pin_max,		tegra_gpio_pin_max),
+	DEVMETHOD(gpio_pin_getname,	tegra_gpio_pin_getname),
+	DEVMETHOD(gpio_pin_getflags,	tegra_gpio_pin_getflags),
+	DEVMETHOD(gpio_pin_getcaps,	tegra_gpio_pin_getcaps),
+	DEVMETHOD(gpio_pin_setflags,	tegra_gpio_pin_setflags),
+	DEVMETHOD(gpio_pin_get,		tegra_gpio_pin_get),
+	DEVMETHOD(gpio_pin_set,		tegra_gpio_pin_set),
+	DEVMETHOD(gpio_pin_toggle,	tegra_gpio_pin_toggle),
+	DEVMETHOD(gpio_map_gpios,	tegra_map_gpios),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,	tegra_gpio_get_node),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_gpio_devclass;
+static DEFINE_CLASS_0(gpio, tegra_gpio_driver, tegra_gpio_methods,
+    sizeof(struct tegra_gpio_softc));
+EARLY_DRIVER_MODULE(tegra_gpio, simplebus, tegra_gpio_driver,
+    tegra_gpio_devclass, NULL, NULL, 70);


Property changes on: trunk/sys/arm/nvidia/tegra_gpio.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_i2c.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_i2c.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_i2c.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,805 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_i2c.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * I2C driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "iicbus_if.h"
+
+#define	I2C_CNFG				0x000
+#define	 I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT		(1 << 15)
+#define	 I2C_CNFG_DEBOUNCE_CNT(x)			(((x) & 0x07) << 12)
+#define	 I2C_CNFG_NEW_MASTER_FSM			(1 << 11)
+#define	 I2C_CNFG_PACKET_MODE_EN			(1 << 10)
+#define	 I2C_CNFG_SEND					(1 <<  9)
+#define	 I2C_CNFG_NOACK					(1 <<  8)
+#define	 I2C_CNFG_CMD2					(1 <<  7)
+#define	 I2C_CNFG_CMD1					(1 <<  6)
+#define	 I2C_CNFG_START					(1 <<  5)
+#define	 I2C_CNFG_SLV2					(1 <<  4)
+#define	 I2C_CNFG_LENGTH_SHIFT				1
+#define	 I2C_CNFG_LENGTH_MASK				0x7
+#define	 I2C_CNFG_A_MOD					(1 <<  0)
+
+#define	I2C_CMD_ADDR0				0x004
+#define	I2C_CMD_ADDR1				0x008
+#define	I2C_CMD_DATA1				0x00c
+#define	I2C_CMD_DATA2				0x010
+#define	I2C_STATUS				0x01c
+#define	I2C_SL_CNFG				0x020
+#define	I2C_SL_RCVD				0x024
+#define	I2C_SL_STATUS				0x028
+#define	I2C_SL_ADDR1				0x02c
+#define	I2C_SL_ADDR2				0x030
+#define	I2C_TLOW_SEXT				0x034
+#define	I2C_SL_DELAY_COUNT			0x03c
+#define	I2C_SL_INT_MASK				0x040
+#define	I2C_SL_INT_SOURCE			0x044
+#define	I2C_SL_INT_SET				0x048
+#define	I2C_TX_PACKET_FIFO			0x050
+#define	I2C_RX_FIFO				0x054
+#define	I2C_PACKET_TRANSFER_STATUS		0x058
+#define	I2C_FIFO_CONTROL			0x05c
+#define	 I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG(x)		(((x) & 0x07) << 13)
+#define	 I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG(x)		(((x) & 0x07) << 10)
+#define	 I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH		(1 <<  9)
+#define	 I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH		(1 <<  8)
+#define	 I2C_FIFO_CONTROL_TX_FIFO_TRIG(x)		(((x) & 0x07) << 5)
+#define	 I2C_FIFO_CONTROL_RX_FIFO_TRIG(x)		(((x) & 0x07) << 2)
+#define	 I2C_FIFO_CONTROL_TX_FIFO_FLUSH			(1 << 1)
+#define	 I2C_FIFO_CONTROL_RX_FIFO_FLUSH			(1 << 0)
+
+#define	I2C_FIFO_STATUS				0x060
+#define	 I2C_FIFO_STATUS_SLV_XFER_ERR_REASON		(1 << 25)
+#define	 I2C_FIFO_STATUS_TX_FIFO_SLV_EMPTY_CNT(x)	(((x) >> 20) & 0xF)
+#define	 I2C_FIFO_STATUS_RX_FIFO_SLV_FULL_CNT(x)	(((x) >> 16) & 0xF)
+#define	 I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(x)		(((x) >>  4) & 0xF)
+#define	 I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(x)		(((x) >>  0) & 0xF)
+
+#define	I2C_INTERRUPT_MASK_REGISTER		0x064
+#define	I2C_INTERRUPT_STATUS_REGISTER		0x068
+#define	 I2C_INT_SLV_ACK_WITHHELD			(1 << 28)
+#define	 I2C_INT_SLV_RD2WR				(1 << 27)
+#define	 I2C_INT_SLV_WR2RD				(1 << 26)
+#define	 I2C_INT_SLV_PKT_XFER_ERR			(1 << 25)
+#define	 I2C_INT_SLV_TX_BUFFER_REQ			(1 << 24)
+#define	 I2C_INT_SLV_RX_BUFFER_FILLED			(1 << 23)
+#define	 I2C_INT_SLV_PACKET_XFER_COMPLETE		(1 << 22)
+#define	 I2C_INT_SLV_TFIFO_OVF				(1 << 21)
+#define	 I2C_INT_SLV_RFIFO_UNF				(1 << 20)
+#define	 I2C_INT_SLV_TFIFO_DATA_REQ			(1 << 17)
+#define	 I2C_INT_SLV_RFIFO_DATA_REQ			(1 << 16)
+#define	 I2C_INT_BUS_CLEAR_DONE				(1 << 11)
+#define	 I2C_INT_TLOW_MEXT_TIMEOUT			(1 << 10)
+#define	 I2C_INT_TLOW_SEXT_TIMEOUT			(1 <<  9)
+#define	 I2C_INT_TIMEOUT				(1 <<  8)
+#define	 I2C_INT_PACKET_XFER_COMPLETE			(1 <<  7)
+#define	 I2C_INT_ALL_PACKETS_XFER_COMPLETE		(1 <<  6)
+#define	 I2C_INT_TFIFO_OVR				(1 <<  5)
+#define	 I2C_INT_RFIFO_UNF				(1 <<  4)
+#define	 I2C_INT_NOACK					(1 <<  3)
+#define	 I2C_INT_ARB_LOST				(1 <<  2)
+#define	 I2C_INT_TFIFO_DATA_REQ				(1 <<  1)
+#define	 I2C_INT_RFIFO_DATA_REQ				(1 <<  0)
+#define	 I2C_ERROR_MASK		(I2C_INT_ARB_LOST | I2C_INT_NOACK |	 \
+				I2C_INT_RFIFO_UNF | I2C_INT_TFIFO_OVR)
+
+#define	I2C_CLK_DIVISOR				0x06c
+#define	 I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT		16
+#define	 I2C_CLK_DIVISOR_STD_FAST_MODE_MASK		0xffff
+#define	 I2C_CLK_DIVISOR_HSMODE_SHIFT			0
+#define	 I2C_CLK_DIVISOR_HSMODE_MASK			0xffff
+#define	I2C_INTERRUPT_SOURCE_REGISTER		0x070
+#define	I2C_INTERRUPT_SET_REGISTER		0x074
+#define	I2C_SLV_TX_PACKET_FIFO			0x07c
+#define	I2C_SLV_PACKET_STATUS			0x080
+#define	I2C_BUS_CLEAR_CONFIG			0x084
+#define	 I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(x)	(((x) & 0xFF) << 16)
+#define	 I2C_BUS_CLEAR_CONFIG_BC_STOP_COND		(1 << 2)
+#define	 I2C_BUS_CLEAR_CONFIG_BC_TERMINATE		(1 << 1)
+#define	 I2C_BUS_CLEAR_CONFIG_BC_ENABLE			(1 << 0)
+
+#define	I2C_BUS_CLEAR_STATUS			0x088
+#define	 I2C_BUS_CLEAR_STATUS_BC_STATUS			(1 << 0)
+
+#define	I2C_CONFIG_LOAD				0x08c
+#define	 I2C_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD		(1 << 2)
+#define	 I2C_CONFIG_LOAD_SLV_CONFIG_LOAD		(1 << 1)
+#define	 I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD		(1 << 0)
+
+#define	I2C_INTERFACE_TIMING_0			0x094
+#define	I2C_INTERFACE_TIMING_1			0x098
+#define	I2C_HS_INTERFACE_TIMING_0		0x09c
+#define	I2C_HS_INTERFACE_TIMING_1		0x0a0
+
+/* Protocol header 0 */
+#define	PACKET_HEADER0_HEADER_SIZE_SHIFT	28
+#define	PACKET_HEADER0_HEADER_SIZE_MASK		0x3
+#define	PACKET_HEADER0_PACKET_ID_SHIFT		16
+#define	PACKET_HEADER0_PACKET_ID_MASK		0xff
+#define	PACKET_HEADER0_CONT_ID_SHIFT		12
+#define	PACKET_HEADER0_CONT_ID_MASK		0xf
+#define	PACKET_HEADER0_PROTOCOL_I2C		(1 << 4)
+#define	PACKET_HEADER0_TYPE_SHIFT		0
+#define	PACKET_HEADER0_TYPE_MASK		0x7
+
+/* I2C header */
+#define	I2C_HEADER_HIGHSPEED_MODE		(1 << 22)
+#define	I2C_HEADER_CONT_ON_NAK			(1 << 21)
+#define	I2C_HEADER_SEND_START_BYTE		(1 << 20)
+#define	I2C_HEADER_READ				(1 << 19)
+#define	I2C_HEADER_10BIT_ADDR			(1 << 18)
+#define	I2C_HEADER_IE_ENABLE			(1 << 17)
+#define	I2C_HEADER_REPEAT_START			(1 << 16)
+#define	I2C_HEADER_CONTINUE_XFER		(1 << 15)
+#define	I2C_HEADER_MASTER_ADDR_SHIFT		12
+#define	I2C_HEADER_MASTER_ADDR_MASK		0x7
+#define	I2C_HEADER_SLAVE_ADDR_SHIFT		0
+#define	I2C_HEADER_SLAVE_ADDR_MASK		0x3ff
+
+#define	I2C_CLK_DIVISOR_STD_FAST_MODE		0x19
+#define	I2C_CLK_MULTIPLIER_STD_FAST_MODE	8
+
+#define	I2C_REQUEST_TIMEOUT			(5 * hz)
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+#define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	SLEEP(_sc, timeout)						\
+	mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", timeout);
+#define	LOCK_INIT(_sc)							\
+	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_i2c", MTX_DEF)
+#define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
+#define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
+#define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-i2c",	1},
+	{NULL,			0}
+};
+enum tegra_i2c_xfer_type {
+	XFER_STOP, 		/* Send stop condition after xfer */
+	XFER_REPEAT_START,	/* Send repeated start after xfer */
+	XFER_CONTINUE		/* Don't send nothing */
+} ;
+
+struct tegra_i2c_softc {
+	device_t		dev;
+	struct mtx		mtx;
+
+	struct resource		*mem_res;
+	struct resource		*irq_res;
+	void			*irq_h;
+
+	device_t		iicbus;
+	clk_t			clk;
+	hwreset_t		reset;
+	uint32_t		core_freq;
+	uint32_t		bus_freq;
+	int			bus_inuse;
+
+	struct iic_msg		*msg;
+	int			msg_idx;
+	uint32_t		bus_err;
+	int			done;
+};
+
+static int
+tegra_i2c_flush_fifo(struct tegra_i2c_softc *sc)
+{
+	int timeout;
+	uint32_t reg;
+
+	reg = RD4(sc, I2C_FIFO_CONTROL);
+	reg |= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
+	WR4(sc, I2C_FIFO_CONTROL, reg);
+
+	timeout = 10;
+	while (timeout > 0) {
+		reg = RD4(sc, I2C_FIFO_CONTROL);
+		reg &= I2C_FIFO_CONTROL_TX_FIFO_FLUSH |
+		    I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
+		if (reg == 0)
+			break;
+		DELAY(10);
+	}
+	if (timeout <= 0) {
+		device_printf(sc->dev, "FIFO flush timedout\n");
+		return (ETIMEDOUT);
+	}
+	return (0);
+}
+
+static void
+tegra_i2c_setup_clk(struct tegra_i2c_softc *sc, int clk_freq)
+{
+	int div;
+
+	div = ((sc->core_freq  / clk_freq)  / 10) - 1;
+	if ((sc->core_freq / (10 * (div + 1)))  > clk_freq)
+		div++;
+	if (div > 65535)
+		div = 65535;
+	WR4(sc, I2C_CLK_DIVISOR,
+	    (1 << I2C_CLK_DIVISOR_HSMODE_SHIFT) |
+	    (div << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT));
+}
+
+static void
+tegra_i2c_bus_clear(struct tegra_i2c_softc *sc)
+{
+	int timeout;
+	uint32_t reg, status;
+
+	WR4(sc, I2C_BUS_CLEAR_CONFIG,
+	    I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(18) |
+	    I2C_BUS_CLEAR_CONFIG_BC_STOP_COND |
+	    I2C_BUS_CLEAR_CONFIG_BC_TERMINATE);
+
+	WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD);
+	for (timeout = 1000; timeout > 0; timeout--) {
+		if (RD4(sc, I2C_CONFIG_LOAD) == 0)
+			break;
+		DELAY(10);
+	}
+	if (timeout <= 0)
+		device_printf(sc->dev, "config load timeouted\n");
+	reg = RD4(sc, I2C_BUS_CLEAR_CONFIG);
+	reg |= I2C_BUS_CLEAR_CONFIG_BC_ENABLE;
+	WR4(sc, I2C_BUS_CLEAR_CONFIG,reg);
+
+	for (timeout = 1000; timeout > 0; timeout--) {
+		if ((RD4(sc, I2C_BUS_CLEAR_CONFIG) &
+		    I2C_BUS_CLEAR_CONFIG_BC_ENABLE) == 0)
+			break;
+		DELAY(10);
+	}
+	if (timeout <= 0)
+		device_printf(sc->dev, "bus clear timeouted\n");
+
+	status = RD4(sc, I2C_BUS_CLEAR_STATUS);
+	if ((status & I2C_BUS_CLEAR_STATUS_BC_STATUS) == 0)
+		device_printf(sc->dev, "bus clear failed\n");
+}
+
+static int
+tegra_i2c_hw_init(struct tegra_i2c_softc *sc)
+{
+	int rv, timeout;
+
+	/* Reset the core. */
+	rv = hwreset_assert(sc->reset);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert reset\n");
+		return (rv);
+	}
+	DELAY(10);
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot clear reset\n");
+		return (rv);
+	}
+
+	WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+	WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF);
+	WR4(sc, I2C_CNFG, I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
+	    I2C_CNFG_DEBOUNCE_CNT(2));
+
+	tegra_i2c_setup_clk(sc, sc->bus_freq);
+
+	WR4(sc, I2C_FIFO_CONTROL, I2C_FIFO_CONTROL_TX_FIFO_TRIG(7) |
+	    I2C_FIFO_CONTROL_RX_FIFO_TRIG(0));
+
+	WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD);
+	for (timeout = 1000; timeout > 0; timeout--) {
+		if (RD4(sc, I2C_CONFIG_LOAD) == 0)
+			break;
+		DELAY(10);
+	}
+	if (timeout <= 0)
+		device_printf(sc->dev, "config load timeouted\n");
+
+	tegra_i2c_bus_clear(sc);
+	return (0);
+}
+
+static int
+tegra_i2c_tx(struct tegra_i2c_softc *sc)
+{
+	uint32_t reg;
+	int cnt, i;
+
+	if (sc->msg_idx >= sc->msg->len)
+		panic("Invalid call to tegra_i2c_tx\n");
+
+	while(sc->msg_idx < sc->msg->len) {
+		reg = RD4(sc, I2C_FIFO_STATUS);
+		if (I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(reg) == 0)
+			break;
+		cnt = min(4, sc->msg->len - sc->msg_idx);
+		reg = 0;
+		for (i = 0;  i < cnt; i++) {
+			reg |=  sc->msg->buf[sc->msg_idx] << (i * 8);
+			sc->msg_idx++;
+		}
+		WR4(sc, I2C_TX_PACKET_FIFO, reg);
+	}
+	if (sc->msg_idx >= sc->msg->len)
+		return (0);
+	return (sc->msg->len - sc->msg_idx - 1);
+}
+
+static int
+tegra_i2c_rx(struct tegra_i2c_softc *sc)
+{
+	uint32_t reg;
+	int cnt, i;
+
+	if (sc->msg_idx >= sc->msg->len)
+		panic("Invalid call to tegra_i2c_rx\n");
+
+	while(sc->msg_idx < sc->msg->len) {
+		reg = RD4(sc, I2C_FIFO_STATUS);
+		if (I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(reg) == 0)
+			break;
+		cnt = min(4, sc->msg->len - sc->msg_idx);
+		reg = RD4(sc, I2C_RX_FIFO);
+		for (i = 0;  i < cnt; i++) {
+			sc->msg->buf[sc->msg_idx] = (reg >> (i * 8)) & 0xFF;
+			sc->msg_idx++;
+		}
+	}
+
+	if (sc->msg_idx >= sc->msg->len)
+		return (0);
+	return (sc->msg->len - sc->msg_idx - 1);
+}
+
+static void
+tegra_i2c_intr(void *arg)
+{
+	struct tegra_i2c_softc *sc;
+	uint32_t status, reg;
+	int rv;
+
+	sc = (struct tegra_i2c_softc *)arg;
+
+	LOCK(sc);
+	status = RD4(sc, I2C_INTERRUPT_SOURCE_REGISTER);
+	if (sc->msg == NULL) {
+		/* Unexpected interrupt - disable FIFOs, clear reset. */
+		reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+		reg &= ~I2C_INT_TFIFO_DATA_REQ;
+		reg &= ~I2C_INT_RFIFO_DATA_REQ;
+		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+		WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status);
+		UNLOCK(sc);
+		return;
+	}
+
+	if ((status & I2C_ERROR_MASK) != 0) {
+		if (status & I2C_INT_NOACK)
+			sc->bus_err = IIC_ENOACK;
+		if (status & I2C_INT_ARB_LOST)
+			sc->bus_err = IIC_EBUSERR;
+		if ((status & I2C_INT_TFIFO_OVR) ||
+		    (status & I2C_INT_RFIFO_UNF))
+			sc->bus_err = IIC_EBUSERR;
+		sc->done = 1;
+	} else if ((status & I2C_INT_RFIFO_DATA_REQ) &&
+	    (sc->msg != NULL) && (sc->msg->flags & IIC_M_RD)) {
+		rv = tegra_i2c_rx(sc);
+		if (rv == 0) {
+			reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+			reg &= ~I2C_INT_RFIFO_DATA_REQ;
+			WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
+		}
+	} else if ((status & I2C_INT_TFIFO_DATA_REQ) &&
+	    (sc->msg != NULL) && !(sc->msg->flags & IIC_M_RD)) {
+		rv = tegra_i2c_tx(sc);
+		if (rv == 0) {
+			reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+			reg &= ~I2C_INT_TFIFO_DATA_REQ;
+			WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
+		}
+	} else if ((status & I2C_INT_RFIFO_DATA_REQ) ||
+		    (status & I2C_INT_TFIFO_DATA_REQ)) {
+		device_printf(sc->dev, "Unexpected data interrupt: 0x%08X\n",
+		    status);
+		reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
+		reg &= ~I2C_INT_TFIFO_DATA_REQ;
+		reg &= ~I2C_INT_RFIFO_DATA_REQ;
+		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
+	}
+	if (status & I2C_INT_PACKET_XFER_COMPLETE)
+		sc->done = 1;
+	WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status);
+	if (sc->done) {
+		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+		wakeup(&(sc->done));
+	}
+	UNLOCK(sc);
+}
+
+static void
+tegra_i2c_start_msg(struct tegra_i2c_softc *sc, struct iic_msg *msg,
+    enum tegra_i2c_xfer_type xtype)
+{
+	uint32_t tmp, mask;
+
+	/* Packet header. */
+	tmp =  (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+	   PACKET_HEADER0_PROTOCOL_I2C |
+	   (1 << PACKET_HEADER0_CONT_ID_SHIFT) |
+	   (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
+	WR4(sc, I2C_TX_PACKET_FIFO, tmp);
+
+
+	/* Packet size. */
+	WR4(sc, I2C_TX_PACKET_FIFO, msg->len - 1);
+
+	/* I2C header. */
+	tmp = I2C_HEADER_IE_ENABLE;
+	if (xtype == XFER_CONTINUE)
+		tmp |= I2C_HEADER_CONTINUE_XFER;
+	else if (xtype == XFER_REPEAT_START)
+		tmp |= I2C_HEADER_REPEAT_START;
+	tmp |= msg->slave << I2C_HEADER_SLAVE_ADDR_SHIFT;
+	if (msg->flags & IIC_M_RD) {
+		tmp |= I2C_HEADER_READ;
+		tmp |= 1 << I2C_HEADER_SLAVE_ADDR_SHIFT;
+	} else
+		tmp &= ~(1 << I2C_HEADER_SLAVE_ADDR_SHIFT);
+
+	WR4(sc, I2C_TX_PACKET_FIFO, tmp);
+
+	/* Interrupt mask. */
+	mask = I2C_INT_NOACK | I2C_INT_ARB_LOST | I2C_INT_PACKET_XFER_COMPLETE;
+	if (msg->flags & IIC_M_RD)
+		mask |= I2C_INT_RFIFO_DATA_REQ;
+	else
+		mask |= I2C_INT_TFIFO_DATA_REQ;
+	WR4(sc, I2C_INTERRUPT_MASK_REGISTER, mask);
+}
+
+static int
+tegra_i2c_poll(struct tegra_i2c_softc *sc)
+{
+	int timeout;
+
+	for(timeout = 10000; timeout > 0; timeout--)  {
+		UNLOCK(sc);
+		tegra_i2c_intr(sc);
+		LOCK(sc);
+		if (sc->done != 0)
+			 break;
+		DELAY(1);
+	}
+	if (timeout <= 0)
+		return (ETIMEDOUT);
+	return (0);
+}
+
+static int
+tegra_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+	int rv, i;
+	struct tegra_i2c_softc *sc;
+	enum tegra_i2c_xfer_type xtype;
+
+	sc = device_get_softc(dev);
+	LOCK(sc);
+
+	/* Get the bus. */
+	while (sc->bus_inuse == 1)
+		SLEEP(sc,  0);
+	sc->bus_inuse = 1;
+
+	rv = 0;
+	for (i = 0; i < nmsgs; i++) {
+		sc->msg = &msgs[i];
+		sc->msg_idx = 0;
+		sc->bus_err = 0;
+		sc->done = 0;
+		/* Check for valid parameters. */
+		if (sc->msg == NULL || sc->msg->buf == NULL ||
+		    sc->msg->len == 0) {
+			rv = EINVAL;
+			break;
+		}
+
+		/* Get flags for next transfer. */
+		if (i == (nmsgs - 1)) {
+			if (msgs[i].flags & IIC_M_NOSTOP)
+				xtype = XFER_CONTINUE;
+			else
+				xtype = XFER_STOP;
+		} else {
+			if (msgs[i + 1].flags & IIC_M_NOSTART)
+				xtype = XFER_CONTINUE;
+			else
+				xtype = XFER_REPEAT_START;
+		}
+		tegra_i2c_start_msg(sc, sc->msg, xtype);
+		if (cold)
+			rv = tegra_i2c_poll(sc);
+		else
+			rv = msleep(&sc->done, &sc->mtx, PZERO, "iic",
+			    I2C_REQUEST_TIMEOUT);
+
+		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
+		WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF);
+		if (rv == 0)
+			rv = sc->bus_err;
+		if (rv != 0)
+			break;
+	}
+
+	if (rv != 0) {
+		tegra_i2c_hw_init(sc);
+		tegra_i2c_flush_fifo(sc);
+	}
+
+	sc->msg = NULL;
+	sc->msg_idx = 0;
+	sc->bus_err = 0;
+	sc->done = 0;
+
+	/* Wake up the processes that are waiting for the bus. */
+	sc->bus_inuse = 0;
+	wakeup(sc);
+	UNLOCK(sc);
+
+	return (rv);
+}
+
+static int
+tegra_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+	struct tegra_i2c_softc *sc;
+	int busfreq;
+
+	sc = device_get_softc(dev);
+	busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
+	sc = device_get_softc(dev);
+	LOCK(sc);
+	tegra_i2c_setup_clk(sc, busfreq);
+	UNLOCK(sc);
+	return (0);
+}
+
+static int
+tegra_i2c_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_i2c_attach(device_t dev)
+{
+	int rv, rid;
+	phandle_t node;
+	struct tegra_i2c_softc *sc;
+	uint64_t freq;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+
+	LOCK_INIT(sc);
+
+	/* Get the memory resource for the register mapping. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot map registers.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* Allocate our IRQ resource. */
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate interrupt.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* FDT resources. */
+	rv = clk_get_by_ofw_name(dev, 0, "div-clk", &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get i2c clock: %d\n", rv);
+		goto fail;
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "i2c", &sc->reset);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get i2c reset\n");
+		return (ENXIO);
+	}
+	rv = OF_getencprop(node, "clock-frequency", &sc->bus_freq,
+	    sizeof(sc->bus_freq));
+	if (rv != sizeof(sc->bus_freq)) {
+		sc->bus_freq = 100000;
+		goto fail;
+	}
+
+	/* Request maximum frequency for I2C block 136MHz (408MHz / 3). */
+	rv = clk_set_freq(sc->clk, 136000000, CLK_SET_ROUND_DOWN);
+	if (rv != 0) {
+		device_printf(dev, "Cannot set clock frequency\n");
+		goto fail;
+	}
+	rv = clk_get_freq(sc->clk, &freq);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get clock frequency\n");
+		goto fail;
+	}
+	sc->core_freq = (uint32_t)freq;
+
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock: %d\n", rv);
+		goto fail;
+	}
+
+	/* Init hardware. */
+	rv = tegra_i2c_hw_init(sc);
+	if (rv) {
+		device_printf(dev, "tegra_i2c_activate failed\n");
+		goto fail;
+	}
+
+	/* Setup interrupt. */
+	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, tegra_i2c_intr, sc, &sc->irq_h);
+	if (rv) {
+		device_printf(dev, "Cannot setup interrupt.\n");
+		goto fail;
+	}
+
+	/* Attach the iicbus. */
+	sc->iicbus = device_add_child(dev, "iicbus", -1);
+	if (sc->iicbus == NULL) {
+		device_printf(dev, "Could not allocate iicbus instance.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* Probe and attach the iicbus. */
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+
+	return (rv);
+}
+
+static int
+tegra_i2c_detach(device_t dev)
+{
+	struct tegra_i2c_softc *sc;
+	int rv;
+
+	sc = device_get_softc(dev);
+	tegra_i2c_hw_init(sc);
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	LOCK_DESTROY(sc);
+	if (sc->iicbus)
+	    rv = device_delete_child(dev, sc->iicbus);
+	return (bus_generic_detach(dev));
+}
+
+static phandle_t
+tegra_i2c_get_node(device_t bus, device_t dev)
+{
+
+	/* Share controller node with iibus device. */
+	return (ofw_bus_get_node(bus));
+}
+
+static device_method_t tegra_i2c_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_i2c_probe),
+	DEVMETHOD(device_attach,	tegra_i2c_attach),
+	DEVMETHOD(device_detach,	tegra_i2c_detach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
+	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
+	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
+	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
+	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
+	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
+	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
+
+	/* OFW methods */
+	DEVMETHOD(ofw_bus_get_node,	tegra_i2c_get_node),
+
+	/* iicbus interface */
+	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
+	DEVMETHOD(iicbus_reset,		tegra_i2c_iicbus_reset),
+	DEVMETHOD(iicbus_transfer,	tegra_i2c_transfer),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_i2c_devclass;
+static DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods,
+    sizeof(struct tegra_i2c_softc));
+EARLY_DRIVER_MODULE(tegra_iic, simplebus, tegra_i2c_driver, tegra_i2c_devclass,
+    NULL, NULL, 73);


Property changes on: trunk/sys/arm/nvidia/tegra_i2c.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_lic.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_lic.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_lic.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,290 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_lic.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * Local interrupt controller driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define	LIC_VIRQ_CPU		0x00
+#define	LIC_VIRQ_COP		0x04
+#define	LIC_VFRQ_CPU		0x08
+#define	LIC_VFRQ_COP		0x0c
+#define	LIC_ISR			0x10
+#define	LIC_FIR			0x14
+#define	LIC_FIR_SET		0x18
+#define	LIC_FIR_CLR		0x1c
+#define	LIC_CPU_IER		0x20
+#define	LIC_CPU_IER_SET		0x24
+#define	LIC_CPU_IER_CLR		0x28
+#define	LIC_CPU_IEP_CLASS	0x2C
+#define	LIC_COP_IER		0x30
+#define	LIC_COP_IER_SET		0x34
+#define	LIC_COP_IER_CLR		0x38
+#define	LIC_COP_IEP_CLASS	0x3c
+
+#define	WR4(_sc, _b, _r, _v)	bus_write_4((_sc)->mem_res[_b], (_r), (_v))
+#define	RD4(_sc, _b, _r)	bus_read_4((_sc)->mem_res[_b], (_r))
+
+static struct resource_spec lic_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
+	{ SYS_RES_MEMORY,	2,	RF_ACTIVE },
+	{ SYS_RES_MEMORY,	3,	RF_ACTIVE },
+	{ SYS_RES_MEMORY,	4,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-ictlr", 	1},
+	{NULL,				0}
+};
+
+struct tegra_lic_sc {
+	device_t		dev;
+	struct resource		*mem_res[nitems(lic_spec)];
+	device_t		parent;
+};
+
+static int
+tegra_lic_activate_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+tegra_lic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	PIC_DISABLE_INTR(sc->parent, isrc);
+}
+
+static void
+tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	PIC_ENABLE_INTR(sc->parent, isrc);
+}
+
+static int
+tegra_lic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	return (PIC_MAP_INTR(sc->parent, data, isrcp));
+}
+
+static int
+tegra_lic_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+tegra_lic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
+}
+
+static int
+tegra_lic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
+}
+
+static void
+tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	PIC_PRE_ITHREAD(sc->parent, isrc);
+}
+
+
+static void
+tegra_lic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	PIC_POST_ITHREAD(sc->parent, isrc);
+}
+
+static void
+tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	PIC_POST_FILTER(sc->parent, isrc);
+}
+
+#ifdef SMP
+static int
+tegra_lic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_lic_sc *sc = device_get_softc(dev);
+
+	return (PIC_BIND_INTR(sc->parent, isrc));
+}
+#endif
+
+static int
+tegra_lic_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_lic_attach(device_t dev)
+{
+	struct tegra_lic_sc *sc;
+	phandle_t node;
+	phandle_t parent_xref;
+	int i, rv;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+
+	rv = OF_getencprop(node, "interrupt-parent", &parent_xref,
+	    sizeof(parent_xref));
+	if (rv <= 0) {
+		device_printf(dev, "Cannot read parent node property\n");
+		goto fail;
+	}
+	sc->parent = OF_device_from_xref(parent_xref);
+	if (sc->parent == NULL) {
+		device_printf(dev, "Cannott find parent controller\n");
+		goto fail;
+	}
+
+	if (bus_alloc_resources(dev, lic_spec, sc->mem_res)) {
+		device_printf(dev, "Cannott allocate resources\n");
+		goto fail;
+	}
+
+	/* Disable all interrupts, route all to irq */
+	for (i = 0; i < nitems(lic_spec); i++) {
+		if (sc->mem_res[i] == NULL)
+			continue;
+		WR4(sc, i, LIC_CPU_IER_CLR, 0xFFFFFFFF);
+		WR4(sc, i, LIC_CPU_IEP_CLASS, 0);
+	}
+
+
+	if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
+		device_printf(dev, "Cannot register PIC\n");
+		goto fail;
+	}
+	return (0);
+
+fail:
+	bus_release_resources(dev, lic_spec, sc->mem_res);
+	return (ENXIO);
+}
+
+static int
+tegra_lic_detach(device_t dev)
+{
+	struct tegra_lic_sc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+	for (i = 0; i < nitems(lic_spec); i++) {
+		if (sc->mem_res[i] == NULL)
+			continue;
+		bus_release_resource(dev, SYS_RES_MEMORY, i,
+		    sc->mem_res[i]);
+	}
+	return (0);
+}
+
+static device_method_t tegra_lic_methods[] = {
+	DEVMETHOD(device_probe,		tegra_lic_probe),
+	DEVMETHOD(device_attach,	tegra_lic_attach),
+	DEVMETHOD(device_detach,	tegra_lic_detach),
+
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_activate_intr,	tegra_lic_activate_intr),
+	DEVMETHOD(pic_disable_intr,	tegra_lic_disable_intr),
+	DEVMETHOD(pic_enable_intr,	tegra_lic_enable_intr),
+	DEVMETHOD(pic_map_intr,		tegra_lic_map_intr),
+	DEVMETHOD(pic_deactivate_intr,	tegra_lic_deactivate_intr),
+	DEVMETHOD(pic_setup_intr,	tegra_lic_setup_intr),
+	DEVMETHOD(pic_teardown_intr,	tegra_lic_teardown_intr),
+	DEVMETHOD(pic_pre_ithread,	tegra_lic_pre_ithread),
+	DEVMETHOD(pic_post_ithread,	tegra_lic_post_ithread),
+	DEVMETHOD(pic_post_filter,	tegra_lic_post_filter),
+#ifdef SMP
+	DEVMETHOD(pic_bind_intr,	tegra_lic_bind_intr),
+#endif
+	DEVMETHOD_END
+};
+
+devclass_t tegra_lic_devclass;
+static DEFINE_CLASS_0(lic, tegra_lic_driver, tegra_lic_methods,
+    sizeof(struct tegra_lic_sc));
+EARLY_DRIVER_MODULE(tegra_lic, simplebus, tegra_lic_driver, tegra_lic_devclass,
+    NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1);


Property changes on: trunk/sys/arm/nvidia/tegra_lic.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_mc.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_mc.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_mc.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,312 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_mc.c 317012 2017-04-16 08:18:37Z mmel $");
+
+/*
+ * Memory controller driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+
+#define	MC_INTSTATUS			0x000
+#define	MC_INTMASK			0x004
+#define	 MC_INT_DECERR_MTS			(1 << 16)
+#define	 MC_INT_SECERR_SEC			(1 << 13)
+#define	 MC_INT_DECERR_VPR			(1 << 12)
+#define	 MC_INT_INVALID_APB_ASID_UPDATE		(1 << 11)
+#define	 MC_INT_INVALID_SMMU_PAGE		(1 << 10)
+#define	 MC_INT_ARBITRATION_EMEM		(1 << 9)
+#define	 MC_INT_SECURITY_VIOLATION		(1 << 8)
+#define	 MC_INT_DECERR_EMEM			(1 << 6)
+#define	 MC_INT_INT_MASK	(MC_INT_DECERR_MTS |			\
+				 MC_INT_SECERR_SEC |			\
+				 MC_INT_DECERR_VPR |			\
+				 MC_INT_INVALID_APB_ASID_UPDATE |	\
+				 MC_INT_INVALID_SMMU_PAGE |		\
+				 MC_INT_ARBITRATION_EMEM |		\
+				 MC_INT_SECURITY_VIOLATION |		\
+				 MC_INT_DECERR_EMEM)
+
+#define	MC_ERR_STATUS			0x008
+#define	 MC_ERR_TYPE(x)				(((x) >> 28) & 0x7)
+#define	 MC_ERR_TYPE_DECERR_EMEM		2
+#define	 MC_ERR_TYPE_SECURITY_TRUSTZONE		3
+#define	 MC_ERR_TYPE_SECURITY_CARVEOUT		4
+#define	 MC_ERR_TYPE_INVALID_SMMU_PAGE		6
+#define	 MC_ERR_INVALID_SMMU_PAGE_READABLE 	(1 << 27)
+#define	 MC_ERR_INVALID_SMMU_PAGE_WRITABLE	(1 << 26)
+#define	 MC_ERR_INVALID_SMMU_PAGE_NONSECURE	(1 << 25)
+#define	 MC_ERR_ADR_HI(x)			(((x) >> 20) & 0x3)
+#define	 MC_ERR_SWAP				(1 << 18)
+#define	 MC_ERR_SECURITY			(1 << 17)
+#define	 MC_ERR_RW				(1 << 16)
+#define	 MC_ERR_ADR1(x)				(((x) >> 12) & 0x7)
+#define	 MC_ERR_ID(x)				(((x) >> 0) & 07F)
+
+#define	MC_ERR_ADDR			0x00C
+#define	MC_EMEM_CFG			0x050
+#define	MC_EMEM_ADR_CFG			0x054
+#define	 MC_EMEM_NUMDEV(x)			(((x) >> 0 ) & 0x1)
+
+#define	MC_EMEM_ADR_CFG_DEV0		0x058
+#define	MC_EMEM_ADR_CFG_DEV1		0x05C
+#define	 EMEM_DEV_DEVSIZE(x)			(((x) >> 16) & 0xF)
+#define	 EMEM_DEV_BANKWIDTH(x)			(((x) >>  8) & 0x3)
+#define	 EMEM_DEV_COLWIDTH(x)			(((x) >>  8) & 0x3)
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+#define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	SLEEP(_sc, timeout)	mtx_sleep(sc, &sc->mtx, 0, "tegra_mc", timeout);
+#define	LOCK_INIT(_sc)							\
+	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_mc", MTX_DEF)
+#define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
+#define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
+#define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-mc",	1},
+	{NULL,			0}
+};
+
+struct tegra_mc_softc {
+	device_t		dev;
+	struct mtx		mtx;
+
+	struct resource		*mem_res;
+	struct resource		*irq_res;
+	void			*irq_h;
+
+	clk_t			clk;
+};
+
+static char *smmu_err_tbl[16] = {
+	"reserved",		/*  0 */
+	"reserved",		/*  1 */
+	"DRAM decode",		/*  2 */
+	"Trustzome Security",	/*  3 */
+	"Security carveout",	/*  4 */
+	"reserved",		/*  5 */
+	"Invalid SMMU page",	/*  6 */
+	"reserved",	/*  7 */
+};
+
+static void
+tegra_mc_intr(void *arg)
+{
+	struct tegra_mc_softc *sc;
+	uint32_t stat, err;
+	uint64_t addr;
+
+	sc = (struct tegra_mc_softc *)arg;
+
+	stat = RD4(sc, MC_INTSTATUS);
+	if ((stat & MC_INT_INT_MASK) == 0) {
+		WR4(sc, MC_INTSTATUS, stat);
+		return;
+	}
+
+	device_printf(sc->dev, "Memory Controller Interrupt:\n");
+	if (stat & MC_INT_DECERR_MTS)
+		printf(" - MTS carveout violation\n");
+	if (stat & MC_INT_SECERR_SEC)
+		printf(" - SEC carveout violation\n");
+	if (stat & MC_INT_DECERR_VPR)
+		printf(" - VPR requirements violated\n");
+	if (stat & MC_INT_INVALID_APB_ASID_UPDATE)
+		printf(" - ivalid APB ASID update\n");
+	if (stat & MC_INT_INVALID_SMMU_PAGE)
+		printf(" - SMMU address translation error\n");
+	if (stat & MC_INT_ARBITRATION_EMEM)
+		printf(" - arbitration deadlock-prevention threshold hit\n");
+	if (stat & MC_INT_SECURITY_VIOLATION)
+		printf(" - SMMU address translation security error\n");
+	if (stat & MC_INT_DECERR_EMEM)
+		printf(" - SMMU address decode error\n");
+
+	if ((stat & (MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
+	   MC_INT_DECERR_EMEM)) != 0) {
+		err = RD4(sc, MC_ERR_STATUS);
+		addr = RD4(sc, MC_ERR_STATUS);
+		addr |= (uint64_t)(MC_ERR_ADR_HI(err)) << 32;
+		printf(" at 0x%012llX [%s %s %s]  - %s error.\n",
+		    addr,
+		    stat & MC_ERR_SWAP ? "Swap, " : "",
+		    stat & MC_ERR_SECURITY ? "Sec, " : "",
+		    stat & MC_ERR_RW ? "Write" : "Read",
+		    smmu_err_tbl[MC_ERR_TYPE(err)]);
+	}
+	WR4(sc, MC_INTSTATUS, stat);
+}
+
+static void
+tegra_mc_init_hw(struct tegra_mc_softc *sc)
+{
+
+	/* Disable and acknowledge all interrupts */
+	WR4(sc, MC_INTMASK, 0);
+	WR4(sc, MC_INTSTATUS, MC_INT_INT_MASK);
+}
+
+static int
+tegra_mc_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+	device_set_desc(dev, "Tegra Memory Controller");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_mc_attach(device_t dev)
+{
+	int rv, rid;
+	struct tegra_mc_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	LOCK_INIT(sc);
+
+	/* Get the memory resource for the register mapping. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot map registers.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* Allocate our IRQ resource. */
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate interrupt.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* OFW resources. */
+	rv = clk_get_by_ofw_name(dev, 0, "mc", &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get mc clock: %d\n", rv);
+		goto fail;
+	}
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock: %d\n", rv);
+		goto fail;
+	}
+
+	/* Init hardware. */
+	tegra_mc_init_hw(sc);
+
+	/* Setup  interrupt */
+	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, tegra_mc_intr, sc, &sc->irq_h);
+	if (rv) {
+		device_printf(dev, "Cannot setup interrupt.\n");
+		goto fail;
+	}
+
+	/* Enable Interrupts */
+	WR4(sc, MC_INTMASK, MC_INT_INT_MASK);
+
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+
+	return (rv);
+}
+
+static int
+tegra_mc_detach(device_t dev)
+{
+	struct tegra_mc_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	LOCK_DESTROY(sc);
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_mc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_mc_probe),
+	DEVMETHOD(device_attach,	tegra_mc_attach),
+	DEVMETHOD(device_detach,	tegra_mc_detach),
+
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_mc_devclass;
+static DEFINE_CLASS_0(mc, tegra_mc_driver, tegra_mc_methods,
+    sizeof(struct tegra_mc_softc));
+DRIVER_MODULE(tegra_mc, simplebus, tegra_mc_driver, tegra_mc_devclass,
+    NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/tegra_mc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_pcie.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_pcie.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_pcie.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1637 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_pcie.c 332025 2018-04-04 13:23:06Z mmel $");
+
+/*
+ * Nvidia Integrated PCI/PCI-Express controller driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/intr.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofwpci.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+
+#include <arm/nvidia/tegra_pmc.h>
+
+#include "ofw_bus_if.h"
+#include "msi_if.h"
+#include "pcib_if.h"
+#include "pic_if.h"
+
+
+#define	AFI_AXI_BAR0_SZ				0x000
+#define	AFI_AXI_BAR1_SZ				0x004
+#define	AFI_AXI_BAR2_SZ				0x008
+#define	AFI_AXI_BAR3_SZ				0x00c
+#define	AFI_AXI_BAR4_SZ				0x010
+#define	AFI_AXI_BAR5_SZ				0x014
+#define	AFI_AXI_BAR0_START			0x018
+#define	AFI_AXI_BAR1_START			0x01c
+#define	AFI_AXI_BAR2_START			0x020
+#define	AFI_AXI_BAR3_START			0x024
+#define	AFI_AXI_BAR4_START			0x028
+#define	AFI_AXI_BAR5_START			0x02c
+#define	AFI_FPCI_BAR0				0x030
+#define	AFI_FPCI_BAR1				0x034
+#define	AFI_FPCI_BAR2				0x038
+#define	AFI_FPCI_BAR3				0x03c
+#define	AFI_FPCI_BAR4				0x040
+#define	AFI_FPCI_BAR5				0x044
+#define	AFI_MSI_BAR_SZ				0x060
+#define	AFI_MSI_FPCI_BAR_ST			0x064
+#define	AFI_MSI_AXI_BAR_ST			0x068
+#define AFI_MSI_VEC(x)				(0x06c + 4 * (x))
+#define AFI_MSI_EN_VEC(x)			(0x08c + 4 * (x))
+#define	 AFI_MSI_INTR_IN_REG				32
+#define	 AFI_MSI_REGS					8
+
+#define	AFI_CONFIGURATION			0x0ac
+#define	 AFI_CONFIGURATION_EN_FPCI			(1 << 0)
+
+#define	AFI_FPCI_ERROR_MASKS			0x0b0
+#define	AFI_INTR_MASK				0x0b4
+#define	 AFI_INTR_MASK_MSI_MASK				(1 << 8)
+#define	 AFI_INTR_MASK_INT_MASK				(1 << 0)
+
+#define	AFI_INTR_CODE				0x0b8
+#define	 AFI_INTR_CODE_MASK				0xf
+#define	 AFI_INTR_CODE_INT_CODE_INI_SLVERR		1
+#define	 AFI_INTR_CODE_INT_CODE_INI_DECERR		2
+#define	 AFI_INTR_CODE_INT_CODE_TGT_SLVERR		3
+#define	 AFI_INTR_CODE_INT_CODE_TGT_DECERR		4
+#define	 AFI_INTR_CODE_INT_CODE_TGT_WRERR		5
+#define	 AFI_INTR_CODE_INT_CODE_SM_MSG			6
+#define	 AFI_INTR_CODE_INT_CODE_DFPCI_DECERR		7
+#define	 AFI_INTR_CODE_INT_CODE_AXI_DECERR		8
+#define	 AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT		9
+#define	 AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE		10
+#define	 AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE		11
+#define	 AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE		12
+#define	 AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE		13
+#define	 AFI_INTR_CODE_INT_CODE_P2P_ERROR		14
+
+
+#define	AFI_INTR_SIGNATURE			0x0bc
+#define	AFI_UPPER_FPCI_ADDRESS			0x0c0
+#define	AFI_SM_INTR_ENABLE			0x0c4
+#define	 AFI_SM_INTR_RP_DEASSERT			(1 << 14)
+#define	 AFI_SM_INTR_RP_ASSERT				(1 << 13)
+#define	 AFI_SM_INTR_HOTPLUG				(1 << 12)
+#define	 AFI_SM_INTR_PME				(1 << 11)
+#define	 AFI_SM_INTR_FATAL_ERROR			(1 << 10)
+#define	 AFI_SM_INTR_UNCORR_ERROR			(1 <<  9)
+#define	 AFI_SM_INTR_CORR_ERROR				(1 <<  8)
+#define	 AFI_SM_INTR_INTD_DEASSERT			(1 <<  7)
+#define	 AFI_SM_INTR_INTC_DEASSERT			(1 <<  6)
+#define	 AFI_SM_INTR_INTB_DEASSERT			(1 <<  5)
+#define	 AFI_SM_INTR_INTA_DEASSERT			(1 <<  4)
+#define	 AFI_SM_INTR_INTD_ASSERT			(1 <<  3)
+#define	 AFI_SM_INTR_INTC_ASSERT			(1 <<  2)
+#define	 AFI_SM_INTR_INTB_ASSERT			(1 <<  1)
+#define	 AFI_SM_INTR_INTA_ASSERT			(1 <<  0)
+
+#define	AFI_AFI_INTR_ENABLE			0x0c8
+#define	 AFI_AFI_INTR_ENABLE_CODE(code)			(1 << (code))
+
+#define	AFI_PCIE_CONFIG				0x0f8
+#define	 AFI_PCIE_CONFIG_PCIE_DISABLE(x)		(1 << ((x) + 1))
+#define	 AFI_PCIE_CONFIG_PCIE_DISABLE_ALL		0x6
+#define	 AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK	(0xf << 20)
+#define	 AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1	(0x0 << 20)
+#define	 AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1	(0x1 << 20)
+
+#define	AFI_FUSE				0x104
+#define	 AFI_FUSE_PCIE_T0_GEN2_DIS			(1 << 2)
+
+#define	AFI_PEX0_CTRL				0x110
+#define	AFI_PEX1_CTRL				0x118
+#define	AFI_PEX2_CTRL				0x128
+#define	 AFI_PEX_CTRL_OVERRIDE_EN			(1 << 4)
+#define	 AFI_PEX_CTRL_REFCLK_EN				(1 << 3)
+#define	 AFI_PEX_CTRL_CLKREQ_EN				(1 << 1)
+#define	 AFI_PEX_CTRL_RST_L				(1 << 0)
+
+#define	AFI_AXI_BAR6_SZ				0x134
+#define	AFI_AXI_BAR7_SZ				0x138
+#define	AFI_AXI_BAR8_SZ				0x13c
+#define	AFI_AXI_BAR6_START			0x140
+#define	AFI_AXI_BAR7_START			0x144
+#define	AFI_AXI_BAR8_START			0x148
+#define	AFI_FPCI_BAR6				0x14c
+#define	AFI_FPCI_BAR7				0x150
+#define	AFI_FPCI_BAR8				0x154
+#define	AFI_PLLE_CONTROL			0x160
+#define	 AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL	(1 << 9)
+#define	 AFI_PLLE_CONTROL_BYPASS_PCIE2PLLE_CONTROL	(1 << 8)
+#define	 AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN		(1 << 1)
+#define	 AFI_PLLE_CONTROL_PCIE2PLLE_CONTROL_EN		(1 << 0)
+
+#define	AFI_PEXBIAS_CTRL			0x168
+
+/* FPCI Address space */
+#define	FPCI_MAP_IO			0xfdfc000000ULL
+#define	FPCI_MAP_TYPE0_CONFIG		0xfdfc000000ULL
+#define	FPCI_MAP_TYPE1_CONFIG		0xfdff000000ULL
+#define	FPCI_MAP_EXT_TYPE0_CONFIG	0xfe00000000ULL
+#define	FPCI_MAP_EXT_TYPE1_CONFIG	0xfe10000000ULL
+
+/* Configuration space */
+#define	RP_VEND_XP	0x00000F00
+#define	 RP_VEND_XP_DL_UP	(1 << 30)
+
+#define	RP_PRIV_MISC	0x00000FE0
+#define	 RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0)
+#define	 RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0)
+
+#define	RP_LINK_CONTROL_STATUS			0x00000090
+#define	 RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE	0x20000000
+#define	 RP_LINK_CONTROL_STATUS_LINKSTAT_MASK	0x3fff0000
+
+/* Wait 50 ms (per port) for link. */
+#define	TEGRA_PCIE_LINKUP_TIMEOUT	50000
+
+#define TEGRA_PCIB_MSI_ENABLE
+
+#define	DEBUG
+#ifdef DEBUG
+#define	debugf(fmt, args...) do { printf(fmt,##args); } while (0)
+#else
+#define	debugf(fmt, args...)
+#endif
+
+/*
+ * Configuration space format:
+ *    [27:24] extended register
+ *    [23:16] bus
+ *    [15:11] slot (device)
+ *    [10: 8] function
+ *    [ 7: 0] register
+ */
+#define	PCI_CFG_EXT_REG(reg)	((((reg) >> 8) & 0x0f) << 24)
+#define	PCI_CFG_BUS(bus)	(((bus) & 0xff) << 16)
+#define	PCI_CFG_DEV(dev)	(((dev) & 0x1f) << 11)
+#define	PCI_CFG_FUN(fun)	(((fun) & 0x07) << 8)
+#define	PCI_CFG_BASE_REG(reg)	((reg)  & 0xff)
+
+#define	PADS_WR4(_sc, _r, _v)	bus_write_4((_sc)-pads_mem_res, (_r), (_v))
+#define	PADS_RD4(_sc, _r)	bus_read_4((_sc)->pads_mem_res, (_r))
+#define	AFI_WR4(_sc, _r, _v)	bus_write_4((_sc)->afi_mem_res, (_r), (_v))
+#define	AFI_RD4(_sc, _r)	bus_read_4((_sc)->afi_mem_res, (_r))
+
+static struct {
+	bus_size_t	axi_start;
+	bus_size_t	fpci_start;
+	bus_size_t	size;
+} bars[] = {
+    {AFI_AXI_BAR0_START, AFI_FPCI_BAR0, AFI_AXI_BAR0_SZ},	/* BAR 0 */
+    {AFI_AXI_BAR1_START, AFI_FPCI_BAR1, AFI_AXI_BAR1_SZ},	/* BAR 1 */
+    {AFI_AXI_BAR2_START, AFI_FPCI_BAR2, AFI_AXI_BAR2_SZ},	/* BAR 2 */
+    {AFI_AXI_BAR3_START, AFI_FPCI_BAR3, AFI_AXI_BAR3_SZ},	/* BAR 3 */
+    {AFI_AXI_BAR4_START, AFI_FPCI_BAR4, AFI_AXI_BAR4_SZ},	/* BAR 4 */
+    {AFI_AXI_BAR5_START, AFI_FPCI_BAR5, AFI_AXI_BAR5_SZ},	/* BAR 5 */
+    {AFI_AXI_BAR6_START, AFI_FPCI_BAR6, AFI_AXI_BAR6_SZ},	/* BAR 6 */
+    {AFI_AXI_BAR7_START, AFI_FPCI_BAR7, AFI_AXI_BAR7_SZ},	/* BAR 7 */
+    {AFI_AXI_BAR8_START, AFI_FPCI_BAR8, AFI_AXI_BAR8_SZ},	/* BAR 8 */
+    {AFI_MSI_AXI_BAR_ST, AFI_MSI_FPCI_BAR_ST, AFI_MSI_BAR_SZ},	/* MSI 9 */
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-pcie",	1},
+	{NULL,		 		0},
+};
+
+#define	TEGRA_FLAG_MSI_USED	0x0001
+struct tegra_pcib_irqsrc {
+	struct intr_irqsrc	isrc;
+	u_int			irq;
+	u_int			flags;
+};
+
+struct tegra_pcib_port {
+	int		enabled;
+	int 		port_idx;		/* chip port index */
+	int		num_lanes;		/* number of lanes */
+	bus_size_t	afi_pex_ctrl;		/* offset of afi_pex_ctrl */
+	phy_t		phy;			/* port phy */
+
+	/* Config space properties. */
+	bus_addr_t	rp_base_addr;		/* PA of config window */
+	bus_size_t	rp_size;		/* size of config window */
+	bus_space_handle_t cfg_handle;		/* handle of config window */
+};
+
+#define	TEGRA_PCIB_MAX_PORTS	3
+#define	TEGRA_PCIB_MAX_MSI	AFI_MSI_INTR_IN_REG * AFI_MSI_REGS
+struct tegra_pcib_softc {
+	struct ofw_pci_softc	ofw_pci;
+	device_t		dev;
+	struct mtx		mtx;
+	struct resource		*pads_mem_res;
+	struct resource		*afi_mem_res;
+	struct resource		*cfg_mem_res;
+	struct resource 	*irq_res;
+	struct resource 	*msi_irq_res;
+	void			*intr_cookie;
+	void			*msi_intr_cookie;
+
+	struct ofw_pci_range	mem_range;
+	struct ofw_pci_range	pref_mem_range;
+	struct ofw_pci_range	io_range;
+
+	clk_t			clk_pex;
+	clk_t			clk_afi;
+	clk_t			clk_pll_e;
+	clk_t			clk_cml;
+	hwreset_t		hwreset_pex;
+	hwreset_t		hwreset_afi;
+	hwreset_t		hwreset_pcie_x;
+	regulator_t		supply_avddio_pex;
+	regulator_t		supply_dvddio_pex;
+	regulator_t		supply_avdd_pex_pll;
+	regulator_t		supply_hvdd_pex;
+	regulator_t		supply_hvdd_pex_pll_e;
+	regulator_t		supply_vddio_pex_ctl;
+	regulator_t		supply_avdd_pll_erefe;
+
+	vm_offset_t		msi_page;	/* VA of MSI page */
+	bus_addr_t		cfg_base_addr;	/* base address of config */
+	bus_size_t		cfg_cur_offs; 	/* currently mapped window */
+	bus_space_handle_t 	cfg_handle;	/* handle of config window */
+	bus_space_tag_t 	bus_tag;	/* tag of config window */
+	int			lanes_cfg;
+	int			num_ports;
+	struct tegra_pcib_port *ports[TEGRA_PCIB_MAX_PORTS];
+	struct tegra_pcib_irqsrc *isrcs;
+};
+
+static int
+tegra_pcib_maxslots(device_t dev)
+{
+	return (16);
+}
+
+static int
+tegra_pcib_route_interrupt(device_t bus, device_t dev, int pin)
+{
+	struct tegra_pcib_softc *sc;
+	u_int irq;
+
+	sc = device_get_softc(bus);
+	irq = intr_map_clone_irq(rman_get_start(sc->irq_res));
+	device_printf(bus, "route pin %d for device %d.%d to %u\n",
+		      pin, pci_get_slot(dev), pci_get_function(dev),
+		      irq);
+
+	return (irq);
+}
+
+static int
+tegra_pcbib_map_cfg(struct tegra_pcib_softc *sc, u_int bus, u_int slot,
+    u_int func, u_int reg)
+{
+	bus_size_t offs;
+	int rv;
+
+	offs = sc->cfg_base_addr;
+	offs |= PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | PCI_CFG_FUN(func) |
+	    PCI_CFG_EXT_REG(reg);
+	if ((sc->cfg_handle != 0) && (sc->cfg_cur_offs == offs))
+		return (0);
+	if (sc->cfg_handle != 0)
+		bus_space_unmap(sc->bus_tag, sc->cfg_handle, 0x800);
+
+	rv = bus_space_map(sc->bus_tag, offs, 0x800, 0, &sc->cfg_handle);
+	if (rv != 0)
+		device_printf(sc->dev, "Cannot map config space\n");
+	else
+		sc->cfg_cur_offs = offs;
+	return (rv);
+}
+
+static uint32_t
+tegra_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+    u_int reg, int bytes)
+{
+	struct tegra_pcib_softc *sc;
+	bus_space_handle_t hndl;
+	uint32_t off;
+	uint32_t val;
+	int rv, i;
+
+	sc = device_get_softc(dev);
+	if (bus == 0) {
+		if (func != 0)
+			return (0xFFFFFFFF);
+		for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+			if ((sc->ports[i] != NULL) &&
+			    (sc->ports[i]->port_idx == slot)) {
+				hndl = sc->ports[i]->cfg_handle;
+				off = reg & 0xFFF;
+				break;
+			}
+		}
+		if (i >= TEGRA_PCIB_MAX_PORTS)
+			return (0xFFFFFFFF);
+	} else {
+		rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg);
+		if (rv != 0)
+			return (0xFFFFFFFF);
+		hndl = sc->cfg_handle;
+		off = PCI_CFG_BASE_REG(reg);
+	}
+
+	val = bus_space_read_4(sc->bus_tag, hndl, off & ~3);
+	switch (bytes) {
+	case 4:
+		break;
+	case 2:
+		if (off & 3)
+			val >>= 16;
+		val &= 0xffff;
+		break;
+	case 1:
+		val >>= ((off & 3) << 3);
+		val &= 0xff;
+		break;
+	}
+	return val;
+}
+
+static void
+tegra_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+    u_int reg, uint32_t val, int bytes)
+{
+	struct tegra_pcib_softc *sc;
+	bus_space_handle_t hndl;
+	uint32_t off;
+	uint32_t val2;
+	int rv, i;
+
+	sc = device_get_softc(dev);
+	if (bus == 0) {
+		if (func != 0)
+			return;
+		for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+			if ((sc->ports[i] != NULL) &&
+			    (sc->ports[i]->port_idx == slot)) {
+				hndl = sc->ports[i]->cfg_handle;
+				off = reg & 0xFFF;
+				break;
+			}
+		}
+		if (i >= TEGRA_PCIB_MAX_PORTS)
+			return;
+	} else {
+		rv = tegra_pcbib_map_cfg(sc, bus, slot, func, reg);
+		if (rv != 0)
+			return;
+		hndl = sc->cfg_handle;
+		off = PCI_CFG_BASE_REG(reg);
+	}
+
+	switch (bytes) {
+	case 4:
+		bus_space_write_4(sc->bus_tag, hndl, off, val);
+		break;
+	case 2:
+		val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3);
+		val2 &= ~(0xffff << ((off & 3) << 3));
+		val2 |= ((val & 0xffff) << ((off & 3) << 3));
+		bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2);
+		break;
+	case 1:
+		val2 = bus_space_read_4(sc->bus_tag, hndl, off & ~3);
+		val2 &= ~(0xff << ((off & 3) << 3));
+		val2 |= ((val & 0xff) << ((off & 3) << 3));
+		bus_space_write_4(sc->bus_tag, hndl, off & ~3, val2);
+		break;
+	}
+}
+
+static int tegra_pci_intr(void *arg)
+{
+	struct tegra_pcib_softc *sc = arg;
+	uint32_t code, signature;
+
+	code = bus_read_4(sc->afi_mem_res, AFI_INTR_CODE) & AFI_INTR_CODE_MASK;
+	signature = bus_read_4(sc->afi_mem_res, AFI_INTR_SIGNATURE);
+	bus_write_4(sc->afi_mem_res, AFI_INTR_CODE, 0);
+	if (code == AFI_INTR_CODE_INT_CODE_SM_MSG)
+		return(FILTER_STRAY);
+
+	printf("tegra_pci_intr: code %x sig %x\n", code, signature);
+	return (FILTER_HANDLED);
+}
+
+/* -----------------------------------------------------------------------
+ *
+ * 	PCI MSI interface
+ */
+static int
+tegra_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount,
+    int *irqs)
+{
+	phandle_t msi_parent;
+
+	/* XXXX ofw_bus_msimap() don't works for Tegra DT.
+	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+	    NULL);
+	*/
+	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+	return (intr_alloc_msi(pci, child, msi_parent, count, maxcount,
+	    irqs));
+}
+
+static int
+tegra_pcib_release_msi(device_t pci, device_t child, int count, int *irqs)
+{
+	phandle_t msi_parent;
+
+	/* XXXX ofw_bus_msimap() don't works for Tegra DT.
+	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+	    NULL);
+	*/
+	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+	return (intr_release_msi(pci, child, msi_parent, count, irqs));
+}
+
+static int
+tegra_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
+    uint32_t *data)
+{
+	phandle_t msi_parent;
+
+	/* XXXX ofw_bus_msimap() don't works for Tegra DT.
+	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+	    NULL);
+	*/
+	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+	return (intr_map_msi(pci, child, msi_parent, irq, addr, data));
+}
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+
+static inline void
+tegra_pcib_isrc_mask(struct tegra_pcib_softc *sc,
+     struct tegra_pcib_irqsrc *tgi, uint32_t val)
+{
+	uint32_t reg;
+	int offs, bit;
+
+	offs = tgi->irq / AFI_MSI_INTR_IN_REG;
+	bit = 1 << (tgi->irq % AFI_MSI_INTR_IN_REG);
+
+	if (val != 0)
+		AFI_WR4(sc, AFI_MSI_VEC(offs), bit);
+	reg = AFI_RD4(sc, AFI_MSI_EN_VEC(offs));
+	if (val !=  0)
+		reg |= bit;
+	else
+		reg &= ~bit;
+	AFI_WR4(sc, AFI_MSI_EN_VEC(offs), reg);
+}
+
+static int
+tegra_pcib_msi_intr(void *arg)
+{
+	u_int irq, i, bit, reg;
+	struct tegra_pcib_softc *sc;
+	struct trapframe *tf;
+	struct tegra_pcib_irqsrc *tgi;
+
+	sc = (struct tegra_pcib_softc *)arg;
+	tf = curthread->td_intr_frame;
+
+	for (i = 0; i < AFI_MSI_REGS; i++) {
+		reg = AFI_RD4(sc, AFI_MSI_VEC(i));
+		/* Handle one vector. */
+		while (reg != 0) {
+			bit = ffs(reg) - 1;
+			/* Send EOI */
+			AFI_WR4(sc, AFI_MSI_VEC(i), 1 << bit);
+			irq = i * AFI_MSI_INTR_IN_REG + bit;
+			tgi = &sc->isrcs[irq];
+			if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) {
+				/* Disable stray. */
+				tegra_pcib_isrc_mask(sc, tgi, 0);
+				device_printf(sc->dev,
+				    "Stray irq %u disabled\n", irq);
+			}
+			reg = AFI_RD4(sc, AFI_MSI_VEC(i));
+		}
+	}
+	return (FILTER_HANDLED);
+}
+
+static int
+tegra_pcib_msi_attach(struct tegra_pcib_softc *sc)
+{
+	int error;
+	uint32_t irq;
+	const char *name;
+
+	sc->isrcs = malloc(sizeof(*sc->isrcs) * TEGRA_PCIB_MAX_MSI, M_DEVBUF,
+	    M_WAITOK | M_ZERO);
+
+	name = device_get_nameunit(sc->dev);
+	for (irq = 0; irq < TEGRA_PCIB_MAX_MSI; irq++) {
+		sc->isrcs[irq].irq = irq;
+		error = intr_isrc_register(&sc->isrcs[irq].isrc,
+		    sc->dev, 0, "%s,%u", name, irq);
+		if (error != 0)
+			return (error); /* XXX deregister ISRCs */
+	}
+	if (intr_msi_register(sc->dev,
+	    OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0)
+		return (ENXIO);
+
+	return (0);
+}
+
+static int
+tegra_pcib_msi_detach(struct tegra_pcib_softc *sc)
+{
+
+	/*
+	 *  There has not been established any procedure yet
+	 *  how to detach PIC from living system correctly.
+	 */
+	device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+	return (EBUSY);
+}
+
+
+static void
+tegra_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_pcib_softc *sc;
+	struct tegra_pcib_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_pcib_irqsrc *)isrc;
+	tegra_pcib_isrc_mask(sc, tgi, 0);
+}
+
+static void
+tegra_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_pcib_softc *sc;
+	struct tegra_pcib_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_pcib_irqsrc *)isrc;
+	tegra_pcib_isrc_mask(sc, tgi, 1);
+}
+
+/* MSI interrupts are edge trigered -> do nothing */
+static void
+tegra_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static void
+tegra_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static void
+tegra_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+}
+
+static int
+tegra_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_pcib_softc *sc;
+	struct tegra_pcib_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_pcib_irqsrc *)isrc;
+
+	if (data == NULL || data->type != INTR_MAP_DATA_MSI)
+		return (ENOTSUP);
+
+	if (isrc->isrc_handlers == 0)
+		tegra_pcib_msi_enable_intr(dev, isrc);
+
+	return (0);
+}
+
+static int
+tegra_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_pcib_softc *sc;
+	struct tegra_pcib_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_pcib_irqsrc *)isrc;
+
+	if (isrc->isrc_handlers == 0)
+		tegra_pcib_isrc_mask(sc, tgi, 0);
+	return (0);
+}
+
+
+static int
+tegra_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+    device_t *pic, struct intr_irqsrc **srcs)
+{
+	struct tegra_pcib_softc *sc;
+	int i, irq, end_irq;
+	bool found;
+
+	KASSERT(powerof2(count), ("%s: bad count", __func__));
+	KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__));
+
+	sc = device_get_softc(dev);
+	mtx_lock(&sc->mtx);
+
+	found = false;
+	for (irq = 0; (irq + count - 1) < TEGRA_PCIB_MAX_MSI; irq++) {
+		/* Start on an aligned interrupt */
+		if ((irq & (maxcount - 1)) != 0)
+			continue;
+
+		/* Assume we found a valid range until shown otherwise */
+		found = true;
+
+		/* Check this range is valid */
+		for (end_irq = irq; end_irq < irq + count; end_irq++) {
+			/* This is already used */
+			if ((sc->isrcs[end_irq].flags & TEGRA_FLAG_MSI_USED) ==
+			    TEGRA_FLAG_MSI_USED) {
+				found = false;
+				break;
+			}
+		}
+
+		if (found)
+			break;
+	}
+
+	/* Not enough interrupts were found */
+	if (!found || irq == (TEGRA_PCIB_MAX_MSI - 1)) {
+		mtx_unlock(&sc->mtx);
+		return (ENXIO);
+	}
+
+	for (i = 0; i < count; i++) {
+		/* Mark the interrupt as used */
+		sc->isrcs[irq + i].flags |= TEGRA_FLAG_MSI_USED;
+
+	}
+	mtx_unlock(&sc->mtx);
+
+	for (i = 0; i < count; i++)
+		srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i];
+	*pic = device_get_parent(dev);
+	return (0);
+}
+
+static int
+tegra_pcib_msi_release_msi(device_t dev, device_t child, int count,
+    struct intr_irqsrc **isrc)
+{
+	struct tegra_pcib_softc *sc;
+	struct tegra_pcib_irqsrc *ti;
+	int i;
+
+	sc = device_get_softc(dev);
+	mtx_lock(&sc->mtx);
+	for (i = 0; i < count; i++) {
+		ti = (struct tegra_pcib_irqsrc *)isrc[i];
+
+		KASSERT((ti->flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED,
+		    ("%s: Trying to release an unused MSI-X interrupt",
+		    __func__));
+
+		ti->flags &= ~TEGRA_FLAG_MSI_USED;
+	}
+	mtx_unlock(&sc->mtx);
+	return (0);
+}
+
+static int
+tegra_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+    uint64_t *addr, uint32_t *data)
+{
+	struct tegra_pcib_softc *sc = device_get_softc(dev);
+	struct tegra_pcib_irqsrc *ti = (struct tegra_pcib_irqsrc *)isrc;
+
+	*addr = vtophys(sc->msi_page);
+	*data = ti->irq;
+	return (0);
+}
+#endif
+
+/* ------------------------------------------------------------------- */
+static bus_size_t
+tegra_pcib_pex_ctrl(struct tegra_pcib_softc *sc, int port)
+{
+	if (port >= TEGRA_PCIB_MAX_PORTS)
+		panic("invalid port number: %d\n", port);
+
+	if (port == 0)
+		return (AFI_PEX0_CTRL);
+	else if (port == 1)
+		return (AFI_PEX1_CTRL);
+	else if (port == 2)
+		return (AFI_PEX2_CTRL);
+	else
+		panic("invalid port number: %d\n", port);
+}
+
+static int
+tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc)
+{
+	int rv;
+
+	rv = hwreset_assert(sc->hwreset_pcie_x);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert 'pcie_x' reset\n");
+		return (rv);
+	}
+	rv = hwreset_assert(sc->hwreset_afi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert  'afi' reset\n");
+		return (rv);
+	}
+	rv = hwreset_assert(sc->hwreset_pex);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot assert  'pex' reset\n");
+		return (rv);
+	}
+
+	tegra_powergate_power_off(TEGRA_POWERGATE_PCX);
+
+	/* Power supplies. */
+	rv = regulator_enable(sc->supply_avddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avddio_pex' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_dvddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'dvddio_pex' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd_pex_pll);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avdd-pex-pll' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_hvdd_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'hvdd-pex-supply' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_hvdd_pex_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'hvdd-pex-pll-e-supply' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_vddio_pex_ctl);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'vddio-pex-ctl' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd_pll_erefe);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avdd-pll-erefe-supply' regulator\n");
+		return (rv);
+	}
+
+	rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCX,
+	    sc->clk_pex, sc->hwreset_pex);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'PCX' powergate\n");
+		return (rv);
+	}
+
+	rv = hwreset_deassert(sc->hwreset_afi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot unreset 'afi' reset\n");
+		return (rv);
+	}
+
+	rv = clk_enable(sc->clk_afi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'afi' clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_cml);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'cml' clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'pll_e' clock\n");
+		return (rv);
+	}
+	return (0);
+}
+
+static struct tegra_pcib_port *
+tegra_pcib_parse_port(struct tegra_pcib_softc *sc, phandle_t node)
+{
+	struct tegra_pcib_port *port;
+	uint32_t tmp[5];
+	char tmpstr[6];
+	int rv;
+
+	port = malloc(sizeof(struct tegra_pcib_port), M_DEVBUF, M_WAITOK);
+
+	rv = OF_getprop(node, "status", tmpstr, sizeof(tmpstr));
+	if (rv <= 0 || strcmp(tmpstr, "okay") == 0 ||
+	   strcmp(tmpstr, "ok") == 0)
+		port->enabled = 1;
+	else
+		port->enabled = 0;
+
+	rv = OF_getencprop(node, "assigned-addresses", tmp, sizeof(tmp));
+	if (rv != sizeof(tmp)) {
+		device_printf(sc->dev, "Cannot parse assigned-address: %d\n",
+		    rv);
+		goto fail;
+	}
+	port->rp_base_addr = tmp[2];
+	port->rp_size = tmp[4];
+	port->port_idx = OFW_PCI_PHYS_HI_DEVICE(tmp[0]) - 1;
+	if (port->port_idx >= TEGRA_PCIB_MAX_PORTS) {
+		device_printf(sc->dev, "Invalid port index: %d\n",
+		    port->port_idx);
+		goto fail;
+	}
+	/* XXX - TODO:
+	 * Implement proper function for parsing pci "reg" property:
+	 *  - it have PCI bus format
+	 *  - its relative to matching "assigned-addresses"
+	 */
+	rv = OF_getencprop(node, "reg", tmp, sizeof(tmp));
+	if (rv != sizeof(tmp)) {
+		device_printf(sc->dev, "Cannot parse reg: %d\n", rv);
+		goto fail;
+	}
+	port->rp_base_addr += tmp[2];
+
+	rv = OF_getencprop(node, "nvidia,num-lanes", &port->num_lanes,
+	    sizeof(port->num_lanes));
+	if (rv != sizeof(port->num_lanes)) {
+		device_printf(sc->dev, "Cannot parse nvidia,num-lanes: %d\n",
+		    rv);
+		goto fail;
+	}
+	if (port->num_lanes > 4) {
+		device_printf(sc->dev, "Invalid nvidia,num-lanes: %d\n",
+		    port->num_lanes);
+		goto fail;
+	}
+
+	port->afi_pex_ctrl = tegra_pcib_pex_ctrl(sc, port->port_idx);
+	sc->lanes_cfg |= port->num_lanes << (4 * port->port_idx);
+
+	/* Phy. */
+	rv = phy_get_by_ofw_name(sc->dev, node, "pcie-0", &port->phy);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'pcie-0' phy for port %d\n",
+		    port->port_idx);
+		goto fail;
+	}
+
+	return (port);
+fail:
+	free(port, M_DEVBUF);
+	return (NULL);
+}
+
+
+static int
+tegra_pcib_parse_fdt_resources(struct tegra_pcib_softc *sc, phandle_t node)
+{
+	phandle_t child;
+	struct tegra_pcib_port *port;
+	int rv;
+
+	/* Power supplies. */
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avddio-pex-supply",
+	    &sc->supply_avddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avddio-pex' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "dvddio-pex-supply",
+	     &sc->supply_dvddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'dvddio-pex' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-pex-pll-supply",
+	     &sc->supply_avdd_pex_pll);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avdd-pex-pll' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "hvdd-pex-supply",
+	     &sc->supply_hvdd_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'hvdd-pex' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "hvdd-pex-pll-e-supply",
+	     &sc->supply_hvdd_pex_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'hvdd-pex-pll-e' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "vddio-pex-ctl-supply",
+	    &sc->supply_vddio_pex_ctl);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'vddio-pex-ctl' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-pll-erefe-supply",
+	     &sc->supply_avdd_pll_erefe);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avdd-pll-erefe' regulator\n");
+		return (ENXIO);
+	}
+
+	/* Resets. */
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "pex", &sc->hwreset_pex);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pex' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "afi", &sc->hwreset_afi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'afi' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "pcie_x", &sc->hwreset_pcie_x);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pcie_x' reset\n");
+		return (ENXIO);
+	}
+
+	/* Clocks. */
+	rv = clk_get_by_ofw_name(sc->dev, 0, "pex", &sc->clk_pex);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pex' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "afi", &sc->clk_afi);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'afi' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "pll_e", &sc->clk_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pll_e' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "cml", &sc->clk_cml);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'cml' clock\n");
+		return (ENXIO);
+	}
+
+	/* Ports */
+	sc->num_ports = 0;
+	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+		port = tegra_pcib_parse_port(sc, child);
+		if (port == NULL) {
+			device_printf(sc->dev, "Cannot parse PCIe port node\n");
+			return (ENXIO);
+		}
+		sc->ports[sc->num_ports++] = port;
+	}
+
+	return (0);
+}
+
+static int
+tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc,
+    struct ofw_pci_range *ranges, int nranges)
+{
+	int i;
+
+	for (i = 2; i < nranges; i++) {
+		if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK)  ==
+		    OFW_PCI_PHYS_HI_SPACE_IO) {
+			if (sc->io_range.size != 0) {
+				device_printf(sc->dev,
+				    "Duplicated IO range found in DT\n");
+				return (ENXIO);
+			}
+			sc->io_range = ranges[i];
+		}
+		if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
+		    OFW_PCI_PHYS_HI_SPACE_MEM32))  {
+			if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) {
+				if (sc->pref_mem_range.size != 0) {
+					device_printf(sc->dev,
+					    "Duplicated memory range found "
+					    "in DT\n");
+					return (ENXIO);
+				}
+				sc->pref_mem_range = ranges[i];
+			} else {
+				if (sc->mem_range.size != 0) {
+					device_printf(sc->dev,
+					    "Duplicated memory range found "
+					    "in DT\n");
+					return (ENXIO);
+				}
+				sc->mem_range = ranges[i];
+			}
+		}
+	}
+	if ((sc->io_range.size == 0) || (sc->mem_range.size == 0)
+	    || (sc->pref_mem_range.size == 0)) {
+		device_printf(sc->dev,
+		    " Not all required ranges are found in DT\n");
+		return (ENXIO);
+	}
+	return (0);
+}
+
+/*
+ * Hardware config.
+ */
+static int
+tegra_pcib_wait_for_link(struct tegra_pcib_softc *sc,
+    struct tegra_pcib_port *port)
+{
+	uint32_t reg;
+	int i;
+
+
+	/* Setup link detection. */
+	reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+	    RP_PRIV_MISC, 4);
+	reg &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
+	reg |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
+	tegra_pcib_write_config(sc->dev, 0, port->port_idx, 0,
+	    RP_PRIV_MISC, reg, 4);
+
+	for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) {
+		reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+		    RP_VEND_XP, 4);
+		if (reg & RP_VEND_XP_DL_UP)
+				break;
+		DELAY(1);
+
+	}
+	if (i <= 0)
+		return (ETIMEDOUT);
+
+	for (i = TEGRA_PCIE_LINKUP_TIMEOUT; i > 0; i--) {
+		reg = tegra_pcib_read_config(sc->dev, 0, port->port_idx, 0,
+		    RP_LINK_CONTROL_STATUS, 4);
+		if (reg & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE)
+				break;
+
+		DELAY(1);
+	}
+	if (i <= 0)
+		return (ETIMEDOUT);
+	return (0);
+}
+
+static void
+tegra_pcib_port_enable(struct tegra_pcib_softc *sc, int port_num)
+{
+	struct tegra_pcib_port *port;
+	uint32_t reg;
+	int rv;
+
+	port = sc->ports[port_num];
+
+	/* Put port to reset. */
+	reg = AFI_RD4(sc, port->afi_pex_ctrl);
+	reg &= ~AFI_PEX_CTRL_RST_L;
+	AFI_WR4(sc, port->afi_pex_ctrl, reg);
+	AFI_RD4(sc, port->afi_pex_ctrl);
+	DELAY(10);
+
+	/* Enable clocks. */
+	reg |= AFI_PEX_CTRL_REFCLK_EN;
+	reg |= AFI_PEX_CTRL_CLKREQ_EN;
+	reg |= AFI_PEX_CTRL_OVERRIDE_EN;
+	AFI_WR4(sc, port->afi_pex_ctrl, reg);
+	AFI_RD4(sc, port->afi_pex_ctrl);
+	DELAY(100);
+
+	/* Release reset. */
+	reg |= AFI_PEX_CTRL_RST_L;
+	AFI_WR4(sc, port->afi_pex_ctrl, reg);
+
+	rv = tegra_pcib_wait_for_link(sc, port);
+	if (bootverbose)
+		device_printf(sc->dev, " port %d (%d lane%s): Link is %s\n",
+			 port->port_idx, port->num_lanes,
+			 port->num_lanes > 1 ? "s": "",
+			 rv == 0 ? "up": "down");
+}
+
+
+static void
+tegra_pcib_port_disable(struct tegra_pcib_softc *sc, uint32_t port_num)
+{
+	struct tegra_pcib_port *port;
+	uint32_t reg;
+
+	port = sc->ports[port_num];
+
+	/* Put port to reset. */
+	reg = AFI_RD4(sc, port->afi_pex_ctrl);
+	reg &= ~AFI_PEX_CTRL_RST_L;
+	AFI_WR4(sc, port->afi_pex_ctrl, reg);
+	AFI_RD4(sc, port->afi_pex_ctrl);
+	DELAY(10);
+
+	/* Disable clocks. */
+	reg &= ~AFI_PEX_CTRL_CLKREQ_EN;
+	reg &= ~AFI_PEX_CTRL_REFCLK_EN;
+	AFI_WR4(sc, port->afi_pex_ctrl, reg);
+
+	if (bootverbose)
+		device_printf(sc->dev, " port %d (%d lane%s): Disabled\n",
+			 port->port_idx, port->num_lanes,
+			 port->num_lanes > 1 ? "s": "");
+}
+
+static void
+tegra_pcib_set_bar(struct tegra_pcib_softc *sc, int bar, uint32_t axi,
+    uint64_t fpci, uint32_t size, int is_memory)
+{
+	uint32_t fpci_reg;
+	uint32_t axi_reg;
+	uint32_t size_reg;
+
+	axi_reg = axi & ~0xFFF;
+	size_reg = size >> 12;
+	fpci_reg = (uint32_t)(fpci >> 8) & ~0xF;
+	fpci_reg |= is_memory ? 0x1 : 0x0;
+	AFI_WR4(sc, bars[bar].axi_start, axi_reg);
+	AFI_WR4(sc, bars[bar].size, size_reg);
+	AFI_WR4(sc, bars[bar].fpci_start, fpci_reg);
+}
+
+static int
+tegra_pcib_enable(struct tegra_pcib_softc *sc)
+{
+	int rv;
+	int i;
+	uint32_t reg;
+
+	rv = tegra_pcib_enable_fdt_resources(sc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable FDT resources\n");
+		return (rv);
+	}
+	/* Enable PLLE control. */
+	reg = AFI_RD4(sc, AFI_PLLE_CONTROL);
+	reg &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
+	reg |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
+	AFI_WR4(sc, AFI_PLLE_CONTROL, reg);
+
+	/* Set bias pad. */
+	AFI_WR4(sc, AFI_PEXBIAS_CTRL, 0);
+
+	/* Configure mode and ports. */
+	reg = AFI_RD4(sc, AFI_PCIE_CONFIG);
+	reg &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+	if (sc->lanes_cfg == 0x14) {
+		if (bootverbose)
+			device_printf(sc->dev,
+			    "Using x1,x4 configuration\n");
+		reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR4_1;
+	} else if (sc->lanes_cfg == 0x12) {
+		if (bootverbose)
+			device_printf(sc->dev,
+			    "Using x1,x2 configuration\n");
+		reg |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_XBAR2_1;
+	} else {
+		device_printf(sc->dev,
+		    "Unsupported lanes configuration: 0x%X\n", sc->lanes_cfg);
+	}
+	reg |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL;
+	for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+		if ((sc->ports[i] != NULL))
+			reg &=
+			 ~AFI_PCIE_CONFIG_PCIE_DISABLE(sc->ports[i]->port_idx);
+	}
+	AFI_WR4(sc, AFI_PCIE_CONFIG, reg);
+
+	/* Enable Gen2 support. */
+	reg = AFI_RD4(sc, AFI_FUSE);
+	reg &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+	AFI_WR4(sc, AFI_FUSE, reg);
+
+	for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+		if (sc->ports[i] != NULL) {
+			rv = phy_enable(sc->ports[i]->phy);
+			if (rv != 0) {
+				device_printf(sc->dev,
+				    "Cannot enable phy for port %d\n",
+				    sc->ports[i]->port_idx);
+				return (rv);
+			}
+		}
+	}
+
+
+	rv = hwreset_deassert(sc->hwreset_pcie_x);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot unreset  'pci_x' reset\n");
+		return (rv);
+	}
+
+	/* Enable config space. */
+	reg = AFI_RD4(sc, AFI_CONFIGURATION);
+	reg |= AFI_CONFIGURATION_EN_FPCI;
+	AFI_WR4(sc, AFI_CONFIGURATION, reg);
+
+	/* Enable AFI errors. */
+	reg = 0;
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_SLVERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_INI_DECERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_SLVERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_DECERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_TGT_WRERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_SM_MSG);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_DFPCI_DECERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_AXI_DECERR);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_FPCI_TIMEOUT);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_PRSNT_SENSE);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_PE_CLKREQ_SENSE);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_CLKCLAMP_SENSE);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_RDY4PD_SENSE);
+	reg |= AFI_AFI_INTR_ENABLE_CODE(AFI_INTR_CODE_INT_CODE_P2P_ERROR);
+	AFI_WR4(sc, AFI_AFI_INTR_ENABLE, reg);
+	AFI_WR4(sc, AFI_SM_INTR_ENABLE, 0xffffffff);
+
+	/* Enable INT, disable MSI. */
+	AFI_WR4(sc, AFI_INTR_MASK, AFI_INTR_MASK_INT_MASK);
+
+	/* Mask all FPCI errors. */
+	AFI_WR4(sc, AFI_FPCI_ERROR_MASKS, 0);
+
+	/* Setup AFI translation windows. */
+	/* BAR 0 - type 1 extended configuration. */
+	tegra_pcib_set_bar(sc, 0, rman_get_start(sc->cfg_mem_res),
+	   FPCI_MAP_EXT_TYPE1_CONFIG, rman_get_size(sc->cfg_mem_res), 0);
+
+	/* BAR 1 - downstream I/O. */
+	tegra_pcib_set_bar(sc, 1, sc->io_range.host, FPCI_MAP_IO,
+	    sc->io_range.size, 0);
+
+	/* BAR 2 - downstream prefetchable memory 1:1. */
+	tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host,
+	    sc->pref_mem_range.host, sc->pref_mem_range.size, 1);
+
+	/* BAR 3 - downstream not prefetchable memory 1:1 .*/
+	tegra_pcib_set_bar(sc, 3, sc->mem_range.host,
+	    sc->mem_range.host, sc->mem_range.size, 1);
+
+	/* BAR 3-8 clear. */
+	tegra_pcib_set_bar(sc, 4, 0, 0, 0, 0);
+	tegra_pcib_set_bar(sc, 5, 0, 0, 0, 0);
+	tegra_pcib_set_bar(sc, 6, 0, 0, 0, 0);
+	tegra_pcib_set_bar(sc, 7, 0, 0, 0, 0);
+	tegra_pcib_set_bar(sc, 8, 0, 0, 0, 0);
+
+	/* MSI BAR - clear. */
+	tegra_pcib_set_bar(sc, 9, 0, 0, 0, 0);
+	return(0);
+}
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+static int
+tegra_pcib_attach_msi(device_t dev)
+{
+	struct tegra_pcib_softc *sc;
+	uint32_t reg;
+	int i, rv;
+
+	sc = device_get_softc(dev);
+
+	sc->msi_page = kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_WAITOK,
+	    0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+
+	/* MSI BAR */
+	tegra_pcib_set_bar(sc, 9, vtophys(sc->msi_page), vtophys(sc->msi_page),
+	    PAGE_SIZE, 0);
+
+	/* Disble and clear all interrupts. */
+	for (i = 0; i < AFI_MSI_REGS; i++) {
+		AFI_WR4(sc, AFI_MSI_EN_VEC(i), 0);
+		AFI_WR4(sc, AFI_MSI_VEC(i), 0xFFFFFFFF);
+	}
+	rv = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+	    tegra_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie);
+	if (rv != 0) {
+		device_printf(dev, "cannot setup MSI interrupt handler\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	if (tegra_pcib_msi_attach(sc) != 0) {
+		device_printf(dev, "WARNING: unable to attach PIC\n");
+		tegra_pcib_msi_detach(sc);
+		goto out;
+	}
+
+	/* Unmask  MSI interrupt. */
+	reg = AFI_RD4(sc, AFI_INTR_MASK);
+	reg |= AFI_INTR_MASK_MSI_MASK;
+	AFI_WR4(sc, AFI_INTR_MASK, reg);
+
+out:
+	return (rv);
+}
+#endif
+
+static int
+tegra_pcib_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+		device_set_desc(dev, "Nvidia Integrated PCI/PCI-E Controller");
+		return (BUS_PROBE_DEFAULT);
+	}
+	return (ENXIO);
+}
+
+static int
+tegra_pcib_attach(device_t dev)
+{
+	struct tegra_pcib_softc *sc;
+	phandle_t node;
+	int rv;
+	int rid;
+	struct tegra_pcib_port *port;
+	int i;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF);
+
+	node = ofw_bus_get_node(dev);
+
+	rv = tegra_pcib_parse_fdt_resources(sc, node);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get FDT resources\n");
+		return (rv);
+	}
+
+	/* Allocate bus_space resources. */
+	rid = 0;
+	sc->pads_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->pads_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate PADS register\n");
+		rv = ENXIO;
+		goto out;
+	}
+	/*
+	 * XXX - FIXME
+	 * tag for config space is not filled when RF_ALLOCATED flag is used.
+	 */
+	sc->bus_tag = rman_get_bustag(sc->pads_mem_res);
+
+	rid = 1;
+	sc->afi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->afi_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate AFI register\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	rid = 2;
+	sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ALLOCATED);
+	if (sc->cfg_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate config space memory\n");
+		rv = ENXIO;
+		goto out;
+	}
+	sc->cfg_base_addr = rman_get_start(sc->cfg_mem_res);
+
+
+	/* Map RP slots */
+	for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+		if (sc->ports[i] == NULL)
+			continue;
+		port = sc->ports[i];
+		rv = bus_space_map(sc->bus_tag, port->rp_base_addr,
+		    port->rp_size, 0, &port->cfg_handle);
+		if (rv != 0) {
+			device_printf(sc->dev, "Cannot allocate memory for "
+			    "port: %d\n", i);
+			rv = ENXIO;
+			goto out;
+		}
+	}
+
+	/*
+	 * Get PCI interrupt
+	 */
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate IRQ resources\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	rid = 1;
+	sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate MSI IRQ resources\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	sc->ofw_pci.sc_range_mask = 0x3;
+	rv = ofw_pci_init(dev);
+	if (rv != 0)
+		goto out;
+
+	rv = tegra_pcib_decode_ranges(sc, sc->ofw_pci.sc_range,
+	    sc->ofw_pci.sc_nrange);
+	if (rv != 0)
+		goto out;
+
+	if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+		    tegra_pci_intr, NULL, sc, &sc->intr_cookie)) {
+		device_printf(dev, "cannot setup interrupt handler\n");
+		rv = ENXIO;
+		goto out;
+	}
+
+	/*
+	 * Enable PCIE device.
+	 */
+	rv = tegra_pcib_enable(sc);
+	if (rv != 0)
+		goto out;
+	for (i = 0; i < TEGRA_PCIB_MAX_PORTS; i++) {
+		if (sc->ports[i] == NULL)
+			continue;
+		if (sc->ports[i]->enabled)
+			tegra_pcib_port_enable(sc, i);
+		else
+			tegra_pcib_port_disable(sc, i);
+	}
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+	rv = tegra_pcib_attach_msi(dev);
+	if (rv != 0)
+		 goto out;
+#endif
+	device_add_child(dev, "pci", -1);
+
+	return (bus_generic_attach(dev));
+
+out:
+
+	return (rv);
+}
+
+
+static device_method_t tegra_pcib_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,			tegra_pcib_probe),
+	DEVMETHOD(device_attach,		tegra_pcib_attach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
+	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
+
+	/* pcib interface */
+	DEVMETHOD(pcib_maxslots,		tegra_pcib_maxslots),
+	DEVMETHOD(pcib_read_config,		tegra_pcib_read_config),
+	DEVMETHOD(pcib_write_config,		tegra_pcib_write_config),
+	DEVMETHOD(pcib_route_interrupt,		tegra_pcib_route_interrupt),
+	DEVMETHOD(pcib_alloc_msi,		tegra_pcib_alloc_msi),
+	DEVMETHOD(pcib_release_msi,		tegra_pcib_release_msi),
+	DEVMETHOD(pcib_map_msi,			tegra_pcib_map_msi),
+
+#ifdef TEGRA_PCIB_MSI_ENABLE
+	/* MSI/MSI-X */
+	DEVMETHOD(msi_alloc_msi,		tegra_pcib_msi_alloc_msi),
+	DEVMETHOD(msi_release_msi,		tegra_pcib_msi_release_msi),
+	DEVMETHOD(msi_map_msi,			tegra_pcib_msi_map_msi),
+
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_intr,		tegra_pcib_msi_disable_intr),
+	DEVMETHOD(pic_enable_intr,		tegra_pcib_msi_enable_intr),
+	DEVMETHOD(pic_setup_intr,		tegra_pcib_msi_setup_intr),
+	DEVMETHOD(pic_teardown_intr,		tegra_pcib_msi_teardown_intr),
+	DEVMETHOD(pic_post_filter,		tegra_pcib_msi_post_filter),
+	DEVMETHOD(pic_post_ithread,		tegra_pcib_msi_post_ithread),
+	DEVMETHOD(pic_pre_ithread,		tegra_pcib_msi_pre_ithread),
+#endif
+
+	/* OFW bus interface */
+	DEVMETHOD(ofw_bus_get_compat,		ofw_bus_gen_get_compat),
+	DEVMETHOD(ofw_bus_get_model,		ofw_bus_gen_get_model),
+	DEVMETHOD(ofw_bus_get_name,		ofw_bus_gen_get_name),
+	DEVMETHOD(ofw_bus_get_node,		ofw_bus_gen_get_node),
+	DEVMETHOD(ofw_bus_get_type,		ofw_bus_gen_get_type),
+
+	DEVMETHOD_END
+};
+
+static devclass_t pcib_devclass;
+DEFINE_CLASS_1(pcib, tegra_pcib_driver, tegra_pcib_methods,
+    sizeof(struct tegra_pcib_softc), ofw_pci_driver);
+DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass,
+    NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/tegra_pcie.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_pinmux.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_pinmux.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_pinmux.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,800 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_pinmux.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * Pin multiplexer driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/* Pin multipexor register. */
+#define	TEGRA_MUX_FUNCTION_MASK  0x03
+#define	TEGRA_MUX_FUNCTION_SHIFT 0
+#define	TEGRA_MUX_PUPD_MASK  0x03
+#define	TEGRA_MUX_PUPD_SHIFT 2
+#define	TEGRA_MUX_TRISTATE_SHIFT 4
+#define	TEGRA_MUX_ENABLE_INPUT_SHIFT 5
+#define	TEGRA_MUX_OPEN_DRAIN_SHIFT 6
+#define	TEGRA_MUX_LOCK_SHIFT 7
+#define	TEGRA_MUX_IORESET_SHIFT 8
+#define	TEGRA_MUX_RCV_SEL_SHIFT 9
+
+
+/* Pin goup register. */
+#define	TEGRA_GRP_HSM_SHIFT 2
+#define	TEGRA_GRP_SCHMT_SHIFT 3
+#define	TEGRA_GRP_DRV_TYPE_SHIFT 6
+#define	TEGRA_GRP_DRV_TYPE_MASK 0x03
+#define	TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT 28
+#define	TEGRA_GRP_DRV_DRVDN_SLWR_MASK 0x03
+#define	TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT 30
+#define	TEGRA_GRP_DRV_DRVUP_SLWF_MASK 0x03
+
+struct pinmux_softc {
+	device_t	dev;
+	struct resource	*pad_mem_res;
+	struct resource	*mux_mem_res;
+	struct resource	*mipi_mem_res;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-pinmux",	1},
+	{NULL,				0},
+};
+
+enum prop_id {
+	PROP_ID_PULL,
+	PROP_ID_TRISTATE,
+	PROP_ID_ENABLE_INPUT,
+	PROP_ID_OPEN_DRAIN,
+	PROP_ID_LOCK,
+	PROP_ID_IORESET,
+	PROP_ID_RCV_SEL,
+	PROP_ID_HIGH_SPEED_MODE,
+	PROP_ID_SCHMITT,
+	PROP_ID_LOW_POWER_MODE,
+	PROP_ID_DRIVE_DOWN_STRENGTH,
+	PROP_ID_DRIVE_UP_STRENGTH,
+	PROP_ID_SLEW_RATE_FALLING,
+	PROP_ID_SLEW_RATE_RISING,
+	PROP_ID_DRIVE_TYPE,
+
+	PROP_ID_MAX_ID
+};
+
+/* Numeric based parameters. */
+static const struct prop_name {
+	const char *name;
+	enum prop_id id;
+} prop_names[] = {
+	{"nvidia,pull",			PROP_ID_PULL},
+	{"nvidia,tristate",		PROP_ID_TRISTATE},
+	{"nvidia,enable-input",		PROP_ID_ENABLE_INPUT},
+	{"nvidia,open-drain",		PROP_ID_OPEN_DRAIN},
+	{"nvidia,lock",			PROP_ID_LOCK},
+	{"nvidia,io-reset",		PROP_ID_IORESET},
+	{"nvidia,rcv-sel",		PROP_ID_RCV_SEL},
+	{"nvidia,high-speed-mode",	PROP_ID_HIGH_SPEED_MODE},
+	{"nvidia,schmitt",		PROP_ID_SCHMITT},
+	{"nvidia,low-power-mode",	PROP_ID_LOW_POWER_MODE},
+	{"nvidia,pull-down-strength",	PROP_ID_DRIVE_DOWN_STRENGTH},
+	{"nvidia,pull-up-strength",	PROP_ID_DRIVE_UP_STRENGTH},
+	{"nvidia,slew-rate-falling",	PROP_ID_SLEW_RATE_FALLING},
+	{"nvidia,slew-rate-rising",	PROP_ID_SLEW_RATE_RISING},
+	{"nvidia,drive-type",		PROP_ID_DRIVE_TYPE},
+};
+
+/*
+ * configuration for one pin group.
+ */
+struct pincfg {
+	char	*function;
+	int	params[PROP_ID_MAX_ID];
+};
+#define	GPIO_BANK_A	 0
+#define	GPIO_BANK_B	 1
+#define	GPIO_BANK_C	 2
+#define	GPIO_BANK_D	 3
+#define	GPIO_BANK_E	 4
+#define	GPIO_BANK_F	 5
+#define	GPIO_BANK_G	 6
+#define	GPIO_BANK_H	 7
+#define	GPIO_BANK_I	 8
+#define	GPIO_BANK_J	 9
+#define	GPIO_BANK_K	10
+#define	GPIO_BANK_L	11
+#define	GPIO_BANK_M	12
+#define	GPIO_BANK_N	13
+#define	GPIO_BANK_O	14
+#define	GPIO_BANK_P	15
+#define	GPIO_BANK_Q	16
+#define	GPIO_BANK_R	17
+#define	GPIO_BANK_S	18
+#define	GPIO_BANK_T	19
+#define	GPIO_BANK_U	20
+#define	GPIO_BANK_V	21
+#define	GPIO_BANK_W	22
+#define	GPIO_BANK_X	23
+#define	GPIO_BANK_Y	24
+#define	GPIO_BANK_Z	25
+#define	GPIO_BANK_AA	26
+#define	GPIO_BANK_BB	27
+#define	GPIO_BANK_CC	28
+#define	GPIO_BANK_DD	29
+#define	GPIO_BANK_EE	30
+#define	GPIO_BANK_FF	31
+#define	GPIO_NUM(b, p) (8 * (b) + (p))
+
+struct tegra_mux {
+	char *name;
+	bus_size_t reg;
+	char *functions[4];
+	int gpio_num;
+};
+
+#define	GMUX(r, gb, gi, nm, f1, f2, f3, f4)				\
+{									\
+	.name = #nm,							\
+	.reg = r,							\
+	.gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi),			\
+	.functions = {#f1, #f2, #f3, #f4},				\
+}
+
+#define	FMUX(r, nm, f1, f2, f3, f4)					\
+{									\
+	.name = #nm,							\
+	.reg = r,							\
+	.gpio_num = -1,							\
+	.functions = {#f1, #f2, #f3, #f4},				\
+}
+
+static const struct tegra_mux pin_mux_tbl[] = {
+	GMUX(0x000,  O, 1, ulpi_data0_po1,         spi3,       hsi,        uarta,        ulpi),
+	GMUX(0x004,  O, 2, ulpi_data1_po2,         spi3,       hsi,        uarta,        ulpi),
+	GMUX(0x008,  O, 3, ulpi_data2_po3,         spi3,       hsi,        uarta,        ulpi),
+	GMUX(0x00C,  O, 4, ulpi_data3_po4,         spi3,       hsi,        uarta,        ulpi),
+	GMUX(0x010,  O, 5, ulpi_data4_po5,         spi2,       hsi,        uarta,        ulpi),
+	GMUX(0x014,  O, 6, ulpi_data5_po6,         spi2,       hsi,        uarta,        ulpi),
+	GMUX(0x018,  O, 7, ulpi_data6_po7,         spi2,       hsi,        uarta,        ulpi),
+	GMUX(0x01C,  O, 0, ulpi_data7_po0,         spi2,       hsi,        uarta,        ulpi),
+	GMUX(0x020,  P, 9, ulpi_clk_py0,           spi1,       spi5,       uartd,        ulpi),
+	GMUX(0x024,  P, 1, ulpi_dir_py1,           spi1,       spi5,       uartd,        ulpi),
+	GMUX(0x028,  P, 2, ulpi_nxt_py2,           spi1,       spi5,       uartd,        ulpi),
+	GMUX(0x02C,  P, 3, ulpi_stp_py3,           spi1,       spi5,       uartd,        ulpi),
+	GMUX(0x030,  P, 0, dap3_fs_pp0,            i2s2,       spi5,       displaya,     displayb),
+	GMUX(0x034,  P, 1, dap3_din_pp1,           i2s2,       spi5,       displaya,     displayb),
+	GMUX(0x038,  P, 2, dap3_dout_pp2,          i2s2,       spi5,       displaya,     rsvd4),
+	GMUX(0x03C,  P, 3, dap3_sclk_pp3,          i2s2,       spi5,       rsvd3,        displayb),
+	GMUX(0x040,  V, 0, pv0,                    rsvd1,      rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x044,  V, 1, pv1,                    rsvd1,      rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x048,  Z, 0, sdmmc1_clk_pz0,         sdmmc1,     clk12,      rsvd3,        rsvd4),
+	GMUX(0x04C,  Z, 1, sdmmc1_cmd_pz1,         sdmmc1,     spdif,      spi4,         uarta),
+	GMUX(0x050,  Y, 4, sdmmc1_dat3_py4,        sdmmc1,     spdif,      spi4,         uarta),
+	GMUX(0x054,  Y, 5, sdmmc1_dat2_py5,        sdmmc1,     pwm0,       spi4,         uarta),
+	GMUX(0x058,  Y, 6, sdmmc1_dat1_py6,        sdmmc1,     pwm1,       spi4,         uarta),
+	GMUX(0x05C,  Y, 7, sdmmc1_dat0_py7,        sdmmc1,     rsvd2,      spi4,         uarta),
+	GMUX(0x068,  W, 5, clk2_out_pw5,           extperiph2, rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x06C, CC, 5, clk2_req_pcc5,          dap,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x110,  N, 7, hdmi_int_pn7,           rsvd1,      rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x114,  V, 4, ddc_scl_pv4,            i2c4,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x118,  V, 5, ddc_sda_pv5,            i2c4,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x164,  V, 3, uart2_rxd_pc3,          irda,       spdif,      uarta,        spi4),
+	GMUX(0x168,  C, 2, uart2_txd_pc2,          irda,       spdif,      uarta,        spi4),
+	GMUX(0x16C,  J, 6, uart2_rts_n_pj6,        uarta,      uartb,      gmi,          spi4),
+	GMUX(0x170,  J, 5, uart2_cts_n_pj5,        uarta,      uartb,      gmi,          spi4),
+	GMUX(0x174,  W, 6, uart3_txd_pw6,          uartc,      rsvd2,      gmi,          spi4),
+	GMUX(0x178,  W, 7, uart3_rxd_pw7,          uartc,      rsvd2,      gmi,          spi4),
+	GMUX(0x17C,  S, 1, uart3_cts_n_pa1,        uartc,      sdmmc1,     dtv,          gmi),
+	GMUX(0x180,  C, 0, uart3_rts_n_pc0,        uartc,      pwm0,       dtv,          gmi),
+	GMUX(0x184,  U, 0, pu0,                    owr,        uarta,      gmi,          rsvd4),
+	GMUX(0x188,  U, 1, pu1,                    rsvd1,      uarta,      gmi,          rsvd4),
+	GMUX(0x18C,  U, 2, pu2,                    rsvd1,      uarta,      gmi,          rsvd4),
+	GMUX(0x190,  U, 3, pu3,                    pwm0,       uarta,      gmi,          displayb),
+	GMUX(0x194,  U, 4, pu4,                    pwm1,       uarta,      gmi,          displayb),
+	GMUX(0x198,  U, 5, pu5,                    pwm2,       uarta,      gmi,          displayb),
+	GMUX(0x19C,  U, 6, pu6,                    pwm3,       uarta,      rsvd3,        gmi),
+	GMUX(0x1A0,  C, 5, gen1_i2c_sda_pc5,       i2c1,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x1A4,  C, 4, gen1_i2c_scl_pc4,       i2c1,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x1A8,  P, 3, dap4_fs_pp4,            i2s3,       gmi,        dtv,          rsvd4),
+	GMUX(0x1AC,  P, 4, dap4_din_pp5,           i2s3,       gmi,        rsvd3,        rsvd4),
+	GMUX(0x1B0,  P, 5, dap4_dout_pp6,          i2s3,       gmi,        dtv,          rsvd4),
+	GMUX(0x1B4,  P, 7, dap4_sclk_pp7,          i2s3,       gmi,        rsvd3,        rsvd4),
+	GMUX(0x1B8,  P, 0, clk3_out_pee0,          extperiph3, rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x1BC, EE, 1, clk3_req_pee1,          dev3,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x1C0,  C, 7, pc7,                    rsvd1,      rsvd2,      gmi,          gmi_alt),
+	GMUX(0x1C4,  I, 5, pi5,                    sdmmc2,     rsvd2,      gmi,          rsvd4),
+	GMUX(0x1C8,  I, 7, pi7,                    rsvd1,      trace,      gmi,          dtv),
+	GMUX(0x1CC,  K, 0, pk0,                    rsvd1,      sdmmc3,     gmi,          soc),
+	GMUX(0x1D0,  K, 1, pk1,                    sdmmc2,     trace,      gmi,          rsvd4),
+	GMUX(0x1D4,  J, 0, pj0,                    rsvd1,      rsvd2,      gmi,          usb),
+	GMUX(0x1D8,  J, 2, pj2,                    rsvd1,      rsvd2,      gmi,          soc),
+	GMUX(0x1DC,  K, 3, pk3,                    sdmmc2,     trace,      gmi,          ccla),
+	GMUX(0x1E0,  K, 4, pk4,                    sdmmc2,     rsvd2,      gmi,          gmi_alt),
+	GMUX(0x1E4,  K, 2, pk2,                    rsvd1,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x1E8,  I, 3, pi3,                    rsvd1,      rsvd2,      gmi,          spi4),
+	GMUX(0x1EC,  I, 6, pi6,                    rsvd1,      rsvd2,      gmi,          sdmmc2),
+	GMUX(0x1F0,  G, 0, pg0,                    rsvd1,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x1F4,  G, 1, pg1,                    rsvd1,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x1F8,  G, 2, pg2,                    rsvd1,      trace,      gmi,          rsvd4),
+	GMUX(0x1FC,  G, 3, pg3,                    rsvd1,      trace,      gmi,          rsvd4),
+	GMUX(0x200,  G, 4, pg4,                    rsvd1,      tmds,       gmi,          spi4),
+	GMUX(0x204,  G, 5, pg5,                    rsvd1,      rsvd2,      gmi,          spi4),
+	GMUX(0x208,  G, 6, pg6,                    rsvd1,      rsvd2,      gmi,          spi4),
+	GMUX(0x20C,  G, 7, pg7,                    rsvd1,      rsvd2,      gmi,          spi4),
+	GMUX(0x210,  H, 0, ph0,                    pwm0,       trace,      gmi,          dtv),
+	GMUX(0x214,  H, 1, ph1,                    pwm1,       tmds,       gmi,          displaya),
+	GMUX(0x218,  H, 2, ph2,                    pwm2,       tmds,       gmi,          cldvfs),
+	GMUX(0x21C,  H, 3, ph3,                    pwm3,       spi4,       gmi,          cldvfs),
+	GMUX(0x220,  H, 4, ph4,                    sdmmc2,     rsvd2,      gmi,          rsvd4),
+	GMUX(0x224,  H, 5, ph5,                    sdmmc2,     rsvd2,      gmi,          rsvd4),
+	GMUX(0x228,  H, 6, ph6,                    sdmmc2,     trace,      gmi,          dtv),
+	GMUX(0x22C,  H, 7, ph7,                    sdmmc2,     trace,      gmi,          dtv),
+	GMUX(0x230,  J, 7, pj7,                    uartd,      rsvd2,      gmi,          gmi_alt),
+	GMUX(0x234,  B, 0, pb0,                    uartd,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x238,  B, 1, pb1,                    uartd,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x23C,  K, 7, pk7,                    uartd,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x240,  I, 0, pi0,                    rsvd1,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x244,  I, 1, pi1,                    rsvd1,      rsvd2,      gmi,          rsvd4),
+	GMUX(0x248,  I, 2, pi2,                    sdmmc2,     trace,      gmi,          rsvd4),
+	GMUX(0x24C,  I, 4, pi4,                    spi4,       trace,      gmi,          displaya),
+	GMUX(0x250,  T, 5, gen2_i2c_scl_pt5,       i2c2,       rsvd2,      gmi,          rsvd4),
+	GMUX(0x254,  T, 6, gen2_i2c_sda_pt6,       i2c2,       rsvd2,      gmi,          rsvd4),
+	GMUX(0x258, CC, 4, sdmmc4_clk_pcc4,        sdmmc4,     rsvd2,      gmi,          rsvd4),
+	GMUX(0x25C,  T, 7, sdmmc4_cmd_pt7,         sdmmc4,     rsvd2,      gmi,          rsvd4),
+	GMUX(0x260, AA, 0, sdmmc4_dat0_paa0,       sdmmc4,     spi3,       gmi,          rsvd4),
+	GMUX(0x264, AA, 1, sdmmc4_dat1_paa1,       sdmmc4,     spi3,       gmi,          rsvd4),
+	GMUX(0x268, AA, 2, sdmmc4_dat2_paa2,       sdmmc4,     spi3,       gmi,          rsvd4),
+	GMUX(0x26C, AA, 3, sdmmc4_dat3_paa3,       sdmmc4,     spi3,       gmi,          rsvd4),
+	GMUX(0x270, AA, 4, sdmmc4_dat4_paa4,       sdmmc4,     spi3,       gmi,          rsvd4),
+	GMUX(0x274, AA, 5, sdmmc4_dat5_paa5,       sdmmc4,     spi3,       rsvd3,        rsvd4),
+	GMUX(0x278, AA, 6, sdmmc4_dat6_paa6,       sdmmc4,     spi3,       gmi,          rsvd4),
+	GMUX(0x27C, AA, 7, sdmmc4_dat7_paa7,       sdmmc4,     rsvd2,      gmi,          rsvd4),
+	GMUX(0x284, CC, 0, cam_mclk_pcc0,          vi,         vi_alt1,    vi_alt3,      sdmmc2),
+	GMUX(0x288, CC, 1, pcc1,                   i2s4,       rsvd2,      rsvd3,        sdmmc2),
+	GMUX(0x28C, BB, 0, pbb0,                   vgp6,       vimclk2,    sdmmc2,       vimclk2_alt),
+	GMUX(0x290, BB, 1, cam_i2c_scl_pbb1,       vgp1,       i2c3,       rsvd3,        sdmmc2),
+	GMUX(0x294, BB, 2, cam_i2c_sda_pbb2,       vgp2,       i2c3,       rsvd3,        sdmmc2),
+	GMUX(0x298, BB, 3, pbb3,                   vgp3,       displaya,   displayb,     sdmmc2),
+	GMUX(0x29C, BB, 4, pbb4,                   vgp4,       displaya,   displayb,     sdmmc2),
+	GMUX(0x2A0, BB, 5, pbb5,                   vgp5,       displaya,   rsvd3,        sdmmc2),
+	GMUX(0x2A4, BB, 6, pbb6,                   i2s4,       rsvd2,      displayb,     sdmmc2),
+	GMUX(0x2A8, BB, 7, pbb7,                   i2s4,       rsvd2,      rsvd3,        sdmmc2),
+	GMUX(0x2AC, CC, 2, pcc2,                   i2s4,       rsvd2,      sdmmc3,       sdmmc2),
+	FMUX(0x2B0,        jtag_rtck,              rtck,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x2B4,  Z, 6, pwr_i2c_scl_pz6,        i2cpwr,     rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x2B8,  Z, 7, pwr_i2c_sda_pz7,        i2cpwr,     rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x2BC,  R, 0, kb_row0_pr0,            kbc,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x2C0,  R, 1, kb_row1_pr1,            kbc,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x2C4,  R, 2, kb_row2_pr2,            kbc,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x2C8,  R, 3, kb_row3_pr3,            kbc,        displaya,   sys,          displayb),
+	GMUX(0x2CC,  R, 4, kb_row4_pr4,            kbc,        displaya,   rsvd3,        displayb),
+	GMUX(0x2D0,  R, 5, kb_row5_pr5,            kbc,        displaya,   rsvd3,        displayb),
+	GMUX(0x2D4,  R, 6, kb_row6_pr6,            kbc,        displaya,   displaya_alt, displayb),
+	GMUX(0x2D8,  R, 7, kb_row7_pr7,            kbc,        rsvd2,      cldvfs,       uarta),
+	GMUX(0x2DC,  S, 0, kb_row8_ps0,            kbc,        rsvd2,      cldvfs,       uarta),
+	GMUX(0x2E0,  S, 1, kb_row9_ps1,            kbc,        rsvd2,      rsvd3,        uarta),
+	GMUX(0x2E4,  S, 2, kb_row10_ps2,           kbc,        rsvd2,      rsvd3,        uarta),
+	GMUX(0x2E8,  S, 3, kb_row11_ps3,           kbc,        rsvd2,      rsvd3,        irda),
+	GMUX(0x2EC,  S, 4, kb_row12_ps4,           kbc,        rsvd2,      rsvd3,        irda),
+	GMUX(0x2F0,  S, 5, kb_row13_ps5,           kbc,        rsvd2,      spi2,         rsvd4),
+	GMUX(0x2F4,  S, 6, kb_row14_ps6,           kbc,        rsvd2,      spi2,         rsvd4),
+	GMUX(0x2F8,  S, 7, kb_row15_ps7,           kbc,        soc,        rsvd3,        rsvd4),
+	GMUX(0x2FC,  Q, 0, kb_col0_pq0,            kbc,        rsvd2,      spi2,         rsvd4),
+	GMUX(0x300,  Q, 1, kb_col1_pq1,            kbc,        rsvd2,      spi2,         rsvd4),
+	GMUX(0x304,  Q, 2, kb_col2_pq2,            kbc,        rsvd2,      spi2,         rsvd4),
+	GMUX(0x308,  Q, 3, kb_col3_pq3,            kbc,        displaya,   pwm2,         uarta),
+	GMUX(0x30C,  Q, 4, kb_col4_pq4,            kbc,        owr,        sdmmc3,       uarta),
+	GMUX(0x310,  Q, 5, kb_col5_pq5,            kbc,        rsvd2,      sdmmc3,       rsvd4),
+	GMUX(0x314,  Q, 6, kb_col6_pq6,            kbc,        rsvd2,      spi2,         uartd),
+	GMUX(0x318,  Q, 7, kb_col7_pq7,            kbc,        rsvd2,      spi2,         uartd),
+	GMUX(0x31C,  A, 0, clk_32k_out_pa0,        blink,      soc,        rsvd3,        rsvd4),
+	FMUX(0x324,        core_pwr_req,           pwron,      rsvd2,      rsvd3,        rsvd4),
+	FMUX(0x328,        cpu_pwr_req,            cpu,        rsvd2,      rsvd3,        rsvd4),
+	FMUX(0x32C,        pwr_int_n,              pmi,        rsvd2,      rsvd3,        rsvd4),
+	FMUX(0x330,        clk_32k_in,             clk,        rsvd2,      rsvd3,        rsvd4),
+	FMUX(0x334,        owr,                    owr,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x338,  N, 0, dap1_fs_pn0,            i2s0,       hda,        gmi,          rsvd4),
+	GMUX(0x33C,  N, 1, dap1_din_pn1,           i2s0,       hda,        gmi,          rsvd4),
+	GMUX(0x340,  N, 2, dap1_dout_pn2,          i2s0,       hda,        gmi,          sata),
+	GMUX(0x344,  N, 3, dap1_sclk_pn3,          i2s0,       hda,        gmi,          rsvd4),
+	GMUX(0x348, EE, 2, dap_mclk1_req_pee2,     dap,        dap1,       sata,         rsvd4),
+	GMUX(0x34C,  W, 4, dap_mclk1_pw4,          extperiph1, dap2,       rsvd3,        rsvd4),
+	GMUX(0x350,  K, 6, spdif_in_pk6,           spdif,      rsvd2,      rsvd3,        i2c3),
+	GMUX(0x354,  K, 5, spdif_out_pk5,          spdif,      rsvd2,      rsvd3,        i2c3),
+	GMUX(0x358,  A, 2, dap2_fs_pa2,            i2s1,       hda,        gmi,          rsvd4),
+	GMUX(0x35C,  A, 4, dap2_din_pa4,           i2s1,       hda,        gmi,          rsvd4),
+	GMUX(0x360,  A, 5, dap2_dout_pa5,          i2s1,       hda,        gmi,          rsvd4),
+	GMUX(0x364,  A, 3, dap2_sclk_pa3,          i2s1,       hda,        gmi,          rsvd4),
+	GMUX(0x368,  X, 0, dvfs_pwm_px0,           spi6,       cldvfs,     gmi,          rsvd4),
+	GMUX(0x36C,  X, 1, gpio_x1_aud_px1,        spi6,       rsvd2,      gmi,          rsvd4),
+	GMUX(0x370,  X, 3, gpio_x3_aud_px3,        spi6,       spi1,       gmi,          rsvd4),
+	GMUX(0x374,  X, 2, dvfs_clk_px2,           spi6,       cldvfs,     gmi,          rsvd4),
+	GMUX(0x378,  X, 4, gpio_x4_aud_px4,        gmi,        spi1,       spi2,         dap2),
+	GMUX(0x37C,  X, 5, gpio_x5_aud_px5,        gmi,        spi1,       spi2,         rsvd4),
+	GMUX(0x380,  X, 6, gpio_x6_aud_px6,        spi6,       spi1,       spi2,         gmi),
+	GMUX(0x384,  X, 7, gpio_x7_aud_px7,        rsvd1,      spi1,       spi2,         rsvd4),
+	GMUX(0x390,  A, 6, sdmmc3_clk_pa6,         sdmmc3,     rsvd2,      rsvd3,        spi3),
+	GMUX(0x394,  A, 7, sdmmc3_cmd_pa7,         sdmmc3,     pwm3,       uarta,        spi3),
+	GMUX(0x398,  B, 7, sdmmc3_dat0_pb7,        sdmmc3,     rsvd2,      rsvd3,        spi3),
+	GMUX(0x39C,  B, 6, sdmmc3_dat1_pb6,        sdmmc3,     pwm2,       uarta,        spi3),
+	GMUX(0x3A0,  B, 5, sdmmc3_dat2_pb5,        sdmmc3,     pwm1,       displaya,     spi3),
+	GMUX(0x3A4,  B, 4, sdmmc3_dat3_pb4,        sdmmc3,     pwm0,       displayb,     spi3),
+	GMUX(0x3BC, DD, 1, pex_l0_rst_n_pdd1,      pe0,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3C0, DD, 2, pex_l0_clkreq_n_pdd2,   pe0,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3C4, DD, 3, pex_wake_n_pdd3,        pe,         rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3CC, DD, 5, pex_l1_rst_n_pdd5,      pe1,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3D0, DD, 6, pex_l1_clkreq_n_pdd6,   pe1,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3E0, EE, 3, hdmi_cec_pee3,          cec,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3E4,  V, 3, sdmmc1_wp_n_pv3,        sdmmc1,     clk12,      spi4,         uarta),
+	GMUX(0x3E8,  V, 2, sdmmc3_cd_n_pv2,        sdmmc3,     owr,        rsvd3,        rsvd4),
+	GMUX(0x3EC,  W, 2, gpio_w2_aud_pw2,        spi6,       rsvd2,      spi2,         i2c1),
+	GMUX(0x3F0,  W, 3, gpio_w3_aud_pw3,        spi6,       spi1,       spi2,         i2c1),
+	GMUX(0x3F4,  N, 4, usb_vbus_en0_pn4,       usb,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3F8,  N, 5, usb_vbus_en1_pn5,       usb,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x3FC, EE, 5, sdmmc3_clk_lb_in_pee5,  sdmmc3,     rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x400, EE, 4, sdmmc3_clk_lb_out_pee4, sdmmc3,     rsvd2,      rsvd3,        rsvd4),
+	FMUX(0x404,        gmi_clk_lb,             sdmmc2,     rsvd2,      gmi,          rsvd4),
+	FMUX(0x408,        reset_out_n,            rsvd1,      rsvd2,      rsvd3,        reset_out_n),
+	GMUX(0x40C,  T, 0, kb_row16_pt0,           kbc,        rsvd2,      rsvd3,        uartc),
+	GMUX(0x410,  T, 1, kb_row17_pt1,           kbc,        rsvd2,      rsvd3,        uartc),
+	GMUX(0x414, FF, 1, usb_vbus_en2_pff1,      usb,        rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x418, FF, 2, pff2,                   sata,       rsvd2,      rsvd3,        rsvd4),
+	GMUX(0x430, FF, 0, dp_hpd_pff0,            dp,         rsvd2,      rsvd3,        rsvd4),
+};
+
+struct tegra_grp {
+	char *name;
+	bus_size_t reg;
+	int drvdn_shift;
+	int drvdn_mask;
+	int drvup_shift;
+	int drvup_mask;
+};
+
+#define	GRP(r, nm, dn_s, dn_w, up_s, up_w)				\
+{									\
+	.name = #nm,							\
+	.reg = r - 0x868,						\
+	.drvdn_shift = dn_s,						\
+	.drvdn_mask = (1 << dn_w) - 1,					\
+	.drvup_shift = up_s,						\
+	.drvup_mask = (1 << dn_w) - 1,					\
+}
+
+/* Use register offsets from TRM */
+static const struct tegra_grp pin_grp_tbl[] = {
+	GRP(0x868, ao1,          12,  5,  20,  5),
+	GRP(0x86C, ao2,          12,  5,  20,  5),
+	GRP(0x870, at1,          12,  7,  20,  7),
+	GRP(0x874, at2,          12,  7,  20,  7),
+	GRP(0x878, at3,          12,  7,  20,  7),
+	GRP(0x87C, at4,          12,  7,  20,  7),
+	GRP(0x880, at5,          14,  5,  19,  5),
+	GRP(0x884, cdev1,        12,  5,  20,  5),
+	GRP(0x888, cdev2,        12,  5,  20,  5),
+	GRP(0x890, dap1,         12,  5,  20,  5),
+	GRP(0x894, dap2,         12,  5,  20,  5),
+	GRP(0x898, dap3,         12,  5,  20,  5),
+	GRP(0x89C, dap4,         12,  5,  20,  5),
+	GRP(0x8A0, dbg,          12,  5,  20,  5),
+	GRP(0x8B0, sdio3,        12,  7,  20,  7),
+	GRP(0x8B4, spi,          12,  5,  20,  5),
+	GRP(0x8B8, uaa,          12,  5,  20,  5),
+	GRP(0x8BC, uab,          12,  5,  20,  5),
+	GRP(0x8C0, uart2,        12,  5,  20,  5),
+	GRP(0x8C4, uart3,        12,  5,  20,  5),
+	GRP(0x8EC, sdio1,        12,  7,  20,  7),
+	GRP(0x8FC, ddc,          12,  5,  20,  5),
+	GRP(0x900, gma,          14,  5,  20,  5),
+	GRP(0x910, gme,          14,  5,  19,  5),
+	GRP(0x914, gmf,          14,  5,  19,  5),
+	GRP(0x918, gmg,          14,  5,  19,  5),
+	GRP(0x91C, gmh,          14,  5,  19,  5),
+	GRP(0x920, owr,          12,  5,  20,  5),
+	GRP(0x924, uda,          12,  5,  20,  5),
+	GRP(0x928, gpv,          12,  5,  20,  5),
+	GRP(0x92C, dev3,         12,  5,  20,  5),
+	GRP(0x938, cec,          12,  5,  20,  5),
+	GRP(0x994, at6,          12,  7,  20,  7),
+	GRP(0x998, dap5,         12,  5,  20,  5),
+	GRP(0x99C, usb_vbus_en,  12,  5,  20,  5),
+	GRP(0x9A8, ao3,          12,  5,  -1,  0),
+	GRP(0x9B0, ao0,          12,  5,  20,  5),
+	GRP(0x9B4, hv0,          12,  5,  -1,  0),
+	GRP(0x9C4, sdio4,        12,  5,  20,  5),
+	GRP(0x9C8, ao4,          12,  7,  20,  7),
+};
+
+static const struct tegra_grp *
+pinmux_search_grp(char *grp_name)
+{
+	int i;
+
+	for (i = 0; i < nitems(pin_grp_tbl); i++) {
+		if (strcmp(grp_name, pin_grp_tbl[i].name) == 0)
+			return 	(&pin_grp_tbl[i]);
+	}
+	return (NULL);
+}
+
+static const struct tegra_mux *
+pinmux_search_mux(char *pin_name)
+{
+	int i;
+
+	for (i = 0; i < nitems(pin_mux_tbl); i++) {
+		if (strcmp(pin_name, pin_mux_tbl[i].name) == 0)
+			return 	(&pin_mux_tbl[i]);
+	}
+	return (NULL);
+}
+
+static int
+pinmux_mux_function(const struct tegra_mux *mux, char *fnc_name)
+{
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (strcmp(fnc_name, mux->functions[i]) == 0)
+			return 	(i);
+	}
+	return (-1);
+}
+
+static int
+pinmux_config_mux(struct pinmux_softc *sc, char *pin_name,
+    const struct tegra_mux *mux, struct pincfg *cfg)
+{
+	int tmp;
+	uint32_t reg;
+
+	reg = bus_read_4(sc->mux_mem_res, mux->reg);
+
+	if (cfg->function != NULL) {
+		tmp = pinmux_mux_function(mux, cfg->function);
+		if (tmp == -1) {
+			device_printf(sc->dev,
+			    "Unknown function %s for pin %s\n", cfg->function,
+			    pin_name);
+			return (ENXIO);
+		}
+		reg &= ~(TEGRA_MUX_FUNCTION_MASK << TEGRA_MUX_FUNCTION_SHIFT);
+		reg |=  (tmp & TEGRA_MUX_FUNCTION_MASK) <<
+		    TEGRA_MUX_FUNCTION_SHIFT;
+	}
+	if (cfg->params[PROP_ID_PULL] != -1) {
+		reg &= ~(TEGRA_MUX_PUPD_MASK << TEGRA_MUX_PUPD_SHIFT);
+		reg |=  (cfg->params[PROP_ID_PULL] & TEGRA_MUX_PUPD_MASK) <<
+		    TEGRA_MUX_PUPD_SHIFT;
+	}
+	if (cfg->params[PROP_ID_TRISTATE] != -1) {
+		reg &= ~(1 << TEGRA_MUX_TRISTATE_SHIFT);
+		reg |=  (cfg->params[PROP_ID_TRISTATE] & 1) <<
+		    TEGRA_MUX_TRISTATE_SHIFT;
+	}
+	if (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] != -1) {
+		reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
+		reg |=  (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] & 1) <<
+		    TEGRA_MUX_ENABLE_INPUT_SHIFT;
+	}
+	if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) {
+		reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
+		reg |=  (cfg->params[PROP_ID_ENABLE_INPUT] & 1) <<
+		    TEGRA_MUX_ENABLE_INPUT_SHIFT;
+	}
+	if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) {
+		reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT);
+		reg |=  (cfg->params[PROP_ID_OPEN_DRAIN] & 1) <<
+		    TEGRA_MUX_ENABLE_INPUT_SHIFT;
+	}
+	if (cfg->params[PROP_ID_LOCK] != -1) {
+		reg &= ~(1 << TEGRA_MUX_LOCK_SHIFT);
+		reg |=  (cfg->params[PROP_ID_LOCK] & 1) <<
+		    TEGRA_MUX_LOCK_SHIFT;
+	}
+	if (cfg->params[PROP_ID_IORESET] != -1) {
+		reg &= ~(1 << TEGRA_MUX_IORESET_SHIFT);
+		reg |=  (cfg->params[PROP_ID_IORESET] & 1) <<
+		    TEGRA_MUX_IORESET_SHIFT;
+	}
+	if (cfg->params[PROP_ID_RCV_SEL] != -1) {
+		reg &= ~(1 << TEGRA_MUX_RCV_SEL_SHIFT);
+		reg |=  (cfg->params[PROP_ID_RCV_SEL] & 1) <<
+		    TEGRA_MUX_RCV_SEL_SHIFT;
+	}
+	bus_write_4(sc->mux_mem_res, mux->reg, reg);
+	return (0);
+}
+
+static int
+pinmux_config_grp(struct pinmux_softc *sc, char *grp_name,
+    const struct tegra_grp *grp, struct pincfg *cfg)
+{
+	uint32_t reg;
+
+	reg = bus_read_4(sc->pad_mem_res, grp->reg);
+
+	if (cfg->params[PROP_ID_HIGH_SPEED_MODE] != -1) {
+		reg &= ~(1 << TEGRA_GRP_HSM_SHIFT);
+		reg |=  (cfg->params[PROP_ID_HIGH_SPEED_MODE] & 1) <<
+		    TEGRA_GRP_HSM_SHIFT;
+	}
+	if (cfg->params[PROP_ID_SCHMITT] != -1) {
+		reg &= ~(1 << TEGRA_GRP_SCHMT_SHIFT);
+		reg |=  (cfg->params[PROP_ID_SCHMITT] & 1) <<
+		    TEGRA_GRP_SCHMT_SHIFT;
+	}
+	if (cfg->params[PROP_ID_DRIVE_TYPE] != -1) {
+		reg &= ~(TEGRA_GRP_DRV_TYPE_MASK << TEGRA_GRP_DRV_TYPE_SHIFT);
+		reg |=  (cfg->params[PROP_ID_DRIVE_TYPE] &
+		    TEGRA_GRP_DRV_TYPE_MASK) << TEGRA_GRP_DRV_TYPE_SHIFT;
+	}
+	if (cfg->params[PROP_ID_SLEW_RATE_RISING] != -1) {
+		reg &= ~(TEGRA_GRP_DRV_DRVDN_SLWR_MASK <<
+		    TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT);
+		reg |=  (cfg->params[PROP_ID_SLEW_RATE_RISING] &
+		    TEGRA_GRP_DRV_DRVDN_SLWR_MASK) <<
+		    TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT;
+	}
+	if (cfg->params[PROP_ID_SLEW_RATE_FALLING] != -1) {
+		reg &= ~(TEGRA_GRP_DRV_DRVUP_SLWF_MASK <<
+		    TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT);
+		reg |=  (cfg->params[PROP_ID_SLEW_RATE_FALLING] &
+		    TEGRA_GRP_DRV_DRVUP_SLWF_MASK) <<
+		    TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT;
+	}
+	if ((cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] != -1) &&
+		 (grp->drvdn_mask != -1)) {
+		reg &= ~(grp->drvdn_shift << grp->drvdn_mask);
+		reg |=  (cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] &
+		    grp->drvdn_mask) << grp->drvdn_shift;
+	}
+	if ((cfg->params[PROP_ID_DRIVE_UP_STRENGTH] != -1) &&
+		 (grp->drvup_mask != -1)) {
+		reg &= ~(grp->drvup_shift << grp->drvup_mask);
+		reg |=  (cfg->params[PROP_ID_DRIVE_UP_STRENGTH] &
+		    grp->drvup_mask) << grp->drvup_shift;
+	}
+	bus_write_4(sc->pad_mem_res, grp->reg, reg);
+	return (0);
+}
+
+static int
+pinmux_config_node(struct pinmux_softc *sc, char *pin_name, struct pincfg *cfg)
+{
+	const struct tegra_mux *mux;
+	const struct tegra_grp *grp;
+	uint32_t reg;
+	int rv;
+
+	/* Handle MIPI special case first */
+	if (strcmp(pin_name, "dsi_b") == 0) {
+		if (cfg->function == NULL) {
+			/* nothing to set */
+			return (0);
+		}
+		reg = bus_read_4(sc->mipi_mem_res, 0); /* register 0x820 */
+		if (strcmp(cfg->function, "csi") == 0)
+			reg &= ~(1 << 1);
+		else if (strcmp(cfg->function, "dsi_b") == 0)
+			reg |= (1 << 1);
+		bus_write_4(sc->mipi_mem_res, 0, reg); /* register 0x820 */
+	}
+
+	/* Handle pin muxes */
+	mux = pinmux_search_mux(pin_name);
+	if (mux != NULL) {
+		if (mux->gpio_num != -1) {
+			/* XXXX TODO: Reserve gpio here */
+		}
+		rv = pinmux_config_mux(sc, pin_name, mux, cfg);
+		return (rv);
+	}
+
+	/* Handle pin groups */
+	grp = pinmux_search_grp(pin_name);
+	if (grp != NULL) {
+		rv = pinmux_config_grp(sc, pin_name, grp, cfg);
+		return (rv);
+	}
+
+	device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
+	return (ENXIO);
+}
+
+static int
+pinmux_read_node(struct pinmux_softc *sc, phandle_t node, struct pincfg *cfg,
+    char **pins, int *lpins)
+{
+	int rv, i;
+
+	*lpins = OF_getprop_alloc(node, "nvidia,pins", 1, (void **)pins);
+	if (*lpins <= 0)
+		return (ENOENT);
+
+	/* Read function (mux) settings. */
+	rv = OF_getprop_alloc(node, "nvidia,function", 1,
+	    (void **)&cfg->function);
+	if (rv <= 0)
+		cfg->function = NULL;
+
+	/* Read numeric properties. */
+	for (i = 0; i < PROP_ID_MAX_ID; i++) {
+		rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i],
+		    sizeof(cfg->params[i]));
+		if (rv <= 0)
+			cfg->params[i] = -1;
+	}
+	return (0);
+}
+
+static int
+pinmux_process_node(struct pinmux_softc *sc, phandle_t node)
+{
+	struct pincfg cfg;
+	char *pins, *pname;
+	int i, len, lpins, rv;
+
+	rv = pinmux_read_node(sc, node, &cfg, &pins, &lpins);
+	if (rv != 0)
+		return (rv);
+
+	len = 0;
+	pname = pins;
+	do {
+		i = strlen(pname) + 1;
+		rv = pinmux_config_node(sc, pname, &cfg);
+		if (rv != 0)
+			device_printf(sc->dev,
+			    "Cannot configure pin: %s: %d\n", pname, rv);
+
+		len += i;
+		pname += i;
+	} while (len < lpins);
+
+	if (pins != NULL)
+		OF_prop_free(pins);
+	if (cfg.function != NULL)
+		OF_prop_free(cfg.function);
+	return (rv);
+}
+
+static int pinmux_configure(device_t dev, phandle_t cfgxref)
+{
+	struct pinmux_softc *sc;
+	phandle_t node, cfgnode;
+	int rv;
+
+	sc = device_get_softc(dev);
+	cfgnode = OF_node_from_xref(cfgxref);
+
+
+	for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
+		if (!fdt_is_enabled(node))
+			continue;
+		rv = pinmux_process_node(sc, node);
+	}
+	return (0);
+}
+
+static int
+pinmux_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra pin configuration");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pinmux_detach(device_t dev)
+{
+
+	/* This device is always present. */
+	return (EBUSY);
+}
+
+static int
+pinmux_attach(device_t dev)
+{
+	struct pinmux_softc * sc;
+	int rid;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	rid = 0;
+	sc->pad_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->pad_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	rid = 1;
+	sc->mux_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mux_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	rid = 2;
+	sc->mipi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mipi_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	/* Register as a pinctrl device and process default configuration */
+	fdt_pinctrl_register(dev, NULL);
+	fdt_pinctrl_configure_by_name(dev, "boot");
+
+	return (0);
+}
+
+
+static device_method_t tegra_pinmux_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,         pinmux_probe),
+	DEVMETHOD(device_attach,        pinmux_attach),
+	DEVMETHOD(device_detach,        pinmux_detach),
+
+	/* fdt_pinctrl interface */
+	DEVMETHOD(fdt_pinctrl_configure,pinmux_configure),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_pinmux_devclass;
+static DEFINE_CLASS_0(pinmux, tegra_pinmux_driver, tegra_pinmux_methods,
+    sizeof(struct pinmux_softc));
+EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver,
+    tegra_pinmux_devclass, NULL, NULL, 71);


Property changes on: trunk/sys/arm/nvidia/tegra_pinmux.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_pmc.h
===================================================================
--- trunk/sys/arm/nvidia/tegra_pmc.h	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_pmc.h	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,116 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/11/sys/arm/nvidia/tegra_pmc.h 296936 2016-03-16 13:01:48Z mmel $
+ */
+
+#ifndef _TEGRA_PMC_H_
+#define	_TEGRA_PMC_H_
+
+enum tegra_suspend_mode {
+      TEGRA_SUSPEND_NONE = 0,
+      TEGRA_SUSPEND_LP2, 	/* CPU voltage off */
+      TEGRA_SUSPEND_LP1, 	/* CPU voltage off, DRAM self-refresh */
+      TEGRA_SUSPEND_LP0, 	/* CPU + core voltage off, DRAM self-refresh */
+};
+
+/* PARTIDs for powergate */
+enum tegra_powergate_id {
+	TEGRA_POWERGATE_CRAIL	= 0,
+	TEGRA_POWERGATE_TD	= 1,
+	TEGRA_POWERGATE_VE	= 2,
+	TEGRA_POWERGATE_PCX	= 3,
+	TEGRA_POWERGATE_VDE	= 4,
+	TEGRA_POWERGATE_L2C	= 5,
+	TEGRA_POWERGATE_MPE	= 6,
+	TEGRA_POWERGATE_HEG	= 7,
+	TEGRA_POWERGATE_SAX	= 8,
+	TEGRA_POWERGATE_CE1	= 9,
+	TEGRA_POWERGATE_CE2	= 10,
+	TEGRA_POWERGATE_CE3	= 11,
+	TEGRA_POWERGATE_CELP	= 12,
+	/* */
+	TEGRA_POWERGATE_CE0	= 14,
+	TEGRA_POWERGATE_C0NC	= 15,
+	TEGRA_POWERGATE_C1NC	= 16,
+	TEGRA_POWERGATE_SOR	= 17,
+	TEGRA_POWERGATE_DIS	= 18,
+	TEGRA_POWERGATE_DISB	= 19,
+	TEGRA_POWERGATE_XUSBA	= 20,
+	TEGRA_POWERGATE_XUSBB	= 21,
+	TEGRA_POWERGATE_XUSBC	= 22,
+	TEGRA_POWERGATE_VIC	= 23,
+	TEGRA_POWERGATE_IRAM	= 24,
+	/* */
+	TEGRA_POWERGATE_3D	= 32
+
+};
+
+/* PARTIDs for power rails */
+enum tegra_powerrail_id {
+	TEGRA_IO_RAIL_CSIA	= 0,
+	TEGRA_IO_RAIL_CSIB	= 1,
+	TEGRA_IO_RAIL_DSI	= 2,
+	TEGRA_IO_RAIL_MIPI_BIAS	= 3,
+	TEGRA_IO_RAIL_PEX_BIAS	= 4,
+	TEGRA_IO_RAIL_PEX_CLK1	= 5,
+	TEGRA_IO_RAIL_PEX_CLK2	= 6,
+	TEGRA_IO_RAIL_USB0	= 9,
+	TEGRA_IO_RAIL_USB1	= 10,
+	TEGRA_IO_RAIL_USB2	= 11,
+	TEGRA_IO_RAIL_USB_BIAS	= 12,
+	TEGRA_IO_RAIL_NAND	= 13,
+	TEGRA_IO_RAIL_UART	= 14,
+	TEGRA_IO_RAIL_BB	= 15,
+	TEGRA_IO_RAIL_AUDIO	= 17,
+	TEGRA_IO_RAIL_HSIC	= 19,
+	TEGRA_IO_RAIL_COMP	= 22,
+	TEGRA_IO_RAIL_HDMI	= 28,
+	TEGRA_IO_RAIL_PEX_CNTRL	= 32,
+	TEGRA_IO_RAIL_SDMMC1	= 33,
+	TEGRA_IO_RAIL_SDMMC3	= 34,
+	TEGRA_IO_RAIL_SDMMC4	= 35,
+	TEGRA_IO_RAIL_CAM	= 36,
+	TEGRA_IO_RAIL_RES	= 37,
+	TEGRA_IO_RAIL_HV	= 38,
+	TEGRA_IO_RAIL_DSIB	= 39,
+	TEGRA_IO_RAIL_DSIC	= 40,
+	TEGRA_IO_RAIL_DSID	= 41,
+	TEGRA_IO_RAIL_CSIE	= 44,
+	TEGRA_IO_RAIL_LVDS	= 57,
+	TEGRA_IO_RAIL_SYS_DDC	= 58,
+};
+
+int tegra_powergate_is_powered(enum tegra_powergate_id id);
+int tegra_powergate_power_on(enum tegra_powergate_id id);
+int tegra_powergate_power_off(enum tegra_powergate_id id);
+int tegra_powergate_remove_clamping(enum tegra_powergate_id id);
+int tegra_powergate_sequence_power_up(enum tegra_powergate_id id,
+    clk_t clk, hwreset_t rst);
+int tegra_io_rail_power_on(int tegra_powerrail_id);
+int tegra_io_rail_power_off(int tegra_powerrail_id);
+
+#endif /*_TEGRA_PMC_H_*/
\ No newline at end of file


Property changes on: trunk/sys/arm/nvidia/tegra_pmc.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_rtc.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_rtc.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_rtc.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,305 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_rtc.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * RTC driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "clock_if.h"
+
+#define	RTC_CONTROL				0x00
+#define	RTC_BUSY				0x04
+#define	 RTC_BUSY_STATUS				(1 << 0)
+#define	RTC_SECONDS				0x08
+#define	RTC_SHADOW_SECONDS			0x0c
+#define	RTC_MILLI_SECONDS			0x10
+#define	RTC_SECONDS_ALARM0			0x14
+#define	RTC_SECONDS_ALARM1			0x18
+#define	RTC_MILLI_SECONDS_ALARM			0x1c
+#define	RTC_SECONDS_COUNTDOWN_ALARM		0x20
+#define	RTC_MILLI_SECONDS_COUNTDOW_ALARM	0x24
+#define	RTC_INTR_MASK				0x28
+#define	 RTC_INTR_MSEC_CDN_ALARM			(1 << 4)
+#define	 RTC_INTR_SEC_CDN_ALARM				(1 << 3)
+#define	 RTC_INTR_MSEC_ALARM				(1 << 2)
+#define	 RTC_INTR_SEC_ALARM1				(1 << 1)
+#define	 RTC_INTR_SEC_ALARM0				(1 << 0)
+
+#define	RTC_INTR_STATUS				0x2c
+#define	RTC_INTR_SOURCE				0x30
+#define	RTC_INTR_SET				0x34
+#define	RTC_CORRECTION_FACTOR			0x38
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+#define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	SLEEP(_sc, timeout)						\
+	mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout);
+#define	LOCK_INIT(_sc)							\
+	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF)
+#define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
+#define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
+#define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-rtc",	1},
+	{NULL,			0}
+};
+
+struct tegra_rtc_softc {
+	device_t		dev;
+	struct mtx		mtx;
+
+	struct resource		*mem_res;
+	struct resource		*irq_res;
+	void			*irq_h;
+
+	clk_t			clk;
+	uint32_t		core_freq;
+};
+
+static void
+tegra_rtc_wait(struct tegra_rtc_softc *sc)
+{
+	int timeout;
+
+	for (timeout = 500; timeout >0; timeout--) {
+		if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0)
+			break;
+		DELAY(1);
+	}
+	if (timeout <= 0)
+		device_printf(sc->dev, "Device busy timeouted\n");
+
+}
+
+/*
+ * Get the time of day clock and return it in ts.
+ * Return 0 on success, an error number otherwise.
+ */
+static int
+tegra_rtc_gettime(device_t dev, struct timespec *ts)
+{
+	struct tegra_rtc_softc *sc;
+	struct timeval tv;
+	uint32_t msec, sec;
+
+	sc = device_get_softc(dev);
+
+	LOCK(sc);
+	msec = RD4(sc, RTC_MILLI_SECONDS);
+	sec = RD4(sc, RTC_SHADOW_SECONDS);
+	UNLOCK(sc);
+	tv.tv_sec = sec;
+	tv.tv_usec = msec * 1000;
+	TIMEVAL_TO_TIMESPEC(&tv, ts);
+	return (0);
+}
+
+
+static int
+tegra_rtc_settime(device_t dev, struct timespec *ts)
+{
+	struct tegra_rtc_softc *sc;
+	struct timeval tv;
+
+	sc = device_get_softc(dev);
+
+	LOCK(sc);
+	TIMESPEC_TO_TIMEVAL(&tv, ts);
+	tegra_rtc_wait(sc);
+	WR4(sc, RTC_SECONDS, tv.tv_sec);
+	UNLOCK(sc);
+
+	return (0);
+}
+
+
+static void
+tegra_rtc_intr(void *arg)
+{
+	struct tegra_rtc_softc *sc;
+	uint32_t status;
+
+	sc = (struct tegra_rtc_softc *)arg;
+	LOCK(sc);
+	status = RD4(sc, RTC_INTR_STATUS);
+	WR4(sc, RTC_INTR_STATUS, status);
+	UNLOCK(sc);
+}
+
+static int
+tegra_rtc_probe(device_t dev)
+{
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_rtc_attach(device_t dev)
+{
+	int rv, rid;
+	struct tegra_rtc_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	LOCK_INIT(sc);
+
+	/* Get the memory resource for the register mapping. */
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot map registers.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* Allocate our IRQ resource. */
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate interrupt.\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	/* OFW resources. */
+	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get i2c clock: %d\n", rv);
+		goto fail;
+	}
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock: %d\n", rv);
+		goto fail;
+	}
+
+	/* Init hardware. */
+	WR4(sc, RTC_SECONDS_ALARM0, 0);
+	WR4(sc, RTC_SECONDS_ALARM1, 0);
+	WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF);
+	WR4(sc, RTC_INTR_MASK, 0);
+
+	/* Setup  interrupt */
+	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, tegra_rtc_intr, sc, &sc->irq_h);
+	if (rv) {
+		device_printf(dev, "Cannot setup interrupt.\n");
+		goto fail;
+	}
+
+	/*
+	 * Register as a time of day clock with 1-second resolution.
+	 *
+	 * XXXX Not yet, we don't have support for multiple RTCs
+	 */
+	/* clock_register(dev, 1000000); */
+
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+	LOCK_DESTROY(sc);
+
+	return (rv);
+}
+
+static int
+tegra_rtc_detach(device_t dev)
+{
+	struct tegra_rtc_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->irq_h != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	LOCK_DESTROY(sc);
+	return (bus_generic_detach(dev));
+}
+
+static device_method_t tegra_rtc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_rtc_probe),
+	DEVMETHOD(device_attach,	tegra_rtc_attach),
+	DEVMETHOD(device_detach,	tegra_rtc_detach),
+
+	/* clock interface */
+	DEVMETHOD(clock_gettime,	tegra_rtc_gettime),
+	DEVMETHOD(clock_settime,	tegra_rtc_settime),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_rtc_devclass;
+static DEFINE_CLASS_0(rtc, tegra_rtc_driver, tegra_rtc_methods,
+    sizeof(struct tegra_rtc_softc));
+DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, tegra_rtc_devclass,
+    NULL, NULL);


Property changes on: trunk/sys/arm/nvidia/tegra_rtc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_sdhci.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_sdhci.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_sdhci.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,468 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_sdhci.c 343504 2019-01-27 19:04:28Z marius $");
+
+/*
+ * SDHCI driver glue for NVIDIA Tegra family
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcbrvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt_gpio.h>
+
+#include "sdhci_if.h"
+
+/* Tegra SDHOST controller vendor register definitions */
+#define	SDMMC_VENDOR_CLOCK_CNTRL		0x100
+#define	 VENDOR_CLOCK_CNTRL_CLK_SHIFT			8
+#define	 VENDOR_CLOCK_CNTRL_CLK_MASK			0xFF
+#define	SDMMC_VENDOR_SYS_SW_CNTRL		0x104
+#define	SDMMC_VENDOR_CAP_OVERRIDES		0x10C
+#define	SDMMC_VENDOR_BOOT_CNTRL			0x110
+#define	SDMMC_VENDOR_BOOT_ACK_TIMEOUT		0x114
+#define	SDMMC_VENDOR_BOOT_DAT_TIMEOUT		0x118
+#define	SDMMC_VENDOR_DEBOUNCE_COUNT		0x11C
+#define	SDMMC_VENDOR_MISC_CNTRL			0x120
+#define	 VENDOR_MISC_CTRL_ENABLE_SDR104			0x8
+#define	 VENDOR_MISC_CTRL_ENABLE_SDR50			0x10
+#define	 VENDOR_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
+#define	 VENDOR_MISC_CTRL_ENABLE_DDR50			0x200
+#define	SDMMC_MAX_CURRENT_OVERRIDE		0x124
+#define	SDMMC_MAX_CURRENT_OVERRIDE_HI		0x128
+#define	SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT 	0x1D0
+#define	SDMMC_VENDOR_PHWRESET_VAL0		0x1D4
+#define	SDMMC_VENDOR_PHWRESET_VAL1		0x1D8
+#define	SDMMC_VENDOR_PHWRESET_VAL2		0x1DC
+#define	SDMMC_SDMEMCOMPPADCTRL_0		0x1E0
+#define	SDMMC_AUTO_CAL_CONFIG			0x1E4
+#define	SDMMC_AUTO_CAL_INTERVAL			0x1E8
+#define	SDMMC_AUTO_CAL_STATUS			0x1EC
+#define	SDMMC_SDMMC_MCCIF_FIFOCTRL		0x1F4
+#define	SDMMC_TIMEOUT_WCOAL_SDMMC		0x1F8
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-sdhci",	1},
+	{NULL,				0},
+};
+
+struct tegra_sdhci_softc {
+	device_t		dev;
+	struct resource *	mem_res;
+	struct resource *	irq_res;
+	void *			intr_cookie;
+	u_int			quirks;	/* Chip specific quirks */
+	u_int			caps;	/* If we override SDHCI_CAPABILITIES */
+	uint32_t		max_clk; /* Max possible freq */
+	clk_t			clk;
+	hwreset_t 		reset;
+	gpio_pin_t		gpio_power;
+	struct sdhci_fdt_gpio	*gpio;
+
+	int			force_card_present;
+	struct sdhci_slot	slot;
+
+};
+
+static inline uint32_t
+RD4(struct tegra_sdhci_softc *sc, bus_size_t off)
+{
+
+	return (bus_read_4(sc->mem_res, off));
+}
+
+static uint8_t
+tegra_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (bus_read_1(sc->mem_res, off));
+}
+
+static uint16_t
+tegra_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	return (bus_read_2(sc->mem_res, off));
+}
+
+static uint32_t
+tegra_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+	struct tegra_sdhci_softc *sc;
+	uint32_t val32;
+
+	sc = device_get_softc(dev);
+	val32 = bus_read_4(sc->mem_res, off);
+	/* Force the card-present state if necessary. */
+	if (off == SDHCI_PRESENT_STATE && sc->force_card_present)
+		val32 |= SDHCI_CARD_PRESENT;
+	return (val32);
+}
+
+static void
+tegra_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+    uint32_t *data, bus_size_t count)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_read_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+tegra_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+    uint8_t val)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_write_1(sc->mem_res, off, val);
+}
+
+static void
+tegra_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+    uint16_t val)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_write_2(sc->mem_res, off, val);
+}
+
+static void
+tegra_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+    uint32_t val)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_write_4(sc->mem_res, off, val);
+}
+
+static void
+tegra_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+    uint32_t *data, bus_size_t count)
+{
+	struct tegra_sdhci_softc *sc;
+
+	sc = device_get_softc(dev);
+	bus_write_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+tegra_sdhci_intr(void *arg)
+{
+	struct tegra_sdhci_softc *sc = arg;
+
+	sdhci_generic_intr(&sc->slot);
+	RD4(sc, SDHCI_INT_STATUS);
+}
+
+static int
+tegra_sdhci_get_ro(device_t brdev, device_t reqdev)
+{
+	struct tegra_sdhci_softc *sc = device_get_softc(brdev);
+
+	return (sdhci_fdt_gpio_get_readonly(sc->gpio));
+}
+
+static bool
+tegra_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot)
+{
+	struct tegra_sdhci_softc *sc = device_get_softc(dev);
+
+	return (sdhci_fdt_gpio_get_present(sc->gpio));
+}
+
+static int
+tegra_sdhci_probe(device_t dev)
+{
+	struct tegra_sdhci_softc *sc;
+	phandle_t node;
+	pcell_t cid;
+	const struct ofw_compat_data *cd;
+
+	sc = device_get_softc(dev);
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_is_compatible(dev, "nvidia,tegra124-sdhci")) {
+		device_set_desc(dev, "Tegra SDHCI controller");
+	} else
+		return (ENXIO);
+	cd = ofw_bus_search_compatible(dev, compat_data);
+	if (cd->ocd_data == 0)
+		return (ENXIO);
+
+	node = ofw_bus_get_node(dev);
+
+	/* Allow dts to patch quirks, slots, and max-frequency. */
+	if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0)
+		sc->quirks = cid;
+	if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0)
+		sc->max_clk = cid;
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tegra_sdhci_attach(device_t dev)
+{
+	struct tegra_sdhci_softc *sc;
+	int rid, rv;
+	uint64_t freq;
+	phandle_t node, prop;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(dev);
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (!sc->mem_res) {
+		device_printf(dev, "cannot allocate memory window\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (!sc->irq_res) {
+		device_printf(dev, "cannot allocate interrupt\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+	    NULL, tegra_sdhci_intr, sc, &sc->intr_cookie)) {
+		device_printf(dev, "cannot setup interrupt handler\n");
+		rv = ENXIO;
+		goto fail;
+	}
+
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "sdhci", &sc->reset);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'sdhci' reset\n");
+		goto fail;
+	}
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot unreset 'sdhci' reset\n");
+		goto fail;
+	}
+
+	gpio_pin_get_by_ofw_property(sc->dev, node, "power-gpios", &sc->gpio_power);
+
+	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+	if (rv != 0) {
+
+		device_printf(dev, "Cannot get clock\n");
+		goto fail;
+	}
+
+	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get clock\n");
+		goto fail;
+	}
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable clock\n");
+		goto fail;
+	}
+	rv = clk_set_freq(sc->clk, 48000000, CLK_SET_ROUND_DOWN);
+	if (rv != 0) {
+		device_printf(dev, "Cannot set clock\n");
+	}
+	rv = clk_get_freq(sc->clk, &freq);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get clock frequency\n");
+		goto fail;
+	}
+	if (bootverbose)
+		device_printf(dev, " Base MMC clock: %lld\n", freq);
+
+	/* Fill slot information. */
+	sc->max_clk = (int)freq;
+	sc->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+	    SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+	    SDHCI_QUIRK_MISSING_CAPS;
+
+	/* Limit real slot capabilities. */
+	sc->caps = RD4(sc, SDHCI_CAPABILITIES);
+	if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) {
+		sc->caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
+		switch (prop) {
+		case 8:
+			sc->caps |= MMC_CAP_8_BIT_DATA;
+			/* FALLTHROUGH */
+		case 4:
+			sc->caps |= MMC_CAP_4_BIT_DATA;
+			break;
+		case 1:
+			break;
+		default:
+			device_printf(dev, "Bad bus-width value %u\n", prop);
+			break;
+		}
+	}
+	if (OF_hasprop(node, "non-removable"))
+		sc->force_card_present = 1;
+	/*
+	 * Clear clock field, so SDHCI driver uses supplied frequency.
+	 * in sc->slot.max_clk
+	 */
+	sc->caps &= ~SDHCI_CLOCK_V3_BASE_MASK;
+
+	sc->slot.quirks = sc->quirks;
+	sc->slot.max_clk = sc->max_clk;
+	sc->slot.caps = sc->caps;
+
+	rv = sdhci_init_slot(dev, &sc->slot, 0);
+	if (rv != 0) {
+		goto fail;
+	}
+
+	sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot);
+
+	bus_generic_probe(dev);
+	bus_generic_attach(dev);
+
+	sdhci_start_slot(&sc->slot);
+
+	return (0);
+
+fail:
+	if (sc->gpio != NULL)
+		sdhci_fdt_gpio_teardown(sc->gpio);
+	if (sc->intr_cookie != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
+	if (sc->gpio_power != NULL)
+		gpio_pin_release(sc->gpio_power);
+	if (sc->clk != NULL)
+		clk_release(sc->clk);
+	if (sc->reset != NULL)
+		hwreset_release(sc->reset);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	return (rv);
+}
+
+static int
+tegra_sdhci_detach(device_t dev)
+{
+	struct tegra_sdhci_softc *sc = device_get_softc(dev);
+	struct sdhci_slot *slot = &sc->slot;
+
+	bus_generic_detach(dev);
+	sdhci_fdt_gpio_teardown(sc->gpio);
+	clk_release(sc->clk);
+	bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
+	bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res),
+			     sc->irq_res);
+
+	sdhci_cleanup_slot(slot);
+	bus_release_resource(dev, SYS_RES_MEMORY,
+			     rman_get_rid(sc->mem_res),
+			     sc->mem_res);
+	return (0);
+}
+
+static device_method_t tegra_sdhci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_sdhci_probe),
+	DEVMETHOD(device_attach,	tegra_sdhci_attach),
+	DEVMETHOD(device_detach,	tegra_sdhci_detach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
+	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
+
+	/* MMC bridge interface */
+	DEVMETHOD(mmcbr_update_ios,	sdhci_generic_update_ios),
+	DEVMETHOD(mmcbr_request,	sdhci_generic_request),
+	DEVMETHOD(mmcbr_get_ro,		tegra_sdhci_get_ro),
+	DEVMETHOD(mmcbr_acquire_host,	sdhci_generic_acquire_host),
+	DEVMETHOD(mmcbr_release_host,	sdhci_generic_release_host),
+
+	/* SDHCI registers accessors */
+	DEVMETHOD(sdhci_read_1,		tegra_sdhci_read_1),
+	DEVMETHOD(sdhci_read_2,		tegra_sdhci_read_2),
+	DEVMETHOD(sdhci_read_4,		tegra_sdhci_read_4),
+	DEVMETHOD(sdhci_read_multi_4,	tegra_sdhci_read_multi_4),
+	DEVMETHOD(sdhci_write_1,	tegra_sdhci_write_1),
+	DEVMETHOD(sdhci_write_2,	tegra_sdhci_write_2),
+	DEVMETHOD(sdhci_write_4,	tegra_sdhci_write_4),
+	DEVMETHOD(sdhci_write_multi_4,	tegra_sdhci_write_multi_4),
+	DEVMETHOD(sdhci_get_card_present, tegra_sdhci_get_card_present),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_sdhci_devclass;
+static DEFINE_CLASS_0(sdhci, tegra_sdhci_driver, tegra_sdhci_methods,
+    sizeof(struct tegra_sdhci_softc));
+DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, tegra_sdhci_devclass,
+    NULL, NULL);
+SDHCI_DEPEND(sdhci_tegra);
+MMC_DECLARE_BRIDGE(sdhci);


Property changes on: trunk/sys/arm/nvidia/tegra_sdhci.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_soctherm.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_soctherm.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_soctherm.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,697 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_soctherm.c 308335 2016-11-05 10:56:32Z mmel $");
+
+/*
+ * Thermometer and thermal zones driver for Tegra SoCs.
+ * Calibration data and algo are taken from Linux, because this part of SoC
+ * is undocumented in TRM.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+#include <gnu/dts/include/dt-bindings/thermal/tegra124-soctherm.h>
+#include "tegra_soctherm_if.h"
+
+/* Per sensors registers - base is 0x0c0*/
+#define	TSENSOR_CONFIG0				0x000
+#define	 TSENSOR_CONFIG0_TALL(x)			(((x) & 0xFFFFF) << 8)
+#define	 TSENSOR_CONFIG0_STATUS_CLR			(1 << 5)
+#define	 TSENSOR_CONFIG0_TCALC_OVERFLOW			(1 << 4)
+#define	 TSENSOR_CONFIG0_OVERFLOW			(1 << 3)
+#define	 TSENSOR_CONFIG0_CPTR_OVERFLOW			(1 << 2)
+#define	 TSENSOR_CONFIG0_RO_SEL				(1 << 1)
+#define	 TSENSOR_CONFIG0_STOP				(1 << 0)
+
+#define	TSENSOR_CONFIG1				0x004
+#define	 TSENSOR_CONFIG1_TEMP_ENABLE			(1U << 31)
+#define	 TSENSOR_CONFIG1_TEN_COUNT(x)			(((x) & 0x3F) << 24)
+#define	 TSENSOR_CONFIG1_TIDDQ_EN(x)			(((x) & 0x3F) << 15)
+#define	 TSENSOR_CONFIG1_TSAMPLE(x)			(((x) & 0x3FF) << 0)
+
+#define	TSENSOR_CONFIG2				0x008
+#define	TSENSOR_CONFIG2_THERMA(x)			(((x) & 0xFFFF) << 16)
+#define	TSENSOR_CONFIG2_THERMB(x)			(((x) & 0xFFFF) << 0)
+
+#define	TSENSOR_STATUS0				0x00c
+#define	 TSENSOR_STATUS0_CAPTURE_VALID			(1U << 31)
+#define	 TSENSOR_STATUS0_CAPTURE(x)			(((x) >> 0) & 0xffff)
+
+#define	TSENSOR_STATUS1				0x010
+#define	 TSENSOR_STATUS1_TEMP_VALID			(1U << 31)
+#define	 TSENSOR_STATUS1_TEMP(x)			(((x) >> 0) & 0xffff)
+
+#define	TSENSOR_STATUS2				0x014
+#define	 TSENSOR_STATUS2_TEMP_MAX(x)			(((x) >> 16) & 0xffff)
+#define	 TSENSOR_STATUS2_TEMP_MIN(x)			(((x) >>  0) & 0xffff)
+
+/* Global registers */
+#define	TSENSOR_PDIV				0x1c0
+#define	 TSENSOR_PDIV_T124				0x8888
+#define	TSENSOR_HOTSPOT_OFF			0x1c4
+#define	 TSENSOR_HOTSPOT_OFF_T124			0x00060600
+#define	TSENSOR_TEMP1				0x1c8
+#define	TSENSOR_TEMP2				0x1cc
+
+/* Readbacks */
+#define	READBACK_VALUE_MASK				0xff00
+#define	READBACK_VALUE_SHIFT				8
+#define	READBACK_ADD_HALF				(1 << 7)
+#define	READBACK_NEGATE					(1 << 0)
+
+
+/* Fuses */
+#define	 FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT		0
+#define	 FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS		13
+#define	 FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT		13
+#define	 FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS		13
+
+#define	FUSE_TSENSOR8_CALIB			0x180
+#define	 FUSE_TSENSOR8_CALIB_CP_TS_BASE(x)		(((x) >>  0) & 0x3ff)
+#define	 FUSE_TSENSOR8_CALIB_FT_TS_BASE(x)		(((x) >> 10) & 0x7ff)
+
+#define	FUSE_SPARE_REALIGNMENT_REG		0x1fc
+#define	 FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 	0
+#define	 FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 	6
+#define	 FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 	21
+#define	 FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS 	5
+#define	 FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP(x) 	(((x) >>  0) & 0x3f)
+#define	 FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT(x) 	(((x) >> 21) & 0x1f)
+
+
+#define	NOMINAL_CALIB_FT_T124	105
+#define	NOMINAL_CALIB_CP_T124	25
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
+
+static struct sysctl_ctx_list soctherm_sysctl_ctx;
+
+struct soctherm_shared_cal {
+	uint32_t		base_cp;
+	uint32_t		base_ft;
+	int32_t			actual_temp_cp;
+	int32_t			actual_temp_ft;
+};
+struct tsensor_cfg {
+	uint32_t		tall;
+	uint32_t		tsample;
+	uint32_t		tiddq_en;
+	uint32_t		ten_count;
+	uint32_t		pdiv;
+	uint32_t		tsample_ate;
+	uint32_t		pdiv_ate;
+};
+
+struct tsensor {
+	char 			*name;
+	int			id;
+	struct tsensor_cfg 	*cfg;
+	bus_addr_t		sensor_base;
+	bus_addr_t		calib_fuse;
+	int 			fuse_corr_alpha;
+	int			fuse_corr_beta;
+
+	int16_t			therm_a;
+	int16_t			therm_b;
+};
+
+struct soctherm_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+	struct resource		*irq_res;
+	void			*irq_ih;
+
+	clk_t			tsensor_clk;
+	clk_t			soctherm_clk;
+	hwreset_t			reset;
+
+	int			ntsensors;
+	struct tsensor		*tsensors;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-soctherm",	1},
+	{NULL,				0},
+};
+
+static struct tsensor_cfg t124_tsensor_config = {
+	.tall = 16300,
+	.tsample = 120,
+	.tiddq_en = 1,
+	.ten_count = 1,
+	.pdiv = 8,
+	.tsample_ate = 480,
+	.pdiv_ate = 8
+};
+
+
+static struct tsensor t124_tsensors[] = {
+	{
+		.name = "cpu0",
+		.id = TEGRA124_SOCTHERM_SENSOR_CPU,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x0c0,
+		.calib_fuse = 0x098,
+		.fuse_corr_alpha = 1135400,
+		.fuse_corr_beta = -6266900,
+	},
+	{
+		.name = "cpu1",
+		.id = -1,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x0e0,
+		.calib_fuse = 0x084,
+		.fuse_corr_alpha = 1122220,
+		.fuse_corr_beta = -5700700,
+	},
+	{
+		.name = "cpu2",
+		.id = -1,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x100,
+		.calib_fuse = 0x088,
+		.fuse_corr_alpha = 1127000,
+		.fuse_corr_beta = -6768200,
+	},
+	{
+		.name = "cpu3",
+		.id = -1,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x120,
+		.calib_fuse = 0x12c,
+		.fuse_corr_alpha = 1110900,
+		.fuse_corr_beta = -6232000,
+	},
+	{
+		.name = "mem0",
+		.id = TEGRA124_SOCTHERM_SENSOR_MEM,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x140,
+		.calib_fuse = 0x158,
+		.fuse_corr_alpha = 1122300,
+		.fuse_corr_beta = -5936400,
+	},
+	{
+		.name = "mem1",
+		.id = -1,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x160,
+		.calib_fuse = 0x15c,
+		.fuse_corr_alpha = 1145700,
+		.fuse_corr_beta = -7124600,
+	},
+	{
+		.name = "gpu",
+		.id = TEGRA124_SOCTHERM_SENSOR_GPU,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x180,
+		.calib_fuse = 0x154,
+		.fuse_corr_alpha = 1120100,
+		.fuse_corr_beta = -6000500,
+	},
+	{
+		.name = "pllX",
+		.id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+		.cfg = &t124_tsensor_config,
+		.sensor_base = 0x1a0,
+		.calib_fuse = 0x160,
+		.fuse_corr_alpha = 1106500,
+		.fuse_corr_beta = -6729300,
+	},
+};
+
+/* Extract signed integer bitfield from register */
+static int
+extract_signed(uint32_t reg, int shift, int bits)
+{
+	int32_t val;
+	uint32_t mask;
+
+	mask = (1 << bits) - 1;
+	val = ((reg >> shift) & mask) << (32 - bits);
+	val >>= 32 - bits;
+	return ((int32_t)val);
+}
+
+static inline int64_t div64_s64_precise(int64_t a, int64_t b)
+{
+	int64_t r, al;
+
+	al = a << 16;
+	r = (al * 2 + 1) / (2 * b);
+	return r >> 16;
+}
+
+static void
+get_shared_cal(struct soctherm_softc *sc, struct soctherm_shared_cal *cal)
+{
+	uint32_t val;
+	int calib_cp, calib_ft;
+
+	val = tegra_fuse_read_4(FUSE_TSENSOR8_CALIB);
+	cal->base_cp = FUSE_TSENSOR8_CALIB_CP_TS_BASE(val);
+	cal->base_ft = FUSE_TSENSOR8_CALIB_FT_TS_BASE(val);
+
+	val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG);
+	calib_ft = extract_signed(val,
+	    FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT,
+	    FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS);
+	calib_cp = extract_signed(val,
+	    FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT,
+	    FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS);
+
+	cal->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + calib_cp;
+	cal->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + calib_ft;
+#ifdef DEBUG
+	printf("%s: base_cp: %u, base_ft: %d,"
+	    " actual_temp_cp: %d, actual_temp_ft: %d\n",
+	    __func__, cal->base_cp, cal->base_ft,
+	    cal->actual_temp_cp, cal->actual_temp_ft);
+#endif
+}
+
+
+static void
+tsensor_calibration(struct tsensor *sensor, struct soctherm_shared_cal *shared)
+{
+	uint32_t val;
+	int mult, div, calib_cp, calib_ft;
+	int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp;
+	int temp_a, temp_b;
+	int64_t tmp;
+
+	val =  tegra_fuse_read_4(sensor->calib_fuse);
+	calib_cp = extract_signed(val,
+	    FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT,
+	    FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS);
+	actual_tsensor_cp = shared->base_cp * 64 + calib_cp;
+
+	calib_ft = extract_signed(val,
+	    FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT,
+	    FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS);
+	actual_tsensor_ft = shared->base_ft * 32 + calib_ft;
+
+	delta_sens = actual_tsensor_ft - actual_tsensor_cp;
+	delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
+	mult = sensor->cfg->pdiv * sensor->cfg->tsample_ate;
+	div = sensor->cfg->tsample * sensor->cfg->pdiv_ate;
+
+
+	temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult,
+				   (int64_t) delta_sens * div);
+
+	tmp = (int64_t)actual_tsensor_ft * shared->actual_temp_cp -
+	      (int64_t)actual_tsensor_cp * shared->actual_temp_ft;
+	temp_b = div64_s64_precise(tmp, (int64_t)delta_sens);
+
+	temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha,
+				   1000000);
+	temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha +
+				   sensor->fuse_corr_beta, 1000000);
+	sensor->therm_a = (int16_t)temp_a;
+	sensor->therm_b = (int16_t)temp_b;
+#ifdef DEBUG
+	printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)"
+	    " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n",
+	    __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF,
+	    calib_cp, calib_cp, calib_ft, calib_ft);
+	printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n",
+	    (uint16_t)sensor->therm_a, temp_a,
+	    (uint16_t)sensor->therm_b, sensor->therm_b);
+#endif
+}
+
+static void
+soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor,
+    struct soctherm_shared_cal *shared_cal)
+{
+	uint32_t val;
+
+	tsensor_calibration(sensor, shared_cal);
+
+	val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
+	val |= TSENSOR_CONFIG0_STOP;
+	val |= TSENSOR_CONFIG0_STATUS_CLR;
+	WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+
+	val = TSENSOR_CONFIG0_TALL(sensor->cfg->tall);
+	val |= TSENSOR_CONFIG0_STOP;
+	WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+
+	val = TSENSOR_CONFIG1_TSAMPLE(sensor->cfg->tsample - 1);
+	val |= TSENSOR_CONFIG1_TIDDQ_EN(sensor->cfg->tiddq_en);
+	val |= TSENSOR_CONFIG1_TEN_COUNT(sensor->cfg->ten_count);
+	val |= TSENSOR_CONFIG1_TEMP_ENABLE;
+	WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val);
+
+	val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) |
+	     TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b);
+	WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val);
+
+	val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
+	val &= ~TSENSOR_CONFIG0_STOP;
+	WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+#ifdef DEBUG
+	printf(" Sensor: %s  cfg:0x%08X, 0x%08X, 0x%08X,"
+	    " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
+	    RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
+	    RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
+	    RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
+	    RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
+	    RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
+	    RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
+	    );
+#endif
+}
+
+static int
+soctherm_convert_raw(uint32_t val)
+{
+	int32_t t;
+
+	t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
+	if (val & READBACK_ADD_HALF)
+		t += 500;
+	if (val & READBACK_NEGATE)
+		t *= -1;
+
+	return t;
+}
+
+static int
+soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp)
+{
+	int timeout;
+	uint32_t val;
+
+
+	/* wait for valid sample */
+	for (timeout = 1000; timeout > 0; timeout--) {
+		val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1);
+		if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0)
+			break;
+		DELAY(100);
+	}
+	if (timeout <= 0)
+		device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name);
+	*temp = soctherm_convert_raw(val);
+#ifdef DEBUG
+	printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp);
+	printf(" Sensor: %s  cfg:0x%08X, 0x%08X, 0x%08X,"
+	    " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
+	    RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
+	    RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
+	    RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
+	    RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
+	    RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
+	    RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
+	    );
+#endif
+	return 0;
+}
+
+static int
+soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val)
+{
+	struct soctherm_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+	/* The direct sensor map starts at 0x100 */
+	if (id >= 0x100) {
+		id -= 0x100;
+		if (id >= sc->ntsensors)
+			return (ERANGE);
+		return(soctherm_read_temp(sc, sc->tsensors + id, val));
+	}
+	/* Linux (DT) compatible thermal zones */
+	for (i = 0; i < sc->ntsensors; i++) {
+		if (sc->tsensors->id == id)
+			return(soctherm_read_temp(sc, sc->tsensors + id, val));
+	}
+	return (ERANGE);
+}
+
+static int
+soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS)
+{
+	struct soctherm_softc *sc;
+	int val;
+	int rv;
+	int id;
+
+	/* Write request */
+	if (req->newptr != NULL)
+		return (EINVAL);
+
+	sc = arg1;
+	id = arg2;
+
+	if (id >= sc->ntsensors)
+		return (ERANGE);
+	rv =  soctherm_read_temp(sc, sc->tsensors + id, &val);
+	if (rv != 0)
+		return (rv);
+
+	val = val / 100;
+	val +=  2731;
+	rv = sysctl_handle_int(oidp, &val, 0, req);
+	return (rv);
+}
+
+static int
+soctherm_init_sysctl(struct soctherm_softc *sc)
+{
+	int i;
+	struct sysctl_oid *oid, *tmp;
+
+	sysctl_ctx_init(&soctherm_sysctl_ctx);
+	/* create node for hw.temp */
+	oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx,
+	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
+	    CTLFLAG_RD, NULL, "");
+	if (oid == NULL)
+		return (ENXIO);
+
+	/* Add sensors */
+	for (i = sc->ntsensors  - 1; i >= 0; i--) {
+		tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx,
+		    SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name,
+		    CTLTYPE_INT | CTLFLAG_RD, sc, i,
+		    soctherm_sysctl_temperature, "IK", "SoC Temperature");
+		if (tmp == NULL)
+			return (ENXIO);
+	}
+
+	return (0);
+}
+
+static int
+soctherm_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra temperature sensors");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+soctherm_attach(device_t dev)
+{
+	struct soctherm_softc *sc;
+	phandle_t node;
+	int i, rid, rv;
+	struct soctherm_shared_cal shared_calib;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	node = ofw_bus_get_node(sc->dev);
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		goto fail;
+	}
+
+	rid = 0;
+	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "Cannot allocate IRQ resources\n");
+		goto fail;
+	}
+
+/*
+	if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
+	    soctherm_intr, NULL, sc, &sc->irq_ih))) {
+		device_printf(dev,
+		    "WARNING: unable to register interrupt handler\n");
+		goto fail;
+	}
+*/
+
+	/* OWF resources */
+	rv = hwreset_get_by_ofw_name(dev, 0, "soctherm", &sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get fuse reset\n");
+		goto fail;
+	}
+	rv = clk_get_by_ofw_name(dev, 0, "tsensor", &sc->tsensor_clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv);
+		goto fail;
+	}
+	rv = clk_get_by_ofw_name(dev, 0, "soctherm", &sc->soctherm_clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv);
+		goto fail;
+	}
+
+	rv = hwreset_assert(sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot assert reset\n");
+		goto fail;
+	}
+	rv = clk_enable(sc->tsensor_clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv);
+		goto fail;
+	}
+	rv = clk_enable(sc->soctherm_clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv);
+		goto fail;
+	}
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot clear reset\n");
+		goto fail;
+	}
+
+	/* Tegra 124 */
+	sc->tsensors = t124_tsensors;
+	sc->ntsensors = nitems(t124_tsensors);
+	get_shared_cal(sc, &shared_calib);
+
+	WR4(sc, TSENSOR_PDIV, TSENSOR_PDIV_T124);
+	WR4(sc, TSENSOR_HOTSPOT_OFF, TSENSOR_HOTSPOT_OFF_T124);
+
+	for (i = 0; i < sc->ntsensors; i++)
+		soctherm_init_tsensor(sc, sc->tsensors + i, &shared_calib);
+
+	rv = soctherm_init_sysctl(sc);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot initialize sysctls\n");
+		goto fail;
+	}
+
+	OF_device_register_xref(OF_xref_from_node(node), dev);
+	return (bus_generic_attach(dev));
+
+fail:
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	sysctl_ctx_free(&soctherm_sysctl_ctx);
+	if (sc->tsensor_clk != NULL)
+		clk_release(sc->tsensor_clk);
+	if (sc->soctherm_clk != NULL)
+		clk_release(sc->soctherm_clk);
+	if (sc->reset != NULL)
+		hwreset_release(sc->reset);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	return (ENXIO);
+}
+
+static int
+soctherm_detach(device_t dev)
+{
+	struct soctherm_softc *sc;
+	sc = device_get_softc(dev);
+
+	if (sc->irq_ih != NULL)
+		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+	sysctl_ctx_free(&soctherm_sysctl_ctx);
+	if (sc->tsensor_clk != NULL)
+		clk_release(sc->tsensor_clk);
+	if (sc->soctherm_clk != NULL)
+		clk_release(sc->soctherm_clk);
+	if (sc->reset != NULL)
+		hwreset_release(sc->reset);
+	if (sc->irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+	return (ENXIO);
+}
+
+static device_method_t tegra_soctherm_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,			soctherm_probe),
+	DEVMETHOD(device_attach,		soctherm_attach),
+	DEVMETHOD(device_detach,		soctherm_detach),
+
+	/* SOCTHERM interface */
+	DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_soctherm_devclass;
+static DEFINE_CLASS_0(soctherm, tegra_soctherm_driver, tegra_soctherm_methods,
+    sizeof(struct soctherm_softc));
+EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver,
+    tegra_soctherm_devclass, NULL, NULL, 79);


Property changes on: trunk/sys/arm/nvidia/tegra_soctherm.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_soctherm_if.m
===================================================================
--- trunk/sys/arm/nvidia/tegra_soctherm_if.m	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_soctherm_if.m	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,43 @@
+/* $MidnightBSD$ */
+#-
+# Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD: stable/11/sys/arm/nvidia/tegra_soctherm_if.m 296936 2016-03-16 13:01:48Z mmel $
+#
+
+#include <machine/bus.h>
+
+INTERFACE tegra_soctherm;
+
+
+/**
+ * Read temperature
+ */
+METHOD int get_temperature{
+	device_t	dev;
+	device_t	consumer;
+	uintptr_t	id;
+	int 		*val;
+};


Property changes on: trunk/sys/arm/nvidia/tegra_soctherm_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_uart.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_uart.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_uart.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,252 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_uart.c 340145 2018-11-04 23:28:56Z mmacy $");
+
+
+/*
+ * UART driver for Tegra SoCs.
+ */
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_dev_ns8250.h>
+#include <dev/ic/ns16550.h>
+
+#include "uart_if.h"
+
+/*
+ * High-level UART interface.
+ */
+struct tegra_softc {
+	struct ns8250_softc 	ns8250_base;
+	clk_t			clk;
+	hwreset_t		reset;
+};
+
+/*
+ * UART class interface.
+ */
+static int
+tegra_uart_attach(struct uart_softc *sc)
+{
+	int rv;
+	struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
+	struct uart_bas *bas = &sc->sc_bas;
+
+	rv = ns8250_bus_attach(sc);
+	if (rv != 0)
+		return (rv);
+
+	ns8250->ier_rxbits = 0x1d;
+	ns8250->ier_mask = 0xc0;
+	ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
+	ns8250->ier |= ns8250->ier_rxbits;
+	uart_setreg(bas, REG_IER, ns8250->ier);
+	uart_barrier(bas);
+	return (0);
+}
+
+static void
+tegra_uart_grab(struct uart_softc *sc)
+{
+	struct uart_bas *bas = &sc->sc_bas;
+	struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
+	u_char ier;
+
+	/*
+	 * turn off all interrupts to enter polling mode. Leave the
+	 * saved mask alone. We'll restore whatever it was in ungrab.
+	 * All pending interrupt signals are reset when IER is set to 0.
+	 */
+	uart_lock(sc->sc_hwmtx);
+	ier = uart_getreg(bas, REG_IER);
+	uart_setreg(bas, REG_IER, ier & ns8250->ier_mask);
+	uart_setreg(bas, REG_FCR, 0);
+	uart_barrier(bas);
+	uart_unlock(sc->sc_hwmtx);
+}
+
+static void
+tegra_uart_ungrab(struct uart_softc *sc)
+{
+	struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
+	struct uart_bas *bas = &sc->sc_bas;
+
+	/*
+	 * Restore previous interrupt mask
+	 */
+	uart_lock(sc->sc_hwmtx);
+	uart_setreg(bas, REG_FCR, ns8250->fcr);
+	uart_setreg(bas, REG_IER, ns8250->ier);
+	uart_barrier(bas);
+	uart_unlock(sc->sc_hwmtx);
+}
+
+static kobj_method_t tegra_methods[] = {
+	KOBJMETHOD(uart_probe,		ns8250_bus_probe),
+	KOBJMETHOD(uart_attach,		tegra_uart_attach),
+	KOBJMETHOD(uart_detach,		ns8250_bus_detach),
+	KOBJMETHOD(uart_flush,		ns8250_bus_flush),
+	KOBJMETHOD(uart_getsig,		ns8250_bus_getsig),
+	KOBJMETHOD(uart_ioctl,		ns8250_bus_ioctl),
+	KOBJMETHOD(uart_ipend,		ns8250_bus_ipend),
+	KOBJMETHOD(uart_param,		ns8250_bus_param),
+	KOBJMETHOD(uart_receive,	ns8250_bus_receive),
+	KOBJMETHOD(uart_setsig,		ns8250_bus_setsig),
+	KOBJMETHOD(uart_transmit,	ns8250_bus_transmit),
+	KOBJMETHOD(uart_grab,		tegra_uart_grab),
+	KOBJMETHOD(uart_ungrab,		tegra_uart_ungrab),
+	KOBJMETHOD_END
+};
+
+static struct uart_class tegra_uart_class = {
+	"tegra class",
+	tegra_methods,
+	sizeof(struct tegra_softc),
+	.uc_ops = &uart_ns8250_ops,
+	.uc_range = 8,
+	.uc_rclk = 0,
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class},
+	{NULL,			(uintptr_t)NULL},
+};
+
+UART_FDT_CLASS(compat_data);
+
+/*
+ * UART Driver interface.
+ */
+static int
+uart_fdt_get_shift1(phandle_t node)
+{
+	pcell_t shift;
+
+	if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
+		shift = 2;
+	return ((int)shift);
+}
+
+static int
+tegra_uart_probe(device_t dev)
+{
+	struct tegra_softc *sc;
+	phandle_t node;
+	uint64_t freq;
+	int shift;
+	int rv;
+	const struct ofw_compat_data *cd;
+
+	sc = device_get_softc(dev);
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+	cd = ofw_bus_search_compatible(dev, compat_data);
+	if (cd->ocd_data == 0)
+		return (ENXIO);
+	sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data;
+
+	rv = hwreset_get_by_ofw_name(dev, 0, "serial", &sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get 'serial' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_deassert(sc->reset);
+	if (rv != 0) {
+		device_printf(dev, "Cannot unreset 'serial' reset\n");
+		return (ENXIO);
+	}
+
+	node = ofw_bus_get_node(dev);
+	shift = uart_fdt_get_shift1(node);
+	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get UART clock: %d\n", rv);
+		return (ENXIO);
+	}
+	rv = clk_enable(sc->clk);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable UART clock: %d\n", rv);
+		return (ENXIO);
+	}
+	rv = clk_get_freq(sc->clk, &freq);
+	if (rv != 0) {
+		device_printf(dev, "Cannot enable UART clock: %d\n", rv);
+		return (ENXIO);
+	}
+	return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0));
+}
+
+static int
+tegra_uart_detach(device_t dev)
+{
+	struct tegra_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (sc->clk != NULL) {
+		clk_release(sc->clk);
+	}
+
+	return (uart_bus_detach(dev));
+}
+
+static device_method_t tegra_uart_bus_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		tegra_uart_probe),
+	DEVMETHOD(device_attach,	uart_bus_attach),
+	DEVMETHOD(device_detach,	tegra_uart_detach),
+	{ 0, 0 }
+};
+
+static driver_t tegra_uart_driver = {
+	uart_driver_name,
+	tegra_uart_bus_methods,
+	sizeof(struct tegra_softc),
+};
+
+DRIVER_MODULE(tegra_uart, simplebus,  tegra_uart_driver, uart_devclass,
+    0, 0);


Property changes on: trunk/sys/arm/nvidia/tegra_uart.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_usbphy.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_usbphy.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_usbphy.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,859 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_usbphy.c 332025 2018-04-04 13:23:06Z mmel $");
+
+
+/*
+ * USB phy driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "phynode_if.h"
+
+#define	CTRL_ICUSB_CTRL			0x15c
+#define	  ICUSB_CTR_IC_ENB1			(1 << 3)
+
+#define	CTRL_USB_USBMODE		0x1f8
+#define	  USB_USBMODE_MASK			(3 << 0)
+#define	  USB_USBMODE_HOST			(3 << 0)
+#define	  USB_USBMODE_DEVICE			(2 << 0)
+
+#define	CTRL_USB_HOSTPC1_DEVLC		0x1b4
+#define	 USB_HOSTPC1_DEVLC_PTS(x)		(((x) & 0x7) << 29)
+#define	 USB_HOSTPC1_DEVLC_STS			(1 << 28)
+#define	 USB_HOSTPC1_DEVLC_PHCD			(1 << 22)
+
+
+#define	IF_USB_SUSP_CTRL		0x400
+#define	 FAST_WAKEUP_RESP			(1 << 26)
+#define	 UTMIP_SUSPL1_SET			(1 << 25)
+#define	 USB_WAKEUP_DEBOUNCE_COUNT(x)		(((x) & 0x7) << 16)
+#define	 USB_SUSP_SET				(1 << 14)
+#define	 UTMIP_PHY_ENB				(1 << 12)
+#define	 UTMIP_RESET				(1 << 11)
+#define	 USB_SUSP_POL				(1 << 10)
+#define	 USB_PHY_CLK_VALID_INT_ENB		(1 << 9)
+#define	 USB_PHY_CLK_VALID_INT_STS		(1 << 8)
+#define	 USB_PHY_CLK_VALID			(1 << 7)
+#define	 USB_CLKEN				(1 << 6)
+#define	 USB_SUSP_CLR				(1 << 5)
+#define	 USB_WAKE_ON_DISCON_EN_DEV		(1 << 4)
+#define	 USB_WAKE_ON_CNNT_EN_DEV		(1 << 3)
+#define	 USB_WAKE_ON_RESUME_EN			(1 << 2)
+#define	 USB_WAKEUP_INT_ENB			(1 << 1)
+#define	 USB_WAKEUP_INT_STS			(1 << 0)
+
+#define	IF_USB_PHY_VBUS_SENSORS		0x404
+#define	 B_SESS_END_SW_VALUE			(1 << 4)
+#define	 B_SESS_END_SW_EN			(1 << 3)
+
+
+#define	UTMIP_XCVR_CFG0			0x808
+#define	 UTMIP_XCVR_HSSLEW_MSB(x)		((((x) & 0x1fc) >> 2) << 25)
+#define	 UTMIP_XCVR_SETUP_MSB(x)		((((x) & 0x70) >> 4) << 22)
+#define	 UTMIP_XCVR_LSBIAS_SEL			(1 << 21)
+#define	 UTMIP_XCVR_DISCON_METHOD		(1 << 20)
+#define	 UTMIP_FORCE_PDZI_POWERUP		(1 << 19)
+#define	 UTMIP_FORCE_PDZI_POWERDOWN		(1 << 18)
+#define	 UTMIP_FORCE_PD2_POWERUP		(1 << 17)
+#define	 UTMIP_FORCE_PD2_POWERDOWN		(1 << 16)
+#define	 UTMIP_FORCE_PD_POWERUP			(1 << 15)
+#define	 UTMIP_FORCE_PD_POWERDOWN		(1 << 14)
+#define	 UTMIP_XCVR_TERMEN			(1 << 13)
+#define	 UTMIP_XCVR_HSLOOPBACK			(1 << 12)
+#define	 UTMIP_XCVR_LSFSLEW(x)			(((x) & 0x3) << 10)
+#define	 UTMIP_XCVR_LSRSLEW(x)			(((x) & 0x3) << 8)
+#define	 UTMIP_XCVR_FSSLEW(x)			(((x) & 0x3) << 6)
+#define	 UTMIP_XCVR_HSSLEW(x)			(((x) & 0x3) << 4)
+#define	 UTMIP_XCVR_SETUP(x)			(((x) & 0xf) << 0)
+
+#define	UTMIP_BIAS_CFG0			0x80C
+#define	 UTMIP_IDDIG_C_VAL			(1 << 30)
+#define	 UTMIP_IDDIG_C_SEL			(1 << 29)
+#define	 UTMIP_IDDIG_B_VAL			(1 << 28)
+#define	 UTMIP_IDDIG_B_SEL			(1 << 27)
+#define	 UTMIP_IDDIG_A_VAL			(1 << 26)
+#define	 UTMIP_IDDIG_A_SEL			(1 << 25)
+#define	 UTMIP_HSDISCON_LEVEL_MSB(x)		((((x) & 0x4) >> 2) << 24)
+#define	 UTMIP_IDPD_VAL				(1 << 23)
+#define	 UTMIP_IDPD_SEL				(1 << 22)
+#define	 UTMIP_IDDIG_VAL			(1 << 21)
+#define	 UTMIP_IDDIG_SEL			(1 << 20)
+#define	 UTMIP_GPI_VAL				(1 << 19)
+#define	 UTMIP_GPI_SEL				(1 << 18)
+#define	 UTMIP_ACTIVE_TERM_OFFSET(x)		(((x) & 0x7) << 15)
+#define	 UTMIP_ACTIVE_PULLUP_OFFSET(x)		(((x) & 0x7) << 12)
+#define	 UTMIP_OTGPD				(1 << 11)
+#define	 UTMIP_BIASPD				(1 << 10)
+#define	 UTMIP_VBUS_LEVEL_LEVEL(x)		(((x) & 0x3) << 8)
+#define	 UTMIP_SESS_LEVEL_LEVEL(x)		(((x) & 0x3) << 6)
+#define	 UTMIP_HSCHIRP_LEVEL(x)			(((x) & 0x3) << 4)
+#define	 UTMIP_HSDISCON_LEVEL(x)		(((x) & 0x3) << 2)
+#define	 UTMIP_HSSQUELCH_LEVEL(x)		(((x) & 0x3) << 0)
+
+
+#define	UTMIP_HSRX_CFG0			0x810
+#define	 UTMIP_KEEP_PATT_ON_ACTIVE(x)		(((x) & 0x3) << 30)
+#define	 UTMIP_ALLOW_CONSEC_UPDN		(1 << 29)
+#define	 UTMIP_REALIGN_ON_NEW_PKT		(1 << 28)
+#define	 UTMIP_PCOUNT_UPDN_DIV(x)		(((x) & 0xf) << 24)
+#define	 UTMIP_SQUELCH_EOP_DLY(x)		(((x) & 0x7) << 21)
+#define	 UTMIP_NO_STRIPPING			(1 << 20)
+#define	 UTMIP_IDLE_WAIT(x)			(((x) & 0x1f) << 15)
+#define	 UTMIP_ELASTIC_LIMIT(x)			(((x) & 0x1f) << 10)
+#define	 UTMIP_ELASTIC_OVERRUN_DISABLE		(1 << 9)
+#define	 UTMIP_ELASTIC_UNDERRUN_DISABLE		(1 << 8)
+#define	 UTMIP_PASS_CHIRP			(1 << 7)
+#define	 UTMIP_PASS_FEEDBACK			(1 << 6)
+#define	 UTMIP_PCOUNT_INERTIA(x)		(((x) & 0x3) << 4)
+#define	 UTMIP_PHASE_ADJUST(x)			(((x) & 0x3) << 2)
+#define	 UTMIP_THREE_SYNCBITS			(1 << 1)
+#define	 UTMIP_USE4SYNC_TRAN			(1 << 0)
+
+#define	UTMIP_HSRX_CFG1			0x814
+#define	 UTMIP_HS_SYNC_START_DLY(x)		(((x) & 0x1F) << 1)
+#define	 UTMIP_HS_ALLOW_KEEP_ALIVE		(1 << 0)
+
+#define	UTMIP_TX_CFG0			0x820
+#define	 UTMIP_FS_PREAMBLE_J			(1 << 19)
+#define	 UTMIP_FS_POSTAMBLE_OUTPUT_ENABLE	(1 << 18)
+#define	 UTMIP_FS_PREAMBLE_OUTPUT_ENABLE	(1 << 17)
+#define	 UTMIP_FSLS_ALLOW_SOP_TX_STUFF_ERR	(1 << 16)
+#define	 UTMIP_HS_READY_WAIT_FOR_VALID		(1 << 15)
+#define	 UTMIP_HS_TX_IPG_DLY(x)			(((x) & 0x1f) << 10)
+#define	 UTMIP_HS_DISCON_EOP_ONLY		(1 << 9)
+#define	 UTMIP_HS_DISCON_DISABLE		(1 << 8)
+#define	 UTMIP_HS_POSTAMBLE_OUTPUT_ENABLE	(1 << 7)
+#define	 UTMIP_HS_PREAMBLE_OUTPUT_ENABLE	(1 << 6)
+#define	 UTMIP_SIE_RESUME_ON_LINESTATE		(1 << 5)
+#define	 UTMIP_SOF_ON_NO_STUFF			(1 << 4)
+#define	 UTMIP_SOF_ON_NO_ENCODE			(1 << 3)
+#define	 UTMIP_NO_STUFFING			(1 << 2)
+#define	 UTMIP_NO_ENCODING			(1 << 1)
+#define	 UTMIP_NO_SYNC_NO_EOP			(1 << 0)
+
+#define	UTMIP_MISC_CFG0			0x824
+#define	 UTMIP_DPDM_OBSERVE_SEL(x)		(((x) & 0xf) << 27)
+#define	 UTMIP_DPDM_OBSERVE			(1 << 26)
+#define	 UTMIP_KEEP_XCVR_PD_ON_SOFT_DISCON	(1 << 25)
+#define	 UTMIP_ALLOW_LS_ON_SOFT_DISCON		(1 << 24)
+#define	 UTMIP_FORCE_FS_DISABLE_ON_DEV_CHIRP	(1 << 23)
+#define	 UTMIP_SUSPEND_EXIT_ON_EDGE		(1 << 22)
+#define	 UTMIP_LS_TO_FS_SKIP_4MS		(1 << 21)
+#define	 UTMIP_INJECT_ERROR_TYPE(x)		(((x) & 0x3) << 19)
+#define	 UTMIP_FORCE_HS_CLOCK_ON		(1 << 18)
+#define	 UTMIP_DISABLE_HS_TERM			(1 << 17)
+#define	 UTMIP_FORCE_HS_TERM			(1 << 16)
+#define	 UTMIP_DISABLE_PULLUP_DP		(1 << 15)
+#define	 UTMIP_DISABLE_PULLUP_DM		(1 << 14)
+#define	 UTMIP_DISABLE_PULLDN_DP		(1 << 13)
+#define	 UTMIP_DISABLE_PULLDN_DM		(1 << 12)
+#define	 UTMIP_FORCE_PULLUP_DP			(1 << 11)
+#define	 UTMIP_FORCE_PULLUP_DM			(1 << 10)
+#define	 UTMIP_FORCE_PULLDN_DP			(1 << 9)
+#define	 UTMIP_FORCE_PULLDN_DM			(1 << 8)
+#define	 UTMIP_STABLE_COUNT(x)			(((x) & 0x7) << 5)
+#define	 UTMIP_STABLE_ALL			(1 << 4)
+#define	 UTMIP_NO_FREE_ON_SUSPEND		(1 << 3)
+#define	 UTMIP_NEVER_FREE_RUNNING_TERMS		(1 << 2)
+#define	 UTMIP_ALWAYS_FREE_RUNNING_TERMS	(1 << 1)
+#define	 UTMIP_COMB_TERMS			(1 << 0)
+
+#define	UTMIP_MISC_CFG1			0x828
+#define	 UTMIP_PHY_XTAL_CLOCKEN			(1 << 30)
+
+#define	UTMIP_DEBOUNCE_CFG0		0x82C
+#define	 UTMIP_BIAS_DEBOUNCE_B(x)		(((x) & 0xffff) << 16)
+#define	 UTMIP_BIAS_DEBOUNCE_A(x)		(((x) & 0xffff) << 0)
+
+#define	UTMIP_BAT_CHRG_CFG0		0x830
+#define	 UTMIP_CHRG_DEBOUNCE_TIMESCALE(x) 	(((x) & 0x1f) << 8)
+#define	 UTMIP_OP_I_SRC_ENG			(1 << 5)
+#define	 UTMIP_ON_SRC_ENG			(1 << 4)
+#define	 UTMIP_OP_SRC_ENG			(1 << 3)
+#define	 UTMIP_ON_SINK_ENG			(1 << 2)
+#define	 UTMIP_OP_SINK_ENG			(1 << 1)
+#define	 UTMIP_PD_CHRG				(1 << 0)
+
+#define	UTMIP_SPARE_CFG0		0x834
+#define	 FUSE_HS_IREF_CAP_CFG			(1 << 7)
+#define	 FUSE_HS_SQUELCH_LEVEL			(1 << 6)
+#define	 FUSE_SPARE				(1 << 5)
+#define	 FUSE_TERM_RANGE_ADJ_SEL		(1 << 4)
+#define	 FUSE_SETUP_SEL				(1 << 3)
+#define	 HS_RX_LATE_SQUELCH			(1 << 2)
+#define	 HS_RX_FLUSH_ALAP  			(1 << 1)
+#define	 HS_RX_IPG_ERROR_ENABLE 		(1 << 0)
+
+#define	UTMIP_XCVR_CFG1			0x838
+#define	 UTMIP_XCVR_RPU_RANGE_ADJ(x)		(((x) & 0x3) << 26)
+#define	 UTMIP_XCVR_HS_IREF_CAP(x)		(((x) & 0x3) << 24)
+#define	 UTMIP_XCVR_SPARE(x)			(((x) & 0x3) << 22)
+#define	 UTMIP_XCVR_TERM_RANGE_ADJ(x)		(((x) & 0xf) << 18)
+#define	 UTMIP_RCTRL_SW_SET			(1 << 17)
+#define	 UTMIP_RCTRL_SW_VAL(x)			(((x) & 0x1f) << 12)
+#define	 UTMIP_TCTRL_SW_SET			(1 << 11)
+#define	 UTMIP_TCTRL_SW_VAL(x)			(((x) & 0x1f) << 6)
+#define	 UTMIP_FORCE_PDDR_POWERUP		(1 << 5)
+#define	 UTMIP_FORCE_PDDR_POWERDOWN		(1 << 4)
+#define	 UTMIP_FORCE_PDCHRP_POWERUP		(1 << 3)
+#define	 UTMIP_FORCE_PDCHRP_POWERDOWN		(1 << 2)
+#define	 UTMIP_FORCE_PDDISC_POWERUP		(1 << 1)
+#define	 UTMIP_FORCE_PDDISC_POWERDOWN		(1 << 0)
+
+#define	UTMIP_BIAS_CFG1			0x83c
+#define	 UTMIP_BIAS_DEBOUNCE_TIMESCALE(x)	(((x) & 0x3f) << 8)
+#define	 UTMIP_BIAS_PDTRK_COUNT(x)		(((x) & 0x1f) << 3)
+#define	 UTMIP_VBUS_WAKEUP_POWERDOWN		(1 << 2)
+#define	 UTMIP_FORCE_PDTRK_POWERUP		(1 << 1)
+#define	 UTMIP_FORCE_PDTRK_POWERDOWN		(1 << 0)
+
+static int usbpby_enable_cnt;
+
+enum usb_ifc_type {
+	USB_IFC_TYPE_UNKNOWN = 0,
+	USB_IFC_TYPE_UTMI,
+	USB_IFC_TYPE_ULPI
+};
+
+enum usb_dr_mode {
+	USB_DR_MODE_UNKNOWN = 0,
+	USB_DR_MODE_DEVICE,
+	USB_DR_MODE_HOST,
+	USB_DR_MODE_OTG
+};
+
+struct usbphy_softc {
+	device_t		dev;
+	struct resource		*mem_res;
+	struct resource		*pads_res;
+	clk_t			clk_reg;
+	clk_t			clk_pads;
+	clk_t			clk_pllu;
+	regulator_t		supply_vbus;
+	hwreset_t		reset_usb;
+	hwreset_t		reset_pads;
+	enum usb_ifc_type	ifc_type;
+	enum usb_dr_mode	dr_mode;
+	bool			have_utmi_regs;
+
+	/* UTMI params */
+	int			hssync_start_delay;
+	int			elastic_limit;
+	int			idle_wait_delay;
+	int			term_range_adj;
+	int			xcvr_lsfslew;
+	int			xcvr_lsrslew;
+	int			xcvr_hsslew;
+	int			hssquelch_level;
+	int			hsdiscon_level;
+	int			xcvr_setup;
+	int			xcvr_setup_use_fuses;
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra30-usb-phy",	1},
+	{NULL,				0},
+};
+
+ /* Phy controller class and methods. */
+static int usbphy_phy_enable(struct phynode *phy, bool enable);
+static phynode_method_t usbphy_phynode_methods[] = {
+	PHYNODEMETHOD(phynode_enable, usbphy_phy_enable),
+
+	PHYNODEMETHOD_END
+};
+DEFINE_CLASS_1(usbphy_phynode, usbphy_phynode_class, usbphy_phynode_methods,
+    0, phynode_class);
+
+#define	RD4(sc, offs)							\
+	 bus_read_4(sc->mem_res, offs)
+
+#define	WR4(sc, offs, val)						\
+	 bus_write_4(sc->mem_res, offs, val)
+
+static int
+reg_wait(struct usbphy_softc *sc, uint32_t reg, uint32_t mask, uint32_t val)
+{
+	int i;
+
+	for (i = 0; i < 1000; i++) {
+		if ((RD4(sc, reg) & mask) == val)
+			return (0);
+		DELAY(10);
+	}
+	return (ETIMEDOUT);
+}
+
+static int
+usbphy_utmi_phy_clk(struct usbphy_softc *sc, bool enable)
+{
+	uint32_t val;
+	int rv;
+
+	val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC);
+	if (enable)
+		val &= ~USB_HOSTPC1_DEVLC_PHCD;
+	else
+		val |= USB_HOSTPC1_DEVLC_PHCD;
+	WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val);
+
+	rv = reg_wait(sc, IF_USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+	    enable ? USB_PHY_CLK_VALID: 0);
+	if (rv != 0) {
+		device_printf(sc->dev, "USB phy clock timeout.\n");
+		return (ETIMEDOUT);
+	}
+	return (0);
+}
+
+static int
+usbphy_utmi_enable(struct usbphy_softc *sc)
+{
+	int rv;
+	uint32_t val;
+
+	/* Reset phy */
+	val = RD4(sc, IF_USB_SUSP_CTRL);
+	val |= UTMIP_RESET;
+	WR4(sc, IF_USB_SUSP_CTRL, val);
+
+
+	val = RD4(sc, UTMIP_TX_CFG0);
+	val |= UTMIP_FS_PREAMBLE_J;
+	WR4(sc, UTMIP_TX_CFG0, val);
+
+	val = RD4(sc, UTMIP_HSRX_CFG0);
+	val &= ~UTMIP_IDLE_WAIT(~0);
+	val &= ~UTMIP_ELASTIC_LIMIT(~0);
+	val |= UTMIP_IDLE_WAIT(sc->idle_wait_delay);
+	val |= UTMIP_ELASTIC_LIMIT(sc->elastic_limit);
+	WR4(sc, UTMIP_HSRX_CFG0, val);
+
+	val = RD4(sc, UTMIP_HSRX_CFG1);
+	val &= ~UTMIP_HS_SYNC_START_DLY(~0);
+	val |= UTMIP_HS_SYNC_START_DLY(sc->hssync_start_delay);
+	WR4(sc, UTMIP_HSRX_CFG1, val);
+
+	val = RD4(sc, UTMIP_DEBOUNCE_CFG0);
+	val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
+	val |= UTMIP_BIAS_DEBOUNCE_A(0x7530);  /* For 12MHz */
+	WR4(sc, UTMIP_DEBOUNCE_CFG0, val);
+
+	val = RD4(sc, UTMIP_MISC_CFG0);
+	val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
+	WR4(sc, UTMIP_MISC_CFG0, val);
+
+	if (sc->dr_mode == USB_DR_MODE_DEVICE) {
+		val = RD4(sc,IF_USB_SUSP_CTRL);
+		val &= ~USB_WAKE_ON_CNNT_EN_DEV;
+		val &= ~USB_WAKE_ON_DISCON_EN_DEV;
+		WR4(sc, IF_USB_SUSP_CTRL, val);
+
+		val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+		val &= ~UTMIP_PD_CHRG;
+		WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+	} else {
+		val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+		val |= UTMIP_PD_CHRG;
+		WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+	}
+
+	usbpby_enable_cnt++;
+	if (usbpby_enable_cnt == 1) {
+		rv = hwreset_deassert(sc->reset_pads);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			     "Cannot unreset 'utmi-pads' reset\n");
+			return (rv);
+		}
+		rv = clk_enable(sc->clk_pads);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot enable 'utmi-pads' clock\n");
+			return (rv);
+		}
+
+		val = bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0);
+		val &= ~UTMIP_OTGPD;
+		val &= ~UTMIP_BIASPD;
+		val &= ~UTMIP_HSSQUELCH_LEVEL(~0);
+		val &= ~UTMIP_HSDISCON_LEVEL(~0);
+		val &= ~UTMIP_HSDISCON_LEVEL_MSB(~0);
+		val |= UTMIP_HSSQUELCH_LEVEL(sc->hssquelch_level);
+		val |= UTMIP_HSDISCON_LEVEL(sc->hsdiscon_level);
+		val |= UTMIP_HSDISCON_LEVEL_MSB(sc->hsdiscon_level);
+		bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val);
+
+		rv = clk_disable(sc->clk_pads);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot disable 'utmi-pads' clock\n");
+			return (rv);
+		}
+	}
+
+	val = RD4(sc, UTMIP_XCVR_CFG0);
+	val &= ~UTMIP_FORCE_PD_POWERDOWN;
+	val &= ~UTMIP_FORCE_PD2_POWERDOWN ;
+	val &= ~UTMIP_FORCE_PDZI_POWERDOWN;
+	val &= ~UTMIP_XCVR_LSBIAS_SEL;
+	val &= ~UTMIP_XCVR_LSFSLEW(~0);
+	val &= ~UTMIP_XCVR_LSRSLEW(~0);
+	val &= ~UTMIP_XCVR_HSSLEW(~0);
+	val &= ~UTMIP_XCVR_HSSLEW_MSB(~0);
+	val |= UTMIP_XCVR_LSFSLEW(sc->xcvr_lsfslew);
+	val |= UTMIP_XCVR_LSRSLEW(sc->xcvr_lsrslew);
+	val |= UTMIP_XCVR_HSSLEW(sc->xcvr_hsslew);
+	val |= UTMIP_XCVR_HSSLEW_MSB(sc->xcvr_hsslew);
+	if (!sc->xcvr_setup_use_fuses) {
+		val &= ~UTMIP_XCVR_SETUP(~0);
+		val &= ~UTMIP_XCVR_SETUP_MSB(~0);
+		val |= UTMIP_XCVR_SETUP(sc->xcvr_setup);
+		val |= UTMIP_XCVR_SETUP_MSB(sc->xcvr_setup);
+	}
+	WR4(sc, UTMIP_XCVR_CFG0, val);
+
+	val = RD4(sc, UTMIP_XCVR_CFG1);
+	val &= ~UTMIP_FORCE_PDDISC_POWERDOWN;
+	val &= ~UTMIP_FORCE_PDCHRP_POWERDOWN;
+	val &= ~UTMIP_FORCE_PDDR_POWERDOWN;
+	val &= ~UTMIP_XCVR_TERM_RANGE_ADJ(~0);
+	val |= UTMIP_XCVR_TERM_RANGE_ADJ(sc->term_range_adj);
+	WR4(sc, UTMIP_XCVR_CFG1, val);
+
+
+	val = RD4(sc, UTMIP_BIAS_CFG1);
+	val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+	val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+	WR4(sc, UTMIP_BIAS_CFG1, val);
+
+	val = RD4(sc, UTMIP_SPARE_CFG0);
+	if (sc->xcvr_setup_use_fuses)
+		val |= FUSE_SETUP_SEL;
+	else
+		val &= ~FUSE_SETUP_SEL;
+	WR4(sc, UTMIP_SPARE_CFG0, val);
+
+	val = RD4(sc, IF_USB_SUSP_CTRL);
+	val |= UTMIP_PHY_ENB;
+	WR4(sc, IF_USB_SUSP_CTRL, val);
+
+	val = RD4(sc, IF_USB_SUSP_CTRL);
+	val &= ~UTMIP_RESET;
+	WR4(sc, IF_USB_SUSP_CTRL, val);
+
+	usbphy_utmi_phy_clk(sc, true);
+
+	val = RD4(sc, CTRL_USB_USBMODE);
+	val &= ~USB_USBMODE_MASK;
+	if (sc->dr_mode == USB_DR_MODE_HOST)
+		val |= USB_USBMODE_HOST;
+	else
+		val |= USB_USBMODE_DEVICE;
+	WR4(sc, CTRL_USB_USBMODE, val);
+
+	val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC);
+	val &= ~USB_HOSTPC1_DEVLC_PTS(~0);
+	val |= USB_HOSTPC1_DEVLC_PTS(0);
+	WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val);
+
+	return (0);
+}
+
+static int
+usbphy_utmi_disable(struct usbphy_softc *sc)
+{
+	int rv;
+	uint32_t val;
+
+	usbphy_utmi_phy_clk(sc, false);
+
+	if (sc->dr_mode == USB_DR_MODE_DEVICE) {
+		val = RD4(sc, IF_USB_SUSP_CTRL);
+		val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
+		val |= USB_WAKE_ON_CNNT_EN_DEV;
+		val |= USB_WAKEUP_DEBOUNCE_COUNT(5);
+		WR4(sc, IF_USB_SUSP_CTRL, val);
+	}
+
+	val = RD4(sc, IF_USB_SUSP_CTRL);
+	val |= UTMIP_RESET;
+	WR4(sc, IF_USB_SUSP_CTRL, val);
+
+	val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+	val |= UTMIP_PD_CHRG;
+	WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+
+	val = RD4(sc, UTMIP_XCVR_CFG0);
+	val |= UTMIP_FORCE_PD_POWERDOWN;
+	val |= UTMIP_FORCE_PD2_POWERDOWN;
+	val |= UTMIP_FORCE_PDZI_POWERDOWN;
+	WR4(sc, UTMIP_XCVR_CFG0, val);
+
+	val = RD4(sc, UTMIP_XCVR_CFG1);
+	val |= UTMIP_FORCE_PDDISC_POWERDOWN;
+	val |= UTMIP_FORCE_PDCHRP_POWERDOWN;
+	val |= UTMIP_FORCE_PDDR_POWERDOWN;
+	WR4(sc, UTMIP_XCVR_CFG1, val);
+
+	usbpby_enable_cnt--;
+	if (usbpby_enable_cnt <= 0) {
+		rv = clk_enable(sc->clk_pads);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot enable 'utmi-pads' clock\n");
+			return (rv);
+		}
+		val =bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0);
+		val |= UTMIP_OTGPD;
+		val |= UTMIP_BIASPD;
+		bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val);
+
+		rv = clk_disable(sc->clk_pads);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot disable 'utmi-pads' clock\n");
+			return (rv);
+		}
+	}
+	return (0);
+}
+
+static int
+usbphy_phy_enable(struct phynode *phy, bool enable)
+{
+	device_t dev;
+	struct usbphy_softc *sc;
+	int rv = 0;
+
+	dev = phynode_get_device(phy);
+	sc = device_get_softc(dev);
+
+	if (sc->ifc_type != USB_IFC_TYPE_UTMI) {
+			device_printf(sc->dev,
+			    "Only UTMI interface is supported.\n");
+			return (ENXIO);
+	}
+	if (enable)
+		rv = usbphy_utmi_enable(sc);
+	else
+		rv = usbphy_utmi_disable(sc);
+
+	return (rv);
+}
+
+static enum usb_ifc_type
+usb_get_ifc_mode(device_t dev, phandle_t node, char *name)
+{
+	char *tmpstr;
+	int rv;
+	enum usb_ifc_type ret;
+
+	rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr);
+	if (rv <= 0)
+		return (USB_IFC_TYPE_UNKNOWN);
+
+	ret = USB_IFC_TYPE_UNKNOWN;
+	if (strcmp(tmpstr, "utmi") == 0)
+		ret = USB_IFC_TYPE_UTMI;
+	else if (strcmp(tmpstr, "ulpi") == 0)
+		ret = USB_IFC_TYPE_ULPI;
+	else
+		device_printf(dev, "Unsupported phy type: %s\n", tmpstr);
+	OF_prop_free(tmpstr);
+	return (ret);
+}
+
+static enum usb_dr_mode
+usb_get_dr_mode(device_t dev, phandle_t node, char *name)
+{
+	char *tmpstr;
+	int rv;
+	enum usb_dr_mode ret;
+
+	rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr);
+	if (rv <= 0)
+		return (USB_DR_MODE_UNKNOWN);
+
+	ret = USB_DR_MODE_UNKNOWN;
+	if (strcmp(tmpstr, "device") == 0)
+		ret = USB_DR_MODE_DEVICE;
+	else if (strcmp(tmpstr, "host") == 0)
+		ret = USB_DR_MODE_HOST;
+	else if (strcmp(tmpstr, "otg") == 0)
+		ret = USB_DR_MODE_OTG;
+	else
+		device_printf(dev, "Unknown dr mode: %s\n", tmpstr);
+	OF_prop_free(tmpstr);
+	return (ret);
+}
+
+static int
+usbphy_utmi_read_params(struct usbphy_softc *sc, phandle_t node)
+{
+	int rv;
+
+	rv = OF_getencprop(node, "nvidia,hssync-start-delay",
+	    &sc->hssync_start_delay, sizeof (sc->hssync_start_delay));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,elastic-limit",
+	    &sc->elastic_limit, sizeof (sc->elastic_limit));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,idle-wait-delay",
+	    &sc->idle_wait_delay, sizeof (sc->idle_wait_delay));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,term-range-adj",
+	    &sc->term_range_adj, sizeof (sc->term_range_adj));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,xcvr-lsfslew",
+	    &sc->xcvr_lsfslew, sizeof (sc->xcvr_lsfslew));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,xcvr-lsrslew",
+	    &sc->xcvr_lsrslew, sizeof (sc->xcvr_lsrslew));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,xcvr-hsslew",
+	    &sc->xcvr_hsslew, sizeof (sc->xcvr_hsslew));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,hssquelch-level",
+	    &sc->hssquelch_level, sizeof (sc->hssquelch_level));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getencprop(node, "nvidia,hsdiscon-level",
+	    &sc->hsdiscon_level, sizeof (sc->hsdiscon_level));
+	if (rv <= 0)
+		return (ENXIO);
+
+	rv = OF_getproplen(node, "nvidia,xcvr-setup-use-fuses");
+	if (rv >= 1) {
+		sc->xcvr_setup_use_fuses = 1;
+	} else {
+		rv = OF_getencprop(node, "nvidia,xcvr-setup",
+		    &sc->xcvr_setup, sizeof (sc->xcvr_setup));
+		if (rv <= 0)
+			return (ENXIO);
+	}
+
+	return (0);
+}
+
+static int
+usbphy_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc(dev, "Tegra USB phy");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+usbphy_attach(device_t dev)
+{
+	struct usbphy_softc *sc;
+	int rid, rv;
+	phandle_t node;
+	struct phynode *phynode;
+	struct phynode_init_def phy_init;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	rid = 0;
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	rid = 1;
+	sc->pads_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	if (sc->mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		return (ENXIO);
+	}
+
+	node = ofw_bus_get_node(dev);
+
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "usb", &sc->reset_usb);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get 'usb' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "utmi-pads", &sc->reset_pads);
+	if (rv != 0) {
+		device_printf(dev, "Cannot get 'utmi-pads' reset\n");
+		return (ENXIO);
+	}
+
+	rv = clk_get_by_ofw_name(sc->dev, 0, "reg", &sc->clk_reg);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'reg' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "pll_u", &sc->clk_pllu);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'pll_u' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "utmi-pads", &sc->clk_pads);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'utmi-pads' clock\n");
+		return (ENXIO);
+	}
+
+	rv = hwreset_deassert(sc->reset_usb);
+	if (rv != 0) {
+		device_printf(dev, "Cannot unreset 'usb' reset\n");
+		return (ENXIO);
+	}
+
+	rv = clk_enable(sc->clk_pllu);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'pllu' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_enable(sc->clk_reg);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable 'reg' clock\n");
+		return (ENXIO);
+	}
+	if (OF_hasprop(node, "nvidia,has-utmi-pad-registers"))
+		sc->have_utmi_regs = true;
+
+	sc->dr_mode = usb_get_dr_mode(dev, node, "dr_mode");
+	if (sc->dr_mode == USB_DR_MODE_UNKNOWN)
+		sc->dr_mode = USB_DR_MODE_HOST;
+
+	sc->ifc_type = usb_get_ifc_mode(dev, node, "phy_type");
+
+	/* We supports only utmi phy mode for now .... */
+	if (sc->ifc_type != USB_IFC_TYPE_UTMI) {
+		device_printf(dev, "Unsupported phy type\n");
+		return (ENXIO);
+	}
+	rv = usbphy_utmi_read_params(sc, node);
+	if (rv < 0)
+		return rv;
+
+	if (OF_hasprop(node, "vbus-supply")) {
+		rv = regulator_get_by_ofw_property(sc->dev, 0, "vbus-supply",
+		    &sc->supply_vbus);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			   "Cannot get \"vbus\" regulator\n");
+			return (ENXIO);
+		}
+		rv = regulator_enable(sc->supply_vbus);
+		if (rv != 0) {
+			device_printf(sc->dev,
+			    "Cannot enable  \"vbus\" regulator\n");
+			return (rv);
+		}
+	}
+
+	/* Create and register phy. */
+	bzero(&phy_init, sizeof(phy_init));
+	phy_init.id = 1;
+	phy_init.ofw_node = node;
+	phynode = phynode_create(dev, &usbphy_phynode_class, &phy_init);
+	if (phynode == NULL) {
+		device_printf(sc->dev, "Cannot create phy\n");
+		return (ENXIO);
+	}
+	if (phynode_register(phynode) == NULL) {
+		device_printf(sc->dev, "Cannot create phy\n");
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
+static int
+usbphy_detach(device_t dev)
+{
+
+	/* This device is always present. */
+	return (EBUSY);
+}
+
+static device_method_t tegra_usbphy_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		usbphy_probe),
+	DEVMETHOD(device_attach,	usbphy_attach),
+	DEVMETHOD(device_detach,	usbphy_detach),
+
+	DEVMETHOD_END
+};
+
+static devclass_t tegra_usbphy_devclass;
+static DEFINE_CLASS_0(usbphy, tegra_usbphy_driver, tegra_usbphy_methods,
+    sizeof(struct usbphy_softc));
+EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver,
+    tegra_usbphy_devclass, NULL, NULL, 79);


Property changes on: trunk/sys/arm/nvidia/tegra_usbphy.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/arm/nvidia/tegra_xhci.c
===================================================================
--- trunk/sys/arm/nvidia/tegra_xhci.c	                        (rev 0)
+++ trunk/sys/arm/nvidia/tegra_xhci.c	2020-03-06 17:08:20 UTC (rev 12396)
@@ -0,0 +1,1161 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_xhci.c 332025 2018-04-04 13:23:06Z mmel $");
+
+/*
+ * XHCI driver for Tegra SoCs.
+ */
+#include "opt_bus.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/condvar.h>
+#include <sys/firmware.h>
+#include <sys/rman.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/xhci.h>
+#include <dev/usb/controller/xhcireg.h>
+
+#include <arm/nvidia/tegra_pmc.h>
+
+#include "usbdevs.h"
+
+/* FPCI address space */
+#define	T_XUSB_CFG_0				0x000
+#define	T_XUSB_CFG_1				0x004
+#define	 CFG_1_BUS_MASTER				(1 << 2)
+#define	 CFG_1_MEMORY_SPACE				(1 << 1)
+#define	 CFG_1_IO_SPACE					(1 << 0)
+
+#define	T_XUSB_CFG_2				0x008
+#define	T_XUSB_CFG_3				0x00C
+#define	T_XUSB_CFG_4				0x010
+#define	 CFG_4_BASE_ADDRESS(x)				(((x) & 0x1FFFF) << 15)
+
+#define	T_XUSB_CFG_5				0x014
+#define	T_XUSB_CFG_ARU_MAILBOX_CMD		0x0E4
+#define  ARU_MAILBOX_CMD_INT_EN				(1U << 31)
+#define  ARU_MAILBOX_CMD_DEST_XHCI			(1  << 30)
+#define  ARU_MAILBOX_CMD_DEST_SMI			(1  << 29)
+#define  ARU_MAILBOX_CMD_DEST_PME			(1  << 28)
+#define  ARU_MAILBOX_CMD_DEST_FALC			(1  << 27)
+
+#define	T_XUSB_CFG_ARU_MAILBOX_DATA_IN		0x0E8
+#define	 ARU_MAILBOX_DATA_IN_DATA(x)			(((x) & 0xFFFFFF) <<  0)
+#define	 ARU_MAILBOX_DATA_IN_TYPE(x)			(((x) & 0x0000FF) << 24)
+
+#define	T_XUSB_CFG_ARU_MAILBOX_DATA_OUT		0x0EC
+#define	 ARU_MAILBOX_DATA_OUT_DATA(x)			(((x) >>  0) & 0xFFFFFF)
+#define	 ARU_MAILBOX_DATA_OUT_TYPE(x)			(((x) >> 24) & 0x0000FF)
+
+#define	T_XUSB_CFG_ARU_MAILBOX_OWNER		0x0F0
+#define	 ARU_MAILBOX_OWNER_SW				2
+#define	 ARU_MAILBOX_OWNER_FW				1
+#define	 ARU_MAILBOX_OWNER_NONE				0
+
+#define	XUSB_CFG_ARU_C11_CSBRANGE		0x41C	/* ! UNDOCUMENTED ! */
+#define	 ARU_C11_CSBRANGE_PAGE(x)			((x) >> 9)
+#define	 ARU_C11_CSBRANGE_ADDR(x)			(0x800 + ((x) & 0x1FF))
+#define	XUSB_CFG_ARU_SMI_INTR			0x428	/* ! UNDOCUMENTED ! */
+#define  ARU_SMI_INTR_EN				(1 << 3)
+#define  ARU_SMI_INTR_FW_HANG				(1 << 1)
+#define	XUSB_CFG_ARU_RST			0x42C	/* ! UNDOCUMENTED ! */
+#define	 ARU_RST_RESET					(1 << 0)
+
+#define	XUSB_HOST_CONFIGURATION			0x180
+#define	 CONFIGURATION_CLKEN_OVERRIDE			(1U<< 31)
+#define	 CONFIGURATION_PW_NO_DEVSEL_ERR_CYA		(1 << 19)
+#define	 CONFIGURATION_INITIATOR_READ_IDLE		(1 << 18)
+#define	 CONFIGURATION_INITIATOR_WRITE_IDLE		(1 << 17)
+#define	 CONFIGURATION_WDATA_LEAD_CYA			(1 << 15)
+#define	 CONFIGURATION_WR_INTRLV_CYA			(1 << 14)
+#define	 CONFIGURATION_TARGET_READ_IDLE			(1 << 11)
+#define	 CONFIGURATION_TARGET_WRITE_IDLE		(1 << 10)
+#define	 CONFIGURATION_MSI_VEC_EMPTY			(1 <<  9)
+#define	 CONFIGURATION_UFPCI_MSIAW			(1 <<  7)
+#define	 CONFIGURATION_UFPCI_PWPASSPW			(1 <<  6)
+#define	 CONFIGURATION_UFPCI_PASSPW			(1 <<  5)
+#define	 CONFIGURATION_UFPCI_PWPASSNPW			(1 <<  4)
+#define	 CONFIGURATION_DFPCI_PWPASSNPW			(1 <<  3)
+#define	 CONFIGURATION_DFPCI_RSPPASSPW			(1 <<  2)
+#define	 CONFIGURATION_DFPCI_PASSPW			(1 <<  1)
+#define	 CONFIGURATION_EN_FPCI				(1 <<  0)
+
+/* IPFS address space */
+#define	XUSB_HOST_FPCI_ERROR_MASKS		0x184
+#define	 FPCI_ERROR_MASTER_ABORT			(1 <<  2)
+#define	 FPCI_ERRORI_DATA_ERROR				(1 <<  1)
+#define	 FPCI_ERROR_TARGET_ABORT			(1 <<  0)
+
+#define	XUSB_HOST_INTR_MASK			0x188
+#define	 INTR_IP_INT_MASK				(1 << 16)
+#define	 INTR_MSI_MASK					(1 <<  8)
+#define	 INTR_INT_MASK					(1 <<  0)
+
+#define	XUSB_HOST_CLKGATE_HYSTERESIS		0x1BC
+
+ /* CSB Falcon CPU */
+#define	XUSB_FALCON_CPUCTL			0x100
+#define	 CPUCTL_STOPPED					(1 << 5)
+#define	 CPUCTL_HALTED					(1 << 4)
+#define	 CPUCTL_HRESET					(1 << 3)
+#define	 CPUCTL_SRESET					(1 << 2)
+#define	 CPUCTL_STARTCPU				(1 << 1)
+#define	 CPUCTL_IINVAL					(1 << 0)
+
+#define	XUSB_FALCON_BOOTVEC			0x104
+#define	XUSB_FALCON_DMACTL			0x10C
+#define	XUSB_FALCON_IMFILLRNG1			0x154
+#define	 IMFILLRNG1_TAG_HI(x)				(((x) & 0xFFF) << 16)
+#define	 IMFILLRNG1_TAG_LO(x)				(((x) & 0xFFF) <<  0)
+#define	XUSB_FALCON_IMFILLCTL			0x158
+
+/* CSB mempool */
+#define	XUSB_CSB_MEMPOOL_APMAP			0x10181C
+#define	 APMAP_BOOTPATH					(1U << 31)
+
+#define	XUSB_CSB_MEMPOOL_ILOAD_ATTR		0x101A00
+#define	XUSB_CSB_MEMPOOL_ILOAD_BASE_LO		0x101A04
+#define	XUSB_CSB_MEMPOOL_ILOAD_BASE_HI		0x101A08
+#define	XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE		0x101A10
+#define	 L2IMEMOP_SIZE_OFFSET(x)			(((x) & 0x3FF) <<  8)
+#define	 L2IMEMOP_SIZE_SIZE(x)				(((x) & 0x0FF) << 24)
+
+#define	XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG		0x101A14
+#define	 L2IMEMOP_INVALIDATE_ALL			(0x40 << 24)
+#define	 L2IMEMOP_LOAD_LOCKED_RESULT			(0x11 << 24)
+
+#define	XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT        0x101A18
+#define	 L2IMEMOP_RESULT_VLD       (1U << 31)
+
+#define XUSB_CSB_IMEM_BLOCK_SIZE	256
+
+#define	TEGRA_XHCI_SS_HIGH_SPEED	120000000
+#define	TEGRA_XHCI_SS_LOW_SPEED		 12000000
+
+/* MBOX commands. */
+#define	MBOX_CMD_MSG_ENABLED			 1
+#define	MBOX_CMD_INC_FALC_CLOCK			 2
+#define	MBOX_CMD_DEC_FALC_CLOCK			 3
+#define	MBOX_CMD_INC_SSPI_CLOCK			 4
+#define	MBOX_CMD_DEC_SSPI_CLOCK			 5
+#define	MBOX_CMD_SET_BW				 6
+#define	MBOX_CMD_SET_SS_PWR_GATING		 7
+#define	MBOX_CMD_SET_SS_PWR_UNGATING		 8
+#define	MBOX_CMD_SAVE_DFE_CTLE_CTX		 9
+#define	MBOX_CMD_AIRPLANE_MODE_ENABLED		10
+#define	MBOX_CMD_AIRPLANE_MODE_DISABLED		11
+#define	MBOX_CMD_START_HSIC_IDLE		12
+#define	MBOX_CMD_STOP_HSIC_IDLE			13
+#define	MBOX_CMD_DBC_WAKE_STACK			14
+#define	MBOX_CMD_HSIC_PRETEND_CONNECT		15
+#define	MBOX_CMD_RESET_SSPI			16
+#define	MBOX_CMD_DISABLE_SS_LFPS_DETECTION	17
+#define	MBOX_CMD_ENABLE_SS_LFPS_DETECTION	18
+
+/* MBOX responses. */
+#define	MBOX_CMD_ACK				(0x80 + 0)
+#define	MBOX_CMD_NAK				(0x80 + 1)
+
+
+#define	IPFS_WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res_ipfs, (_r), (_v))
+#define	IPFS_RD4(_sc, _r)	bus_read_4((_sc)->mem_res_ipfs, (_r))
+#define	FPCI_WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res_fpci, (_r), (_v))
+#define	FPCI_RD4(_sc, _r)	bus_read_4((_sc)->mem_res_fpci, (_r))
+
+#define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
+#define	SLEEP(_sc, timeout)						\
+    mtx_sleep(sc, &sc->mtx, 0, "tegra_xhci", timeout);
+#define	LOCK_INIT(_sc)							\
+    mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_xhci", MTX_DEF)
+#define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
+#define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
+#define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
+
+struct tegra_xusb_fw_hdr {
+	uint32_t	boot_loadaddr_in_imem;
+	uint32_t	boot_codedfi_offset;
+	uint32_t	boot_codetag;
+	uint32_t	boot_codesize;
+
+	uint32_t	phys_memaddr;
+	uint16_t	reqphys_memsize;
+	uint16_t	alloc_phys_memsize;
+
+	uint32_t	rodata_img_offset;
+	uint32_t	rodata_section_start;
+	uint32_t	rodata_section_end;
+	uint32_t	main_fnaddr;
+
+	uint32_t	fwimg_cksum;
+	uint32_t	fwimg_created_time;
+
+	uint32_t	imem_resident_start;
+	uint32_t	imem_resident_end;
+	uint32_t	idirect_start;
+	uint32_t	idirect_end;
+	uint32_t	l2_imem_start;
+	uint32_t	l2_imem_end;
+	uint32_t	version_id;
+	uint8_t		init_ddirect;
+	uint8_t		reserved[3];
+	uint32_t	phys_addr_log_buffer;
+	uint32_t	total_log_entries;
+	uint32_t	dequeue_ptr;
+	uint32_t	dummy[2];
+	uint32_t	fwimg_len;
+	uint8_t		magic[8];
+	uint32_t	ss_low_power_entry_timeout;
+	uint8_t		num_hsic_port;
+	uint8_t		ss_portmap;
+	uint8_t		build;
+	uint8_t		padding[137]; /* Pad to 256 bytes */
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+	{"nvidia,tegra124-xusb",	1},
+	{NULL,		 		0}
+};
+
+struct tegra_xhci_softc {
+	struct xhci_softc 	xhci_softc;
+	device_t		dev;
+	struct mtx		mtx;
+	struct resource		*mem_res_fpci;
+	struct resource		*mem_res_ipfs;
+	struct resource		*irq_res_mbox;
+	void			*irq_hdl_mbox;
+
+	clk_t			clk_xusb_host;
+	clk_t			clk_xusb_gate;
+	clk_t			clk_xusb_falcon_src;
+	clk_t			clk_xusb_ss;
+	clk_t			clk_xusb_hs_src;
+	clk_t			clk_xusb_fs_src;
+	hwreset_t		hwreset_xusb_host;
+	hwreset_t		hwreset_xusb_ss;
+	regulator_t		supply_avddio_pex;
+	regulator_t		supply_dvddio_pex;
+	regulator_t		supply_avdd_usb;
+	regulator_t		supply_avdd_pll_utmip;
+	regulator_t		supply_avdd_pll_erefe;
+	regulator_t		supply_avdd_usb_ss_pll;
+	regulator_t		supply_hvdd_usb_ss;
+	regulator_t		supply_hvdd_usb_ss_pll_e;
+	phy_t 			phy_usb2_0;
+	phy_t 			phy_usb2_1;
+	phy_t 			phy_usb2_2;
+	phy_t 			phy_usb3_0;
+
+	struct intr_config_hook	irq_hook;
+	bool			xhci_inited;
+	char			*fw_name;
+	vm_offset_t		fw_vaddr;
+	vm_size_t		fw_size;
+};
+
+static uint32_t
+CSB_RD4(struct tegra_xhci_softc *sc, uint32_t addr)
+{
+
+	FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr));
+	return (FPCI_RD4(sc, ARU_C11_CSBRANGE_ADDR(addr)));
+}
+
+static void
+CSB_WR4(struct tegra_xhci_softc *sc, uint32_t addr, uint32_t val)
+{
+
+	FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr));
+	FPCI_WR4(sc, ARU_C11_CSBRANGE_ADDR(addr), val);
+}
+
+static int
+get_fdt_resources(struct tegra_xhci_softc *sc, phandle_t node)
+{
+	int rv;
+
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avddio-pex-supply",
+	    &sc->supply_avddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avddio-pex' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "dvddio-pex-supply",
+	    &sc->supply_dvddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'dvddio-pex' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-usb-supply",
+	    &sc->supply_avdd_usb);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avdd-usb' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-pll-utmip-supply",
+	    &sc->supply_avdd_pll_utmip);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avdd-pll-utmip' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-pll-erefe-supply",
+	    &sc->supply_avdd_pll_erefe);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avdd-pll-erefe' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "avdd-usb-ss-pll-supply",
+	    &sc->supply_avdd_usb_ss_pll);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'avdd-usb-ss-pll' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0, "hvdd-usb-ss-supply",
+	    &sc->supply_hvdd_usb_ss);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'hvdd-usb-ss' regulator\n");
+		return (ENXIO);
+	}
+	rv = regulator_get_by_ofw_property(sc->dev, 0,
+	    "hvdd-usb-ss-pll-e-supply", &sc->supply_hvdd_usb_ss_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot get 'hvdd-usb-ss-pll-e' regulator\n");
+		return (ENXIO);
+	}
+
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_host",
+	    &sc->hwreset_xusb_host);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_host' reset\n");
+		return (ENXIO);
+	}
+	rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_ss",
+	    &sc->hwreset_xusb_ss);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_ss' reset\n");
+		return (ENXIO);
+	}
+
+	rv = phy_get_by_ofw_name(sc->dev, 0, "usb2-0", &sc->phy_usb2_0);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'usb2-0' phy\n");
+		return (ENXIO);
+	}
+	rv = phy_get_by_ofw_name(sc->dev, 0, "usb2-1", &sc->phy_usb2_1);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'usb2-1' phy\n");
+		return (ENXIO);
+	}
+	rv = phy_get_by_ofw_name(sc->dev, 0, "usb2-2", &sc->phy_usb2_2);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'usb2-2' phy\n");
+		return (ENXIO);
+	}
+	rv = phy_get_by_ofw_name(sc->dev, 0, "usb3-0", &sc->phy_usb3_0);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'usb3-0' phy\n");
+		return (ENXIO);
+	}
+
+	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_host",
+	    &sc->clk_xusb_host);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_host' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_falcon_src",
+	    &sc->clk_xusb_falcon_src);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_falcon_src' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_ss",
+	    &sc->clk_xusb_ss);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_ss' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_hs_src",
+	    &sc->clk_xusb_hs_src);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_hs_src' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_fs_src",
+	    &sc->clk_xusb_fs_src);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_fs_src' clock\n");
+		return (ENXIO);
+	}
+	rv = clk_get_by_ofw_index_prop(sc->dev, 0, "freebsd,clock-xusb-gate", 0,
+	    &sc->clk_xusb_gate);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot get 'xusb_gate' clock\n");
+		return (ENXIO);
+	}
+	return (0);
+}
+
+static int
+enable_fdt_resources(struct tegra_xhci_softc *sc)
+{
+	int rv;
+
+	rv = hwreset_assert(sc->hwreset_xusb_host);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot reset 'xusb_host' reset\n");
+		return (rv);
+	}
+	rv = hwreset_assert(sc->hwreset_xusb_ss);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot reset 'xusb_ss' reset\n");
+		return (rv);
+	}
+
+	rv = regulator_enable(sc->supply_avddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avddio_pex' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_dvddio_pex);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'dvddio_pex' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd_usb);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avdd_usb' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd_pll_utmip);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avdd_pll_utmip-5v' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd_pll_erefe);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avdd_pll_erefe' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_avdd_usb_ss_pll);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'avdd_usb_ss_pll' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_hvdd_usb_ss);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'hvdd_usb_ss' regulator\n");
+		return (rv);
+	}
+	rv = regulator_enable(sc->supply_hvdd_usb_ss_pll_e);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'hvdd_usb_ss_pll_e' regulator\n");
+		return (rv);
+	}
+
+	/* Power off XUSB host and XUSB SS domains. */
+	rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot powerdown  'xusba' domain\n");
+		return (rv);
+	}
+	rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot powerdown  'xusbc' domain\n");
+		return (rv);
+	}
+
+	/* Setup XUSB ss_src clock first */
+	clk_set_freq(sc->clk_xusb_ss, TEGRA_XHCI_SS_HIGH_SPEED, 0);
+	if (rv != 0)
+		return (rv);
+
+	/* The XUSB gate clock must be enabled before XUSBA can be powered. */
+	rv = clk_enable(sc->clk_xusb_gate);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'xusb_gate' clock\n");
+		return (rv);
+	}
+
+	/* Power on XUSB host and XUSB SS domains. */
+	rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
+	    sc->clk_xusb_host, sc->hwreset_xusb_host);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot powerup 'xusbc' domain\n");
+		return (rv);
+	}
+	rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
+	    sc->clk_xusb_ss, sc->hwreset_xusb_ss);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot powerup 'xusba' domain\n");
+		return (rv);
+	}
+
+	/* Enable rest of clocks */
+	rv = clk_enable(sc->clk_xusb_falcon_src);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'xusb_falcon_src' clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_xusb_fs_src);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'xusb_fs_src' clock\n");
+		return (rv);
+	}
+	rv = clk_enable(sc->clk_xusb_hs_src);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Cannot enable 'xusb_hs_src' clock\n");
+		return (rv);
+	}
+
+	rv = phy_enable(sc->phy_usb2_0);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable USB2_0 phy\n");
+		return (rv);
+	}
+	rv = phy_enable(sc->phy_usb2_1);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable USB2_1 phy\n");
+		return (rv);
+	}
+	rv = phy_enable(sc->phy_usb2_2);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable USB2_2 phy\n");
+		return (rv);
+	}
+	rv = phy_enable(sc->phy_usb3_0);
+	if (rv != 0) {
+		device_printf(sc->dev, "Cannot enable USB3_0 phy\n");
+		return (rv);
+	}
+
+	return (0);
+}
+
+/* Respond by ACK/NAK back to FW */
+static void
+mbox_send_ack(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data)
+{
+	uint32_t reg;
+
+	reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data);
+	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg);
+
+	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
+	reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN;
+	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
+}
+
+/* Sent command to FW */
+static int
+mbox_send_cmd(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data)
+{
+	uint32_t reg;
+	int i;
+
+	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
+	if (reg != ARU_MAILBOX_OWNER_NONE) {
+		device_printf(sc->dev,
+		    "CPU mailbox is busy: 0x%08X\n", reg);
+		return (EBUSY);
+	}
+	/* XXX Is this right? Retry loop? Wait before send? */
+	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, ARU_MAILBOX_OWNER_SW);
+	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
+	if (reg != ARU_MAILBOX_OWNER_SW) {
+		device_printf(sc->dev,
+		    "Cannot acquire CPU mailbox: 0x%08X\n", reg);
+		return (EBUSY);
+	}
+	reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data);
+	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg);
+
+	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
+	reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN;
+	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
+
+	for (i = 250; i > 0; i--) {
+		reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
+		if (reg == ARU_MAILBOX_OWNER_NONE)
+			break;
+		DELAY(100);
+	}
+	if (i <= 0) {
+		device_printf(sc->dev,
+		    "Command response timeout: 0x%08X\n", reg);
+		return (ETIMEDOUT);
+	}
+
+	return(0);
+}
+
+static void
+process_msg(struct tegra_xhci_softc *sc, uint32_t req_cmd, uint32_t req_data,
+    uint32_t *resp_cmd, uint32_t *resp_data)
+{
+	uint64_t freq;
+	int rv;
+
+	/* In most cases, data are echoed back. */
+	*resp_data = req_data;
+	switch (req_cmd) {
+	case MBOX_CMD_INC_FALC_CLOCK:
+	case MBOX_CMD_DEC_FALC_CLOCK:
+		rv = clk_set_freq(sc->clk_xusb_falcon_src, req_data * 1000ULL,
+		    0);
+		if (rv == 0) {
+			rv = clk_get_freq(sc->clk_xusb_falcon_src, &freq);
+			*resp_data = (uint32_t)(freq / 1000);
+		}
+		*resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK;
+		break;
+
+	case MBOX_CMD_INC_SSPI_CLOCK:
+	case MBOX_CMD_DEC_SSPI_CLOCK:
+		rv = clk_set_freq(sc->clk_xusb_ss, req_data * 1000ULL,
+		    0);
+		if (rv == 0) {
+			rv = clk_get_freq(sc->clk_xusb_ss, &freq);
+			*resp_data = (uint32_t)(freq / 1000);
+		}
+		*resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK;
+		break;
+
+	case MBOX_CMD_SET_BW:
+		/* No respense is expected. */
+		*resp_cmd = 0;
+		break;
+
+	case MBOX_CMD_SET_SS_PWR_GATING:
+	case MBOX_CMD_SET_SS_PWR_UNGATING:
+		*resp_cmd = MBOX_CMD_NAK;
+		break;
+
+	case MBOX_CMD_SAVE_DFE_CTLE_CTX:
+		/* Not implemented yet. */
+		*resp_cmd = MBOX_CMD_ACK;
+		break;
+
+
+	case MBOX_CMD_START_HSIC_IDLE:
+	case MBOX_CMD_STOP_HSIC_IDLE:
+		/* Not implemented yet. */
+		*resp_cmd = MBOX_CMD_NAK;
+		break;
+
+	case MBOX_CMD_DISABLE_SS_LFPS_DETECTION:
+	case MBOX_CMD_ENABLE_SS_LFPS_DETECTION:
+		/* Not implemented yet. */
+		*resp_cmd = MBOX_CMD_NAK;
+		break;
+
+	case MBOX_CMD_AIRPLANE_MODE_ENABLED:
+	case MBOX_CMD_AIRPLANE_MODE_DISABLED:
+	case MBOX_CMD_DBC_WAKE_STACK:
+	case MBOX_CMD_HSIC_PRETEND_CONNECT:
+	case MBOX_CMD_RESET_SSPI:
+		device_printf(sc->dev,
+		    "Received unused/unexpected command: %u\n", req_cmd);
+		*resp_cmd = 0;
+		break;
+
+	default:
+		device_printf(sc->dev,
+		    "Received unknown command: %u\n", req_cmd);
+	}
+}
+
+static void
+intr_mbox(void *arg)
+{
+	struct tegra_xhci_softc *sc;
+	uint32_t reg, msg, resp_cmd, resp_data;
+
+	sc = (struct tegra_xhci_softc *)arg;
+
+	/* Clear interrupt first */
+	reg = FPCI_RD4(sc, XUSB_CFG_ARU_SMI_INTR);
+	FPCI_WR4(sc, XUSB_CFG_ARU_SMI_INTR, reg);
+	if (reg & ARU_SMI_INTR_FW_HANG) {
+		device_printf(sc->dev,
+		    "XUSB CPU firmware hang!!! CPUCTL: 0x%08X\n",
+		    CSB_RD4(sc, XUSB_FALCON_CPUCTL));
+	}
+
+	msg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_OUT);
+	resp_cmd = 0;
+	process_msg(sc, ARU_MAILBOX_DATA_OUT_TYPE(msg),
+	   ARU_MAILBOX_DATA_OUT_DATA(msg), &resp_cmd, &resp_data);
+	if (resp_cmd != 0)
+		mbox_send_ack(sc, resp_cmd, resp_data);
+	else
+		FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER,
+		    ARU_MAILBOX_OWNER_NONE);
+
+	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
+	reg &= ~ARU_MAILBOX_CMD_DEST_SMI;
+	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
+
+}
+
+static int
+load_fw(struct tegra_xhci_softc *sc)
+{
+	const struct firmware *fw;
+	const struct tegra_xusb_fw_hdr *fw_hdr;
+	vm_paddr_t fw_paddr, fw_base;
+	vm_offset_t fw_vaddr;
+	vm_size_t fw_size;
+	uint32_t code_tags, code_size;
+	struct clocktime fw_clock;
+	struct timespec	fw_timespec;
+	int i;
+
+	/* Reset ARU */
+	FPCI_WR4(sc, XUSB_CFG_ARU_RST, ARU_RST_RESET);
+	DELAY(3000);
+
+	/* Check if FALCON already runs */
+	if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO) != 0) {
+		device_printf(sc->dev,
+		    "XUSB CPU is already loaded, CPUCTL: 0x%08X\n",
+			 CSB_RD4(sc, XUSB_FALCON_CPUCTL));
+		return (0);
+	}
+
+	fw = firmware_get(sc->fw_name);
+	if (fw == NULL) {
+		device_printf(sc->dev, "Cannot read xusb firmware\n");
+		return (ENOENT);
+	}
+
+	/* Allocate uncached memory and copy firmware into. */
+	fw_hdr = (const struct tegra_xusb_fw_hdr *)fw->data;
+	fw_size = fw_hdr->fwimg_len;
+
+	fw_vaddr = kmem_alloc_contig(kernel_arena, fw_size,
+	    M_WAITOK, 0, -1UL, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
+	fw_paddr = vtophys(fw_vaddr);
+	fw_hdr = (const struct tegra_xusb_fw_hdr *)fw_vaddr;
+	memcpy((void *)fw_vaddr, fw->data, fw_size);
+
+	firmware_put(fw, FIRMWARE_UNLOAD);
+	sc->fw_vaddr = fw_vaddr;
+	sc->fw_size = fw_size;
+
+	/* Setup firmware physical address and size. */
+	fw_base = fw_paddr + sizeof(*fw_hdr);
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_ATTR, fw_size);
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO, fw_base & 0xFFFFFFFF);
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_HI, (uint64_t)fw_base >> 32);
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_APMAP, APMAP_BOOTPATH);
+
+	/* Invalidate full L2IMEM context. */
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG,
+	    L2IMEMOP_INVALIDATE_ALL);
+
+	/* Program load of L2IMEM by boot code. */
+	code_tags = howmany(fw_hdr->boot_codetag, XUSB_CSB_IMEM_BLOCK_SIZE);
+	code_size = howmany(fw_hdr->boot_codesize, XUSB_CSB_IMEM_BLOCK_SIZE);
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE,
+	    L2IMEMOP_SIZE_OFFSET(code_tags) |
+	    L2IMEMOP_SIZE_SIZE(code_size));
+
+	/* Execute L2IMEM boot code fetch. */
+	CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG,
+	    L2IMEMOP_LOAD_LOCKED_RESULT);
+
+	/* Program FALCON auto-fill range and block count */
+	CSB_WR4(sc, XUSB_FALCON_IMFILLCTL, code_size);
+	CSB_WR4(sc, XUSB_FALCON_IMFILLRNG1,
+	    IMFILLRNG1_TAG_LO(code_tags) |
+	    IMFILLRNG1_TAG_HI(code_tags + code_size));
+
+	CSB_WR4(sc, XUSB_FALCON_DMACTL, 0);
+	/* Wait for CPU */
+	for (i = 500; i > 0; i--) {
+		if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT) &
+		     L2IMEMOP_RESULT_VLD)
+			break;
+		DELAY(100);
+	}
+	if (i <= 0) {
+		device_printf(sc->dev, "Timedout while wating for DMA, "
+		    "state: 0x%08X\n",
+		    CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT));
+		return (ETIMEDOUT);
+	}
+
+	/* Boot FALCON cpu */
+	CSB_WR4(sc, XUSB_FALCON_BOOTVEC, fw_hdr->boot_codetag);
+	CSB_WR4(sc, XUSB_FALCON_CPUCTL, CPUCTL_STARTCPU);
+
+	/* Wait for CPU */
+	for (i = 50; i > 0; i--) {
+		if (CSB_RD4(sc, XUSB_FALCON_CPUCTL) == CPUCTL_STOPPED)
+			break;
+		DELAY(100);
+	}
+	if (i <= 0) {
+		device_printf(sc->dev, "Timedout while wating for FALCON cpu, "
+		    "state: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL));
+		return (ETIMEDOUT);
+	}
+
+	fw_timespec.tv_sec = fw_hdr->fwimg_created_time;
+	fw_timespec.tv_nsec = 0;
+	clock_ts_to_ct(&fw_timespec, &fw_clock);
+	device_printf(sc->dev,
+	    " Falcon firmware version: %02X.%02X.%04X,"
+	    " (%d/%d/%d %d:%02d:%02d UTC)\n",
+	    (fw_hdr->version_id >> 24) & 0xFF,(fw_hdr->version_id >> 15) & 0xFF,
+	    fw_hdr->version_id & 0xFFFF,
+	    fw_clock.day, fw_clock.mon, fw_clock.year,
+	    fw_clock.hour, fw_clock.min, fw_clock.sec);
+
+	return (0);
+}
+
+static int
+init_hw(struct tegra_xhci_softc *sc)
+{
+	int rv;
+	uint32_t reg;
+	rman_res_t base_addr;
+
+	base_addr = rman_get_start(sc->xhci_softc.sc_io_res);
+
+	/* Enable FPCI access */
+	reg = IPFS_RD4(sc, XUSB_HOST_CONFIGURATION);
+	reg |= CONFIGURATION_EN_FPCI;
+	IPFS_WR4(sc, XUSB_HOST_CONFIGURATION, reg);
+	IPFS_RD4(sc, XUSB_HOST_CONFIGURATION);
+
+
+	/* Program bar for XHCI base address */
+	reg = FPCI_RD4(sc, T_XUSB_CFG_4);
+	reg &= ~CFG_4_BASE_ADDRESS(~0);
+	reg |= CFG_4_BASE_ADDRESS((uint32_t)base_addr >> 15);
+	FPCI_WR4(sc, T_XUSB_CFG_4, reg);
+	FPCI_WR4(sc, T_XUSB_CFG_5, (uint32_t)((uint64_t)(base_addr) >> 32));
+
+	/* Enable bus master */
+	reg = FPCI_RD4(sc, T_XUSB_CFG_1);
+	reg |= CFG_1_IO_SPACE;
+	reg |= CFG_1_MEMORY_SPACE;
+	reg |= CFG_1_BUS_MASTER;
+	FPCI_WR4(sc, T_XUSB_CFG_1, reg);
+
+	/* Enable Interrupts */
+	reg = IPFS_RD4(sc, XUSB_HOST_INTR_MASK);
+	reg |= INTR_IP_INT_MASK;
+	IPFS_WR4(sc, XUSB_HOST_INTR_MASK, reg);
+
+	/* Set hysteresis */
+	IPFS_WR4(sc, XUSB_HOST_CLKGATE_HYSTERESIS, 128);
+
+	rv = load_fw(sc);
+	if (rv != 0)
+		return rv;
+	return (0);
+}
+
+static int
+tegra_xhci_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+		device_set_desc(dev, "Nvidia Tegra XHCI controller");
+		return (BUS_PROBE_DEFAULT);
+	}
+	return (ENXIO);
+}
+
+static int
+tegra_xhci_detach(device_t dev)
+{
+	struct tegra_xhci_softc *sc;
+	struct xhci_softc *xsc;
+
+	sc = device_get_softc(dev);
+	xsc = &sc->xhci_softc;
+
+	/* during module unload there are lots of children leftover */
+	device_delete_children(dev);
+	if (sc->xhci_inited) {
+		usb_callout_drain(&xsc->sc_callout);
+		xhci_halt_controller(xsc);
+	}
+
+	if (xsc->sc_irq_res && xsc->sc_intr_hdl) {
+		bus_teardown_intr(dev, xsc->sc_irq_res, xsc->sc_intr_hdl);
+		xsc->sc_intr_hdl = NULL;
+	}
+	if (xsc->sc_irq_res) {
+		bus_release_resource(dev, SYS_RES_IRQ,
+		    rman_get_rid(xsc->sc_irq_res), xsc->sc_irq_res);
+		xsc->sc_irq_res = NULL;
+	}
+	if (xsc->sc_io_res != NULL) {
+		bus_release_resource(dev, SYS_RES_MEMORY,
+		    rman_get_rid(xsc->sc_io_res), xsc->sc_io_res);
+		xsc->sc_io_res = NULL;
+	}
+	if (sc->xhci_inited)
+		xhci_uninit(xsc);
+	if (sc->irq_hdl_mbox != NULL)
+		bus_teardown_intr(dev, sc->irq_res_mbox, sc->irq_hdl_mbox);
+	if (sc->fw_vaddr != 0)
+		kmem_free(kernel_arena, sc->fw_vaddr, sc->fw_size);
+	LOCK_DESTROY(sc);
+	return (0);
+}
+
+static int
+tegra_xhci_attach(device_t dev)
+{
+	struct tegra_xhci_softc *sc;
+	struct xhci_softc *xsc;
+	int rv, rid;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->fw_name = "tegra124_xusb_fw";
+	node = ofw_bus_get_node(dev);
+	xsc = &sc->xhci_softc;
+	LOCK_INIT(sc);
+
+	rv = get_fdt_resources(sc, node);
+	if (rv != 0) {
+		rv = ENXIO;
+		goto error;
+	}
+	rv = enable_fdt_resources(sc);
+	if (rv != 0) {
+		rv = ENXIO;
+		goto error;
+	}
+
+	/* Allocate resources. */
+	rid = 0;
+	xsc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (xsc->sc_io_res == NULL) {
+		device_printf(dev,
+		    "Could not allocate HCD memory resources\n");
+		rv = ENXIO;
+		goto error;
+	}
+	rid = 1;
+	sc->mem_res_fpci = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res_fpci == NULL) {
+		device_printf(dev,
+		    "Could not allocate FPCI memory resources\n");
+		rv = ENXIO;
+		goto error;
+	}
+	rid = 2;
+	sc->mem_res_ipfs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->mem_res_ipfs == NULL) {
+		device_printf(dev,
+		    "Could not allocate IPFS memory resources\n");
+		rv = ENXIO;
+		goto error;
+	}
+
+	rid = 0;
+	xsc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (xsc->sc_irq_res == NULL) {
+		device_printf(dev, "Could not allocate HCD IRQ resources\n");
+		rv = ENXIO;
+		goto error;
+	}
+	rid = 1;
+	sc->irq_res_mbox = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->irq_res_mbox == NULL) {
+		device_printf(dev, "Could not allocate MBOX IRQ resources\n");
+		rv = ENXIO;
+		goto error;
+	}
+
+	rv = init_hw(sc);
+	if (rv != 0) {
+		device_printf(dev, "Could not initialize  XUSB hardware\n");
+		goto error;
+	}
+
+	/* Wakeup and enable firmaware */
+	rv = mbox_send_cmd(sc, MBOX_CMD_MSG_ENABLED, 0);
+	if (rv != 0) {
+		device_printf(sc->dev, "Could not enable XUSB firmware\n");
+		goto error;
+	}
+
+	/* Fill data for XHCI driver. */
+	xsc->sc_bus.parent = dev;
+	xsc->sc_bus.devices = xsc->sc_devices;
+	xsc->sc_bus.devices_max = XHCI_MAX_DEVICES;
+
+	xsc->sc_io_tag = rman_get_bustag(xsc->sc_io_res);
+	xsc->sc_io_hdl = rman_get_bushandle(xsc->sc_io_res);
+	xsc->sc_io_size = rman_get_size(xsc->sc_io_res);
+	strlcpy(xsc->sc_vendor, "Nvidia", sizeof(xsc->sc_vendor));
+
+	/* Add USB bus device. */
+	xsc->sc_bus.bdev = device_add_child(sc->dev, "usbus", -1);
+	if (xsc->sc_bus.bdev == NULL) {
+		device_printf(sc->dev, "Could not add USB device\n");
+		rv = ENXIO;
+		goto error;
+	}
+	device_set_ivars(xsc->sc_bus.bdev, &xsc->sc_bus);
+	device_set_desc(xsc->sc_bus.bdev, "Nvidia USB 3.0 controller");
+
+	rv = xhci_init(xsc, sc->dev, 1);
+	if (rv != 0) {
+		device_printf(sc->dev, "USB init failed: %d\n", rv);
+		goto error;
+	}
+	sc->xhci_inited = true;
+	rv = xhci_start_controller(xsc);
+	if (rv != 0) {
+		device_printf(sc->dev,
+		    "Could not start XHCI controller: %d\n", rv);
+		goto error;
+	}
+
+	rv = bus_setup_intr(dev, sc->irq_res_mbox, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, intr_mbox, sc, &sc->irq_hdl_mbox);
+	if (rv != 0) {
+		device_printf(dev, "Could not setup error IRQ: %d\n",rv);
+		xsc->sc_intr_hdl = NULL;
+		goto error;
+	}
+
+	rv = bus_setup_intr(dev, xsc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+	    NULL, (driver_intr_t *)xhci_interrupt, xsc, &xsc->sc_intr_hdl);
+	if (rv != 0) {
+		device_printf(dev, "Could not setup error IRQ: %d\n",rv);
+		xsc->sc_intr_hdl = NULL;
+		goto error;
+	}
+
+	/* Probe the bus. */
+	rv = device_probe_and_attach(xsc->sc_bus.bdev);
+	if (rv != 0) {
+		device_printf(sc->dev, "Could not initialize USB: %d\n", rv);
+		goto error;
+	}
+
+	return (0);
+
+error:
+panic("XXXXX");
+	tegra_xhci_detach(dev);
+	return (rv);
+}
+
+static device_method_t xhci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, tegra_xhci_probe),
+	DEVMETHOD(device_attach, tegra_xhci_attach),
+	DEVMETHOD(device_detach, tegra_xhci_detach),
+	DEVMETHOD(device_suspend, bus_generic_suspend),
+	DEVMETHOD(device_resume, bus_generic_resume),
+	DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+	/* Bus interface */
+	DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+	DEVMETHOD_END
+};
+
+static devclass_t xhci_devclass;
+static DEFINE_CLASS_0(xhci, xhci_driver, xhci_methods,
+    sizeof(struct tegra_xhci_softc));
+DRIVER_MODULE(tegra_xhci, simplebus, xhci_driver, xhci_devclass, NULL, NULL);
+MODULE_DEPEND(tegra_xhci, usb, 1, 1, 1);


Property changes on: trunk/sys/arm/nvidia/tegra_xhci.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list