Index: stable/11/sys/arm/nvidia/as3722.c =================================================================== --- stable/11/sys/arm/nvidia/as3722.c (revision 308334) +++ stable/11/sys/arm/nvidia/as3722.c (revision 308335) @@ -1,411 +1,411 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * AS3722 PMIC driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; -DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods, +static DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods, sizeof(struct as3722_softc)); EARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass, - 0, 0, 74); + NULL, NULL, 74); Index: stable/11/sys/arm/nvidia/tegra124/tegra124_car.c =================================================================== --- stable/11/sys/arm/nvidia/tegra124/tegra124_car.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra124/tegra124_car.c (revision 308335) @@ -1,613 +1,608 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_pllmcp_clkm) = {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"}; PLIST(mux_xusb_hs) = {"pc_xusb_ss", "pllU_60"}; PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"}; /* Clocks ajusted online. */ static struct clk_fixed_def fixed_clk_m = FRATE(0, "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), }; 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), MUX(0, "emc_mux", mux_pllmcp_clkm, CLK_SOURCE_EMC, 29, 3), /* USB. */ MUX(0, "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(0, "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 driver_t tegra124_car_driver = { - "tegra124_car", - tegra124_car_methods, - sizeof(struct tegra124_car_softc), -}; - +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, 0, 0, BUS_PASS_TIMER); + tegra124_car_devclass, NULL, NULL, BUS_PASS_TIMER); Index: stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c =================================================================== --- stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c (revision 308335) @@ -1,273 +1,268 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { 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 Frequency Control"); 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 driver_t tegra124_coretemp_driver = { - "tegra124_coretemp", - tegra124_coretemp_methods, - sizeof(struct tegra124_coretemp_softc), -}; - +static DEFINE_CLASS_0(coretemp, tegra124_coretemp_driver, + tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc)); DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, - tegra124_coretemp_devclass, 0, 0); + tegra124_coretemp_devclass, NULL, NULL); Index: stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c =================================================================== --- stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra124/tegra124_cpufreq.c (revision 308335) @@ -1,598 +1,594 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 = 900000, /* 0.9 V */ .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, 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) { 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) { if (device_get_unit(dev) != 0) return (ENXIO); 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 driver_t tegra124_cpufreq_driver = { - "tegra124_cpufreq", - tegra124_cpufreq_methods, - sizeof(struct tegra124_cpufreq_softc), -}; - +static DEFINE_CLASS_0(cpufreq, tegra124_cpufreq_driver, + tegra124_cpufreq_methods, sizeof(struct tegra124_cpufreq_softc)); DRIVER_MODULE(tegra124_cpufreq, cpu, tegra124_cpufreq_driver, - tegra124_cpufreq_devclass, 0, 0); + tegra124_cpufreq_devclass, NULL, NULL); Index: stable/11/sys/arm/nvidia/tegra124/tegra124_pmc.c =================================================================== --- stable/11/sys/arm/nvidia/tegra124/tegra124_pmc.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra124/tegra124_pmc.c (revision 308335) @@ -1,566 +1,562 @@ /*- * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 driver_t tegra124_pmc_driver = { - "tegra124_pmc", - tegra124_pmc_methods, - sizeof(struct tegra124_pmc_softc), -}; - 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, 0, 0, 70); + tegra124_pmc_devclass, NULL, NULL, 70); Index: stable/11/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c =================================================================== --- stable/11/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra124/tegra124_xusbpadctl.c (revision 308335) @@ -1,603 +1,597 @@ /*- * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phy_if.h" #define XUSB_PADCTL_USB2_PAD_MUX 0x004 #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 XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 #define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) #define IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (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_USB3_PAD_MUX 0x134 #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 struct lane_cfg { char *function; char **lanes; int iddq; }; struct xusbpadctl_softc { device_t dev; struct resource *mem_res; hwreset_t rst; int phy_ena_cnt; }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-xusb-padctl", 1}, {NULL, 0}, }; struct padctl_lane { const char *name; bus_size_t reg; uint32_t shift; uint32_t mask; int iddq; char **mux; int nmux; }; static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"}; static char *usb_mux[] = {"snps", "xusb"}; static char *pci_mux[] = {"pcie", "usb3", "sata", "rsvd"}; #define LANE(n, r, s, m, i, mx) \ { \ .name = n, \ .reg = r, \ .shift = s, \ .mask = m, \ .iddq = i, \ .mux = mx, \ .nmux = nitems(mx), \ } static const struct padctl_lane lanes_tbl[] = { LANE("otg-0", XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, -1, otg_mux), LANE("otg-1", XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, -1, otg_mux), LANE("otg-2", XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, -1, otg_mux), LANE("ulpi-0", XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, -1, usb_mux), LANE("hsic-0", XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, -1, usb_mux), LANE("hsic-1", XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, -1, usb_mux), LANE("pcie-0", XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, 1, pci_mux), LANE("pcie-1", XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, 2, pci_mux), LANE("pcie-2", XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, 3, pci_mux), LANE("pcie-3", XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, 4, pci_mux), LANE("pcie-4", XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, 5, pci_mux), LANE("sata-0", XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, 6, pci_mux), }; static int xusbpadctl_mux_function(const 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 xusbpadctl_config_lane(struct xusbpadctl_softc *sc, char *lane_name, const struct padctl_lane *lane, struct lane_cfg *cfg) { int tmp; uint32_t reg; reg = bus_read_4(sc->mem_res, lane->reg); if (cfg->function != NULL) { tmp = xusbpadctl_mux_function(lane, cfg->function); if (tmp == -1) { device_printf(sc->dev, "Unknown function %s for lane %s\n", cfg->function, lane_name); return (EINVAL); } reg &= ~(lane->mask << lane->shift); reg |= (tmp & lane->mask) << lane->shift; } if (cfg->iddq != -1) { if (lane->iddq == -1) { device_printf(sc->dev, "Invalid IDDQ for lane %s\n", lane_name); return (EINVAL); } if (cfg->iddq != 0) reg &= ~(1 << lane->iddq); else reg |= 1 << lane->iddq; } bus_write_4(sc->mem_res, lane->reg, reg); return (0); } static const struct padctl_lane * xusbpadctl_search_lane(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 int xusbpadctl_config_node(struct xusbpadctl_softc *sc, char *lane_name, struct lane_cfg *cfg) { const struct padctl_lane *lane; int rv; lane = xusbpadctl_search_lane(lane_name); if (lane == NULL) { device_printf(sc->dev, "Unknown lane: %s\n", lane_name); return (ENXIO); } rv = xusbpadctl_config_lane(sc, lane_name, lane, cfg); return (rv); } static int xusbpadctl_read_node(struct xusbpadctl_softc *sc, phandle_t node, struct lane_cfg *cfg, char **lanes, int *llanes) { int rv; *llanes = OF_getprop_alloc(node, "nvidia,lanes", 1, (void **)lanes); if (*llanes <= 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. */ rv = OF_getencprop(node, "nvidia,iddq", &cfg->iddq, sizeof(cfg->iddq)); if (rv <= 0) cfg->iddq = -1; return (0); } static int xusbpadctl_process_node(struct xusbpadctl_softc *sc, phandle_t node) { struct lane_cfg cfg; char *lanes, *lname; int i, len, llanes, rv; rv = xusbpadctl_read_node(sc, node, &cfg, &lanes, &llanes); if (rv != 0) return (rv); len = 0; lname = lanes; do { i = strlen(lname) + 1; rv = xusbpadctl_config_node(sc, lname, &cfg); if (rv != 0) device_printf(sc->dev, "Cannot configure lane: %s: %d\n", lname, rv); len += i; lname += i; } while (len < llanes); if (lanes != NULL) OF_prop_free(lanes); if (cfg.function != NULL) OF_prop_free(cfg.function); return (rv); } static int xusbpadctl_pinctrl_cfg(device_t dev, phandle_t cfgxref) { struct xusbpadctl_softc *sc; phandle_t node, cfgnode; int rv; sc = device_get_softc(dev); cfgnode = OF_node_from_xref(cfgxref); rv = 0; for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { if (!fdt_is_enabled(node)) continue; rv = xusbpadctl_process_node(sc, node); if (rv != 0) return (rv); } return (rv); } static int xusbpadctl_phy_pcie_powerup(struct xusbpadctl_softc *sc) { uint32_t reg; int i; reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); DELAY(100); reg = bus_read_4(sc->mem_res, 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; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); reg |= IOPHY_PLL_P0_CTL1_PLL_RST; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); DELAY(100); for (i = 0; i < 100; i++) { reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) return (0); DELAY(10); } return (ETIMEDOUT); } static int xusbpadctl_phy_pcie_powerdown(struct xusbpadctl_softc *sc) { uint32_t reg; reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); DELAY(100); return (0); } static int xusbpadctl_phy_sata_powerup(struct xusbpadctl_softc *sc) { uint32_t reg; int i; reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); for (i = 100; i >= 0; i--) { reg = bus_read_4(sc->mem_res, 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); } return (0); } static int xusbpadctl_phy_sata_powerdown(struct xusbpadctl_softc *sc) { uint32_t reg; reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ; bus_write_4(sc->mem_res, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); DELAY(100); return (0); } static int xusbpadctl_phy_powerup(struct xusbpadctl_softc *sc) { uint32_t reg; reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); return (0); } static int xusbpadctl_phy_powerdown(struct xusbpadctl_softc *sc) { uint32_t reg; reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); reg = bus_read_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM); reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; bus_write_4(sc->mem_res, XUSB_PADCTL_ELPG_PROGRAM, reg); DELAY(100); return (0); } static int xusbpadctl_phy_enable(device_t dev, intptr_t id, bool enable) { struct xusbpadctl_softc *sc; int rv; sc = device_get_softc(dev); if ((id != TEGRA_XUSB_PADCTL_PCIE) && (id != TEGRA_XUSB_PADCTL_SATA)) { device_printf(dev, "Unknown phy: %d\n", id); return (ENXIO); } rv = 0; if (enable) { if (sc->phy_ena_cnt == 0) { rv = xusbpadctl_phy_powerup(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt++; } if (id == TEGRA_XUSB_PADCTL_PCIE) { if (enable) rv = xusbpadctl_phy_pcie_powerup(sc); else rv = xusbpadctl_phy_pcie_powerdown(sc); if (rv != 0) return (rv); } else if (id == TEGRA_XUSB_PADCTL_SATA) { if (enable) rv = xusbpadctl_phy_sata_powerup(sc); else rv = xusbpadctl_phy_sata_powerdown(sc); if (rv != 0) return (rv); } if (!enable) { if (sc->phy_ena_cnt == 1) { rv = xusbpadctl_phy_powerdown(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt--; } return (0); } 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 xusbpadctl_softc * sc; int rid, rv; phandle_t node; sc = device_get_softc(dev); sc->dev = 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); } node = ofw_bus_get_node(dev); 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); } /* Register as a pinctrl device and use default configuration */ fdt_pinctrl_register(dev, NULL); fdt_pinctrl_configure_by_name(dev, "default"); phy_register_provider(dev); 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), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure, xusbpadctl_pinctrl_cfg), /* phy interface */ DEVMETHOD(phy_enable, xusbpadctl_phy_enable), DEVMETHOD_END }; -static driver_t tegra_xusbpadctl_driver = { - "tegra_xusbpadctl", - tegra_xusbpadctl_methods, - sizeof(struct xusbpadctl_softc), -}; - static devclass_t tegra_xusbpadctl_devclass; - +static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver, + tegra_xusbpadctl_methods, sizeof(struct xusbpadctl_softc)); EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, - tegra_xusbpadctl_devclass, 0, 0, 73); + tegra_xusbpadctl_devclass, NULL, NULL, 73); Index: stable/11/sys/arm/nvidia/tegra_abpmisc.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_abpmisc.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_abpmisc.c (revision 308335) @@ -1,194 +1,194 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * SoC misc configuration and indentification driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 }; -DEFINE_CLASS_0(tegra_abpmisc, tegra_abpmisc_driver, tegra_abpmisc_methods, - sizeof(struct tegra_abpmisc_softc)); 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, 0, 0, BUS_PASS_TIMER); + tegra_abpmisc_devclass, NULL, NULL, BUS_PASS_TIMER); Index: stable/11/sys/arm/nvidia/tegra_ahci.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_ahci.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_ahci.c (revision 308335) @@ -1,627 +1,626 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * AHCI driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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-phy", &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->dev, 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)); } -devclass_t genahci_devclass; -static device_method_t genahci_methods[] = { +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 driver_t genahci_driver = { - "ahci", - genahci_methods, - sizeof(struct tegra_ahci_sc) -}; -DRIVER_MODULE(genahci, simplebus, genahci_driver, genahci_devclass, NULL, NULL); + +static devclass_t tegra_ahci_devclass; +static DEFINE_CLASS_0(ahci, tegra_ahci_driver, tegra_ahci_methods, + sizeof(struct tegra_ahci_sc)); +DRIVER_MODULE(tegra_ahci, simplebus, tegra_ahci_driver, tegra_ahci_devclass, + NULL, NULL); Index: stable/11/sys/arm/nvidia/tegra_efuse.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_efuse.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_efuse.c (revision 308335) @@ -1,368 +1,367 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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() { 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 }; -DEFINE_CLASS_0(tegra_efuse, tegra_efuse_driver, tegra_efuse_methods, - sizeof(struct tegra_efuse_softc)); 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, 0, 0, BUS_PASS_TIMER); + tegra_efuse_devclass, NULL, NULL, BUS_PASS_TIMER); Index: stable/11/sys/arm/nvidia/tegra_ehci.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_ehci.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_ehci.c (revision 308335) @@ -1,322 +1,318 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * EHCI driver for Tegra SoCs. */ #include "opt_bus.h" #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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->dev, 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 driver_t ehci_driver = { - "ehci", - ehci_methods, - sizeof(struct tegra_ehci_softc) -}; - static devclass_t ehci_devclass; -DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb, 1, 1, 1); \ No newline at end of file +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); Index: stable/11/sys/arm/nvidia/tegra_gpio.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_gpio.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_gpio.c (revision 308335) @@ -1,904 +1,892 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * Tegra GPIO driver. */ #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 driver_t tegra_gpio_driver = { - "tegra_gpio", - tegra_gpio_methods, - sizeof(struct tegra_gpio_softc), -}; 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, 0, 0, 70); - -extern devclass_t ofwgpiobus_devclass; -extern driver_t ofw_gpiobus_driver; -EARLY_DRIVER_MODULE(ofw_gpiobus, tegra_gpio, ofw_gpiobus_driver, - ofwgpiobus_devclass, 0, 0, BUS_PASS_BUS); -extern devclass_t gpioc_devclass; -extern driver_t gpioc_driver; -DRIVER_MODULE(gpioc, tegra_gpio, gpioc_driver, gpioc_devclass, 0, 0); + tegra_gpio_devclass, NULL, NULL, 70); Index: stable/11/sys/arm/nvidia/tegra_i2c.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_i2c.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_i2c.c (revision 308335) @@ -1,808 +1,804 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * I2C driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 }; -DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods, - sizeof(struct tegra_i2c_softc)); 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, - 0, 0, 73); -extern devclass_t ofwiicbus_devclass; -extern driver_t ofw_iicbus_driver; -EARLY_DRIVER_MODULE(ofw_iicbus, tegra_iic, ofw_iicbus_driver, - ofwiicbus_devclass, 0, 0, BUS_PASS_BUS); + NULL, NULL, 73); Index: stable/11/sys/arm/nvidia/tegra_lic.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_lic.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_lic.c (revision 308335) @@ -1,288 +1,289 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * Local interrupt controller driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #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; -DEFINE_CLASS_0(tegra_lic, tegra_lic_driver, tegra_lic_methods, +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); Index: stable/11/sys/arm/nvidia/tegra_pcie.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_pcie.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_pcie.c (revision 308335) @@ -1,1632 +1,1633 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * Nvidia Integrated PCI/PCI-Express controller driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 */ /* 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; phy_t phy; 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 < TEGRA_PCIB_MAX_MSI && !found; 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 - 1; end_irq++) { /* No free interrupts */ if (end_irq == (TEGRA_PCIB_MAX_MSI - 1)) { found = false; break; } /* This is already used */ if ((sc->isrcs[irq].flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED) { found = false; 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; 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); 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); } /* Phy. */ rv = phy_get_by_ofw_name(sc->dev, 0, "pcie", &sc->phy); if (rv != 0) { device_printf(sc->dev, "Cannot get 'pcie' phy\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, uint32_t port) { 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); /* Enable PCIe phy. */ rv = phy_enable(sc->dev, sc->phy); if (rv != 0) { device_printf(sc->dev, "Cannot enable phy\n"); 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; uint32_t unit; int rv; int rid; struct tegra_pcib_port *port; int i; sc = device_get_softc(dev); sc->dev = dev; unit = fdt_get_unit(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, unit); 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); -devclass_t pcib_devclass; -DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass, 0, 0); +DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass, + NULL, NULL); Index: stable/11/sys/arm/nvidia/tegra_pinmux.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_pinmux.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_pinmux.c (revision 308335) @@ -1,804 +1,799 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * Pin multiplexer driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 driver_t tegra_pinmux_driver = { - "tegra_pinmux", - tegra_pinmux_methods, - sizeof(struct pinmux_softc), -}; - 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, 0, 0, 71); + tegra_pinmux_devclass, NULL, NULL, 71); Index: stable/11/sys/arm/nvidia/tegra_rtc.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_rtc.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_rtc.c (revision 308335) @@ -1,303 +1,304 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * RTC driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 }; -DEFINE_CLASS_0(tegra_rtc, tegra_rtc_driver, tegra_rtc_methods, - sizeof(struct tegra_rtc_softc)); static devclass_t tegra_rtc_devclass; -DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, tegra_rtc_devclass, 0, 0); +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); Index: stable/11/sys/arm/nvidia/tegra_sdhci.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_sdhci.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_sdhci.c (revision 308335) @@ -1,465 +1,461 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * SDHCI driver glue for NVIDIA Tegra family * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_cd; gpio_pin_t gpio_wp; gpio_pin_t gpio_power; 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_generic_get_ro(device_t brdev, device_t reqdev) { return (0); } 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, "cd-gpios", &sc->gpio_cd); gpio_pin_get_by_ofw_property(sc->dev, node, "power-gpios", &sc->gpio_power); gpio_pin_get_by_ofw_property(sc->dev, node, "wp-gpios", &sc->gpio_wp); 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; } bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->slot); return (0); fail: if (sc->gpio_cd != NULL) gpio_pin_release(sc->gpio_cd); if (sc->gpio_wp != NULL) gpio_pin_release(sc->gpio_wp); 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->intr_cookie != NULL) bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); 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); 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_generic_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), - { 0, 0 } + DEVMETHOD_END }; static devclass_t tegra_sdhci_devclass; - -static driver_t tegra_sdhci_driver = { - "sdhci_tegra", - tegra_sdhci_methods, - sizeof(struct tegra_sdhci_softc), -}; - +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, - 0, 0); + NULL, NULL); MODULE_DEPEND(sdhci_tegra, sdhci, 1, 1, 1); -DRIVER_MODULE(mmc, sdhci_tegra, mmc_driver, mmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_tegra, mmc, 1, 1, 1); Index: stable/11/sys/arm/nvidia/tegra_soctherm.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_soctherm.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_soctherm.c (revision 308335) @@ -1,696 +1,696 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; -DEFINE_CLASS_0(tegra_soctherm, tegra_soctherm_driver, tegra_soctherm_methods, +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, 0, 0, 79); + tegra_soctherm_devclass, NULL, NULL, 79); Index: stable/11/sys/arm/nvidia/tegra_usbphy.c =================================================================== --- stable/11/sys/arm/nvidia/tegra_usbphy.c (revision 308334) +++ stable/11/sys/arm/nvidia/tegra_usbphy.c (revision 308335) @@ -1,839 +1,834 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* * USB phy driver for Tegra SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phy_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}, }; #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(device_t dev, int id, bool enable) { struct usbphy_softc *sc; int rv = 0; 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; 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); } } phy_register_provider(dev); 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), /* phy interface */ DEVMETHOD(phy_enable, usbphy_phy_enable), DEVMETHOD_END }; -static driver_t tegra_usbphy_driver = { - "tegra_usbphy", - tegra_usbphy_methods, - sizeof(struct usbphy_softc), -}; - 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, 0, 0, 79); + tegra_usbphy_devclass, NULL, NULL, 79); Index: stable/11 =================================================================== --- stable/11 (revision 308334) +++ stable/11 (revision 308335) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r306447,306477