[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, ®);
+ 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, ®);
+ 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 &= ~(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, ®);
+ 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, ®);
+ 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, ®);
+ 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, ®);
+ 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, ®);
+ 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 &= PLLRE_MISC_LOCK;
+ break;
+
+ case PLL_E:
+ RD4(sc, sc->misc_reg, ®);
+ reg &= PLLE_MISC_LOCK;
+ break;
+
+ default:
+ RD4(sc, sc->base_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 &= ~PLLE_BASE_LOCK_OVERRIDE;
+ WR4(sc, sc->base_reg, reg);
+
+ RD4(sc, PLLE_AUX, ®);
+ reg |= PLLE_AUX_ENABLE_SWCTL;
+ reg &= ~PLLE_AUX_SEQ_ENABLE;
+ WR4(sc, PLLE_AUX, reg);
+ DELAY(10);
+
+ RD4(sc, sc->misc_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 |= PLLE_SS_CNTL_DISABLE;
+ WR4(sc, PLLE_SS_CNTL, reg);
+
+ RD4(sc, sc->base_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 &= ~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 &= ~PLLE_MISC_IDDQ_SWCTL;
+ WR4(sc, sc->misc_reg, reg);
+
+ RD4(sc, PLLE_AUX, ®);
+ 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 |= 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 &= ~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 |= 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 = 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 |= PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+
+ /* Enable lock detection. */
+ RD4(sc, sc->misc_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 &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+ return (rv);
+ }
+ RD4(sc, sc->misc_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 &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+
+ /* Set PLL. */
+ RD4(sc, sc->base_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, ®);
+ DELAY(100);
+
+ /* Enable lock detection. */
+ RD4(sc, sc->misc_reg, ®);
+ reg |= sc->lock_enable;
+ WR4(sc, sc->misc_reg, reg);
+
+ /* Enable PLL. */
+ RD4(sc, sc->base_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 &= ~PLL_BASE_ENABLE;
+ WR4(sc, sc->base_reg, reg);
+ return (rv);
+ }
+ RD4(sc, sc->misc_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, ®);
+ if (reg & PLL_BASE_ENABLE) {
+ RD4(sc, sc->misc_reg, ®);
+ reg |= sc->lock_enable;
+ WR4(sc, sc->misc_reg, reg);
+ }
+ if (sc->type == PLL_REFE) {
+ RD4(sc, sc->misc_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, ®);
+ 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 &= ~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 &= ~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 |= 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 &= ~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 |= 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 |= 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, ®);
+ 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, ®);
+ 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