diff --git a/sys/arm64/nvidia/tegra210/max77620.c b/sys/arm64/nvidia/tegra210/max77620.c index a4fddb25e5a2..d35948fbfb60 100644 --- a/sys/arm64/nvidia/tegra210/max77620.c +++ b/sys/arm64/nvidia/tegra210/max77620.c @@ -1,511 +1,511 @@ /*- * Copyright (c) 2019 Michal Meloun * * 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$"); /* * MAX77620 PMIC driver */ #include #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 "max77620.h" static struct ofw_compat_data compat_data[] = { {"maxim,max77620", 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, "max77620") #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 MAX77620_DEVICE_ID 0x0C /* * Raw register access function. */ int max77620_read(struct max77620_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 max77620_read_buf(struct max77620_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 max77620_write(struct max77620_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 max77620_write_buf(struct max77620_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 max77620_modify(struct max77620_softc *sc, uint8_t reg, uint8_t clear, uint8_t set) { uint8_t val; int rv; rv = max77620_read(sc, reg, &val); if (rv != 0) return (rv); val &= ~clear; val |= set; rv = max77620_write(sc, reg, val); if (rv != 0) return (rv); return (0); } static int max77620_parse_fps(struct max77620_softc *sc, int id, phandle_t node) { int val; if (OF_getencprop(node, "maxim,shutdown-fps-time-period-us", &val, sizeof(val)) >= 0) { val = min(val, MAX77620_FPS_PERIOD_MAX_US); val = max(val, MAX77620_FPS_PERIOD_MIN_US); sc->shutdown_fps[id] = val; } if (OF_getencprop(node, "maxim,suspend-fps-time-period-us", &val, sizeof(val)) >= 0) { val = min(val, MAX77620_FPS_PERIOD_MAX_US); val = max(val, MAX77620_FPS_PERIOD_MIN_US); sc->suspend_fps[id] = val; } if (OF_getencprop(node, "maxim,fps-event-source", &val, sizeof(val)) >= 0) { if (val > 2) { device_printf(sc->dev, "Invalid 'fps-event-source' " "value: %d\n", val); return (EINVAL); } sc->event_source[id] = val; } return (0); } static int max77620_parse_fdt(struct max77620_softc *sc, phandle_t node) { phandle_t fpsnode; char fps_name[6]; int i, rv; for (i = 0; i < MAX77620_FPS_COUNT; i++) { sc->shutdown_fps[i] = -1; sc->suspend_fps[i] = -1; sc->event_source[i] = -1; } fpsnode = ofw_bus_find_child(node, "fps"); if (fpsnode > 0) { for (i = 0; i < MAX77620_FPS_COUNT; i++) { sprintf(fps_name, "fps%d", i); node = ofw_bus_find_child(node, fps_name); if (node <= 0) continue; rv = max77620_parse_fps(sc, i, node); if (rv != 0) return (rv); } } return (0); } static int max77620_get_version(struct max77620_softc *sc) { uint8_t buf[6]; int i; int rv; /* Verify ID string (5 bytes ). */ for (i = 0; i <= 6; i++) { rv = RD1(sc, MAX77620_REG_CID0 + i , buf + i); if (rv != 0) { device_printf(sc->dev, "Cannot read chip ID: %d\n", rv); return (ENXIO); } } if (bootverbose) { device_printf(sc->dev, " ID: [0x%02X, 0x%02X, 0x%02X, 0x%02X]\n", buf[0], buf[1], buf[2], buf[3]); } device_printf(sc->dev, " MAX77620 version - OTP: 0x%02X, ES: 0x%02X\n", buf[4], buf[5]); return (0); } static uint8_t max77620_encode_fps_period(struct max77620_softc *sc, int val) { uint8_t i; int period; period = MAX77620_FPS_PERIOD_MIN_US; for (i = 0; i < 7; i++) { if (period >= val) return (i); period *= 2; } return (i); } static int max77620_init(struct max77620_softc *sc) { uint8_t mask, val, tmp;; int i, rv; mask = 0; val = 0; for (i = 0; i < MAX77620_FPS_COUNT; i++) { if (sc->shutdown_fps[i] != -1) { mask |= MAX77620_FPS_TIME_PERIOD_MASK; tmp = max77620_encode_fps_period(sc, sc->shutdown_fps[i]); val |= (tmp << MAX77620_FPS_TIME_PERIOD_SHIFT) & MAX77620_FPS_TIME_PERIOD_MASK; } if (sc->event_source[i] != -1) { mask |= MAX77620_FPS_EN_SRC_MASK; tmp = sc->event_source[i]; val |= (tmp << MAX77620_FPS_EN_SRC_SHIFT) & MAX77620_FPS_EN_SRC_MASK; if (sc->event_source[i] == 2) { mask |= MAX77620_FPS_ENFPS_SW_MASK; val |= MAX77620_FPS_ENFPS_SW; } } rv = RM1(sc, MAX77620_REG_FPS_CFG0 + i, mask, val); if (rv != 0) { device_printf(sc->dev, "I/O error: %d\n", rv); return (ENXIO); } } /* Global mask interrupts */ rv = RM1(sc, MAX77620_REG_INTENLBT, 0x81, 0x81); rv = RM1(sc, MAX77620_REG_IRQTOPM, 0x81, 0x81); if (rv != 0) return (ENXIO); return (0); } #ifdef notyet static void max77620_intr(void *arg) { struct max77620_softc *sc; uint8_t intenlbt, intlbt, irqtop, irqtopm, irqsd, irqmasksd; uint8_t irq_lvl2_l0_7, irq_lvl2_l8, irq_lvl2_gpio, irq_msk_l0_7, irq_msk_l8; uint8_t onoffirq, onoffirqm; sc = (struct max77620_softc *)arg; /* XXX Finish temperature alarms. */ RD1(sc, MAX77620_REG_INTENLBT, &intenlbt); RD1(sc, MAX77620_REG_INTLBT, &intlbt); RD1(sc, MAX77620_REG_IRQTOP, &irqtop); RD1(sc, MAX77620_REG_IRQTOPM, &irqtopm); RD1(sc, MAX77620_REG_IRQSD, &irqsd); RD1(sc, MAX77620_REG_IRQMASKSD, &irqmasksd); RD1(sc, MAX77620_REG_IRQ_LVL2_L0_7, &irq_lvl2_l0_7); RD1(sc, MAX77620_REG_IRQ_MSK_L0_7, &irq_msk_l0_7); RD1(sc, MAX77620_REG_IRQ_LVL2_L8, &irq_lvl2_l8); RD1(sc, MAX77620_REG_IRQ_MSK_L8, &irq_msk_l8); RD1(sc, MAX77620_REG_IRQ_LVL2_GPIO, &irq_lvl2_gpio); RD1(sc, MAX77620_REG_ONOFFIRQ, &onoffirq); RD1(sc, MAX77620_REG_ONOFFIRQM, &onoffirqm); printf("%s: intlbt: 0x%02X, intenlbt: 0x%02X\n", __func__, intlbt, intenlbt); printf("%s: irqtop: 0x%02X, irqtopm: 0x%02X\n", __func__, irqtop, irqtopm); printf("%s: irqsd: 0x%02X, irqmasksd: 0x%02X\n", __func__, irqsd, irqmasksd); printf("%s: onoffirq: 0x%02X, onoffirqm: 0x%02X\n", __func__, onoffirq, onoffirqm); printf("%s: irq_lvl2_l0_7: 0x%02X, irq_msk_l0_7: 0x%02X\n", __func__, irq_lvl2_l0_7, irq_msk_l0_7); printf("%s: irq_lvl2_l8: 0x%02X, irq_msk_l8: 0x%02X\n", __func__, irq_lvl2_l8, irq_msk_l8); printf("%s: irq_lvl2_gpio: 0x%02X\n", __func__, irq_lvl2_gpio); } #endif static int max77620_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, "MAX77620 PMIC"); return (BUS_PROBE_DEFAULT); } static int max77620_attach(device_t dev) { struct max77620_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); #ifdef notyet /* Interrupt parent is not implemented */ if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate interrupt.\n"); rv = ENXIO; goto fail; } #endif rv = max77620_parse_fdt(sc, node); if (rv != 0) goto fail; rv = max77620_get_version(sc); if (rv != 0) goto fail; rv = max77620_init(sc); if (rv != 0) goto fail; rv = max77620_regulator_attach(sc, node); if (rv != 0) goto fail; rv = max77620_gpio_attach(sc, node); if (rv != 0) goto fail; rv = max77620_rtc_create(sc, node); if (rv != 0) goto fail; fdt_pinctrl_register(dev, NULL); fdt_pinctrl_configure_by_name(dev, "default"); /* Setup interrupt. */ #ifdef notyet rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, max77620_intr, sc, &sc->irq_h); if (rv) { device_printf(dev, "Cannot setup interrupt.\n"); goto fail; } #endif 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 max77620_detach(device_t dev) { struct max77620_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 max77620_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 max77620_methods[] = { /* Device interface */ DEVMETHOD(device_probe, max77620_probe), DEVMETHOD(device_attach, max77620_attach), DEVMETHOD(device_detach, max77620_detach), /* Regdev interface */ DEVMETHOD(regdev_map, max77620_regulator_map), /* GPIO protocol interface */ DEVMETHOD(gpio_get_bus, max77620_gpio_get_bus), DEVMETHOD(gpio_pin_max, max77620_gpio_pin_max), DEVMETHOD(gpio_pin_getname, max77620_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, max77620_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, max77620_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, max77620_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, max77620_gpio_pin_get), DEVMETHOD(gpio_pin_set, max77620_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, max77620_gpio_pin_toggle), DEVMETHOD(gpio_map_gpios, max77620_gpio_map_gpios), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure, max77620_pinmux_configure), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, max77620_gpio_get_node), DEVMETHOD_END }; static devclass_t max77620_devclass; static DEFINE_CLASS_0(gpio, max77620_driver, max77620_methods, sizeof(struct max77620_softc)); EARLY_DRIVER_MODULE(max77620, iicbus, max77620_driver, max77620_devclass, NULL, NULL, 74); diff --git a/sys/arm64/nvidia/tegra210/max77620_regulators.c b/sys/arm64/nvidia/tegra210/max77620_regulators.c index b70f96e2db4f..beb34405a90e 100644 --- a/sys/arm64/nvidia/tegra210/max77620_regulators.c +++ b/sys/arm64/nvidia/tegra210/max77620_regulators.c @@ -1,888 +1,888 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * 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 "max77620.h" MALLOC_DEFINE(M_MAX77620_REG, "MAX77620 regulator", "MAX77620 power regulator"); #define DIV_ROUND_UP(n,d) howmany(n, d) enum max77620_reg_id { MAX77620_REG_ID_SD0, MAX77620_REG_ID_SD1, MAX77620_REG_ID_SD2, MAX77620_REG_ID_SD3, MAX77620_REG_ID_LDO0, MAX77620_REG_ID_LDO1, MAX77620_REG_ID_LDO2, MAX77620_REG_ID_LDO3, MAX77620_REG_ID_LDO4, MAX77620_REG_ID_LDO5, MAX77620_REG_ID_LDO6, MAX77620_REG_ID_LDO7, MAX77620_REG_ID_LDO8, }; /* Initial configuration. */ struct max77620_regnode_init_def { struct regnode_init_def reg_init_def; int active_fps_src; int active_fps_pu_slot; int active_fps_pd_slot; int suspend_fps_src; int suspend_fps_pu_slot; int suspend_fps_pd_slot; int ramp_rate_setting; }; /* Regulator HW definition. */ struct reg_def { intptr_t id; /* ID */ char *name; /* Regulator name */ char *supply_name; /* Source property name */ bool is_sd_reg; /* SD or LDO regulator? */ uint8_t volt_reg; uint8_t volt_vsel_mask; uint8_t cfg_reg; uint8_t fps_reg; uint8_t pwr_mode_reg; uint8_t pwr_mode_mask; uint8_t pwr_mode_shift; struct regulator_range *ranges; int nranges; }; struct max77620_reg_sc { struct regnode *regnode; struct max77620_softc *base_sc; struct reg_def *def; phandle_t xref; struct regnode_std_param *param; /* Configured values */ int active_fps_src; int active_fps_pu_slot; int active_fps_pd_slot; int suspend_fps_src; int suspend_fps_pu_slot; int suspend_fps_pd_slot; int ramp_rate_setting; int enable_usec; uint8_t enable_pwr_mode; /* Cached values */ uint8_t fps_src; uint8_t pwr_mode; int pwr_ramp_delay; }; static struct regulator_range max77620_sd0_ranges[] = { REG_RANGE_INIT(0, 64, 600000, 12500), /* 0.6V - 1.4V / 12.5mV */ }; static struct regulator_range max77620_sd1_ranges[] = { REG_RANGE_INIT(0, 76, 600000, 12500), /* 0.6V - 1.55V / 12.5mV */ }; static struct regulator_range max77620_sdx_ranges[] = { REG_RANGE_INIT(0, 255, 600000, 12500), /* 0.6V - 3.7875V / 12.5mV */ }; static struct regulator_range max77620_ldo0_1_ranges[] = { REG_RANGE_INIT(0, 63, 800000, 25000), /* 0.8V - 2.375V / 25mV */ }; static struct regulator_range max77620_ldo4_ranges[] = { REG_RANGE_INIT(0, 63, 800000, 12500), /* 0.8V - 1.5875V / 12.5mV */ }; static struct regulator_range max77620_ldox_ranges[] = { REG_RANGE_INIT(0, 63, 800000, 50000), /* 0.8V - 3.95V / 50mV */ }; static struct reg_def max77620s_def[] = { { .id = MAX77620_REG_ID_SD0, .name = "sd0", .supply_name = "in-sd0", .is_sd_reg = true, .volt_reg = MAX77620_REG_SD0, .volt_vsel_mask = MAX77620_SD0_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG_SD0, .fps_reg = MAX77620_REG_FPS_SD0, .pwr_mode_reg = MAX77620_REG_CFG_SD0, .pwr_mode_mask = MAX77620_SD_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_SD_POWER_MODE_SHIFT, .ranges = max77620_sd0_ranges, .nranges = nitems(max77620_sd0_ranges), }, { .id = MAX77620_REG_ID_SD1, .name = "sd1", .supply_name = "in-sd1", .is_sd_reg = true, .volt_reg = MAX77620_REG_SD1, .volt_vsel_mask = MAX77620_SD1_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG_SD1, .fps_reg = MAX77620_REG_FPS_SD1, .pwr_mode_reg = MAX77620_REG_CFG_SD1, .pwr_mode_mask = MAX77620_SD_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_SD_POWER_MODE_SHIFT, .ranges = max77620_sd1_ranges, .nranges = nitems(max77620_sd1_ranges), }, { .id = MAX77620_REG_ID_SD2, .name = "sd2", .supply_name = "in-sd2", .is_sd_reg = true, .volt_reg = MAX77620_REG_SD2, .volt_vsel_mask = MAX77620_SDX_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG_SD2, .fps_reg = MAX77620_REG_FPS_SD2, .pwr_mode_reg = MAX77620_REG_CFG_SD2, .pwr_mode_mask = MAX77620_SD_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_SD_POWER_MODE_SHIFT, .ranges = max77620_sdx_ranges, .nranges = nitems(max77620_sdx_ranges), }, { .id = MAX77620_REG_ID_SD3, .name = "sd3", .supply_name = "in-sd3", .is_sd_reg = true, .volt_reg = MAX77620_REG_SD3, .volt_vsel_mask = MAX77620_SDX_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG_SD3, .fps_reg = MAX77620_REG_FPS_SD3, .pwr_mode_reg = MAX77620_REG_CFG_SD3, .pwr_mode_mask = MAX77620_SD_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_SD_POWER_MODE_SHIFT, .ranges = max77620_sdx_ranges, .nranges = nitems(max77620_sdx_ranges), }, { .id = MAX77620_REG_ID_LDO0, .name = "ldo0", .supply_name = "vin-ldo0-1", .volt_reg = MAX77620_REG_CFG_LDO0, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .is_sd_reg = false, .cfg_reg = MAX77620_REG_CFG2_LDO0, .fps_reg = MAX77620_REG_FPS_LDO0, .pwr_mode_reg = MAX77620_REG_CFG_LDO0, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldo0_1_ranges, .nranges = nitems(max77620_ldo0_1_ranges), }, { .id = MAX77620_REG_ID_LDO1, .name = "ldo1", .supply_name = "in-ldo0-1", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO1, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO1, .fps_reg = MAX77620_REG_FPS_LDO1, .pwr_mode_reg = MAX77620_REG_CFG_LDO1, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldo0_1_ranges, .nranges = nitems(max77620_ldo0_1_ranges), }, { .id = MAX77620_REG_ID_LDO2, .name = "ldo2", .supply_name = "in-ldo2", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO2, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO2, .fps_reg = MAX77620_REG_FPS_LDO2, .pwr_mode_reg = MAX77620_REG_CFG_LDO2, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldox_ranges, .nranges = nitems(max77620_ldox_ranges), }, { .id = MAX77620_REG_ID_LDO3, .name = "ldo3", .supply_name = "in-ldo3-5", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO3, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO3, .fps_reg = MAX77620_REG_FPS_LDO3, .pwr_mode_reg = MAX77620_REG_CFG_LDO3, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldox_ranges, .nranges = nitems(max77620_ldox_ranges), }, { .id = MAX77620_REG_ID_LDO4, .name = "ldo4", .supply_name = "in-ldo4-6", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO4, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO4, .fps_reg = MAX77620_REG_FPS_LDO4, .pwr_mode_reg = MAX77620_REG_CFG_LDO4, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldo4_ranges, .nranges = nitems(max77620_ldo4_ranges), }, { .id = MAX77620_REG_ID_LDO5, .name = "ldo5", .supply_name = "in-ldo3-5", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO5, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO5, .fps_reg = MAX77620_REG_FPS_LDO5, .pwr_mode_reg = MAX77620_REG_CFG_LDO5, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldox_ranges, .nranges = nitems(max77620_ldox_ranges), }, { .id = MAX77620_REG_ID_LDO6, .name = "ldo6", .supply_name = "in-ldo4-6", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO6, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO6, .fps_reg = MAX77620_REG_FPS_LDO6, .pwr_mode_reg = MAX77620_REG_CFG_LDO6, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldox_ranges, .nranges = nitems(max77620_ldox_ranges), }, { .id = MAX77620_REG_ID_LDO7, .name = "ldo7", .supply_name = "in-ldo7-8", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO7, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO7, .fps_reg = MAX77620_REG_FPS_LDO7, .pwr_mode_reg = MAX77620_REG_CFG_LDO7, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldox_ranges, .nranges = nitems(max77620_ldox_ranges), }, { .id = MAX77620_REG_ID_LDO8, .name = "ldo8", .supply_name = "in-ldo7-8", .is_sd_reg = false, .volt_reg = MAX77620_REG_CFG_LDO8, .volt_vsel_mask = MAX77620_LDO_VSEL_MASK, .cfg_reg = MAX77620_REG_CFG2_LDO8, .fps_reg = MAX77620_REG_FPS_LDO8, .pwr_mode_reg = MAX77620_REG_CFG_LDO8, .pwr_mode_mask = MAX77620_LDO_POWER_MODE_MASK, .pwr_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, .ranges = max77620_ldox_ranges, .nranges = nitems(max77620_ldox_ranges), }, }; static int max77620_regnode_init(struct regnode *regnode); static int max77620_regnode_enable(struct regnode *regnode, bool enable, int *udelay); static int max77620_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay); static int max77620_regnode_get_volt(struct regnode *regnode, int *uvolt); static regnode_method_t max77620_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, max77620_regnode_init), REGNODEMETHOD(regnode_enable, max77620_regnode_enable), REGNODEMETHOD(regnode_set_voltage, max77620_regnode_set_volt), REGNODEMETHOD(regnode_get_voltage, max77620_regnode_get_volt), REGNODEMETHOD_END }; DEFINE_CLASS_1(max77620_regnode, max77620_regnode_class, max77620_regnode_methods, sizeof(struct max77620_reg_sc), regnode_class); static int max77620_get_sel(struct max77620_reg_sc *sc, uint8_t *sel) { int rv; rv = RD1(sc->base_sc, sc->def->volt_reg, sel); if (rv != 0) { printf("%s: cannot read volatge selector: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } *sel &= sc->def->volt_vsel_mask; *sel >>= ffs(sc->def->volt_vsel_mask) - 1; return (0); } static int max77620_set_sel(struct max77620_reg_sc *sc, uint8_t sel) { int rv; sel <<= ffs(sc->def->volt_vsel_mask) - 1; sel &= sc->def->volt_vsel_mask; rv = RM1(sc->base_sc, sc->def->volt_reg, sc->def->volt_vsel_mask, sel); if (rv != 0) { printf("%s: cannot set volatge selector: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } return (rv); } static int max77620_get_fps_src(struct max77620_reg_sc *sc, uint8_t *fps_src) { uint8_t val; int rv; rv = RD1(sc->base_sc, sc->def->fps_reg, &val); if (rv != 0) return (rv); *fps_src = (val & MAX77620_FPS_SRC_MASK) >> MAX77620_FPS_SRC_SHIFT; return (0); } static int max77620_set_fps_src(struct max77620_reg_sc *sc, uint8_t fps_src) { int rv; rv = RM1(sc->base_sc, sc->def->fps_reg, MAX77620_FPS_SRC_MASK, fps_src << MAX77620_FPS_SRC_SHIFT); if (rv != 0) return (rv); sc->fps_src = fps_src; return (0); } static int max77620_set_fps_slots(struct max77620_reg_sc *sc, bool suspend) { uint8_t mask, val; int pu_slot, pd_slot, rv; if (suspend) { pu_slot = sc->suspend_fps_pu_slot; pd_slot = sc->suspend_fps_pd_slot; } else { pu_slot = sc->active_fps_pu_slot; pd_slot = sc->active_fps_pd_slot; } mask = 0; val = 0; if (pu_slot >= 0) { mask |= MAX77620_FPS_PU_PERIOD_MASK; val |= ((uint8_t)pu_slot << MAX77620_FPS_PU_PERIOD_SHIFT) & MAX77620_FPS_PU_PERIOD_MASK; } if (pd_slot >= 0) { mask |= MAX77620_FPS_PD_PERIOD_MASK; val |= ((uint8_t)pd_slot << MAX77620_FPS_PD_PERIOD_SHIFT) & MAX77620_FPS_PD_PERIOD_MASK; } rv = RM1(sc->base_sc, sc->def->fps_reg, mask, val); if (rv != 0) return (rv); return (0); } static int max77620_get_pwr_mode(struct max77620_reg_sc *sc, uint8_t *pwr_mode) { uint8_t val; int rv; rv = RD1(sc->base_sc, sc->def->pwr_mode_reg, &val); if (rv != 0) return (rv); *pwr_mode = (val & sc->def->pwr_mode_mask) >> sc->def->pwr_mode_shift; return (0); } static int max77620_set_pwr_mode(struct max77620_reg_sc *sc, uint8_t pwr_mode) { int rv; rv = RM1(sc->base_sc, sc->def->pwr_mode_reg, sc->def->pwr_mode_shift, pwr_mode << sc->def->pwr_mode_shift); if (rv != 0) return (rv); sc->pwr_mode = pwr_mode; return (0); } static int max77620_get_pwr_ramp_delay(struct max77620_reg_sc *sc, int *rate) { uint8_t val; int rv; rv = RD1(sc->base_sc, sc->def->cfg_reg, &val); if (rv != 0) return (rv); if (sc->def->is_sd_reg) { val = (val & MAX77620_SD_SR_MASK) >> MAX77620_SD_SR_SHIFT; if (val == 0) *rate = 13750; else if (val == 1) *rate = 27500; else if (val == 2) *rate = 55000; else *rate = 100000; } else { val = (val & MAX77620_LDO_SLEW_RATE_MASK) >> MAX77620_LDO_SLEW_RATE_SHIFT; if (val == 0) *rate = 100000; else *rate = 5000; } sc->pwr_ramp_delay = *rate; return (0); } static int max77620_set_pwr_ramp_delay(struct max77620_reg_sc *sc, int rate) { uint8_t val, mask; int rv; if (sc->def->is_sd_reg) { if (rate <= 13750) val = 0; else if (rate <= 27500) val = 1; else if (rate <= 55000) val = 2; else val = 3; val <<= MAX77620_SD_SR_SHIFT; mask = MAX77620_SD_SR_MASK; } else { if (rate <= 5000) val = 1; else val = 0; val <<= MAX77620_LDO_SLEW_RATE_SHIFT; mask = MAX77620_LDO_SLEW_RATE_MASK; } rv = RM1(sc->base_sc, sc->def->cfg_reg, mask, val); if (rv != 0) return (rv); return (0); } static int max77620_regnode_init(struct regnode *regnode) { struct max77620_reg_sc *sc; uint8_t val; int intval, rv; sc = regnode_get_softc(regnode); sc->enable_usec = 500; sc->enable_pwr_mode = MAX77620_POWER_MODE_NORMAL; #if 0 { uint8_t val1, val2, val3; RD1(sc->base_sc, sc->def->volt_reg, &val1); RD1(sc->base_sc, sc->def->cfg_reg, &val2); RD1(sc->base_sc, sc->def->fps_reg, &val3); printf("%s: Volt: 0x%02X, CFG: 0x%02X, FPS: 0x%02X\n", regnode_get_name(sc->regnode), val1, val2, val3); } #endif /* Get current power mode */ rv = max77620_get_pwr_mode(sc, &val); if (rv != 0) { printf("%s: cannot read current power mode: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } sc->pwr_mode = val; /* Get current power ramp delay */ rv = max77620_get_pwr_ramp_delay(sc, &intval); if (rv != 0) { printf("%s: cannot read current power mode: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } sc->pwr_ramp_delay = intval; /* Get FPS source if is not specified. */ if (sc->active_fps_src == -1) { rv = max77620_get_fps_src(sc, &val); if (rv != 0) { printf("%s: cannot read current FPS source: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } sc->active_fps_src = val; } /* Configure power mode non-FPS controlled regulators. */ if (sc->active_fps_src != MAX77620_FPS_SRC_NONE || (sc->pwr_mode != MAX77620_POWER_MODE_DISABLE && sc->pwr_mode != sc->enable_pwr_mode)) { rv = max77620_set_pwr_mode(sc, (uint8_t)sc->enable_pwr_mode); if (rv != 0) { printf("%s: cannot set power mode: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } } /* Set FPS source. */ rv = max77620_set_fps_src(sc, sc->active_fps_src); if (rv != 0) { printf("%s: cannot setup FPS source: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } /* Set FPS slots. */ rv = max77620_set_fps_slots(sc, false); if (rv != 0) { printf("%s: cannot setup power slots: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } /* Setup power ramp . */ if (sc->ramp_rate_setting != -1) { rv = max77620_set_pwr_ramp_delay(sc, sc->pwr_ramp_delay); if (rv != 0) { printf("%s: cannot set power ramp delay: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } } return (0); } static void max77620_fdt_parse(struct max77620_softc *sc, phandle_t node, struct reg_def *def, struct max77620_regnode_init_def *init_def) { int rv; phandle_t parent, supply_node; char prop_name[64]; /* Maximum OFW property name length. */ rv = regulator_parse_ofw_stdparam(sc->dev, node, &init_def->reg_init_def); rv = OF_getencprop(node, "maxim,active-fps-source", &init_def->active_fps_src, sizeof(init_def->active_fps_src)); if (rv <= 0) init_def->active_fps_src = MAX77620_FPS_SRC_DEF; rv = OF_getencprop(node, "maxim,active-fps-power-up-slot", &init_def->active_fps_pu_slot, sizeof(init_def->active_fps_pu_slot)); if (rv <= 0) init_def->active_fps_pu_slot = -1; rv = OF_getencprop(node, "maxim,active-fps-power-down-slot", &init_def->active_fps_pd_slot, sizeof(init_def->active_fps_pd_slot)); if (rv <= 0) init_def->active_fps_pd_slot = -1; rv = OF_getencprop(node, "maxim,suspend-fps-source", &init_def->suspend_fps_src, sizeof(init_def->suspend_fps_src)); if (rv <= 0) init_def->suspend_fps_src = -1; rv = OF_getencprop(node, "maxim,suspend-fps-power-up-slot", &init_def->suspend_fps_pu_slot, sizeof(init_def->suspend_fps_pu_slot)); if (rv <= 0) init_def->suspend_fps_pu_slot = -1; rv = OF_getencprop(node, "maxim,suspend-fps-power-down-slot", &init_def->suspend_fps_pd_slot, sizeof(init_def->suspend_fps_pd_slot)); if (rv <= 0) init_def->suspend_fps_pd_slot = -1; rv = OF_getencprop(node, "maxim,ramp-rate-setting", &init_def->ramp_rate_setting, sizeof(init_def->ramp_rate_setting)); if (rv <= 0) init_def->ramp_rate_setting = -1; /* Get parent supply. */ if (def->supply_name == NULL) return; parent = OF_parent(node); snprintf(prop_name, sizeof(prop_name), "%s-supply", def->supply_name); rv = OF_getencprop(parent, prop_name, &supply_node, sizeof(supply_node)); if (rv <= 0) return; supply_node = OF_node_from_xref(supply_node); rv = OF_getprop_alloc(supply_node, "regulator-name", (void **)&init_def->reg_init_def.parent_name); if (rv <= 0) init_def->reg_init_def.parent_name = NULL; } static struct max77620_reg_sc * max77620_attach(struct max77620_softc *sc, phandle_t node, struct reg_def *def) { struct max77620_reg_sc *reg_sc; struct max77620_regnode_init_def init_def; struct regnode *regnode; bzero(&init_def, sizeof(init_def)); max77620_fdt_parse(sc, node, def, &init_def); init_def.reg_init_def.id = def->id; init_def.reg_init_def.ofw_node = node; regnode = regnode_create(sc->dev, &max77620_regnode_class, &init_def.reg_init_def); if (regnode == NULL) { device_printf(sc->dev, "Cannot create regulator.\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); /* Init regulator softc. */ reg_sc->regnode = regnode; reg_sc->base_sc = sc; reg_sc->def = def; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); reg_sc->active_fps_src = init_def.active_fps_src; reg_sc->active_fps_pu_slot = init_def.active_fps_pu_slot; reg_sc->active_fps_pd_slot = init_def.active_fps_pd_slot; reg_sc->suspend_fps_src = init_def.suspend_fps_src; reg_sc->suspend_fps_pu_slot = init_def.suspend_fps_pu_slot; reg_sc->suspend_fps_pd_slot = init_def.suspend_fps_pd_slot; reg_sc->ramp_rate_setting = init_def.ramp_rate_setting; regnode_register(regnode); if (bootverbose) { int volt, rv; regnode_topo_slock(); rv = regnode_get_voltage(regnode, &volt); if (rv == ENODEV) { device_printf(sc->dev, " Regulator %s: parent doesn't exist yet.\n", regnode_get_name(regnode)); } else if (rv != 0) { device_printf(sc->dev, " Regulator %s: voltage: INVALID!!!\n", regnode_get_name(regnode)); } else { device_printf(sc->dev, " Regulator %s: voltage: %d uV\n", regnode_get_name(regnode), volt); device_printf(sc->dev, " FPS source: %d, mode: %d, ramp delay: %d\n", reg_sc->fps_src, reg_sc->pwr_mode, reg_sc->pwr_ramp_delay); } regnode_topo_unlock(); } return (reg_sc); } int max77620_regulator_attach(struct max77620_softc *sc, phandle_t node) { struct max77620_reg_sc *reg; phandle_t child, rnode; int i; rnode = ofw_bus_find_child(node, "regulators"); if (rnode <= 0) { device_printf(sc->dev, " Cannot find regulators subnode\n"); return (ENXIO); } sc->nregs = nitems(max77620s_def); sc->regs = malloc(sizeof(struct max77620_reg_sc *) * sc->nregs, M_MAX77620_REG, M_WAITOK | M_ZERO); /* Attach all known regulators if exist in DT. */ for (i = 0; i < sc->nregs; i++) { child = ofw_bus_find_child(rnode, max77620s_def[i].name); if (child == 0) { if (bootverbose) device_printf(sc->dev, "Regulator %s missing in DT\n", max77620s_def[i].name); continue; } if (ofw_bus_node_status_okay(child) == 0) continue; reg = max77620_attach(sc, child, max77620s_def + i); if (reg == NULL) { device_printf(sc->dev, "Cannot attach regulator: %s\n", max77620s_def[i].name); return (ENXIO); } sc->regs[i] = reg; } return (0); } int max77620_regulator_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *num) { struct max77620_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->nregs; i++) { if (sc->regs[i] == NULL) continue; if (sc->regs[i]->xref == xref) { *num = sc->regs[i]->def->id; return (0); } } return (ENXIO); } static int max77620_regnode_enable(struct regnode *regnode, bool val, int *udelay) { struct max77620_reg_sc *sc; uint8_t mode; int rv; sc = regnode_get_softc(regnode); if (sc->active_fps_src != MAX77620_FPS_SRC_NONE) { *udelay = 0; return (0); } if (val) mode = sc->enable_pwr_mode; else mode = MAX77620_POWER_MODE_DISABLE; rv = max77620_set_pwr_mode(sc, mode); if (rv != 0) { printf("%s: cannot set power mode: %d\n", regnode_get_name(sc->regnode), rv); return (rv); } *udelay = sc->enable_usec; return (0); } static int max77620_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct max77620_reg_sc *sc; uint8_t sel; int rv; sc = regnode_get_softc(regnode); *udelay = 0; rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges, min_uvolt, max_uvolt, &sel); if (rv != 0) return (rv); rv = max77620_set_sel(sc, sel); return (rv); } static int max77620_regnode_get_volt(struct regnode *regnode, int *uvolt) { struct max77620_reg_sc *sc; uint8_t sel; int rv; sc = regnode_get_softc(regnode); rv = max77620_get_sel(sc, &sel); if (rv != 0) return (rv); rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges, sel, uvolt); return (rv); return(0); } diff --git a/sys/arm64/nvidia/tegra210/tegra210_car.c b/sys/arm64/nvidia/tegra210/tegra210_car.c index 20249942a7cc..36400aa88d1f 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_car.c +++ b/sys/arm64/nvidia/tegra210/tegra210_car.c @@ -1,601 +1,601 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * 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 #include "clkdev_if.h" #include "hwreset_if.h" #include "tegra210_car.h" static struct ofw_compat_data compat_data[] = { {"nvidia,tegra210-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, }; /* Parent lists. */ PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60", "pc_xusb_ss" }; PLIST(mux_xusb_ssp) = {"xusb_ss", "osc_div_clk"}; /* Clocks ajusted online. */ static struct clk_fixed_def fixed_osc = FRATE(TEGRA210_CLK_CLK_M, "osc", 38400000); static struct clk_fixed_def fixed_clk_m = FACT(0, "clk_m", "osc", 1, 1); static struct clk_fixed_def fixed_osc_div = FACT(0, "osc_div_clk", "osc", 1, 1); static struct clk_fixed_def tegra210_fixed_clks[] = { /* Core clocks. */ FRATE(0, "bogus", 1), FRATE(0, "clk_s", 32768), /* Audio clocks. */ FRATE(0, "vimclk_sync", 1), FRATE(0, "i2s1_sync", 1), FRATE(0, "i2s2_sync", 1), FRATE(0, "i2s3_sync", 1), FRATE(0, "i2s4_sync", 1), FRATE(0, "i2s5_sync", 1), FRATE(0, "spdif_in_sync", 1), /* XUSB */ FACT(TEGRA210_CLK_XUSB_SS_DIV2, "xusb_ss_div2", "xusb_ss", 1, 2), /* SOR */ FACT(0, "sor_safe_div", "pllP_out0", 1, 17), FACT(0, "dpaux_div", "sor_safe", 1, 17), FACT(0, "dpaux1_div", "sor_safe", 1, 17), /* Not Yet Implemented */ FRATE(0, "audio", 10000000), 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), FRATE(0, "audiod1", 10000000), FRATE(0, "audiod2", 10000000), FRATE(0, "audiod3", 10000000), FRATE(0, "dfllCPU_out", 10000000), }; static struct clk_mux_def tegra210_mux_clks[] = { /* USB. */ MUX(TEGRA210_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 2), MUX(0, "xusb_ssp", mux_xusb_ssp, CLK_SOURCE_XUSB_SS, 24, 1), }; static struct clk_gate_def tegra210_gate_clks[] = { /* Base peripheral clocks. */ GATE_INV(TEGRA210_CLK_HCLK, "hclk", "hclk_div", CLK_SYSTEM_RATE, 7), GATE_INV(TEGRA210_CLK_PCLK, "pclk", "pclk_div", CLK_SYSTEM_RATE, 3), GATE(TEGRA210_CLK_CML0, "cml0", "pllE_out0", PLLE_AUX, 0), GATE(TEGRA210_CLK_CML1, "cml1", "pllE_out0", PLLE_AUX, 1), GATE(0, "pllD_dsi_csi", "pllD_out0", PLLD_MISC, 21), GATE(0, "pllP_hsio", "pllP_out0", PLLP_MISC1, 29), GATE(0, "pllP_xusb", "pllP_hsio", PLLP_MISC1, 28), }; static struct clk_div_def tegra210_div_clks[] = { /* 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 tegra210_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", NULL, 564480000, 1}, {"pllA_out0", NULL, 11289600, 1}, {"extperiph1", "pllA_out0", 0, 1}, {"i2s1", "pllA_out0", 11289600, 0}, {"i2s2", "pllA_out0", 11289600, 0}, {"i2s3", "pllA_out0", 11289600, 0}, {"i2s4", "pllA_out0", 11289600, 0}, {"i2s5", "pllA_out0", 11289600, 0}, {"host1x", "pllP_out0", 136000000, 1}, {"sclk", "pllP_out2", 102000000, 1}, {"dvfs_soc", "pllP_out0", 51000000, 1}, {"dvfs_ref", "pllP_out0", 51000000, 1}, {"spi4", "pllP_out0", 12000000, 1}, {"pllREFE", NULL, 672000000, 0}, {"xusb", NULL, 0, 1}, {"xusb_ss", "pllU_480", 120000000, 0}, {"pc_xusb_fs", "pllU_48", 48000000, 0}, {"xusb_hs", "pc_xusb_ss", 120000000, 0}, {"xusb_ssp", "xusb_ss", 120000000, 0}, {"pc_xusb_falcon", "pllP_xusb", 204000000, 0}, {"pc_xusb_core_host", "pllP_xusb", 102000000, 0}, {"pc_xusb_core_dev", "pllP_xusb", 102000000, 0}, {"sata", "pllP_out0", 104000000, 0}, {"sata_oob", "pllP_out0", 204000000, 0}, {"emc", NULL, 0, 1}, {"mselect", NULL, 0, 1}, {"csite", NULL, 0, 1}, {"dbgapb", NULL, 0, 1 }, {"tsensor", "clk_m", 400000, 0}, {"i2c1", "pllP_out0", 0, 0}, {"i2c2", "pllP_out0", 0, 0}, {"i2c3", "pllP_out0", 0, 0}, {"i2c4", "pllP_out0", 0, 0}, {"i2c5", "pllP_out0", 0, 0}, {"i2c6", "pllP_out0", 0, 0}, {"pllDP_out0", NULL, 270000000, 0}, {"soc_therm", "pllP_out0", 51000000, 0}, {"cclk_g", NULL, 0, 1}, {"pllU_out1", NULL, 48000000, 1}, {"pllU_out2", NULL, 60000000, 1}, {"pllC4", NULL, 1000000000, 1}, {"pllC4_out0", NULL, 1000000000, 1}, }; static void init_divs(struct tegra210_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 tegra210_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 tegra210_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 tegra210_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 = OSC_CTRL_OSC_FREQ_GET(val); fixed_osc.freq = osc_freqs[osc_idx]; if (fixed_osc.freq == 0) panic("Undefined input frequency"); rv = clknode_fixed_register(sc->clkdom, &fixed_osc); if (rv != 0) panic("clk_fixed_register failed"); fixed_osc_div.div = 1 << OSC_CTRL_PLL_REF_DIV_GET(val); rv = clknode_fixed_register(sc->clkdom, &fixed_osc_div); if (rv != 0) panic("clk_fixed_register failed"); CLKDEV_READ_4(sc->dev, SPARE_REG0, &val); fixed_clk_m.div = SPARE_REG0_MDIV_GET(val) + 1; rv = clknode_fixed_register(sc->clkdom, &fixed_clk_m); 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 tegra210_car_softc *sc) { int i; struct tegra210_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 tegra210_car_softc *sc; sc = device_get_softc(dev); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) panic("clkdom == NULL"); init_fixeds(sc, tegra210_fixed_clks, nitems(tegra210_fixed_clks)); tegra210_init_plls(sc); init_muxes(sc, tegra210_mux_clks, nitems(tegra210_mux_clks)); init_divs(sc, tegra210_div_clks, nitems(tegra210_div_clks)); init_gates(sc, tegra210_gate_clks, nitems(tegra210_gate_clks)); tegra210_periph_clock(sc); tegra210_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 tegra210_car_clkdev_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); *val = bus_read_4(sc->mem_res, addr); return (0); } static int tegra210_car_clkdev_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); bus_write_4(sc->mem_res, addr, val); return (0); } static int tegra210_car_clkdev_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, uint32_t set_mask) { struct tegra210_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 tegra210_car_clkdev_device_lock(device_t dev) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void tegra210_car_clkdev_device_unlock(device_t dev) { struct tegra210_car_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } static int tegra210_car_detach(device_t dev) { device_printf(dev, "Error: Clock driver cannot be detached\n"); return (EBUSY); } static int tegra210_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 tegra210_car_attach(device_t dev) { struct tegra210_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 tegra210_car_hwreset_assert(device_t dev, intptr_t id, bool value) { struct tegra210_car_softc *sc = device_get_softc(dev); return (tegra210_hwreset_by_idx(sc, id, value)); } static device_method_t tegra210_car_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra210_car_probe), DEVMETHOD(device_attach, tegra210_car_attach), DEVMETHOD(device_detach, tegra210_car_detach), /* Clkdev interface*/ DEVMETHOD(clkdev_read_4, tegra210_car_clkdev_read_4), DEVMETHOD(clkdev_write_4, tegra210_car_clkdev_write_4), DEVMETHOD(clkdev_modify_4, tegra210_car_clkdev_modify_4), DEVMETHOD(clkdev_device_lock, tegra210_car_clkdev_device_lock), DEVMETHOD(clkdev_device_unlock, tegra210_car_clkdev_device_unlock), /* Reset interface */ DEVMETHOD(hwreset_assert, tegra210_car_hwreset_assert), DEVMETHOD_END }; static devclass_t tegra210_car_devclass; static DEFINE_CLASS_0(car, tegra210_car_driver, tegra210_car_methods, sizeof(struct tegra210_car_softc)); EARLY_DRIVER_MODULE(tegra210_car, simplebus, tegra210_car_driver, tegra210_car_devclass, NULL, NULL, BUS_PASS_TIMER); diff --git a/sys/arm64/nvidia/tegra210/tegra210_clk_per.c b/sys/arm64/nvidia/tegra210/tegra210_clk_per.c index b2f45062c02f..302dd1d85fd3 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_clk_per.c +++ b/sys/arm64/nvidia/tegra210/tegra210_clk_per.c @@ -1,951 +1,951 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * 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 "tegra210_car.h" /* Bits in base register. */ #define PERLCK_AMUX_MASK 0x0F #define PERLCK_AMUX_SHIFT 16 #define PERLCK_AMUX_DIS (1 << 20) #define PERLCK_UDIV_DIS (1 << 24) #define PERLCK_ENA_MASK (1 << 28) #define PERLCK_MUX_SHIFT 29 #define PERLCK_MUX_MASK 0x07 struct periph_def { struct clknode_init_def clkdef; uint32_t base_reg; uint32_t div_width; uint32_t div_mask; uint32_t div_f_width; uint32_t div_f_mask; uint32_t flags; }; struct pgate_def { struct clknode_init_def clkdef; uint32_t idx; uint32_t flags; }; #define PLIST(x) static const char *x[] #define GATE(_id, cname, plist, _idx) \ { \ .clkdef.id = TEGRA210_CLK_##_id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .idx = _idx, \ .flags = 0, \ } /* Sources for multiplexors. */ PLIST(mux_N_N_c_N_p_N_a) = {"bogus", NULL, "pllC_out0", NULL, "pllP_out0", NULL, "pllA_out0", NULL}; PLIST(mux_N_N_p_N_N_N_clkm) = {NULL, NULL, "pllP_out0", NULL, NULL, NULL, "clk_m", NULL}; PLIST(mux_N_c_p_a1_c2_c3_clkm) = {NULL, "pllC_out0", "pllP_out0", "pllA1_out0", "pllC2_out0", "pllC3_out0", "clk_m", NULL}; PLIST(mux_N_c_p_a1_c2_c3_clkm_c4) = {NULL, "pllC_out0", "pllP_out0", "pllA1_out0", "pllC2_out0", "pllC3_out0", "clk_m", "pllC4_out0"}; PLIST(mux_N_c_p_clkm_N_c4_c4o1_c4o1) = {NULL, "pllC_out0", "pllP_out0", "clk_m", NULL, "pllC4_out0", "pllC4_out1", "pllC4_out1"}; PLIST(mux_N_c_p_clkm_N_c4_c4o1_c4o2) = {NULL, "pllC_out0", "pllP_out0", "clk_m", NULL, "pllC4_out0", "pllC4_out1", "pllC4_out2"}; PLIST(mux_N_c2_c_c3_p_N_a) = {NULL, "pllC2_out0", "pllC_out0", "pllC3_out0", "pllP_out0", NULL, "pllA_out0", NULL}; PLIST(mux_N_c2_c_c3_p_clkm_a1_c4) = {NULL, "pllC2_out0", "pllC_out0", "pllC3_out0", "pllP_out0", "clk_m", "pllA1_out0", "pllC4_out0"}; PLIST(mux_N_c2_c_c3_p_N_a1_clkm) = {NULL, "pllC2_out0", "pllC_out0", "pllC3_out0", "pllP_out0", NULL, "pllA1_out0", "clk_m"}; PLIST(mux_a_N_audio_N_p_N_clkm) = {"pllA_out0", NULL, "audio", NULL, "pllP_out0", NULL, "clk_m"}; PLIST(mux_a_N_audio0_N_p_N_clkm) = {"pllA_out0", NULL, "audio0", NULL, "pllP_out0", NULL, "clk_m"}; PLIST(mux_a_N_audio1_N_p_N_clkm) = {"pllA_out0", NULL, "audio1", NULL, "pllP_out0", NULL, "clk_m"}; PLIST(mux_a_N_audio2_N_p_N_clkm) = {"pllA_out0", NULL, "audio2", NULL, "pllP_out0", NULL, "clk_m"}; PLIST(mux_a_N_audio3_N_p_N_clkm) = {"pllA_out0", NULL, "audio3", NULL, "pllP_out0", NULL, "clk_m"}; PLIST(mux_a_N_audio4_N_p_N_clkm) = {"pllA_out0", NULL, "audio4", NULL, "pllP_out0", NULL, "clk_m"}; PLIST(mux_a_audiod1_p_clkm) = {"pllA_out0", "audiod1", "pllP_out0", "clk_m", NULL, NULL, NULL, NULL}; PLIST(mux_a_audiod2_p_clkm) = {"pllA_out0", "audiod2", "pllP_out0", "clk_m", NULL, NULL, NULL, NULL}; PLIST(mux_a_audiod3_p_clkm) = {"pllA_out0", "audiod3", "pllP_out0", "clk_m", NULL, NULL, NULL, NULL}; PLIST(mux_a_c4_c_c4o1_p_N_clkm_c4o2) = {"pllA_out0", "pllC4_out0", "pllC_out0", "pllC4_out1", "pllP_out0", NULL, "clk_m", "pllC4_out2"}; PLIST(mux_a_clks_p_clkm_e) = {"pllA_out0", "clk_s", "pllP_out0", "clk_m", "pllE_out0"}; PLIST(mux_c4o1_c2_c_c4_p_clkm_a_c4) = {"pllC4_out1", "pllC2_out0", "pllC_out0", "pllC4_out0", "pllP_out0", "clk_m","pllA_out0", "pllC4_out0", }; PLIST(mux_m_c_p_clkm_mud_mbud_mb_pud) = {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m", "pllM_UD", "pllMB_UD", "pllMB_out0", "pllP_UD"}; PLIST(mux_p_N_N_c4o2_c4o1_N_clkm_c4) = {"pllP_out0", NULL, NULL, "pllC4_out2", "pllC4_out1", NULL, "clk_m", "pllC4_out0"}; PLIST(mux_p_N_c_c4_c4o1_c4o2_clkm) = {"pllP_out0", NULL, "pllC_out0", "pllC4_out0", "pllC4_out1", "pllC4_out2", "clk_m"}; PLIST(mux_p_N_c_c4_N_c4o1_clkm_c4o2) = {"pllP_out0", NULL, "pllC_out0", "pllC4_out0", NULL, "pllC4_out1", "clk_m", "pllC4_out2"}; PLIST(mux_p_N_d_N_N_d2_clkm) = {"pllP_out0", NULL, "pllD_out0", NULL, NULL, "pllD2_out0", "clk_m"}; PLIST(mux_p_N_clkm_N_clks_N_E) = {"pllP_out0", NULL, "clk_m", NULL, NULL, "clk_s", NULL, "pllE_out0"}; PLIST(mux_p_c_c2_N_c2_N_clkm) = {"pllP_out0", "pllC_out0", "pllC2_out0", NULL, "pllC2_out0", NULL, "clk_m", NULL}; PLIST(mux_p_co1_c_N_c4o2_c4o1_clkm_c4) = {"pllP_out0", "pllC_out1", "pllC_out0", NULL, "pllC4_out2", "pllC4_out1" ,"clk_m", "pllC4_out0"}; PLIST(mux_p_c2_c_c3_N_a1_clkm_c4) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", NULL, "pllA1_out0", "clk_m", "pllC4_out0"}; PLIST(mux_p_c2_c_c3_N_N_clkm) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", NULL, NULL, "clk_m", NULL}; PLIST(mux_p_c2_c_c3_m_e_clkm) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC3_out0", "pllM_out0", "pllE_out0", "clk_m"}; PLIST(mux_p_c2_c_c4_N_c4o1_clkm_c4o2) = {"pllP_out0", "pllC2_out0", "pllC4_out0", NULL, "pllC4_out1", "clk_m", "pllC4_out2"}; PLIST(mux_p_c2_c_c4_a_c4o1_clkm_c4o2) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC4_out0", "pllA_out0", "pllC4_out1", "clk_m", "pllC4_out2"}; PLIST(mux_p_c2_c_c4o2_c4o1_clks_clkm_c4) = {"pllP_out0", "pllC2_out0", "pllC4_out2", "pllC4_out1", "clk_s", "clk_m", "pllC4_out0"}; PLIST(mux_p_c2_c_c4_c4o1_clkm_c4o2) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC4_out0", "pllC4_out1", "clk_m", "pllC4_out2"}; PLIST(mux_p_c2_c_c4_clkm_c4o1_c4o2) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC4_out0", "clk_m", "pllC4_out1", "pllC4_out2"}; PLIST(mux_p_c2_c_c4_clks_c4o1_clkm_c4o2) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC4_out0", "clk_s", "pllC4_out1", "clk_m", "pllC4_out2"}; PLIST(mux_p_c2_c_c4_clkm_c4o1_clks_c4o2) = {"pllP_out0", "pllC2_out0", "pllC_out0", "pllC4_out0", "clk_m", "pllC4_out1", "clk_s", "pllC4_out2"}; PLIST(mux_p_c2_refe1_c3_m_a1_clkm_C4) = {"pllP_out0", "pllC2_out0", "pllREFE_out1", "pllC3_out0", "pllM_out0", "pllA1_out0", "clk_m", "pllC4_out0"}; PLIST(mux_p_c4_c_c4o1_N_c4o2_clkm) = {"pllP_out0", "pllC4_out0", "pllC_out0", "pllC4_out1", NULL, "pllC4_out2", "clk_m", NULL}; PLIST(mux_p_m_d_a_c_d2_clkm) = {"pllP_out0", "pllM_out0", "pllD_out0", "pllA_out0", "pllC_out0", "pllD2_out0", "clk_m"}; PLIST(mux_p_po3_clkm_clks_a) = {"pllP_out0", "pllP_out3", "clk_m", "clk_s", "pllA_out0", NULL, NULL, NULL}; PLIST(mux_po3_c_c2_clkm_p_c4_c4o1_c4o2) = {"pllP_out3", "pllC_out0", "pllC2_out0", "clk_m", "pllP_out0", "pllC4_out0", "pllC4_out1", "pllC4_out2"}; PLIST(mux_clkm_p_N_N_N_refre) = {"clk_m", "pllP_xusb", NULL, NULL, NULL, "pllREFE_out0", NULL, NULL}; PLIST(mux_clkm_N_u48_N_p_N_u480) = {"clk_m", NULL, "pllU_48", NULL, "pllP_out0", NULL, "pllU_480"}; PLIST(mux_clkm_refe_clks_u480) = {"clk_m", "pllREFE_out0", "clk_s", "pllU_480", NULL, NULL, NULL, NULL}; PLIST(mux_sep_audio) = {"pllA_out0", "pllC4_out0", "pllC_out0", "pllC4_out0", "pllP_out0", "pllC4_out0", "clk_m", NULL, "spdif_in", "i2s1", "i2s2", "i2s3", "i2s4", "i2s5", "pllA_out0", "ext_vimclk"}; static uint32_t clk_enable_reg[] = { CLK_OUT_ENB_L, CLK_OUT_ENB_H, CLK_OUT_ENB_U, CLK_OUT_ENB_V, CLK_OUT_ENB_W, CLK_OUT_ENB_X, CLK_OUT_ENB_Y, }; static uint32_t clk_reset_reg[] = { RST_DEVICES_L, RST_DEVICES_H, RST_DEVICES_U, RST_DEVICES_V, RST_DEVICES_W, RST_DEVICES_X, RST_DEVICES_Y, }; #define L(n) ((0 * 32) + (n)) #define H(n) ((1 * 32) + (n)) #define U(n) ((2 * 32) + (n)) #define V(n) ((3 * 32) + (n)) #define W(n) ((4 * 32) + (n)) #define X(n) ((5 * 32) + (n)) #define Y(n) ((6 * 32) + (n)) /* Clock IDs not yet defined in binding header file. */ #define TEGRA210_CLK_STAT_MON H(5) #define TEGRA210_CLK_IRAMA U(20) #define TEGRA210_CLK_IRAMB U(21) #define TEGRA210_CLK_IRAMC U(22) #define TEGRA210_CLK_IRAMD U(23) #define TEGRA210_CLK_CRAM2 U(24) #define TEGRA210_CLK_M_DOUBLER U(26) #define TEGRA210_CLK_DEVD2_OUT U(29) #define TEGRA210_CLK_DEVD1_OUT U(30) #define TEGRA210_CLK_CPUG V(0) #define TEGRA210_CLK_ATOMICS V(16) #define TEGRA210_CLK_PCIERX0 W(2) #define TEGRA210_CLK_PCIERX1 W(3) #define TEGRA210_CLK_PCIERX2 W(4) #define TEGRA210_CLK_PCIERX3 W(5) #define TEGRA210_CLK_PCIERX4 W(6) #define TEGRA210_CLK_PCIERX5 W(7) #define TEGRA210_CLK_PCIE2_IOBIST W(9) #define TEGRA210_CLK_EMC_IOBIST W(10) #define TEGRA210_CLK_SATA_IOBIST W(12) #define TEGRA210_CLK_MIPI_IOBIST W(13) #define TEGRA210_CLK_EMC_LATENCY W(29) #define TEGRA210_CLK_MC1 W(30) #define TEGRA210_CLK_ETR X(3) #define TEGRA210_CLK_CAM_MCLK X(4) #define TEGRA210_CLK_CAM_MCLK2 X(5) #define TEGRA210_CLK_MC_CAPA X(7) #define TEGRA210_CLK_MC_CBPA X(8) #define TEGRA210_CLK_MC_CPU X(9) #define TEGRA210_CLK_MC_BBC X(10) #define TEGRA210_CLK_EMC_DLL X(14) #define TEGRA210_CLK_UART_FST_MIPI_CAL X(17) #define TEGRA210_CLK_HPLL_ADSP X(26) #define TEGRA210_CLK_PLLP_ADSP X(27) #define TEGRA210_CLK_PLLA_ADSP X(28) #define TEGRA210_CLK_PLLG_REF X(29) #define TEGRA210_CLK_AXIAP Y(4) #define TEGRA210_CLK_MC_CDPA Y(8) #define TEGRA210_CLK_MC_CCPA Y(9) static struct pgate_def pgate_def[] = { /* bank L -> 0-31 */ GATE(ISPB, "ispb", "clk_m", L(3)), GATE(RTC, "rtc", "clk_s", L(4)), GATE(TIMER, "timer", "clk_m", L(5)), GATE(UARTA, "uarta", "pc_uarta" , L(6)), GATE(UARTB, "uartb", "pc_uartb", L(7)), GATE(GPIO, "gpio", "clk_m", L(8)), GATE(SDMMC2, "sdmmc2", "pc_sdmmc2", L(9)), GATE(SPDIF_OUT, "spdif_out", "pc_spdif_out", L(10)), GATE(SPDIF_IN, "spdif_in", "pc_spdif_in", L(10)), GATE(I2S1, "i2s2", "pc_i2s2", L(11)), GATE(I2C1, "i2c1", "pc_i2c1", L(12)), GATE(SDMMC1, "sdmmc1", "pc_sdmmc1", L(14)), GATE(SDMMC4, "sdmmc4", "pc_sdmmc4", L(15)), GATE(PWM, "pwm", "pc_pwm", L(17)), GATE(I2S2, "i2s3", "pc_i2s3", L(18)), GATE(VI, "vi", "pc_vi", L(20)), GATE(USBD, "usbd", "clk_m", L(22)), GATE(ISP, "isp", "pc_isp", L(23)), GATE(DISP2, "disp2", "pc_disp2", L(26)), GATE(DISP1, "disp1", "pc_disp1", L(27)), GATE(HOST1X, "host1x", "pc_host1x", L(28)), GATE(I2S0, "i2s1", "pc_i2s1", L(30)), /* bank H -> 32-63 */ GATE(MC, "mem", "clk_m", H(0)), GATE(AHBDMA, "ahbdma", "clk_m", H(1)), GATE(APBDMA, "apbdma", "clk_m", H(2)), GATE(STAT_MON, "stat_mon", "clk_s", H(5)), GATE(PMC, "pmc", "clk_s", H(6)), GATE(FUSE, "fuse", "clk_m", H(7)), GATE(KFUSE, "kfuse", "clk_m", H(8)), GATE(SBC1, "spi1", "pc_spi1", H(9)), GATE(SBC2, "spi2", "pc_spi2", H(12)), GATE(SBC3, "spi3", "pc_spi3", H(14)), GATE(I2C5, "i2c5", "pc_i2c5", H(15)), GATE(DSIA, "dsia", "pllD_dsi_csi", H(16)), GATE(CSI, "csi", "pllP_out3", H(20)), GATE(I2C2, "i2c2", "pc_i2c2", H(22)), GATE(UARTC, "uartc", "pc_uartc", H(23)), GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)), GATE(EMC, "emc", "pc_emc", H(25)), GATE(USB2, "usb2", "clk_m", H(26)), GATE(BSEV, "bsev", "clk_m", H(31)), /* bank U -> 64-95 */ GATE(UARTD, "uartd", "pc_uartd", U(1)), GATE(I2C3, "i2c3", "pc_i2c3", U(3)), GATE(SBC4, "spi4", "pc_spi4", U(4)), GATE(SDMMC3, "sdmmc3", "pc_sdmmc3", U(5)), GATE(PCIE, "pcie", "clk_m", U(6)), GATE(AFI, "afi", "clk_m", U(8)), GATE(CSITE, "csite", "pc_csite", U(9)), GATE(SOC_THERM, "soc_therm", "pc_soc_therm", U(14)), GATE(DTV, "dtv", "clk_m", U(15)), GATE(I2CSLOW, "i2c_slow", "pc_i2c_slow", U(17)), GATE(DSIB, "dsib", "pllD_dsi_csi", U(18)), GATE(TSEC, "tsec", "pc_tsec", U(19)), GATE(IRAMA, "irama", "clk_m", U(20)), GATE(IRAMB, "iramb", "clk_m", U(21)), GATE(IRAMC, "iramc", "clk_m", U(22)), GATE(IRAMD, "iramd", "clk_m", U(23)), GATE(CRAM2, "cram2", "clk_m", U(24)), GATE(XUSB_HOST, "xusb_host", "pc_xusb_core_host", U(25)), GATE(M_DOUBLER, "m_doubler", "clk_m", U(26)), GATE(CSUS, "sus_out", "clk_m", U(28)), GATE(DEVD2_OUT, "devd2_out", "clk_m", U(29)), GATE(DEVD1_OUT, "devd1_out", "clk_m", U(30)), GATE(XUSB_DEV, "xusb_core_dev", "pc_xusb_core_dev", U(31)), /* bank V -> 96-127 */ GATE(CPUG, "cpug", "clk_m", V(0)), GATE(MSELECT, "mselect", "pc_mselect", V(3)), GATE(TSENSOR, "tsensor", "pc_tsensor", V(4)), GATE(I2S4, "i2s5", "pc_i2s5", V(5)), GATE(I2S3, "i2s4", "pc_i2s4", V(6)), GATE(I2C4, "i2c4", "pc_i2c4", V(7)), GATE(D_AUDIO, "ahub", "pc_ahub", V(10)), GATE(APB2APE, "apb2ape", "clk_m", V(11)), GATE(HDA2CODEC_2X, "hda2codec_2x", "pc_hda2codec_2x", V(15)), GATE(ATOMICS, "atomics", "clk_m", V(16)), GATE(SPDIF_2X, "spdif_doubler", "clk_m", V(22)), GATE(ACTMON, "actmon", "pc_actmon", V(23)), GATE(EXTERN1, "extperiph1", "pc_extperiph1", V(24)), GATE(EXTERN2, "extperiph2", "pc_extperiph2", V(25)), GATE(EXTERN3, "extperiph3", "pc_extperiph3", V(26)), GATE(SATA_OOB, "sata_oob", "pc_sata_oob", V(27)), GATE(SATA, "sata", "pc_sata", V(28)), GATE(HDA, "hda", "pc_hda", V(29)), /* bank W -> 128-159*/ GATE(HDA2HDMI, "hda2hdmi", "clk_m", W(0)), /* GATE(SATA_COLD, "sata_cold", "clk_m", W(1)),*/ /* Reset only */ GATE(PCIERX0, "pcierx0", "clk_m", W(2)), GATE(PCIERX1, "pcierx1", "clk_m", W(3)), GATE(PCIERX2, "pcierx2", "clk_m", W(4)), GATE(PCIERX3, "pcierx3", "clk_m", W(5)), GATE(PCIERX4, "pcierx4", "clk_m", W(6)), GATE(PCIERX5, "pcierx5", "clk_m", W(7)), GATE(CEC, "cec", "clk_m", W(8)), GATE(PCIE2_IOBIST, "pcie2_iobist", "clk_m", W(9)), GATE(EMC_IOBIST, "emc_iobist", "clk_m", W(10)), GATE(SATA_IOBIST, "sata_iobist", "clk_m", W(12)), GATE(MIPI_IOBIST, "mipi_iobist", "clk_m", W(13)), GATE(XUSB_GATE, "xusb_gate", "clk_m", W(15)), GATE(CILAB, "cilab", "pc_cilab", W(16)), GATE(CILCD, "cilcd", "pc_cilcd", W(17)), GATE(CILE, "cilef", "pc_cilef", W(18)), GATE(DSIALP, "dsia_lp", "pc_dsia_lp", W(19)), GATE(DSIBLP, "dsib_lp", "pc_dsib_lp", W(20)), GATE(ENTROPY, "entropy", "pc_entropy", W(21)), GATE(DFLL_REF, "dvfs_ref", "pc_dvfs_ref", W(27)), GATE(DFLL_SOC, "dvfs_soc", "pc_dvfs_soc", W(27)), GATE(XUSB_SS, "xusb_ss", "pc_xusb_ss", W(28)), GATE(EMC_LATENCY, "emc_latency", "pc_emc_latency", W(29)), GATE(MC1, "mc1", "clk_m", W(30)), /* bank X -> 160-191*/ /*GATE(SPARE, "spare", "clk_m", X(0)), */ GATE(DMIC1, "dmic1", "clk_m", X(1)), GATE(DMIC2, "dmic2", "clk_m", X(2)), GATE(ETR, "etr", "clk_m", X(3)), GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), GATE(I2C6, "i2c6", "pc_i2c6", X(6)), GATE(MC_CAPA, "mc_capa", "clk_m", X(7)), GATE(MC_CBPA, "mc_cbpa", "clk_m", X(8)), GATE(MC_CPU, "mc_cpu", "clk_m", X(9)), GATE(MC_BBC, "mc_bbc", "clk_m", X(10)), GATE(VIM2_CLK, "vim2_clk", "clk_m", X(11)), GATE(MIPIBIF, "mipibif", "clk_m", X(13)), GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), GATE(UART_FST_MIPI_CAL, "uart_fst_mipi_cal", "clk_m", X(17)), GATE(VIC03, "vic", "pc_vic", X(18)), GATE(DPAUX, "dpaux", "dpaux_div", X(21)), GATE(SOR0, "sor0", "pc_sor0", X(22)), GATE(SOR1, "sor1", "pc_sor1", X(23)), GATE(GPU, "gpu", "osc_div_clk", X(24)), GATE(DBGAPB, "dbgapb", "clk_m", X(25)), GATE(HPLL_ADSP, "hpll_adsp", "clk_m", X(26)), GATE(PLLP_ADSP, "pllp_adsp", "clk_m", X(27)), GATE(PLLA_ADSP, "plla_adsp", "clk_m", X(28)), GATE(PLLG_REF, "pllg_ref", "clk_m", X(29)), /* bank Y -> 192-224*/ /* GATE(SPARE1, "spare1", "clk_m", Y(0)), */ GATE(SDMMC_LEGACY, "sdmmc_legacy_tm", "pc_sdmmc_legacy_tm", Y(1)), GATE(NVDEC, "nvdec", "pc_nvdec", Y(2)), GATE(NVJPG, "nvjpg", "clk_m", Y(3)), GATE(AXIAP, "axiap", "clk_m", Y(4)), GATE(DMIC3, "dmic3", "clk_m", Y(5)), GATE(APE, "ape", "clk_m", Y(6)), GATE(ADSP, "adsp", "clk_m", Y(7)), GATE(MC_CDPA, "mc_cdpa", "clk_m", Y(8)), GATE(MC_CCPA, "mc_ccpa", "clk_m", Y(9)), GATE(MAUD, "mc_maud", "clk_m", Y(10)), GATE(TSECB, "tsecb", "clk_m", Y(14)), GATE(DPAUX1, "dpaux1", "dpaux1_div", Y(15)), GATE(VI_I2C, "vi_i2c", "clk_m", Y(16)), GATE(HSIC_TRK, "hsic_trk", "clk_m", Y(17)), GATE(USB2_TRK, "usb2_trk", "clk_m", Y(18)), GATE(QSPI, "qspi", "clk_m", Y(19)), GATE(UARTAPE, "uarape", "clk_m", Y(20)), GATE(ADSP_NEON, "adspneon", "clk_m", Y(26)), GATE(NVENC, "nvenc", "clk_m", Y(27)), GATE(IQC2, "iqc2", "clk_m", Y(28)), GATE(IQC1, "iqc1", "clk_m", Y(29)), GATE(SOR_SAFE, "sor_safe", "sor_safe_div", Y(30)), GATE(PLL_P_OUT_CPU, "pllp_out_cpu", "clk_m", Y(31)), }; /* Peripheral clock clock */ #define DCF_HAVE_MUX 0x0100 /* Block with multipexor */ #define DCF_HAVE_ENA 0x0200 /* Block with enable bit */ #define DCF_HAVE_DIV 0x0400 /* Block with divider */ /* Mark block with additional bits / functionality. */ #define DCF_IS_MASK 0x00FF #define DCF_IS_UART 0x0001 #define DCF_IS_VI 0x0002 #define DCF_IS_HOST1X 0x0003 #define DCF_IS_XUSB_SS 0x0004 #define DCF_IS_EMC_DLL 0x0005 #define DCF_IS_SATA 0x0006 #define DCF_IS_VIC 0x0007 #define DCF_IS_AHUB 0x0008 #define DCF_IS_SOR0 0x0009 #define DCF_IS_EMC 0x000A #define DCF_IS_QSPI 0x000B #define DCF_IS_EMC_SAFE 0x000C /* Basic pheripheral clock */ #define PER_CLK(_id, cn, pl, r, diw, fiw, f) \ { \ .clkdef.id = _id, \ .clkdef.name = cn, \ .clkdef.parent_names = pl, \ .clkdef.parent_cnt = nitems(pl), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .base_reg = r, \ .div_width = diw, \ .div_f_width = fiw, \ .flags = f, \ } /* Mux with fractional 8.1 divider. */ #define CLK_8_1(id, cn, pl, r, f) \ PER_CLK(id, cn, pl, r, 8, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) /* Mux with integer 8bits divider. */ #define CLK_8_0(id, cn, pl, r, f) \ PER_CLK(id, cn, pl, r, 8, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) /* Mux with fractional 16.1 divider. */ #define CLK16_1(id, cn, pl, r, f) \ PER_CLK(id, cn, pl, r, 16, 1, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) /* Mux with integer 16bits divider. */ #define CLK16_0(id, cn, pl, r, f) \ PER_CLK(id, cn, pl, r, 16, 0, (f) | DCF_HAVE_MUX | DCF_HAVE_DIV) /* Mux wihout divider. */ #define CLK_0_0(id, cn, pl, r, f) \ PER_CLK(id, cn, pl, r, 0, 0, (f) | DCF_HAVE_MUX) static struct periph_def periph_def[] = { CLK_8_1(0, "pc_i2s2", mux_a_N_audio1_N_p_N_clkm, CLK_SOURCE_I2S2, DCF_HAVE_ENA), CLK_8_1(0, "pc_i2s3", mux_a_N_audio2_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA), CLK_8_1(0, "pc_spdif_out", mux_a_N_audio_N_p_N_clkm, CLK_SOURCE_SPDIF_OUT, 0), CLK_8_1(0, "pc_spdif_in", mux_p_c2_c_c4_clkm_c4o1_c4o2, CLK_SOURCE_SPDIF_IN, 0), CLK_8_1(0, "pc_pwm", mux_p_c2_c_c4_clks_c4o1_clkm_c4o2, CLK_SOURCE_PWM, 0), CLK_8_1(0, "pc_spi2", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_SPI2, 0), CLK_8_1(0, "pc_spi3", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_SPI3, 0), CLK16_0(0, "pc_i2c1", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_I2C1, 0), CLK16_0(0, "pc_i2c5", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_I2C5, 0), CLK_8_1(0, "pc_spi1", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_SPI1, 0), CLK_0_0(0, "pc_disp1", mux_p_N_d_N_N_d2_clkm, CLK_SOURCE_DISP1, 0), CLK_0_0(0, "pc_disp2", mux_p_N_d_N_N_d2_clkm, CLK_SOURCE_DISP2, 0), CLK_8_1(0, "pc_isp", mux_N_c_p_a1_c2_c3_clkm_c4, CLK_SOURCE_ISP, 0), CLK_8_1(0, "pc_vi", mux_N_c2_c_c3_p_clkm_a1_c4, CLK_SOURCE_VI, DCF_IS_VI), CLK_8_1(0, "pc_sdmmc1", mux_p_N_N_c4o2_c4o1_N_clkm_c4, CLK_SOURCE_SDMMC1, 0), CLK_8_1(0, "pc_sdmmc2", mux_p_N_N_c4o2_c4o1_N_clkm_c4, CLK_SOURCE_SDMMC2, 0), CLK_8_1(0, "pc_sdmmc4", mux_p_N_N_c4o2_c4o1_N_clkm_c4, CLK_SOURCE_SDMMC4, 0), CLK16_1(0, "pc_uarta", mux_p_c2_c_c4_c4o1_clkm_c4o2, CLK_SOURCE_UARTA, DCF_IS_UART), CLK16_1(0, "pc_uartb", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_UARTB, DCF_IS_UART), CLK_8_1(0, "pc_host1x", mux_c4o1_c2_c_c4_p_clkm_a_c4, CLK_SOURCE_HOST1X, DCF_IS_HOST1X), CLK16_0(0, "pc_i2c2", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_I2C2, 0), CLK_8_1(0, "pc_emc", mux_m_c_p_clkm_mud_mbud_mb_pud, CLK_SOURCE_EMC, DCF_IS_EMC), CLK16_1(0, "pc_uartc", mux_p_c2_c_c4_c4o1_clkm_c4o2, CLK_SOURCE_UARTC, DCF_IS_UART), CLK_8_1(0, "pc_vi_sensor", mux_N_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0), CLK_8_1(0, "pc_spi4", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_SPI4, 0), CLK16_0(0, "pc_i2c3", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_I2C3, 0), CLK_8_1(0, "pc_sdmmc3", mux_p_c2_c_c3_m_e_clkm, CLK_SOURCE_SDMMC3, 0), CLK16_1(0, "pc_uartd", mux_p_c2_c_c4_c4o1_clkm_c4o2, CLK_SOURCE_UARTD, DCF_IS_UART), CLK_8_1(0, "pc_csite", mux_p_c2_refe1_c3_m_a1_clkm_C4, CLK_SOURCE_CSITE, 0), CLK_8_1(0, "pc_i2s1", mux_a_N_audio0_N_p_N_clkm, CLK_SOURCE_I2S1, 0), /* DTV xxx */ CLK_8_1(0, "pc_tsec", mux_p_c2_c_c3_N_a1_clkm_c4, CLK_SOURCE_TSEC, 0), /* SPARE2 */ CLK_8_1(0, "pc_mselect", mux_p_c2_c_c4o2_c4o1_clks_clkm_c4, CLK_SOURCE_MSELECT, 0), CLK_8_1(0, "pc_tsensor", mux_p_c2_c_c4_clkm_c4o1_clks_c4o2, CLK_SOURCE_TSENSOR, 0), CLK_8_1(0, "pc_i2s4", mux_a_N_audio3_N_p_N_clkm, CLK_SOURCE_I2S3, DCF_HAVE_ENA), CLK_8_1(0, "pc_i2s5", mux_a_N_audio4_N_p_N_clkm, CLK_SOURCE_I2S4, DCF_HAVE_ENA), CLK16_0(0, "pc_i2c4", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_I2C4, 0), CLK_8_1(0, "pc_ahub", mux_sep_audio, CLK_SOURCE_AHUB, DCF_IS_AHUB), CLK_8_1(0, "pc_hda2codec_2x", mux_p_c2_c_c4_a_c4o1_clkm_c4o2, CLK_SOURCE_HDA2CODEC_2X, 0), CLK_8_1(0, "pc_actmon", mux_p_c2_c_c4_clks_c4o1_clkm_c4o2, CLK_SOURCE_ACTMON, 0), CLK_8_1(0, "pc_extperiph1", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH1, 0), CLK_8_1(0, "pc_extperiph2", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH2, 0), CLK_8_1(0, "pc_extperiph3", mux_a_clks_p_clkm_e, CLK_SOURCE_EXTPERIPH3, 0), CLK_8_1(0, "pc_i2c_slow", mux_p_c2_c_c4_clks_c4o1_clkm_c4o2, CLK_SOURCE_I2C_SLOW, 0), /* SYS */ CLK_8_1(0, "pc_ispb", mux_N_N_c_N_p_N_a, CLK_SOURCE_ISPB, 0), CLK_8_1(0, "pc_sor1", mux_p_N_d_N_N_d2_clkm, CLK_SOURCE_SOR1, DCF_IS_SOR0), CLK_8_1(0, "pc_sor0", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_SOR0, DCF_IS_SOR0), CLK_8_1(0, "pc_sata_oob", mux_p_c4_c_c4o1_N_c4o2_clkm, CLK_SOURCE_SATA_OOB, 0), CLK_8_1(0, "pc_sata", mux_p_N_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_SATA, DCF_IS_SATA), CLK_8_1(0, "pc_hda", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_HDA, 0), CLK_8_1(TEGRA210_CLK_XUSB_HOST_SRC, "pc_xusb_core_host", mux_clkm_p_N_N_N_refre, CLK_SOURCE_XUSB_CORE_HOST, 0), CLK_8_1(TEGRA210_CLK_XUSB_FALCON_SRC, "pc_xusb_falcon", mux_clkm_p_N_N_N_refre, CLK_SOURCE_XUSB_FALCON, 0), CLK_8_1(TEGRA210_CLK_XUSB_FS_SRC, "pc_xusb_fs", mux_clkm_N_u48_N_p_N_u480, CLK_SOURCE_XUSB_FS, 0), CLK_8_1(TEGRA210_CLK_XUSB_DEV_SRC, "pc_xusb_core_dev", mux_clkm_p_N_N_N_refre, CLK_SOURCE_XUSB_CORE_DEV, 0), CLK_8_1(TEGRA210_CLK_XUSB_SS_SRC, "pc_xusb_ss", mux_clkm_refe_clks_u480, CLK_SOURCE_XUSB_SS, DCF_IS_XUSB_SS), CLK_8_1(0, "pc_cilab", mux_p_N_c_c4_c4o1_c4o2_clkm, CLK_SOURCE_CILAB, 0), CLK_8_1(0, "pc_cilcd", mux_p_N_c_c4_c4o1_c4o2_clkm, CLK_SOURCE_CILCD, 0), CLK_8_1(0, "pc_cilef", mux_p_N_c_c4_c4o1_c4o2_clkm, CLK_SOURCE_CILEF, 0), CLK_8_1(0, "pc_dsia_lp", mux_p_N_c_c4_c4o1_c4o2_clkm, CLK_SOURCE_DSIA_LP, 0), CLK_8_1(0, "pc_dsib_lp", mux_p_N_c_c4_c4o1_c4o2_clkm, CLK_SOURCE_DSIB_LP, 0), CLK_8_1(0, "pc_entropy", mux_p_N_clkm_N_clks_N_E, CLK_SOURCE_ENTROPY, 0), CLK_8_1(0, "pc_dvfs_ref", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_DVFS_REF, DCF_HAVE_ENA), CLK_8_1(0, "pc_dvfs_soc", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_DVFS_SOC, DCF_HAVE_ENA), CLK_8_1(0, "pc_emc_latency", mux_N_c_p_clkm_N_c4_c4o1_c4o2, CLK_SOURCE_EMC_LATENCY, 0), CLK_8_1(0, "pc_soc_therm", mux_N_c_p_clkm_N_c4_c4o1_c4o1, CLK_SOURCE_SOC_THERM, 0), CLK_8_1(0, "pc_dmic1", mux_a_audiod1_p_clkm, CLK_SOURCE_DMIC1, 0), CLK_8_1(0, "pc_dmic2", mux_a_audiod2_p_clkm, CLK_SOURCE_DMIC2, 0), CLK_8_1(0, "pc_vi_sensor2", mux_N_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR2, 0), CLK16_0(0, "pc_i2c6", mux_p_c2_c_c4_N_c4o1_clkm_c4o2, CLK_SOURCE_I2C6, 0), /* MIPIBIF */ CLK_8_1(0, "pc_emc_dll", mux_m_c_p_clkm_mud_mbud_mb_pud, CLK_SOURCE_EMC_DLL, DCF_IS_EMC_DLL), CLK_8_1(0, "pc_uart_fst_mipi_cal", mux_p_c_c2_N_c2_N_clkm, CLK_SOURCE_UART_FST_MIPI_CAL, 0), CLK_8_1(0, "pc_vic", mux_N_c_p_a1_c2_c3_clkm, CLK_SOURCE_VIC, DCF_IS_VIC), CLK_8_1(0, "pc_sdmmc_legacy_tm", mux_po3_c_c2_clkm_p_c4_c4o1_c4o2, CLK_SOURCE_SDMMC_LEGACY_TM, 0), CLK_8_1(0, "pc_nvdec", mux_N_c2_c_c3_p_N_a1_clkm, CLK_SOURCE_NVDEC, 0), CLK_8_1(0, "pc_nvjpg", mux_N_c2_c_c3_p_N_a1_clkm, CLK_SOURCE_NVJPG, 0), CLK_8_1(0, "pc_nvenc", mux_N_c2_c_c3_p_N_a1_clkm, CLK_SOURCE_NVENC, 0), CLK_8_1(0, "pc_dmic3", mux_a_audiod3_p_clkm, CLK_SOURCE_DMIC3, 0), CLK_8_1(0, "pc_ape", mux_a_c4_c_c4o1_p_N_clkm_c4o2, CLK_SOURCE_APE, 0), CLK_8_1(0, "pc_qspi", mux_p_co1_c_N_c4o2_c4o1_clkm_c4, CLK_SOURCE_QSPI, DCF_IS_QSPI), CLK_8_1(0, "pc_vi_i2c", mux_p_c2_c_c3_N_N_clkm, CLK_SOURCE_VI_I2C, 0), /* USB2_HSIC_TRK */ CLK_8_0(0, "pc_maud", mux_p_po3_clkm_clks_a, CLK_SOURCE_MAUD, 0), CLK_8_1(0, "pc_tsecb", mux_p_c2_c_c3_N_a1_clkm_c4, CLK_SOURCE_TSECB, 0), CLK_8_1(0, "pc_uartape", mux_p_c2_c_c3_N_N_clkm, CLK_SOURCE_UARTAPE, 0), CLK_8_1(0, "pc_dbgapb", mux_N_N_p_N_N_N_clkm, CLK_SOURCE_DBGAPB, 0), CLK_8_1(0, "pc_emc_safe", mux_m_c_p_clkm_mud_mbud_mb_pud, CLK_SOURCE_EMC_SAFE, DCF_IS_EMC_SAFE), }; static int periph_init(struct clknode *clk, device_t dev); static int periph_recalc(struct clknode *clk, uint64_t *freq); static int periph_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop); static int periph_set_mux(struct clknode *clk, int idx); struct periph_sc { device_t clkdev; uint32_t base_reg; uint32_t div_shift; uint32_t div_width; uint32_t div_mask; uint32_t div_f_width; uint32_t div_f_mask; uint32_t flags; uint32_t divider; int mux; }; static clknode_method_t periph_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, periph_init), CLKNODEMETHOD(clknode_recalc_freq, periph_recalc), CLKNODEMETHOD(clknode_set_freq, periph_set_freq), CLKNODEMETHOD(clknode_set_mux, periph_set_mux), CLKNODEMETHOD_END }; DEFINE_CLASS_1(tegra210_periph, tegra210_periph_class, periph_methods, sizeof(struct periph_sc), clknode_class); static int periph_init(struct clknode *clk, device_t dev) { struct periph_sc *sc; uint32_t reg; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); if (sc->flags & DCF_HAVE_ENA) MD4(sc, sc->base_reg, PERLCK_ENA_MASK, PERLCK_ENA_MASK); RD4(sc, sc->base_reg, ®); DEVICE_UNLOCK(sc); /* Stnadard mux. */ if (sc->flags & DCF_HAVE_MUX) sc->mux = (reg >> PERLCK_MUX_SHIFT) & PERLCK_MUX_MASK; else sc->mux = 0; if (sc->flags & DCF_HAVE_DIV) sc->divider = (reg & sc->div_mask) + 2; else sc->divider = 1; if ((sc->flags & DCF_IS_MASK) == DCF_IS_UART) { if (!(reg & PERLCK_UDIV_DIS)) sc->divider = 2; } /* AUDIO MUX */ if ((sc->flags & DCF_IS_MASK) == DCF_IS_AHUB) { if (!(reg & PERLCK_AMUX_DIS) && (sc->mux == 7)) { sc->mux = 8 + ((reg >> PERLCK_AMUX_SHIFT) & PERLCK_MUX_MASK); } } clknode_init_parent_idx(clk, sc->mux); return(0); } static int periph_set_mux(struct clknode *clk, int idx) { struct periph_sc *sc; uint32_t reg; sc = clknode_get_softc(clk); if (!(sc->flags & DCF_HAVE_MUX)) return (ENXIO); sc->mux = idx; DEVICE_LOCK(sc); RD4(sc, sc->base_reg, ®); reg &= ~(PERLCK_MUX_MASK << PERLCK_MUX_SHIFT); if ((sc->flags & DCF_IS_MASK) == DCF_IS_AHUB) { reg &= ~PERLCK_AMUX_DIS; reg &= ~(PERLCK_MUX_MASK << PERLCK_AMUX_SHIFT); if (idx <= 7) { reg |= idx << PERLCK_MUX_SHIFT; } else { reg |= 7 << PERLCK_MUX_SHIFT; reg |= (idx - 8) << PERLCK_AMUX_SHIFT; } } else { reg |= idx << PERLCK_MUX_SHIFT; } WR4(sc, sc->base_reg, reg); DEVICE_UNLOCK(sc); return(0); } static int periph_recalc(struct clknode *clk, uint64_t *freq) { struct periph_sc *sc; uint32_t reg; sc = clknode_get_softc(clk); if (sc->flags & DCF_HAVE_DIV) { DEVICE_LOCK(sc); RD4(sc, sc->base_reg, ®); DEVICE_UNLOCK(sc); *freq = (*freq << sc->div_f_width) / sc->divider; } return (0); } static int periph_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop) { struct periph_sc *sc; uint64_t tmp, divider; sc = clknode_get_softc(clk); if (!(sc->flags & DCF_HAVE_DIV)) { *stop = 0; return (0); } tmp = fin << sc->div_f_width; divider = tmp / *fout; if ((tmp % *fout) != 0) divider++; if (divider < (1 << sc->div_f_width)) divider = 1 << (sc->div_f_width - 1); if (flags & CLK_SET_DRYRUN) { if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && (*fout != (tmp / divider))) return (ERANGE); } else { DEVICE_LOCK(sc); MD4(sc, sc->base_reg, sc->div_mask, (divider - (1 << sc->div_f_width))); DEVICE_UNLOCK(sc); sc->divider = divider; } *fout = tmp / divider; *stop = 1; return (0); } static int periph_register(struct clkdom *clkdom, struct periph_def *clkdef) { struct clknode *clk; struct periph_sc *sc; clk = clknode_create(clkdom, &tegra210_periph_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->clkdev = clknode_get_device(clk); sc->base_reg = clkdef->base_reg; sc->div_width = clkdef->div_width; sc->div_mask = (1 <div_width) - 1; sc->div_f_width = clkdef->div_f_width; sc->div_f_mask = (1 <div_f_width) - 1; sc->flags = clkdef->flags; clknode_register(clkdom, clk); return (0); } /* -------------------------------------------------------------------------- */ static int pgate_init(struct clknode *clk, device_t dev); static int pgate_set_gate(struct clknode *clk, bool enable); struct pgate_sc { device_t clkdev; uint32_t idx; uint32_t flags; uint32_t enabled; }; static clknode_method_t pgate_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, pgate_init), CLKNODEMETHOD(clknode_set_gate, pgate_set_gate), CLKNODEMETHOD_END }; DEFINE_CLASS_1(tegra210_pgate, tegra210_pgate_class, pgate_methods, sizeof(struct pgate_sc), clknode_class); static uint32_t get_enable_reg(int idx) { KASSERT(idx / 32 < nitems(clk_enable_reg), ("Invalid clock index for enable: %d", idx)); return (clk_enable_reg[idx / 32]); } static uint32_t get_reset_reg(int idx) { KASSERT(idx / 32 < nitems(clk_reset_reg), ("Invalid clock index for reset: %d", idx)); return (clk_reset_reg[idx / 32]); } static int pgate_init(struct clknode *clk, device_t dev) { struct pgate_sc *sc; uint32_t ena_reg, rst_reg, mask; sc = clknode_get_softc(clk); mask = 1 << (sc->idx % 32); DEVICE_LOCK(sc); RD4(sc, get_enable_reg(sc->idx), &ena_reg); RD4(sc, get_reset_reg(sc->idx), &rst_reg); DEVICE_UNLOCK(sc); sc->enabled = ena_reg & mask ? 1 : 0; clknode_init_parent_idx(clk, 0); return(0); } static int pgate_set_gate(struct clknode *clk, bool enable) { struct pgate_sc *sc; uint32_t reg, mask, base_reg; sc = clknode_get_softc(clk); mask = 1 << (sc->idx % 32); sc->enabled = enable; base_reg = get_enable_reg(sc->idx); DEVICE_LOCK(sc); MD4(sc, base_reg, mask, enable ? mask : 0); RD4(sc, base_reg, ®); DEVICE_UNLOCK(sc); DELAY(2); return(0); } int tegra210_hwreset_by_idx(struct tegra210_car_softc *sc, intptr_t idx, bool reset) { uint32_t reg, mask, reset_reg; CLKDEV_DEVICE_LOCK(sc->dev); if (idx == TEGRA210_RST_DFLL_DVCO) { CLKDEV_MODIFY_4(sc->dev, DFLL_BASE, DFLL_BASE_DVFS_DFLL_RESET, reset ? DFLL_BASE_DVFS_DFLL_RESET : 0); CLKDEV_READ_4(sc->dev, DFLL_BASE, ®); } if (idx == TEGRA210_RST_ADSP) { reset_reg = (reset) ? RST_DEV_Y_SET: RST_DEV_Y_CLR; mask = (0x1F << 22) |(1 << 7); CLKDEV_WRITE_4(sc->dev, reset_reg, mask); CLKDEV_READ_4(sc->dev, reset_reg, ®); } else { mask = 1 << (idx % 32); reset_reg = get_reset_reg(idx); CLKDEV_MODIFY_4(sc->dev, reset_reg, mask, reset ? mask : 0); CLKDEV_READ_4(sc->dev, reset_reg, ®); } CLKDEV_DEVICE_UNLOCK(sc->dev); return(0); } static int pgate_register(struct clkdom *clkdom, struct pgate_def *clkdef) { struct clknode *clk; struct pgate_sc *sc; clk = clknode_create(clkdom, &tegra210_pgate_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->clkdev = clknode_get_device(clk); sc->idx = clkdef->idx; sc->flags = clkdef->flags; clknode_register(clkdom, clk); return (0); } void tegra210_periph_clock(struct tegra210_car_softc *sc) { int i, rv; for (i = 0; i < nitems(periph_def); i++) { rv = periph_register(sc->clkdom, &periph_def[i]); if (rv != 0) panic("tegra210_periph_register failed"); } for (i = 0; i < nitems(pgate_def); i++) { rv = pgate_register(sc->clkdom, &pgate_def[i]); if (rv != 0) panic("tegra210_pgate_register failed"); } } diff --git a/sys/arm64/nvidia/tegra210/tegra210_clk_pll.c b/sys/arm64/nvidia/tegra210/tegra210_clk_pll.c index 88c3916f4eae..624404c3e074 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_clk_pll.c +++ b/sys/arm64/nvidia/tegra210/tegra210_clk_pll.c @@ -1,1494 +1,1494 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * 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 "tegra210_car.h" #if 0 #define dprintf(...) printf(__VA_ARGS__) #else #define dprintf(...) #endif /* All PLLs. */ enum pll_type { PLL_M, PLL_MB, PLL_X, PLL_C, PLL_C2, PLL_C3, PLL_C4, PLL_P, PLL_A, PLL_A1, PLL_U, PLL_D, PLL_D2, PLL_DP, PLL_E, PLL_REFE}; /* Flags for PLLs */ #define PLL_FLAG_PDIV_POWER2 0x01 /* P Divider is 2^n */ #define PLL_FLAG_VCO_OUT 0x02 /* Output VCO directly */ #define PLL_FLAG_HAVE_SDM 0x04 /* Have SDM implemented */ #define PLL_FLAG_HAVE_SDA 0x04 /* Have SDA implemented */ /* Common base register bits. */ #define PLL_BASE_BYPASS (1U << 31) #define PLL_BASE_ENABLE (1 << 30) #define PLL_BASE_REFDISABLE (1 << 29) #define PLL_BASE_LOCK (1 << 27) #define PLLREFE_MISC_LOCK (1 << 27) #define PLL_MISC_LOCK_ENABLE (1 << 18) #define PLLM_LOCK_ENABLE (1 << 4) #define PLLMB_LOCK_ENABLE (1 << 16) #define PLLC_LOCK_ENABLE (1 << 24) #define PLLC4_LOCK_ENABLE (1 << 30) #define PLLA_LOCK_ENABLE (1 << 28) #define PLLD2_LOCK_ENABLE (1 << 30) #define PLLU_LOCK_ENABLE (1 << 29) #define PLLREFE_LOCK_ENABLE (1 << 30) #define PLLPD_LOCK_ENABLE (1 << 30) #define PLLE_LOCK_ENABLE (1 << 9) #define PLLM_IDDQ_BIT 5 #define PLLMB_IDDQ_BIT 17 #define PLLC_IDDQ_BIT 27 #define PLLC4_IDDQ_BIT 18 #define PLLP_IDDQ_BIT 3 #define PLLA_IDDQ_BIT 25 #define PLLA1_IDDQ_BIT 27 #define PLLU_IDDQ_BIT 31 #define PLLD_IDDQ_BIT 20 #define PLLD2_IDDQ_BIT 18 #define PLLX_IDDQ_BIT 3 #define PLLREFE_IDDQ_BIT 24 #define PLLDP_IDDQ_BIT 18 #define PLL_LOCK_TIMEOUT 5000 /* Post divider <-> register value mapping. */ struct pdiv_table { uint32_t divider; /* real divider */ uint32_t value; /* register value */ }; /* Bits definition of M, N and P fields. */ struct mnp_bits { uint32_t m_width; uint32_t n_width; uint32_t p_width; uint32_t m_shift; uint32_t n_shift; uint32_t p_shift; }; struct clk_pll_def { struct clknode_init_def clkdef; enum pll_type type; uint32_t base_reg; uint32_t misc_reg; uint32_t lock_enable; uint32_t iddq_reg; uint32_t iddq_mask; uint32_t flags; struct pdiv_table *pdiv_table; struct mnp_bits mnp_bits; }; #define PLIST(x) static const char *x[] #define PLL(_id, cname, pname) \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS /* multiplexer for pll sources. */ #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) for PLL branch. */ #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, \ } /* P divider (2^n). for PLL branch. */ #define DIV5_E(_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, \ .i_width = 5, \ } /* P divider (2^n). for PLL branch. */ #define DIV_TB(_id, cname, plist, o, s, n, table) \ { \ .clkdef.id = _id, \ .clkdef.name = cname, \ .clkdef.parent_names = (const char *[]){plist}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .div_flags = CLK_DIV_WITH_TABLE | CLK_DIV_ZERO_BASED, \ .offset = o, \ .i_shift = s, \ .i_width = n, \ .div_table = table, \ } /* 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, \ } /* Gate for PLL branch. */ #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, \ } /* 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 struct pdiv_table qlin_map[] = { { 1, 0}, { 2, 1}, { 3, 2}, { 4, 3}, { 5, 4}, { 6, 5}, { 8, 6}, { 9, 7}, {10, 8}, {12, 9}, {15, 10}, {16, 11}, {18, 12}, {20, 13}, {24, 14}, {30, 15}, {32, 16}, { 0, 0}, }; static struct clk_pll_def pll_clks[] = { /* PLLM: 880 MHz Clock source for EMC 2x clock */ { PLL(TEGRA210_CLK_PLL_M, "pllM_out0", "osc"), .type = PLL_M, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC2, .lock_enable = PLLM_LOCK_ENABLE, .iddq_reg = PLLM_MISC2, .iddq_mask = 1 << PLLM_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 20}, }, /* PLLMB: 880 MHz Clock source for EMC 2x clock */ { PLL(TEGRA210_CLK_PLL_M, "pllMB_out0", "osc"), .type = PLL_MB, .base_reg = PLLMB_BASE, .misc_reg = PLLMB_MISC1, .lock_enable = PLLMB_LOCK_ENABLE, .iddq_reg = PLLMB_MISC1, .iddq_mask = 1 << PLLMB_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 20}, }, /* PLLX: 1GHz Clock source for the fast CPU cluster and the shadow CPU */ { PLL(TEGRA210_CLK_PLL_X, "pllX_out0", "osc_div_clk"), .type = PLL_X, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, .lock_enable = PLL_MISC_LOCK_ENABLE, .iddq_reg = PLLX_MISC_3, .iddq_mask = 1 << PLLX_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 20}, }, /* PLLC: 510 MHz Clock source for camera use */ { PLL(TEGRA210_CLK_PLL_C, "pllC_out0", "osc_div_clk"), .type = PLL_C, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC_0, .iddq_reg = PLLC_MISC_1, .iddq_mask = 1 << PLLC_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 10, 20}, }, /* PLLC2: 510 MHz Clock source for SE, VIC, TSECB, NVJPG scaling */ { PLL(TEGRA210_CLK_PLL_C2, "pllC2_out0", "osc_div_clk"), .type = PLL_C2, .base_reg = PLLC2_BASE, .misc_reg = PLLC2_MISC_0, .iddq_reg = PLLC2_MISC_1, .iddq_mask = 1 << PLLC_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 10, 20}, }, /* PLLC3: 510 MHz Clock source for NVENC, NVDEC scaling */ { PLL(TEGRA210_CLK_PLL_C3, "pllC3_out0", "osc_div_clk"), .type = PLL_C3, .base_reg = PLLC3_BASE, .misc_reg = PLLC3_MISC_0, .lock_enable = PLL_MISC_LOCK_ENABLE, .iddq_reg = PLLC3_MISC_1, .iddq_mask = 1 << PLLC_IDDQ_BIT, .mnp_bits = {8, 8, 5, 0, 10, 20}, }, /* PLLC4: 600 MHz Clock source for SD/eMMC ans system busses */ { PLL(TEGRA210_CLK_PLL_C4, "pllC4", "pllC4_src"), .type = PLL_C4, .flags = PLL_FLAG_VCO_OUT, .base_reg = PLLC4_BASE, .misc_reg = PLLC4_MISC, .lock_enable = PLLC4_LOCK_ENABLE, .iddq_reg = PLLC4_BASE, .iddq_mask = 1 << PLLC4_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 19}, }, /* PLLP: 408 MHz Clock source for most peripherals */ { /* * VCO is directly exposed as pllP_out0, P div is used for * pllP_out2 */ PLL(TEGRA210_CLK_PLL_P, "pllP_out0", "osc_div_clk"), .type = PLL_P, .flags = PLL_FLAG_VCO_OUT, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, .lock_enable = PLL_MISC_LOCK_ENABLE, .iddq_reg = PLLP_MISC, .iddq_mask = 1 << PLLA_IDDQ_BIT, .mnp_bits = {8, 8, 5, 0, 10, 20}, }, /* PLLA: Audio clock for precise codec sampling */ { PLL(TEGRA210_CLK_PLL_A, "pllA", "osc_div_clk"), .type = PLL_A, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, .lock_enable = PLLA_LOCK_ENABLE, .iddq_reg = PLLA_BASE, .iddq_mask = 1 << PLLA_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 20}, }, /* PLLA1: Audio clock for ADSP */ { PLL(TEGRA210_CLK_PLL_A1, "pllA1_out0", "osc_div_clk"), .type = PLL_A1, .base_reg = PLLA1_BASE, .misc_reg = PLLA1_MISC_1, .iddq_reg = PLLA1_MISC_1, .iddq_mask = 1 << PLLA_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 20}, }, /* PLLU: 480 MHz Clock source for USB PHY, provides 12/60/480 MHz */ { PLL(TEGRA210_CLK_PLL_U, "pllU", "osc_div_clk"), .type = PLL_U, .flags = PLL_FLAG_VCO_OUT | PLL_FLAG_HAVE_SDA, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, .lock_enable = PLLU_LOCK_ENABLE, .iddq_reg = PLLU_MISC, .iddq_mask = 1 << PLLU_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 16}, }, /* PLLD: 594 MHz Clock sources for the DSI and display subsystem */ { PLL(TEGRA210_CLK_PLL_D, "pllD_out", "osc_div_clk"), .type = PLL_D, .flags = PLL_FLAG_PDIV_POWER2, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, .lock_enable = PLL_MISC_LOCK_ENABLE, .iddq_reg = PLLA1_MISC_1, .iddq_mask = 1 << PLLA_IDDQ_BIT, .mnp_bits = {8, 8, 3, 0, 11, 20}, }, /* PLLD2: 594 MHz Clock sources for the DSI and display subsystem */ { PLL(TEGRA210_CLK_PLL_D2, "pllD2_out", "pllD2_src"), .type = PLL_D2, .flags = PLL_FLAG_HAVE_SDM, .base_reg = PLLD2_BASE, .misc_reg = PLLD2_MISC, .lock_enable = PLLD2_LOCK_ENABLE, .iddq_reg = PLLD2_BASE, .iddq_mask = 1 << PLLD_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 19}, }, /* PLLREFE: 624 Mhz*/ { PLL(0, "pllREFE", "osc_div_clk"), .type = PLL_REFE, .flags = PLL_FLAG_VCO_OUT, .base_reg = PLLREFE_BASE, .misc_reg = PLLREFE_MISC, .lock_enable = PLLREFE_LOCK_ENABLE, .iddq_reg = PLLREFE_MISC, .iddq_mask = 1 << PLLREFE_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 16}, }, /* PLLE: 100 MHz reference clock for PCIe/SATA/USB 3.0 (spread spectrum) */ { PLL(TEGRA210_CLK_PLL_E, "pllE_out0", "pllE_src"), .type = PLL_E, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, .lock_enable = PLLE_LOCK_ENABLE, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 24}, }, /* PLLDP: 270 MHz Clock source fordisplay SOR (spread spectrum) */ { PLL(0, "pllDP_out0", "pllDP_src"), .type = PLL_DP, .flags = PLL_FLAG_HAVE_SDM, .base_reg = PLLDP_BASE, .misc_reg = PLLDP_MISC, .lock_enable = PLLPD_LOCK_ENABLE, .iddq_reg = PLLDP_BASE, .iddq_mask = 1 << PLLDP_IDDQ_BIT, .pdiv_table = qlin_map, .mnp_bits = {8, 8, 5, 0, 8, 19}, }, }; /* Fixed rate dividers. */ static struct clk_fixed_def tegra210_pll_fdivs[] = { FACT(0, "pllP_UD", "pllP_out0", 1, 1), FACT(0, "pllC_UD", "pllC_out0", 1, 1), FACT(0, "pllD_UD", "pllD_out0", 1, 1), FACT(0, "pllM_UD", "pllM_out0", 1, 1), FACT(0, "pllMB_UD", "pllMB_out0", 1, 1), FACT(TEGRA210_CLK_PLL_D_OUT0, "pllD_out0", "pllD_out", 1, 2), FACT(0, "pllC4_out1", "pllC4", 1, 3), FACT(0, "pllC4_out2", "pllC4", 1, 5), FACT(0, "pllD2_out0", "pllD2_out", 1, 2), /* Aliases used in super mux. */ FACT(0, "pllX_out0_alias", "pllX_out0", 1, 1), FACT(0, "dfllCPU_out_alias", "dfllCPU_out", 1, 1), }; /* MUXes for PLL sources. */ 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_out0"}; static struct clk_mux_def tegra210_pll_sources[] = { /* 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), }; /* Gates for PLL branches. */ static struct clk_gate_def tegra210_pll_gates[] = { /* Core clocks. */ GATE_PLL(0, "pllC_out1", "pllC_out1_div", PLLC_OUT, 0), GATE_PLL(0, "pllP_out1", "pllP_out1_div", PLLP_OUTA, 0), GATE_PLL(0, "pllP_out3", "pllP_out3_div", PLLP_OUTB, 0), GATE_PLL(TEGRA210_CLK_PLL_P_OUT4, "pllP_out4", "pllP_out4_div", PLLP_OUTB, 16), GATE_PLL(0, "pllP_out5", "pllP_out5_div", PLLP_OUTC, 16), GATE_PLL(0, "pllU_out1", "pllU_out1_div", PLLU_OUTA, 0), GATE_PLL(0, "pllU_out2", "pllU_out2_div", PLLU_OUTA, 16), GATE(0, "pllU_480", "pllU", PLLU_BASE, 22), GATE(0, "pllU_60", "pllU_out2", PLLU_BASE, 23), GATE(0, "pllU_48", "pllU_out1", PLLU_BASE, 25), GATE_PLL(0, "pllREFE_out1", "pllREFE_out1_div", PLLREFE_OUT, 0), GATE_PLL(0, "pllC4_out3", "pllC4_out3_div", PLLC4_OUT, 0), GATE_PLL(0, "pllA_out0", "pllA_out0_div", PLLA_OUT, 0), }; struct clk_div_table tegra210_pll_pdiv_tbl[] = { /* value , divider */ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 }, { 5, 6 }, { 6, 8 }, { 7, 10 }, { 8, 12 }, { 9, 16 }, {10, 12 }, {11, 16 }, {12, 20 }, {13, 24 }, {14, 32 }, { 0, 0 }, }; /* Dividers for PLL branches. */ static struct clk_div_def tegra210_pll_divs[] = { /* Core clocks. */ DIV7_1(0, "pllC_out1_div", "pllC_out0", PLLC_OUT, 8), DIV7_1(0, "pllP_out1_div", "pllP_out0", PLLP_OUTA, 8), DIV_TB(0, "pllP_out2", "pllP_out0", PLLP_BASE, 20, 5, tegra210_pll_pdiv_tbl), 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), DIV_TB(0, "pllU_out0", "pllU", PLLU_BASE, 16, 5, tegra210_pll_pdiv_tbl), DIV7_1(0, "pllU_out1_div", "pllU_out0", PLLU_OUTA, 8), DIV7_1(0, "pllU_out2_div", "pllU_out0", PLLU_OUTA, 24), DIV_TB(0, "pllREFE_out0", "pllREFE", PLLREFE_BASE, 16, 5, tegra210_pll_pdiv_tbl), DIV7_1(0, "pllREFE_out1_div", "pllREFE", PLLREFE_OUT, 8), DIV_TB(TEGRA210_CLK_PLL_C4_OUT0, "pllC4_out0", "pllC4", PLLC4_BASE, 19, 5, tegra210_pll_pdiv_tbl), DIV7_1(0, "pllC4_out3_div", "pllC4_out0", PLLC4_OUT, 8), DIV7_1(0, "pllA_out0_div", "pllA", PLLA_OUT, 8), }; static int tegra210_pll_init(struct clknode *clk, device_t dev); static int tegra210_pll_set_gate(struct clknode *clk, bool enable); static int tegra210_pll_recalc(struct clknode *clk, uint64_t *freq); static int tegra210_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout, int flags, int *stop); struct pll_sc { device_t clkdev; enum pll_type type; uint32_t base_reg; uint32_t misc_reg; uint32_t lock_enable; uint32_t iddq_reg; uint32_t iddq_mask; uint32_t flags; struct pdiv_table *pdiv_table; struct mnp_bits mnp_bits; }; static clknode_method_t tegra210_pll_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, tegra210_pll_init), CLKNODEMETHOD(clknode_set_gate, tegra210_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, tegra210_pll_recalc), CLKNODEMETHOD(clknode_set_freq, tegra210_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(tegra210_pll, tegra210_pll_class, tegra210_pll_methods, sizeof(struct pll_sc), clknode_class); static int pll_enable(struct pll_sc *sc) { uint32_t reg; RD4(sc, sc->base_reg, ®); if (sc->type != PLL_E) reg &= ~PLL_BASE_BYPASS; reg |= PLL_BASE_ENABLE; WR4(sc, sc->base_reg, reg); return (0); } static int pll_disable(struct pll_sc *sc) { uint32_t reg; RD4(sc, sc->base_reg, ®); if (sc->type != PLL_E) reg |= PLL_BASE_BYPASS; reg &= ~PLL_BASE_ENABLE; WR4(sc, sc->base_reg, reg); return (0); } static uint32_t pdiv_to_reg(struct pll_sc *sc, uint32_t p_div) { struct pdiv_table *tbl; tbl = sc->pdiv_table; if (tbl == NULL) { if (sc->flags & PLL_FLAG_PDIV_POWER2) return (ffs(p_div) - 1); else return (p_div); } while (tbl->divider != 0) { if (p_div <= tbl->divider) return (tbl->value); tbl++; } return (0xFFFFFFFF); } static uint32_t reg_to_pdiv(struct pll_sc *sc, uint32_t reg) { struct pdiv_table *tbl; tbl = sc->pdiv_table; if (tbl == NULL) { if (sc->flags & PLL_FLAG_PDIV_POWER2) return (1 << reg); else return (reg == 0 ? 1: reg); } while (tbl->divider) { if (reg == tbl->value) return (tbl->divider); tbl++; } return (0); } static uint32_t get_masked(uint32_t val, uint32_t shift, uint32_t width) { return ((val >> shift) & ((1 << width) - 1)); } static uint32_t set_masked(uint32_t val, uint32_t v, uint32_t shift, uint32_t width) { val &= ~(((1 << width) - 1) << shift); val |= (v & ((1 << width) - 1)) << shift; return (val); } static void get_divisors(struct pll_sc *sc, uint32_t *m, uint32_t *n, uint32_t *p) { uint32_t val; struct mnp_bits *mnp_bits; mnp_bits = &sc->mnp_bits; RD4(sc, sc->base_reg, &val); *m = get_masked(val, mnp_bits->m_shift, mnp_bits->m_width); *n = get_masked(val, mnp_bits->n_shift, mnp_bits->n_width); *p = get_masked(val, mnp_bits->p_shift, mnp_bits->p_width); } static uint32_t set_divisors(struct pll_sc *sc, uint32_t val, uint32_t m, uint32_t n, uint32_t p) { struct mnp_bits *mnp_bits; mnp_bits = &sc->mnp_bits; val = set_masked(val, m, mnp_bits->m_shift, mnp_bits->m_width); val = set_masked(val, n, mnp_bits->n_shift, mnp_bits->n_width); val = set_masked(val, p, mnp_bits->p_shift, mnp_bits->p_width); return (val); } static bool is_locked(struct pll_sc *sc) { uint32_t reg; switch (sc->type) { case PLL_REFE: RD4(sc, sc->misc_reg, ®); reg &= PLLREFE_MISC_LOCK; break; case PLL_E: RD4(sc, sc->misc_reg, ®); reg &= PLLE_MISC_LOCK; break; default: RD4(sc, sc->base_reg, ®); reg &= PLL_BASE_LOCK; break; } return (reg != 0); } static int wait_for_lock(struct pll_sc *sc) { int i; for (i = PLL_LOCK_TIMEOUT / 10; i > 0; i--) { if (is_locked(sc)) break; DELAY(10); } if (i <= 0) { printf("PLL lock timeout\n"); return (ETIMEDOUT); } return (0); } static int plle_enable(struct pll_sc *sc) { uint32_t reg; int rv; struct mnp_bits *mnp_bits; uint32_t pll_m = 2; uint32_t pll_n = 125; uint32_t pll_cml = 14; mnp_bits = &sc->mnp_bits; /* Disable lock override. */ RD4(sc, sc->base_reg, ®); reg &= ~PLLE_BASE_LOCK_OVERRIDE; WR4(sc, sc->base_reg, reg); /* Enable SW control */ RD4(sc, PLLE_AUX, ®); reg |= PLLE_AUX_ENABLE_SWCTL; reg &= ~PLLE_AUX_SEQ_ENABLE; WR4(sc, PLLE_AUX, reg); DELAY(10); RD4(sc, sc->misc_reg, ®); reg |= PLLE_MISC_LOCK_ENABLE; reg |= PLLE_MISC_IDDQ_SWCTL; reg &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE; reg |= PLLE_MISC_PTS; reg &= ~PLLE_MISC_VREG_BG_CTRL(~0); reg &= ~PLLE_MISC_VREG_CTRL(~0); WR4(sc, sc->misc_reg, reg); DELAY(10); RD4(sc, PLLE_SS_CNTL, ®); reg |= PLLE_SS_CNTL_DISABLE; WR4(sc, PLLE_SS_CNTL, reg); RD4(sc, sc->base_reg, ®); reg = set_divisors(sc, reg, pll_m, pll_n, pll_cml); WR4(sc, sc->base_reg, reg); DELAY(10); pll_enable(sc); rv = wait_for_lock(sc); if (rv != 0) return (rv); RD4(sc, PLLE_SS_CNTL, ®); reg &= ~PLLE_SS_CNTL_SSCINCINTRV(~0); reg &= ~PLLE_SS_CNTL_SSCINC(~0); reg &= ~PLLE_SS_CNTL_SSCINVERT; reg &= ~PLLE_SS_CNTL_SSCCENTER; reg &= ~PLLE_SS_CNTL_SSCMAX(~0); reg |= PLLE_SS_CNTL_SSCINCINTRV(0x23); reg |= PLLE_SS_CNTL_SSCINC(0x1); reg |= PLLE_SS_CNTL_SSCMAX(0x21); WR4(sc, PLLE_SS_CNTL, reg); reg &= ~PLLE_SS_CNTL_SSCBYP; reg &= ~PLLE_SS_CNTL_BYPASS_SS; WR4(sc, PLLE_SS_CNTL, reg); DELAY(10); reg &= ~PLLE_SS_CNTL_INTERP_RESET; WR4(sc, PLLE_SS_CNTL, reg); DELAY(10); /* HW control of brick pll. */ RD4(sc, sc->misc_reg, ®); reg &= ~PLLE_MISC_IDDQ_SWCTL; WR4(sc, sc->misc_reg, reg); RD4(sc, PLLE_AUX, ®); reg |= PLLE_AUX_USE_LOCKDET; reg |= PLLE_AUX_SS_SEQ_INCLUDE; reg &= ~PLLE_AUX_ENABLE_SWCTL; reg &= ~PLLE_AUX_SS_SWCTL; WR4(sc, PLLE_AUX, reg); reg |= PLLE_AUX_SEQ_START_STATE; DELAY(10); reg |= PLLE_AUX_SEQ_ENABLE; WR4(sc, PLLE_AUX, reg); /* Enable and start XUSBIO PLL HW control*/ RD4(sc, XUSBIO_PLL_CFG0, ®); reg &= ~XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL; reg &= ~XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL; reg |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET; reg |= XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ; reg &= ~XUSBIO_PLL_CFG0_SEQ_ENABLE; WR4(sc, XUSBIO_PLL_CFG0, reg); DELAY(10); reg |= XUSBIO_PLL_CFG0_SEQ_ENABLE; WR4(sc, XUSBIO_PLL_CFG0, reg); /* Enable and start SATA PLL HW control */ RD4(sc, SATA_PLL_CFG0, ®); reg &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL; reg &= ~SATA_PLL_CFG0_PADPLL_RESET_OVERRIDE_VALUE; reg |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET; reg |= SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ; reg &= ~SATA_PLL_CFG0_SEQ_IN_SWCTL; reg &= ~SATA_PLL_CFG0_SEQ_RESET_INPUT_VALUE; reg &= ~SATA_PLL_CFG0_SEQ_LANE_PD_INPUT_VALUE; reg &= ~SATA_PLL_CFG0_SEQ_PADPLL_PD_INPUT_VALUE; reg &= ~SATA_PLL_CFG0_SEQ_ENABLE; WR4(sc, SATA_PLL_CFG0, reg); DELAY(10); reg |= SATA_PLL_CFG0_SEQ_ENABLE; WR4(sc, SATA_PLL_CFG0, reg); /* Enable HW control of PCIe PLL. */ RD4(sc, PCIE_PLL_CFG, ®); reg |= PCIE_PLL_CFG_SEQ_ENABLE; WR4(sc, PCIE_PLL_CFG, reg); return (0); } static int tegra210_pll_set_gate(struct clknode *clknode, bool enable) { int rv; struct pll_sc *sc; sc = clknode_get_softc(clknode); if (enable == 0) { rv = pll_disable(sc); return(rv); } if (sc->type == PLL_E) rv = plle_enable(sc); else rv = pll_enable(sc); return (rv); } static int pll_set_std(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags, uint32_t m, uint32_t n, uint32_t p) { uint32_t reg; struct mnp_bits *mnp_bits; int rv; mnp_bits = &sc->mnp_bits; if (m >= (1 << mnp_bits->m_width)) return (ERANGE); if (n >= (1 << mnp_bits->n_width)) return (ERANGE); if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width)) return (ERANGE); if (flags & CLK_SET_DRYRUN) { if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && (*fout != (((fin / m) * n) /p))) return (ERANGE); *fout = ((fin / m) * n) /p; return (0); } pll_disable(sc); /* take pll out of IDDQ */ if (sc->iddq_reg != 0) MD4(sc, sc->iddq_reg, sc->iddq_mask, 0); RD4(sc, sc->base_reg, ®); reg = set_masked(reg, m, mnp_bits->m_shift, mnp_bits->m_width); reg = set_masked(reg, n, mnp_bits->n_shift, mnp_bits->n_width); reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, mnp_bits->p_width); WR4(sc, sc->base_reg, reg); /* Enable PLL. */ RD4(sc, sc->base_reg, ®); reg |= PLL_BASE_ENABLE; WR4(sc, sc->base_reg, reg); /* Enable lock detection. */ RD4(sc, sc->misc_reg, ®); reg |= sc->lock_enable; WR4(sc, sc->misc_reg, reg); rv = wait_for_lock(sc); if (rv != 0) { /* Disable PLL */ RD4(sc, sc->base_reg, ®); reg &= ~PLL_BASE_ENABLE; WR4(sc, sc->base_reg, reg); return (rv); } RD4(sc, sc->misc_reg, ®); pll_enable(sc); *fout = ((fin / m) * n) / p; return 0; } static int plla_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t m, n, p; p = 1; m = 3; n = (*fout * p * m + fin / 2)/ fin; dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); return (pll_set_std(sc, fin, fout, flags, m, n, p)); } static int pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t m, n, p; p = 2; m = 3; n = (*fout * p * m + fin / 2)/ fin; dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); return (pll_set_std( sc, fin, fout, flags, m, n, p)); } static int pllc4_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t m, n, p; p = 1; m = 4; n = (*fout * p * m + fin / 2)/ fin; dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); return (pll_set_std( sc, fin, fout, flags, m, n, p)); } static int plldp_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t m, n, p; p = 1; m = 4; n = (*fout * p * m + fin / 2)/ fin; dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); return (pll_set_std( sc, fin, fout, flags, m, n, p)); } /* * PLLD2 is used as source for pixel clock for HDMI. * We must be able to set it frequency very flexibly and * precisely (within 5% tolerance limit allowed by HDMI specs). * * For this reason, it is necessary to search the full state space. * Fortunately, thanks to early cycle terminations, performance * is within acceptable limits. */ #define PLLD2_PFD_MIN 12000000 /* 12 MHz */ #define PLLD2_PFD_MAX 38400000 /* 38.4 MHz */ #define PLLD2_VCO_MIN 750000000 /* 750 MHz */ #define PLLD2_VCO_MAX 1500000000 /* 1.5 GHz */ static int plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t m, n, p; uint32_t best_m, best_n, best_p; uint64_t vco, pfd; int64_t err, best_err; struct mnp_bits *mnp_bits; struct pdiv_table *tbl; int p_idx, rv; mnp_bits = &sc->mnp_bits; tbl = sc->pdiv_table; best_err = INT64_MAX; for (p_idx = 0; tbl[p_idx].divider != 0; p_idx++) { p = tbl[p_idx].divider; /* Check constraints */ vco = *fout * p; if (vco < PLLD2_VCO_MIN) continue; if (vco > PLLD2_VCO_MAX) break; for (m = 1; m < (1 << mnp_bits->m_width); m++) { n = (*fout * p * m + fin / 2) / fin; /* Check constraints */ if (n == 0) continue; if (n >= (1 << mnp_bits->n_width)) break; vco = (fin * n) / m; if (vco > PLLD2_VCO_MAX || vco < PLLD2_VCO_MIN) continue; pfd = fin / m; if (pfd > PLLD2_PFD_MAX || vco < PLLD2_PFD_MIN) continue; /* Constraints passed, save best result */ err = *fout - vco / p; if (err < 0) err = -err; if (err < best_err) { best_err = err; best_p = p; best_m = m; best_n = n; } if (err == 0) goto done; } } done: /* * HDMI specification allows 5% pixel clock tolerance, * we will by a slightly stricter */ if (best_err > ((*fout * 100) / 4)) return (ERANGE); if (flags & CLK_SET_DRYRUN) return (0); rv = pll_set_std(sc, fin, fout, flags, best_m, best_n, best_p); /* XXXX Panic for rv == ERANGE ? */ return (rv); } static int pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { uint32_t m, n, p; m = 1; p = 1; n = *fout * p * m / fin; dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); return (pll_set_std(sc, fin, fout, flags, m, n, p)); } #define PLLX_PFD_MIN 12000000LL /* 12 MHz */ #define PLLX_PFD_MAX 38400000LL /* 38.4 MHz */ #define PLLX_VCO_MIN 900000000LL /* 0.9 GHz */ #define PLLX_VCO_MAX 3000000000LL /* 3 GHz */ static int pllx_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags) { struct mnp_bits *mnp_bits; uint32_t m, n, p; uint32_t old_m, old_n, old_p; uint32_t reg; int i, rv; mnp_bits = &sc->mnp_bits; get_divisors(sc, &old_m, &old_n, &old_p); old_p = reg_to_pdiv(sc, old_p); /* Pre-divider is fixed, Compute post-divider */ m = old_m; p = 1; while ((*fout * p) < PLLX_VCO_MIN) p++; if ((*fout * p) > PLLX_VCO_MAX) return (ERANGE); n = (*fout * p * m + fin / 2) / fin; dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p); if (m >= (1 << mnp_bits->m_width)) return (ERANGE); if (n >= (1 << mnp_bits->n_width)) return (ERANGE); if (pdiv_to_reg(sc, p) >= (1 << mnp_bits->p_width)) return (ERANGE); if (flags & CLK_SET_DRYRUN) { if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && (*fout != (((fin / m) * n) /p))) return (ERANGE); *fout = ((fin / m) * n) /p; return (0); } /* If new post-divider is bigger that original, set it now. */ if (p < old_p) { RD4(sc, sc->base_reg, ®); reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, mnp_bits->p_width); WR4(sc, sc->base_reg, reg); } DELAY(100); /* vvv Program dynamic VCO ramp. vvv */ /* 1 - disable dynamic ramp mode. */ RD4(sc, PLLX_MISC_2, ®); reg &= ~PLLX_MISC_2_EN_DYNRAMP; WR4(sc, PLLX_MISC_2, reg); /* 2 - Setup new ndiv. */ RD4(sc, PLLX_MISC_2, ®); reg &= ~PLLX_MISC_2_NDIV_NEW(~0); reg |= PLLX_MISC_2_NDIV_NEW(n); WR4(sc, PLLX_MISC_2, reg); /* 3 - enable dynamic ramp. */ RD4(sc, PLLX_MISC_2, ®); reg |= PLLX_MISC_2_EN_DYNRAMP; WR4(sc, PLLX_MISC_2, reg); /* 4 - wait for done. */ for (i = PLL_LOCK_TIMEOUT / 10; i > 0; i--) { RD4(sc, PLLX_MISC_2, ®); if (reg & PLLX_MISC_2_DYNRAMP_DONE) break; DELAY(10); } if (i <= 0) { printf("PLL X dynamic ramp timedout\n"); return (ETIMEDOUT); } /* 5 - copy new ndiv to base register. */ RD4(sc, sc->base_reg, ®); reg = set_masked(reg, n, mnp_bits->n_shift, mnp_bits->n_width); WR4(sc, sc->base_reg, reg); /* 6 - disable dynamic ramp mode. */ RD4(sc, PLLX_MISC_2, ®); reg &= ~PLLX_MISC_2_EN_DYNRAMP; WR4(sc, PLLX_MISC_2, reg); rv = wait_for_lock(sc); if (rv != 0) { printf("PLL X is not locked !!\n"); } /* ^^^ Dynamic ramp done. ^^^ */ /* If new post-divider is smaller that original, set it. */ if (p > old_p) { RD4(sc, sc->base_reg, ®); reg = set_masked(reg, pdiv_to_reg(sc, p), mnp_bits->p_shift, mnp_bits->p_width); WR4(sc, sc->base_reg, reg); } *fout = ((fin / m) * n) / p; return (0); } /* Simplified setup for 38.4 MHz clock. */ #define PLLX_STEP_A 0x04 #define PLLX_STEP_B 0x05 static int pllx_init(struct pll_sc *sc) { uint32_t reg; RD4(sc, PLLX_MISC, ®); reg = PLLX_MISC_LOCK_ENABLE; WR4(sc, PLLX_MISC, reg); /* Setup dynamic ramp. */ reg = 0; reg |= PLLX_MISC_2_DYNRAMP_STEPA(PLLX_STEP_A); reg |= PLLX_MISC_2_DYNRAMP_STEPB(PLLX_STEP_B); WR4(sc, PLLX_MISC_2, reg); /* Disable SDM. */ reg = 0; WR4(sc, PLLX_MISC_4, reg); WR4(sc, PLLX_MISC_5, reg); return (0); } static int tegra210_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout, int flags, int *stop) { *stop = 1; int rv; struct pll_sc *sc; sc = clknode_get_softc(clknode); dprintf("%s: %s requested freq: %lu, input freq: %lu\n", __func__, clknode_get_name(clknode), *fout, fin); switch (sc->type) { case PLL_A: rv = plla_set_freq(sc, fin, fout, flags); break; case PLL_C: case PLL_C2: case PLL_C3: rv = pllc_set_freq(sc, fin, fout, flags); break; case PLL_C4: rv = pllc4_set_freq(sc, fin, fout, flags); break; case PLL_D2: rv = plld2_set_freq(sc, fin, fout, flags); break; case PLL_DP: rv = plldp_set_freq(sc, fin, fout, flags); break; case PLL_REFE: rv = pllrefe_set_freq(sc, fin, fout, flags); break; case PLL_X: rv = pllx_set_freq(sc, fin, fout, flags); break; case PLL_U: if (*fout == 480000000) /* PLLU is fixed to 480 MHz */ rv = 0; else rv = ERANGE; break; default: rv = ENXIO; break; } return (rv); } static int tegra210_pll_init(struct clknode *clk, device_t dev) { struct pll_sc *sc; uint32_t reg, rv; sc = clknode_get_softc(clk); if (sc->type == PLL_X) { rv = pllx_init(sc); if (rv != 0) return (rv); } /* If PLL is enabled, enable lock detect too. */ RD4(sc, sc->base_reg, ®); if (reg & PLL_BASE_ENABLE) { RD4(sc, sc->misc_reg, ®); reg |= sc->lock_enable; WR4(sc, sc->misc_reg, reg); } if (sc->type == PLL_REFE) { RD4(sc, sc->misc_reg, ®); reg &= ~(1 << 29); /* Disable lock override */ WR4(sc, sc->misc_reg, reg); } clknode_init_parent_idx(clk, 0); return(0); } static int tegra210_pll_recalc(struct clknode *clk, uint64_t *freq) { struct pll_sc *sc; uint32_t m, n, p, pr; uint32_t reg, misc_reg; int locked; sc = clknode_get_softc(clk); RD4(sc, sc->base_reg, ®); RD4(sc, sc->misc_reg, &misc_reg); get_divisors(sc, &m, &n, &pr); /* If VCO is directlu exposed, P divider is handled by external node */ if (sc->flags & PLL_FLAG_VCO_OUT) p = 1; else p = reg_to_pdiv(sc, pr); locked = is_locked(sc); dprintf("%s: %s (0x%08x, 0x%08x) - m: %d, n: %d, p: %d (%d): " "e: %d, r: %d, o: %d - %s\n", __func__, clknode_get_name(clk), reg, misc_reg, m, n, p, pr, (reg >> 30) & 1, (reg >> 29) & 1, (reg >> 28) & 1, locked ? "locked" : "unlocked"); if ((m == 0) || (n == 0) || (p == 0)) { *freq = 0; return (EINVAL); } if (!locked) { *freq = 0; return (0); } *freq = ((*freq / m) * n) / p; return (0); } static int pll_register(struct clkdom *clkdom, struct clk_pll_def *clkdef) { struct clknode *clk; struct pll_sc *sc; clk = clknode_create(clkdom, &tegra210_pll_class, &clkdef->clkdef); if (clk == NULL) return (ENXIO); sc = clknode_get_softc(clk); sc->clkdev = clknode_get_device(clk); sc->type = clkdef->type; sc->base_reg = clkdef->base_reg; sc->misc_reg = clkdef->misc_reg; sc->lock_enable = clkdef->lock_enable; sc->iddq_reg = clkdef->iddq_reg; sc->iddq_mask = clkdef->iddq_mask; sc->flags = clkdef->flags; sc->pdiv_table = clkdef->pdiv_table; sc->mnp_bits = clkdef->mnp_bits; clknode_register(clkdom, clk); return (0); } static void config_utmi_pll(struct tegra210_car_softc *sc) { uint32_t reg; /* * XXX Simplified UTMIP settings for 38.4MHz base clock. */ #define ENABLE_DELAY_COUNT 0x00 #define STABLE_COUNT 0x00 #define ACTIVE_DELAY_COUNT 0x06 #define XTAL_FREQ_COUNT 0x80 CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG2, ®); reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); reg |= UTMIP_PLL_CFG2_STABLE_COUNT(STABLE_COUNT); reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(ACTIVE_DELAY_COUNT); CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG2, reg); CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, ®); reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(ENABLE_DELAY_COUNT); reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(XTAL_FREQ_COUNT); reg |= UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; reg |= UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); DELAY(20); /* Setup samplers. */ CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG2, ®); reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP; reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP; reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP; reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN; CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG2, reg); /* Powerup UTMIP. */ CLKDEV_READ_4(sc->dev, UTMIP_PLL_CFG1, ®); reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; CLKDEV_WRITE_4(sc->dev, UTMIP_PLL_CFG1, reg); DELAY(10); /* Prepare UTMIP sequencer. */ CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); DELAY(10); CLKDEV_READ_4(sc->dev, XUSB_PLL_CFG0, ®); reg &= ~XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY; CLKDEV_WRITE_4(sc->dev, XUSB_PLL_CFG0, reg); DELAY(10); /* HW control of UTMIPLL. */ CLKDEV_READ_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, ®); reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; CLKDEV_WRITE_4(sc->dev, UTMIPLL_HW_PWRDN_CFG0, reg); } void tegra210_init_plls(struct tegra210_car_softc *sc) { int i, rv; for (i = 0; i < nitems(tegra210_pll_sources); i++) { rv = clknode_mux_register(sc->clkdom, tegra210_pll_sources + i); if (rv != 0) panic("clk_mux_register failed"); } for (i = 0; i < nitems(pll_clks); i++) { rv = pll_register(sc->clkdom, pll_clks + i); if (rv != 0) panic("pll_register failed"); } config_utmi_pll(sc); for (i = 0; i < nitems(tegra210_pll_fdivs); i++) { rv = clknode_fixed_register(sc->clkdom, tegra210_pll_fdivs + i); if (rv != 0) panic("clk_fixed_register failed"); } for (i = 0; i < nitems(tegra210_pll_gates); i++) { rv = clknode_gate_register(sc->clkdom, tegra210_pll_gates + i); if (rv != 0) panic("clk_gate_register failed"); } for (i = 0; i < nitems(tegra210_pll_divs); i++) { rv = clknode_div_register(sc->clkdom, tegra210_pll_divs + i); if (rv != 0) panic("clk_div_register failed"); } } diff --git a/sys/arm64/nvidia/tegra210/tegra210_clk_super.c b/sys/arm64/nvidia/tegra210/tegra210_clk_super.c index 63005165c9ba..2c35ab53a9df 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_clk_super.c +++ b/sys/arm64/nvidia/tegra210/tegra210_clk_super.c @@ -1,231 +1,231 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * 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 "tegra210_car.h" struct super_mux_def { struct clknode_init_def clkdef; uint32_t base_reg; uint32_t flags; }; #define PLIST(x) static const char *x[] #define SM(_id, cn, pl, r) \ { \ .clkdef.id = _id, \ .clkdef.name = cn, \ .clkdef.parent_names = pl, \ .clkdef.parent_cnt = nitems(pl), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .base_reg = r, \ } PLIST(cclk_g_parents) = { "clk_m", NULL, "clk_s", NULL, "pllP_out0", "pllP_out4", NULL, NULL, "pllX_out0", "dfllCPU_out_alias", NULL, NULL, NULL, NULL, "pllX_out0_alias", "dfllCPU_out", }; PLIST(cclk_lp_parents) = { "clk_m", NULL, "clk_s", NULL, "pllP_out0", "pllP_out4", NULL, NULL, "pllX_out0", "dfllCPU_out_alias", NULL, NULL, NULL, NULL, "pllX_out0_alias", "dfllCPU_out", }; PLIST(sclk_parents) = { "clk_m", "pllC_out1", "pllC4_out3", "pllP_out0", "pllP_out2", "pllC4_out1", "clk_s", "pllC4_out1", }; static struct super_mux_def super_mux_def[] = { SM(TEGRA210_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY), SM(TEGRA210_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY), SM(TEGRA210_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY), }; static int super_mux_init(struct clknode *clk, device_t dev); static int super_mux_set_mux(struct clknode *clk, int idx); struct super_mux_sc { device_t clkdev; uint32_t base_reg; uint32_t flags; int mux; }; static clknode_method_t super_mux_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, super_mux_init), CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux), CLKNODEMETHOD_END }; DEFINE_CLASS_1(tegra210_super_mux, tegra210_super_mux_class, super_mux_methods, sizeof(struct super_mux_sc), clknode_class); /* Mux status. */ #define SUPER_MUX_STATE_STDBY 0 #define SUPER_MUX_STATE_IDLE 1 #define SUPER_MUX_STATE_RUN 2 #define SUPER_MUX_STATE_IRQ 3 #define SUPER_MUX_STATE_FIQ 4 /* Mux register bits. */ #define SUPER_MUX_STATE_BIT_SHIFT 28 #define SUPER_MUX_STATE_BIT_MASK 0xF /* State is Priority encoded */ #define SUPER_MUX_STATE_BIT_STDBY 0x00 #define SUPER_MUX_STATE_BIT_IDLE 0x01 #define SUPER_MUX_STATE_BIT_RUN 0x02 #define SUPER_MUX_STATE_BIT_IRQ 0x04 #define SUPER_MUX_STATE_BIT_FIQ 0x08 #define SUPER_MUX_MUX_WIDTH 4 static uint32_t super_mux_get_state(uint32_t reg) { reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK; if (reg & SUPER_MUX_STATE_BIT_FIQ) return (SUPER_MUX_STATE_FIQ); if (reg & SUPER_MUX_STATE_BIT_IRQ) return (SUPER_MUX_STATE_IRQ); if (reg & SUPER_MUX_STATE_BIT_RUN) return (SUPER_MUX_STATE_RUN); if (reg & SUPER_MUX_STATE_BIT_IDLE) return (SUPER_MUX_STATE_IDLE); return (SUPER_MUX_STATE_STDBY); } static int super_mux_init(struct clknode *clk, device_t dev) { struct super_mux_sc *sc; uint32_t reg; int shift, state; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); RD4(sc, sc->base_reg, ®); DEVICE_UNLOCK(sc); state = super_mux_get_state(reg); if ((state != SUPER_MUX_STATE_RUN) && (state != SUPER_MUX_STATE_IDLE)) { panic("Unexpected super mux state: %u", state); } shift = state * SUPER_MUX_MUX_WIDTH; sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1); clknode_init_parent_idx(clk, sc->mux); return(0); } static int super_mux_set_mux(struct clknode *clk, int idx) { struct super_mux_sc *sc; int shift, state; uint32_t reg, dummy; sc = clknode_get_softc(clk); DEVICE_LOCK(sc); RD4(sc, sc->base_reg, ®); state = super_mux_get_state(reg); if ((state != SUPER_MUX_STATE_RUN) && (state != SUPER_MUX_STATE_IDLE)) { panic("Unexpected super mux state: %u", state); } shift = (state - 1) * SUPER_MUX_MUX_WIDTH; sc->mux = idx; reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift); reg |= idx << shift; WR4(sc, sc->base_reg, reg); RD4(sc, sc->base_reg, &dummy); DEVICE_UNLOCK(sc); return(0); } static int super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef) { struct clknode *clk; struct super_mux_sc *sc; clk = clknode_create(clkdom, &tegra210_super_mux_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->clkdev = clknode_get_device(clk); sc->base_reg = clkdef->base_reg; sc->flags = clkdef->flags; clknode_register(clkdom, clk); return (0); } void tegra210_super_mux_clock(struct tegra210_car_softc *sc) { int i, rv; for (i = 0; i < nitems(super_mux_def); i++) { rv = super_mux_register(sc->clkdom, &super_mux_def[i]); if (rv != 0) panic("super_mux_register failed"); } } diff --git a/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c b/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c index f3ad8cf53222..4ecbceb98049 100644 --- a/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c +++ b/sys/arm64/nvidia/tegra210/tegra210_xusbpadctl.c @@ -1,1963 +1,1963 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * 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 "phynode_if.h" /* FUSE calibration data. */ #define FUSE_SKU_CALIB_0 0x0F0 #define FUSE_SKU_CALIB_0_HS_CURR_LEVEL_123(x, i) (((x) >> (11 + ((i) - 1) * 6)) & 0x3F); #define FUSE_SKU_CALIB_0_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F); #define FUSE_SKU_CALIB_0_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F); #define FUSE_USB_CALIB_EXT_0 0x250 #define FUSE_USB_CALIB_EXT_0_RPD_CTRL(x) (((x) >> 0) & 0x1F); /* Registers. */ #define XUSB_PADCTL_USB2_PAD_MUX 0x004 #define XUSB_PADCTL_USB2_PORT_CAP 0x008 #define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4)) #define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4)) #define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4)) #define USB2_PORT_CAP_PORT_CAP_OTG 0x3 #define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2 #define USB2_PORT_CAP_PORT_CAP_HOST 0x1 #define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0 #define XUSB_PADCTL_SS_PORT_MAP 0x014 #define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4)) #define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4)) #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 #define ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) #define ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN (1 << 29) #define ELPG_PROGRAM1_SSP_ELPG_VCORE_DOWN(x) (1 << (2 + (x) * 3)) #define ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (1 + (x) * 3)) #define ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN(x) (1 << (0 + (x) * 3)) #define XUSB_PADCTL_USB3_PAD_MUX 0x028 #define USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x))) #define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1(x) (0x084 + (x) * 0x40) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPU_OVRD_VAL (1 << 23) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPU_OVRD ( 1 << 22) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPD_OVRD_VAL (1 << 21) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBON_RPD_OVRD (1 << 20) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPU_OVRD_VAL (1 << 19) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPU_OVRD (1 << 18) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPD_OVRD_VAL (1 << 17) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_USBOP_RPD_OVRD (1 << 16) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_DYN_DLY(x) (((x) & 0x3) << 9) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(x) (((x) & 0x3) << 7) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_DIV_DET_EN (1 << 4) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VOP_DIV2P7_DET (1 << 3) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VOP_DIV2P0_DET (1 << 2) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VON_DIV2P7_DET (1 << 1) #define USB2_BATTERY_CHRG_OTGPAD_CTL1_VON_DIV2P0_DET (1 << 0) #define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x088 + (x) * 0x40) #define USB2_OTG_PAD_CTL0_PD_ZI (1 << 29) #define USB2_OTG_PAD_CTL0_PD2_OVRD_EN (1 << 28) #define USB2_OTG_PAD_CTL0_PD2 (1 << 27) #define USB2_OTG_PAD_CTL0_PD (1 << 26) #define USB2_OTG_PAD_CTL0_TERM_EN (1 << 25) #define USB2_OTG_PAD_CTL0_LS_FSLEW(x) (((x) & 0x0F) << 21) #define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x0F) << 17) #define USB2_OTG_PAD_CTL0_FS_FSLEW(x) (((x) & 0x0F) << 13) #define USB2_OTG_PAD_CTL0_FS_RSLEW(x) (((x) & 0x0F) << 9) #define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6) #define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x08C + (x) * 0x40) #define USB2_OTG_PAD_CTL1_RPD_CTRL(x) (((x) & 0x1F) << 26) #define USB2_OTG_PAD_CTL1_RPU_STATUS_HIGH (1 << 25) #define USB2_OTG_PAD_CTL1_RPU_SWITCH_LOW (1 << 24) #define USB2_OTG_PAD_CTL1_RPU_SWITCH_OVRD (1 << 23) #define USB2_OTG_PAD_CTL1_HS_LOOPBACK_OVRD_VAL (1 << 22) #define USB2_OTG_PAD_CTL1_HS_LOOPBACK_OVRD_EN (1 << 21) #define USB2_OTG_PAD_CTL1_PTERM_RANGE_ADJ(x) (((x) & 0x0F) << 17) #define USB2_OTG_PAD_CTL1_PD_DISC_OVRD_VAL (1 << 16) #define USB2_OTG_PAD_CTL1_PD_CHRP_OVRD_VAL (1 << 15) #define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x03) << 13) #define USB2_OTG_PAD_CTL1_HS_COUP_EN(x) (((x) & 0x03) << 11) #define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x0F) << 7) #define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0x0F) << 3) #define USB2_OTG_PAD_CTL1_PD_DR (1 << 2) #define USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) #define USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0(x) (0x0C0 + (x) * 0x40) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0284 #define USB2_BIAS_PAD_CTL0_TRK_PWR_ENA (1 << 29) #define USB2_BIAS_PAD_CTL0_SPARE(x) (((x) & 0xF) << 25) #define USB2_BIAS_PAD_CTL0_CHG_DIV(x) (((x) & 0xF) << 21) #define USB2_BIAS_PAD_CTL0_TEMP_COEF(x) (((x) & 0x7) << 18) #define USB2_BIAS_PAD_CTL0_VREF_CTRL(x) (((x) & 0x7) << 15) #define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 12) #define USB2_BIAS_PAD_CTL0_PD (1 << 11) #define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x7) << 8) #define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 6) #define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 3) #define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x7) << 0) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 0x0288 #define USB2_BIAS_PAD_CTL1_FORCE_TRK_CLK_EN (1 << 30) #define USB2_BIAS_PAD_CTL1_TRK_SW_OVRD (1 << 29) #define USB2_BIAS_PAD_CTL1_TRK_DONE (1 << 28) #define USB2_BIAS_PAD_CTL1_TRK_START (1 << 27) #define USB2_BIAS_PAD_CTL1_PD_TRK (1 << 26) #define USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER(x) (((x) & 0x7F) << 19) #define USB2_BIAS_PAD_CTL1_TRK_START_TIMER(x) (((x) & 0x7F) << 12) #define USB2_BIAS_PAD_CTL1_PCTRL(x) (((x) & 0x3F) << 6) #define USB2_BIAS_PAD_CTL1_TCTRL(x) (((x) & 0x3F) << 0) #define XUSB_PADCTL_HSIC_PAD_CTL0(x) (0x300 + (x) * 0x20) #define HSIC_PAD_CTL0_RPU_STROBE (1 << 18) #define HSIC_PAD_CTL0_RPU_DATA1 (1 << 17) #define HSIC_PAD_CTL0_RPU_DATA0 (1 << 16) #define HSIC_PAD_CTL0_RPD_STROBE (1 << 15) #define HSIC_PAD_CTL0_RPD_DATA1 (1 << 14) #define HSIC_PAD_CTL0_RPD_DATA0 (1 << 13) #define HSIC_PAD_CTL0_LPBK_STROBE (1 << 12) #define HSIC_PAD_CTL0_LPBK_DATA1 (1 << 11) #define HSIC_PAD_CTL0_LPBK_DATA0 (1 << 10) #define HSIC_PAD_CTL0_PD_ZI_STROBE (1 << 9) #define HSIC_PAD_CTL0_PD_ZI_DATA1 (1 << 8) #define HSIC_PAD_CTL0_PD_ZI_DATA0 (1 << 7) #define HSIC_PAD_CTL0_PD_RX_STROBE (1 << 6) #define HSIC_PAD_CTL0_PD_RX_DATA1 (1 << 5) #define HSIC_PAD_CTL0_PD_RX_DATA0 (1 << 4) #define HSIC_PAD_CTL0_PD_TX_STROBE (1 << 3) #define HSIC_PAD_CTL0_PD_TX_DATA1 (1 << 2) #define HSIC_PAD_CTL0_PD_TX_DATA0 (1 << 1) #define HSIC_PAD_CTL0_IDDQ (1 << 0) #define XUSB_PADCTL_HSIC_PAD_CTL1(x) (0x304 + (x) * 0x20) #define HSIC_PAD_CTL1_RTERM(x) (((x) & 0xF) << 12) #define HSIC_PAD_CTL1_HSIC_OPT(x) (((x) & 0xF) << 8) #define HSIC_PAD_CTL1_TX_SLEW(x) (((x) & 0xF) << 4) #define HSIC_PAD_CTL1_TX_RTUNEP(x) (((x) & 0xF) << 0) #define XUSB_PADCTL_HSIC_PAD_CTL2(x) (0x308 + (x) * 0x20) #define HSIC_PAD_CTL2_RX_STROBE_TRIM(x) (((x) & 0xF) << 8) #define HSIC_PAD_CTL2_RX_DATA1_TRIM(x) (((x) & 0xF) << 4) #define HSIC_PAD_CTL2_RX_DATA0_TRIM(x) (((x) & 0xF) << 0) #define XUSB_PADCTL_HSIC_PAD_TRK_CTL 0x340 #define HSIC_PAD_TRK_CTL_AUTO_RTERM_EN (1 << 24) #define HSIC_PAD_TRK_CTL_FORCE_TRK_CLK_EN (1 << 23) #define HSIC_PAD_TRK_CTL_TRK_SW_OVRD (1 << 22) #define HSIC_PAD_TRK_CTL_TRK_DONE (1 << 21) #define HSIC_PAD_TRK_CTL_TRK_START (1 << 20) #define HSIC_PAD_TRK_CTL_PD_TRK (1 << 19) #define HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER(x) (((x) & 0x3F) << 12) #define HSIC_PAD_TRK_CTL_TRK_START_TIMER(x) (((x) & 0x7F) << 5) #define HSIC_PAD_TRK_CTL_RTERM_OUT(x) (((x) & 0x1F) << 0) #define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x344 #define XUSB_PADCTL_UPHY_PLL_P0_CTL1 0x360 #define UPHY_PLL_P0_CTL1_PLL0_FREQ_PSDIV(x) (((x) & 0x03) << 28) #define UPHY_PLL_P0_CTL1_PLL0_FREQ_NDIV(x) (((x) & 0xFF) << 20) #define UPHY_PLL_P0_CTL1_PLL0_FREQ_MDIV(x) (((x) & 0x03) << 16) #define UPHY_PLL_P0_CTL1_PLL0_LOCKDET_STATUS (1 << 15) #define UPHY_PLL_P0_CTL1_PLL0_MODE_GET(x) (((x) >> 8) & 0x03) #define UPHY_PLL_P0_CTL1_PLL0_BYPASS_EN (1 << 7) #define UPHY_PLL_P0_CTL1_PLL0_FREERUN_EN (1 << 6) #define UPHY_PLL_P0_CTL1_PLL0_PWR_OVRD (1 << 4) #define UPHY_PLL_P0_CTL1_PLL0_ENABLE (1 << 3) #define UPHY_PLL_P0_CTL1_PLL0_SLEEP(x) (((x) & 0x03) << 1) #define UPHY_PLL_P0_CTL1_PLL0_IDDQ (1 << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL2 0x364 #define UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(x) (((x) & 0xFFFFFF) << 4) #define UPHY_PLL_P0_CTL2_PLL0_CAL_RESET (1 << 3) #define UPHY_PLL_P0_CTL2_PLL0_CAL_OVRD (1 << 2) #define UPHY_PLL_P0_CTL2_PLL0_CAL_DONE (1 << 1) #define UPHY_PLL_P0_CTL2_PLL0_CAL_EN (1 << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c #define UPHY_PLL_P0_CTL4_PLL0_TCLKOUT_EN (1 << 28) #define UPHY_PLL_P0_CTL4_PLL0_CLKDIST_CTRL(x) (((x) & 0xF) << 20) #define UPHY_PLL_P0_CTL4_PLL0_XDIGCLK_EN (1 << 19) #define UPHY_PLL_P0_CTL4_PLL0_XDIGCLK_SEL(x) (((x) & 0x7) << 16) #define UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_EN (1 << 15) #define UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_SEL(x) (((x) & 0x3) << 12) #define UPHY_PLL_P0_CTL4_PLL0_FBCLKBUF_EN (1 << 9) #define UPHY_PLL_P0_CTL4_PLL0_REFCLKBUF_EN (1 << 8) #define UPHY_PLL_P0_CTL4_PLL0_REFCLK_SEL(x) (((x) & 0xF) << 4) #define UPHY_PLL_P0_CTL4_PLL0_REFCLK_TERM100 (1 << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL5 0x370 #define UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(x) (((x) & 0xFF) << 16) #define UPHY_PLL_P0_CTL5_PLL0_LPF_CTRL(x) (((x) & 0xFF) << 8) #define UPHY_PLL_P0_CTL5_PLL0_CP_CTRL(x) (((x) & 0x0F) << 4) #define UPHY_PLL_P0_CTL5_PLL0_PFD_CTRL(x) (((x) & 0x03) << 0) #define XUSB_PADCTL_UPHY_PLL_P0_CTL8 0x37c #define UPHY_PLL_P0_CTL8_PLL0_RCAL_DONE (1U << 31) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_VAL(x) (((x) & 0x1F) << 24) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_BYP_EN (1 << 23) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_BYP_CODE(x) (((x) & 0x1F) << 16) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_OVRD (1 << 15) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_CLK_EN (1 << 13) #define UPHY_PLL_P0_CTL8_PLL0_RCAL_EN (1 << 12) #define UPHY_PLL_P0_CTL8_PLL0_BGAP_CTRL(x) (((x) & 0xFFF) << 0) #define XUSB_PADCTL_UPHY_MISC_PAD_P_CTL1(x) (0x460 + (x) * 0x40) #define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860 #define UPHY_PLL_S0_CTL1_PLL0_FREQ_PSDIV(x) (((x) & 0x03) << 28) #define UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(x) (((x) & 0xFF) << 20) #define UPHY_PLL_S0_CTL1_PLL0_FREQ_MDIV(x) (((x) & 0x03) << 16) #define UPHY_PLL_S0_CTL1_PLL0_LOCKDET_STATUS (1 << 15) #define UPHY_PLL_S0_CTL1_PLL0_MODE_GET(x) (((x) >> 8) & 0x03) #define UPHY_PLL_S0_CTL1_PLL0_BYPASS_EN (1 << 7) #define UPHY_PLL_S0_CTL1_PLL0_FREERUN_EN (1 << 6) #define UPHY_PLL_S0_CTL1_PLL0_PWR_OVRD (1 << 4) #define UPHY_PLL_S0_CTL1_PLL0_ENABLE (1 << 3) #define UPHY_PLL_S0_CTL1_PLL0_SLEEP(x) (((x) & 0x03) << 1) #define UPHY_PLL_S0_CTL1_PLL0_IDDQ (1 << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864 #define UPHY_PLL_S0_CTL2_PLL0_CAL_CTRL(x) (((x) & 0xFFFFFF) << 4) #define UPHY_PLL_S0_CTL2_PLL0_CAL_RESET (1 << 3) #define UPHY_PLL_S0_CTL2_PLL0_CAL_OVRD (1 << 2) #define UPHY_PLL_S0_CTL2_PLL0_CAL_DONE (1 << 1) #define UPHY_PLL_S0_CTL2_PLL0_CAL_EN (1 << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL4 0x86c #define UPHY_PLL_S0_CTL4_PLL0_TCLKOUT_EN (1 << 28) #define UPHY_PLL_S0_CTL4_PLL0_CLKDIST_CTRL(x) (((x) & 0xF) << 20) #define UPHY_PLL_S0_CTL4_PLL0_XDIGCLK_EN (1 << 19) #define UPHY_PLL_S0_CTL4_PLL0_XDIGCLK_SEL(x) (((x) & 0x7) << 16) #define UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_EN (1 << 15) #define UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(x) (((x) & 0x3) << 12) #define UPHY_PLL_S0_CTL4_PLL0_FBCLKBUF_EN (1 << 9) #define UPHY_PLL_S0_CTL4_PLL0_REFCLKBUF_EN (1 << 8) #define UPHY_PLL_S0_CTL4_PLL0_REFCLK_SEL(x) (((x) & 0xF) << 4) #define UPHY_PLL_S0_CTL4_PLL0_REFCLK_TERM100 (1 << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL5 0x870 #define UPHY_PLL_S0_CTL5_PLL0_DCO_CTRL(x) (((x) & 0xFF) << 16) #define UPHY_PLL_S0_CTL5_PLL0_LPF_CTRL(x) (((x) & 0xFF) << 8) #define UPHY_PLL_S0_CTL5_PLL0_CP_CTRL(x) (((x) & 0x0F) << 4) #define UPHY_PLL_S0_CTL5_PLL0_PFD_CTRL(x) (((x) & 0x03) << 0) #define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c #define UPHY_PLL_S0_CTL8_PLL0_RCAL_DONE (1U << 31) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_VAL(x) (((x) & 0x1F) << 24) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_BYP_EN (1 << 23) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_BYP_CODE(x) (((x) & 0x1F) << 16) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_OVRD (1 << 15) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_CLK_EN (1 << 13) #define UPHY_PLL_S0_CTL8_PLL0_RCAL_EN (1 << 12) #define UPHY_PLL_S0_CTL8_PLL0_BGAP_CTRL(x) (((x) & 0xFFF) << 0) #define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960 #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1(x) (0xa60 + (x) * 0x40) #define UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL(x) (((x) & 0x3) << 16) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2(x) (0xa64 + (x) * 0x40) #define UPHY_USB3_PAD_ECTL2_RX_IQ_CTRL(x) (((x) & 0x000F) << 16) #define UPHY_USB3_PAD_ECTL2_RX_CTLE(x) (((x) & 0xFFFF) << 0) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL3(x) (0xa68 + (x) * 0x40) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4(x) (0xa6c + (x) * 0x40) #define UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL(x) (((x) & 0xFFFF) << 16) #define UPHY_USB3_PAD_ECTL4_RX_PI_CTRL(x) (((x) & 0x00FF) << 0) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6(x) (0xa74 + (x) * 0x40) #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) struct padctl_softc { device_t dev; struct resource *mem_res; hwreset_t rst; int phy_ena_cnt; int pcie_ena_cnt; int sata_ena_cnt; /* Fuses calibration data */ /* USB2 */ uint32_t hs_curr_level[4]; uint32_t hs_curr_level_offs; /* Not inited yet, always 0 */ uint32_t hs_term_range_adj; uint32_t rpd_ctrl; /* HSIC */ uint32_t rx_strobe_trim; /* Not inited yet, always 0 */ uint32_t rx_data0_trim; /* Not inited yet, always 0 */ uint32_t rx_data1_trim; /* Not inited yet, always 0 */ uint32_t tx_rtune_p; /* Not inited yet, always 0 */ uint32_t strobe_trim; /* Not inited yet, always 0 */ }; static struct ofw_compat_data compat_data[] = { {"nvidia,tegra210-xusb-padctl", 1}, {NULL, 0}, }; /* Ports. */ enum padctl_port_type { PADCTL_PORT_USB2, PADCTL_PORT_HSIC, PADCTL_PORT_USB3, }; struct padctl_lane; struct padctl_port { enum padctl_port_type type; const char *name; const char *base_name; int idx; int (*init)(struct padctl_softc *sc, struct padctl_port *port); /* Runtime data. */ phandle_t xref; bool enabled; bool internal; uint32_t companion; regulator_t supply_vbus; struct padctl_lane *lane; }; static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port); #define PORT(t, n, p, i) { \ .type = t, \ .name = n "-" #p, \ .base_name = n, \ .idx = p, \ .init = i, \ } static struct padctl_port ports_tbl[] = { PORT(PADCTL_PORT_USB2, "usb2", 0, NULL), PORT(PADCTL_PORT_USB2, "usb2", 1, NULL), PORT(PADCTL_PORT_USB2, "usb2", 2, NULL), PORT(PADCTL_PORT_USB2, "usb2", 3, NULL), PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL), PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL), PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init), PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init), }; /* Pads - a group of lannes. */ enum padctl_pad_type { PADCTL_PAD_USB2, PADCTL_PAD_HSIC, PADCTL_PAD_PCIE, PADCTL_PAD_SATA, }; struct padctl_lane; struct padctl_pad { const char *name; enum padctl_pad_type type; const char *clock_name; char *reset_name; /* XXX constify !!!!!! */ int (*enable)(struct padctl_softc *sc, struct padctl_lane *lane); int (*disable)(struct padctl_softc *sc, struct padctl_lane *lane); /* Runtime data. */ bool enabled; clk_t clk; hwreset_t reset; int nlanes; struct padctl_lane *lanes[8]; /* Safe maximum value. */ }; static int usb2_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int usb2_disable(struct padctl_softc *sc, struct padctl_lane *lane); static int hsic_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int hsic_disable(struct padctl_softc *sc, struct padctl_lane *lane); static int pcie_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int pcie_disable(struct padctl_softc *sc, struct padctl_lane *lane); static int sata_enable(struct padctl_softc *sc, struct padctl_lane *lane); static int sata_disable(struct padctl_softc *sc, struct padctl_lane *lane); #define PAD(n, t, cn, rn, e, d) { \ .name = n, \ .type = t, \ .clock_name = cn, \ .reset_name = rn, \ .enable = e, \ .disable = d, \ } static struct padctl_pad pads_tbl[] = { PAD("usb2", PADCTL_PAD_USB2, "trk", NULL, usb2_enable, usb2_disable), PAD("hsic", PADCTL_PAD_HSIC, "trk", NULL, hsic_enable, hsic_disable), PAD("pcie", PADCTL_PAD_PCIE, "pll", "phy", pcie_enable, pcie_disable), PAD("sata", PADCTL_PAD_SATA, "pll", "phy", sata_enable, sata_disable), }; /* Lanes. */ static char *usb_mux[] = {"snps", "xusb", "uart", "rsvd"}; static char *hsic_mux[] = {"snps", "xusb"}; static char *pci_mux[] = {"pcie-x1", "usb3-ss", "sata", "pcie-x4"}; struct padctl_lane { const char *name; int idx; bus_size_t reg; uint32_t shift; uint32_t mask; char **mux; int nmux; /* Runtime data. */ bool enabled; phandle_t xref; struct padctl_pad *pad; struct padctl_port *port; int mux_idx; }; #define LANE(n, p, r, s, m, mx) { \ .name = n "-" #p, \ .idx = p, \ .reg = r, \ .shift = s, \ .mask = m, \ .mux = mx, \ .nmux = nitems(mx), \ } static struct padctl_lane lanes_tbl[] = { LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, usb_mux), LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, usb_mux), LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, usb_mux), LANE("usb2", 3, XUSB_PADCTL_USB2_PAD_MUX, 6, 0x3, usb_mux), LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, hsic_mux), LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, hsic_mux), LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 12, 0x3, pci_mux), LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 14, 0x3, pci_mux), LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux), LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux), LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux), LANE("pcie", 5, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux), LANE("pcie", 6, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux), LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 30, 0x3, pci_mux), }; /* Define all possible mappings for USB3 port lanes */ struct padctl_lane_map { int port_idx; enum padctl_pad_type pad_type; int lane_idx; }; #define LANE_MAP(pi, pt, li) { \ .port_idx = pi, \ .pad_type = pt, \ .lane_idx = li, \ } static struct padctl_lane_map lane_map_tbl[] = { LANE_MAP(0, PADCTL_PAD_PCIE, 6), /* port USB3-0 -> lane PCIE-0 */ LANE_MAP(1, PADCTL_PAD_PCIE, 5), /* port USB3-1 -> lane PCIE-1 */ LANE_MAP(2, PADCTL_PAD_PCIE, 0), /* port USB3-2 -> lane PCIE-0 */ LANE_MAP(2, PADCTL_PAD_PCIE, 2), /* port USB3-2 -> lane PCIE-2 */ LANE_MAP(3, PADCTL_PAD_PCIE, 4), /* port USB3-3 -> lane PCIE-4 */ }; /* Phy class and methods. */ static int xusbpadctl_phy_enable(struct phynode *phy, bool enable); static phynode_method_t xusbpadctl_phynode_methods[] = { PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable), PHYNODEMETHOD_END }; DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class, xusbpadctl_phynode_methods, 0, phynode_class); static struct padctl_port *search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane); static void tegra210_xusb_pll_hw_control_enable(void) {} static void tegra210_xusb_pll_hw_sequence_start(void) {} static void tegra210_sata_pll_hw_control_enable(void) {} static void tegra210_sata_pll_hw_sequence_start(void) {} /* ------------------------------------------------------------------------- * * PEX functions */ static int uphy_pex_enable(struct padctl_softc *sc, struct padctl_pad *pad) { uint32_t reg; int rv, i; if (sc->pcie_ena_cnt > 0) { sc->pcie_ena_cnt++; return (0); } /* 22.8.4 UPHY PLLs, Step 4, page 1346 */ /* 1. Deassert PLL/Lane resets. */ rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); return (rv); } rv = hwreset_deassert(pad->reset); if (rv < 0) { device_printf(sc->dev, "Cannot unreset pad '%s': %d\n", pad->name, rv); clk_disable(pad->clk); return (rv); } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(~0); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(0x136); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5); reg &= ~UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(~0); reg |= UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(0x2a); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg |= UPHY_PLL_P0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg |= UPHY_PLL_P0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); /* * 2. For the following registers, default values * take care of the desired frequency. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4); reg &= ~UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_SEL(~0); reg &= ~UPHY_PLL_P0_CTL4_PLL0_REFCLK_SEL(~0); reg |= UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_SEL(0x2); reg |= UPHY_PLL_P0_CTL4_PLL0_TXCLKREF_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_FREQ_MDIV(~0); reg &= ~UPHY_PLL_P0_CTL1_PLL0_FREQ_NDIV(~0); reg |= UPHY_PLL_P0_CTL1_PLL0_FREQ_NDIV(0x19); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_IDDQ; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_SLEEP(~0); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); /* 3. Wait 100 ns. */ DELAY(10); /* XXX This in not in TRM */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4); reg |= UPHY_PLL_P0_CTL4_PLL0_REFCLKBUF_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL4, reg); /* 4. Calibration. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); for (i = 30; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); if (reg & UPHY_PLL_P0_CTL2_PLL0_CAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 1 " "for pad '%s' (0x%08X).\n", pad->name, reg); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); if ((reg & UPHY_PLL_P0_CTL2_PLL0_CAL_DONE) == 0) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 5. Enable the PLL (20 µs Lock time) */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg |= UPHY_PLL_P0_CTL1_PLL0_ENABLE; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); if (reg & UPHY_PLL_P0_CTL1_PLL0_LOCKDET_STATUS) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout while enabling PLL " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 6. RCAL. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg |= UPHY_PLL_P0_CTL8_PLL0_RCAL_EN; reg |= UPHY_PLL_P0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); if (reg & UPHY_PLL_P0_CTL8_PLL0_RCAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 1 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg &= ~UPHY_PLL_P0_CTL8_PLL0_RCAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); if (!(reg & UPHY_PLL_P0_CTL8_PLL0_RCAL_DONE)) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg &= ~UPHY_PLL_P0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); /* Enable Hardware Power Sequencer. */ tegra210_xusb_pll_hw_control_enable(); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1); reg &= ~UPHY_PLL_P0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8); reg &= ~UPHY_PLL_P0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL8, reg); DELAY(50); tegra210_xusb_pll_hw_sequence_start(); sc->pcie_ena_cnt++; return (0); err: hwreset_deassert(pad->reset); clk_disable(pad->clk); return (rv); } static void uphy_pex_disable(struct padctl_softc *sc, struct padctl_pad *pad) { int rv; sc->pcie_ena_cnt--; if (sc->pcie_ena_cnt <= 0) { rv = hwreset_assert(pad->reset); if (rv != 0) { device_printf(sc->dev, "Cannot reset pad '%s': %d\n", pad->name, rv); } rv = clk_disable(pad->clk); if (rv != 0) { device_printf(sc->dev, "Cannot dicable clock for pad '%s': %d\n", pad->name, rv); } } } static int uphy_sata_enable(struct padctl_softc *sc, struct padctl_pad *pad, bool usb) { uint32_t reg; int rv, i; /* 22.8.4 UPHY PLLs, Step 4, page 1346 */ /* 1. Deassert PLL/Lane resets. */ if (sc->sata_ena_cnt > 0) { sc->sata_ena_cnt++; return (0); } rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); return (rv); } rv = hwreset_deassert(pad->reset); if (rv < 0) { device_printf(sc->dev, "Cannot unreset pad '%s': %d\n", pad->name, rv); clk_disable(pad->clk); return (rv); } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2); reg &= ~UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(~0); reg |= UPHY_PLL_P0_CTL2_PLL0_CAL_CTRL(0x136); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5); reg &= ~UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(~0); reg |= UPHY_PLL_P0_CTL5_PLL0_DCO_CTRL(0x2a); WR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL5, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg |= UPHY_PLL_S0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg |= UPHY_PLL_S0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg |= UPHY_PLL_S0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); /* * 2. For the following registers, default values * take care of the desired frequency. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4); reg &= ~UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(~0); reg &= ~UPHY_PLL_S0_CTL4_PLL0_REFCLK_SEL(~0); reg |= UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_EN; if (usb) reg |= UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(0x2); else reg |= UPHY_PLL_S0_CTL4_PLL0_TXCLKREF_SEL(0x0); /* XXX PLL0_XDIGCLK_EN */ /* value &= ~(1 << 19); WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4, reg); */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_FREQ_MDIV(~0); reg &= ~UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(~0); if (usb) reg |= UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(0x19); else reg |= UPHY_PLL_S0_CTL1_PLL0_FREQ_NDIV(0x1e); WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_IDDQ; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_SLEEP(~0); WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); /* 3. Wait 100 ns. */ DELAY(1); /* XXX This in not in TRM */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4); reg |= UPHY_PLL_S0_CTL4_PLL0_REFCLKBUF_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL4, reg); /* 4. Calibration. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg |= UPHY_PLL_S0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); for (i = 30; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); if (reg & UPHY_PLL_S0_CTL2_PLL0_CAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 1 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg &= ~UPHY_PLL_S0_CTL2_PLL0_CAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); if ((reg & UPHY_PLL_S0_CTL2_PLL0_CAL_DONE) == 0) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 5. Enable the PLL (20 µs Lock time) */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg |= UPHY_PLL_S0_CTL1_PLL0_ENABLE; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); if (reg & UPHY_PLL_S0_CTL1_PLL0_LOCKDET_STATUS) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout while enabling PLL " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } /* 6. RCAL. */ reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg |= UPHY_PLL_S0_CTL8_PLL0_RCAL_EN; reg |= UPHY_PLL_S0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); if (reg & UPHY_PLL_S0_CTL8_PLL0_RCAL_DONE) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 1 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg &= ~UPHY_PLL_S0_CTL8_PLL0_RCAL_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); for (i = 10; i > 0; i--) { reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); if (!(reg & UPHY_PLL_S0_CTL8_PLL0_RCAL_DONE)) break; DELAY(10); } if (i <= 0) { device_printf(sc->dev, "Timedout in RX calibration step 2 " "for pad '%s'.\n", pad->name); rv = ETIMEDOUT; goto err; } reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg &= ~UPHY_PLL_S0_CTL8_PLL0_RCAL_CLK_EN; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); /* Enable Hardware Power Sequencer. */ tegra210_sata_pll_hw_control_enable(); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1); reg &= ~UPHY_PLL_S0_CTL1_PLL0_PWR_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2); reg &= ~UPHY_PLL_S0_CTL2_PLL0_CAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL2, reg); reg = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8); reg &= ~UPHY_PLL_S0_CTL8_PLL0_RCAL_OVRD; WR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL8, reg); DELAY(50); tegra210_sata_pll_hw_sequence_start(); sc->sata_ena_cnt++; return (0); err: hwreset_deassert(pad->reset); clk_disable(pad->clk); return (rv); } static void uphy_sata_disable(struct padctl_softc *sc, struct padctl_pad *pad) { int rv; sc->sata_ena_cnt--; if (sc->sata_ena_cnt <= 0) { rv = hwreset_assert(pad->reset); if (rv != 0) { device_printf(sc->dev, "Cannot reset pad '%s': %d\n", pad->name, rv); } rv = clk_disable(pad->clk); if (rv != 0) { device_printf(sc->dev, "Cannot dicable clock for pad '%s': %d\n", pad->name, rv); } } } static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port) { uint32_t reg; struct padctl_pad *pad; int rv; pad = port->lane->pad; reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP); if (port->internal) reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx); else reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx); reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0); reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion); WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg); if (port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } reg = RD4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL1(port->idx)); reg &= ~UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL(~0); reg |= UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL(2); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL1(port->idx), reg); reg = RD4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL2(port->idx)); reg &= ~UPHY_USB3_PAD_ECTL2_RX_CTLE(~0); reg |= UPHY_USB3_PAD_ECTL2_RX_CTLE(0x00fc); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL2(port->idx), reg); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3(port->idx), 0xc0077f1f); reg = RD4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL4(port->idx)); reg &= ~UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL(~0); reg |= UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL(0x01c7); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL4(port->idx), reg); WR4(sc, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6(port->idx), 0xfcf01368); if (pad->type == PADCTL_PAD_SATA) rv = uphy_sata_enable(sc, pad, true); else rv = uphy_pex_enable(sc, pad); if (rv != 0) return (rv); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_SSP_ELPG_VCORE_DOWN(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN_EARLY(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_SSP_ELPG_CLAMP_EN(port->idx); WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); return (0); } static int pcie_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; int rv; rv = uphy_pex_enable(sc, lane->pad); if (rv != 0) return (rv); reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); return (0); } static int pcie_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); uphy_pex_disable(sc, lane->pad); return (0); } static int sata_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; int rv; rv = uphy_sata_enable(sc, lane->pad, false); if (rv != 0) return (rv); reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); return (0); } static int sata_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->idx); WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); uphy_sata_disable(sc, lane->pad); return (0); } static int hsic_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_pad *pad; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } pad = lane->pad; if (port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } WR4(sc, XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL, sc->strobe_trim); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL1(lane->idx)); reg &= ~HSIC_PAD_CTL1_TX_RTUNEP(~0); reg |= HSIC_PAD_CTL1_TX_RTUNEP(sc->tx_rtune_p); WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL1(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL2(lane->idx)); reg &= ~HSIC_PAD_CTL2_RX_STROBE_TRIM(~0); reg &= ~HSIC_PAD_CTL2_RX_DATA1_TRIM(~0); reg &= ~HSIC_PAD_CTL2_RX_DATA0_TRIM(~0); reg |= HSIC_PAD_CTL2_RX_STROBE_TRIM(sc->rx_strobe_trim); reg |= HSIC_PAD_CTL2_RX_DATA1_TRIM(sc->rx_data1_trim); reg |= HSIC_PAD_CTL2_RX_DATA0_TRIM(sc->rx_data0_trim); WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL2(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL0(lane->idx)); reg &= ~HSIC_PAD_CTL0_RPU_DATA0; reg &= ~HSIC_PAD_CTL0_RPU_DATA1; reg &= ~HSIC_PAD_CTL0_RPU_STROBE; reg &= ~HSIC_PAD_CTL0_PD_RX_DATA0; reg &= ~HSIC_PAD_CTL0_PD_RX_DATA1; reg &= ~HSIC_PAD_CTL0_PD_RX_STROBE; reg &= ~HSIC_PAD_CTL0_PD_ZI_DATA0; reg &= ~HSIC_PAD_CTL0_PD_ZI_DATA1; reg &= ~HSIC_PAD_CTL0_PD_ZI_STROBE; reg &= ~HSIC_PAD_CTL0_PD_TX_DATA0; reg &= ~HSIC_PAD_CTL0_PD_TX_DATA1; reg &= ~HSIC_PAD_CTL0_PD_TX_STROBE; reg |= HSIC_PAD_CTL0_RPD_DATA0; reg |= HSIC_PAD_CTL0_RPD_DATA1; reg |= HSIC_PAD_CTL0_RPD_STROBE; WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL0(lane->idx), reg); rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); if (port->supply_vbus != NULL) regulator_disable(port->supply_vbus); return (rv); } reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL); reg &= ~HSIC_PAD_TRK_CTL_TRK_START_TIMER(~0); reg &= ~HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER(~0); reg |= HSIC_PAD_TRK_CTL_TRK_START_TIMER(0x1e); reg |= HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER(0x0a); WR4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL, reg); DELAY(10); reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL); reg &= ~HSIC_PAD_TRK_CTL_PD_TRK; WR4(sc, XUSB_PADCTL_HSIC_PAD_TRK_CTL, reg); DELAY(50); clk_disable(pad->clk); return (0); } static int hsic_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } reg = RD4(sc, XUSB_PADCTL_HSIC_PAD_CTL0(lane->idx)); reg |= HSIC_PAD_CTL0_PD_RX_DATA0; reg |= HSIC_PAD_CTL0_PD_RX_DATA1; reg |= HSIC_PAD_CTL0_PD_RX_STROBE; reg |= HSIC_PAD_CTL0_PD_ZI_DATA0; reg |= HSIC_PAD_CTL0_PD_ZI_DATA1; reg |= HSIC_PAD_CTL0_PD_ZI_STROBE; reg |= HSIC_PAD_CTL0_PD_TX_DATA0; reg |= HSIC_PAD_CTL0_PD_TX_DATA1; reg |= HSIC_PAD_CTL0_PD_TX_STROBE; WR4(sc, XUSB_PADCTL_HSIC_PAD_CTL1(lane->idx), reg); if (port->supply_vbus != NULL) { rv = regulator_disable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot disable vbus regulator\n"); return (rv); } } return (0); } static int usb2_enable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_pad *pad; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } pad = lane->pad; reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0); reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0); reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(0x7); WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP); reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0); reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST); WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg); reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx)); reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0); reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0); reg &= ~USB2_OTG_PAD_CTL0_PD; reg &= ~USB2_OTG_PAD_CTL0_PD2; reg &= ~USB2_OTG_PAD_CTL0_PD_ZI; reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14); reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level[lane->idx] + sc->hs_curr_level_offs); WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx)); reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0); reg &= ~USB2_OTG_PAD_CTL1_RPD_CTRL(~0); reg &= ~USB2_OTG_PAD_CTL1_PD_DR; reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_OVRD; reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_OVRD; reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj); reg |= USB2_OTG_PAD_CTL1_RPD_CTRL(sc->rpd_ctrl); WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg); reg = RD4(sc, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1(lane->idx)); reg &= ~USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV(~0); reg |= USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; WR4(sc, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1(lane->idx), reg); if (port->supply_vbus != NULL) { rv = regulator_enable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot enable vbus regulator\n"); return (rv); } } rv = clk_enable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot enable clock for pad '%s': %d\n", pad->name, rv); if (port->supply_vbus != NULL) regulator_disable(port->supply_vbus); return (rv); } reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); reg &= ~USB2_BIAS_PAD_CTL1_TRK_START_TIMER(~0); reg &= ~USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER(~0); reg |= USB2_BIAS_PAD_CTL1_TRK_START_TIMER(0x1e); reg |= USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER(0x0a); WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL1, reg); reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~USB2_BIAS_PAD_CTL0_PD; WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); return (0); } static int usb2_disable(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; struct padctl_pad *pad; struct padctl_port *port; int rv; port = search_lane_port(sc, lane); if (port == NULL) { device_printf(sc->dev, "Cannot find port for lane: %s\n", lane->name); } pad = lane->pad; reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg |= USB2_BIAS_PAD_CTL0_PD; WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); if (port->supply_vbus != NULL) { rv = regulator_disable(port->supply_vbus); if (rv != 0) { device_printf(sc->dev, "Cannot disable vbus regulator\n"); return (rv); } } rv = clk_disable(pad->clk); if (rv < 0) { device_printf(sc->dev, "Cannot disable clock for pad '%s': %d\n", pad->name, rv); return (rv); } return (0); } static int pad_common_enable(struct padctl_softc *sc) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg &= ~ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); return (0); } static int pad_common_disable(struct padctl_softc *sc) { uint32_t reg; reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg |= ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg |= ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM1); reg |= ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; WR4(sc, XUSB_PADCTL_ELPG_PROGRAM1, reg); DELAY(100); return (0); } static int xusbpadctl_phy_enable(struct phynode *phy, bool enable) { device_t dev; intptr_t id; struct padctl_softc *sc; struct padctl_lane *lane; struct padctl_pad *pad; int rv; dev = phynode_get_device(phy); id = phynode_get_id(phy); sc = device_get_softc(dev); if (id < 0 || id >= nitems(lanes_tbl)) { device_printf(dev, "Unknown phy: %d\n", (int)id); return (ENXIO); } lane = lanes_tbl + id; if (!lane->enabled) { device_printf(dev, "Lane is not enabled/configured: %s\n", lane->name); return (ENXIO); } pad = lane->pad; if (enable) { if (sc->phy_ena_cnt == 0) { rv = pad_common_enable(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt++; } if (enable) rv = pad->enable(sc, lane); else rv = pad->disable(sc, lane); if (rv != 0) return (rv); if (!enable) { if (sc->phy_ena_cnt == 1) { rv = pad_common_disable(sc); if (rv != 0) return (rv); } sc->phy_ena_cnt--; } return (0); } /* ------------------------------------------------------------------------- * * FDT processing */ static struct padctl_port * search_port(struct padctl_softc *sc, char *port_name) { int i; for (i = 0; i < nitems(ports_tbl); i++) { if (strcmp(port_name, ports_tbl[i].name) == 0) return (&ports_tbl[i]); } return (NULL); } static struct padctl_port * search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane) { int i; for (i = 0; i < nitems(ports_tbl); i++) { if (!ports_tbl[i].enabled) continue; if (ports_tbl[i].lane == lane) return (ports_tbl + i); } return (NULL); } static struct padctl_lane * search_lane(struct padctl_softc *sc, char *lane_name) { int i; for (i = 0; i < nitems(lanes_tbl); i++) { if (strcmp(lane_name, lanes_tbl[i].name) == 0) return (lanes_tbl + i); } return (NULL); } static struct padctl_lane * search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx) { int i; for (i = 0; i < nitems(lanes_tbl); i++) { if (!lanes_tbl[i].enabled) continue; if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx) return (lanes_tbl + i); } return (NULL); } static struct padctl_lane * search_usb3_pad_lane(struct padctl_softc *sc, int idx) { int i; struct padctl_lane *lane, *tmp; lane = NULL; for (i = 0; i < nitems(lane_map_tbl); i++) { if (idx != lane_map_tbl[i].port_idx) continue; tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type, lane_map_tbl[i].lane_idx); if (tmp == NULL) continue; if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0) continue; if (lane != NULL) { device_printf(sc->dev, "Duplicated mappings found for" " lanes: %s and %s\n", lane->name, tmp->name); return (NULL); } lane = tmp; } return (lane); } static struct padctl_pad * search_pad(struct padctl_softc *sc, char *pad_name) { int i; for (i = 0; i < nitems(pads_tbl); i++) { if (strcmp(pad_name, pads_tbl[i].name) == 0) return (pads_tbl + i); } return (NULL); } static int search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name) { int i; for (i = 0; i < lane->nmux; i++) { if (strcmp(fnc_name, lane->mux[i]) == 0) return (i); } return (-1); } static int config_lane(struct padctl_softc *sc, struct padctl_lane *lane) { uint32_t reg; reg = RD4(sc, lane->reg); reg &= ~(lane->mask << lane->shift); reg |= (lane->mux_idx & lane->mask) << lane->shift; WR4(sc, lane->reg, reg); return (0); } static int process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad) { struct padctl_lane *lane; struct phynode *phynode; struct phynode_init_def phy_init; char *name; char *function; int rv; name = NULL; function = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read lane name.\n"); return (ENXIO); } lane = search_lane(sc, name); if (lane == NULL) { device_printf(sc->dev, "Unknown lane: %s\n", name); rv = ENXIO; goto end; } /* Read function (mux) settings. */ rv = OF_getprop_alloc(node, "nvidia,function", (void **)&function); if (rv <= 0) { device_printf(sc->dev, "Cannot read lane function.\n"); rv = ENXIO; goto end; } lane->mux_idx = search_mux(sc, lane, function); if (lane->mux_idx == ~0) { device_printf(sc->dev, "Unknown function %s for lane %s\n", function, name); rv = ENXIO; goto end; } rv = config_lane(sc, lane); if (rv != 0) { device_printf(sc->dev, "Cannot configure lane: %s: %d\n", name, rv); rv = ENXIO; goto end; } lane->xref = OF_xref_from_node(node); lane->pad = pad; lane->enabled = true; pad->lanes[pad->nlanes++] = lane; /* Create and register phy. */ bzero(&phy_init, sizeof(phy_init)); phy_init.id = lane - lanes_tbl; phy_init.ofw_node = node; phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init); if (phynode == NULL) { device_printf(sc->dev, "Cannot create phy\n"); rv = ENXIO; goto end; } if (phynode_register(phynode) == NULL) { device_printf(sc->dev, "Cannot create phy\n"); return (ENXIO); } rv = 0; end: if (name != NULL) OF_prop_free(name); if (function != NULL) OF_prop_free(function); return (rv); } static int process_pad(struct padctl_softc *sc, phandle_t node) { phandle_t xref; struct padctl_pad *pad; char *name; int rv; name = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read pad name.\n"); return (ENXIO); } pad = search_pad(sc, name); if (pad == NULL) { device_printf(sc->dev, "Unknown pad: %s\n", name); rv = ENXIO; goto end; } if (pad->clock_name != NULL) { rv = clk_get_by_ofw_name(sc->dev, node, pad->clock_name, &pad->clk); if (rv != 0) { device_printf(sc->dev, "Cannot get '%s' clock\n", pad->clock_name); return (ENXIO); } } if (pad->reset_name != NULL) { rv = hwreset_get_by_ofw_name(sc->dev, node, pad->reset_name, &pad->reset); if (rv != 0) { device_printf(sc->dev, "Cannot get '%s' reset\n", pad->reset_name); return (ENXIO); } } /* Read and process associated lanes. */ node = ofw_bus_find_child(node, "lanes"); if (node <= 0) { device_printf(sc->dev, "Cannot find 'lanes' subnode\n"); rv = ENXIO; goto end; } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_lane(sc, node, pad); if (rv != 0) goto end; xref = OF_xref_from_node(node); OF_device_register_xref(xref, sc->dev); } pad->enabled = true; rv = 0; end: if (name != NULL) OF_prop_free(name); return (rv); } static int process_port(struct padctl_softc *sc, phandle_t node) { struct padctl_port *port; struct padctl_pad *pad; char *name; int rv; name = NULL; rv = OF_getprop_alloc(node, "name", (void **)&name); if (rv <= 0) { device_printf(sc->dev, "Cannot read port name.\n"); return (ENXIO); } port = search_port(sc, name); if (port == NULL) { device_printf(sc->dev, "Unknown port: %s\n", name); rv = ENXIO; goto end; } regulator_get_by_ofw_property(sc->dev, node, "vbus-supply", &port->supply_vbus); if (OF_hasprop(node, "nvidia,internal")) port->internal = true; /* Find assigned lane */ if (port->lane == NULL) { switch(port->type) { /* Routing is fixed for USB2 AND HSIC. */ case PADCTL_PORT_USB2: port->lane = search_pad_lane(sc, PADCTL_PAD_USB2, port->idx); break; case PADCTL_PORT_HSIC: port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC, port->idx); break; case PADCTL_PORT_USB3: port->lane = search_usb3_pad_lane(sc, port->idx); break; } } if (port->lane == NULL) { device_printf(sc->dev, "Cannot find lane for port: %s\n", name); rv = ENXIO; goto end; } pad = port->lane->pad; if (port->type == PADCTL_PORT_USB3) { rv = OF_getencprop(node, "nvidia,usb2-companion", &(port->companion), sizeof(port->companion)); if (rv <= 0) { device_printf(sc->dev, "Missing 'nvidia,usb2-companion' property " "for port: %s\n", name); rv = ENXIO; goto end; } } port->enabled = true; rv = 0; end: if (name != NULL) OF_prop_free(name); return (rv); } static int parse_fdt(struct padctl_softc *sc, phandle_t base_node) { phandle_t node; int rv; rv = 0; node = ofw_bus_find_child(base_node, "pads"); if (node <= 0) { device_printf(sc->dev, "Cannot find pads subnode.\n"); return (ENXIO); } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_pad(sc, node); if (rv != 0) return (rv); } node = ofw_bus_find_child(base_node, "ports"); if (node <= 0) { device_printf(sc->dev, "Cannot find ports subnode.\n"); return (ENXIO); } for (node = OF_child(node); node != 0; node = OF_peer(node)) { if (!ofw_bus_node_status_okay(node)) continue; rv = process_port(sc, node); if (rv != 0) return (rv); } return (0); } static void load_calibration(struct padctl_softc *sc) { uint32_t reg; int i; reg = tegra_fuse_read_4(FUSE_SKU_CALIB_0); sc->hs_curr_level[0] = FUSE_SKU_CALIB_0_HS_CURR_LEVEL_0(reg); for (i = 1; i < nitems(sc->hs_curr_level); i++) { sc->hs_curr_level[i] = FUSE_SKU_CALIB_0_HS_CURR_LEVEL_123(reg, i); } sc->hs_term_range_adj = FUSE_SKU_CALIB_0_HS_TERM_RANGE_ADJ(reg); tegra_fuse_read_4(FUSE_USB_CALIB_EXT_0); sc->rpd_ctrl = FUSE_USB_CALIB_EXT_0_RPD_CTRL(reg); } /* ------------------------------------------------------------------------- * * BUS functions */ static int xusbpadctl_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Tegra XUSB phy"); return (BUS_PROBE_DEFAULT); } static int xusbpadctl_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int xusbpadctl_attach(device_t dev) { struct padctl_softc * sc; int i, rid, rv; struct padctl_port *port; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst); if (rv != 0) { device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv); return (rv); } rv = hwreset_deassert(sc->rst); if (rv != 0) { device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv); return (rv); } load_calibration(sc); rv = parse_fdt(sc, node); if (rv != 0) { device_printf(dev, "Cannot parse fdt configuration: %d\n", rv); return (rv); } for (i = 0; i < nitems(ports_tbl); i++) { port = ports_tbl + i; if (!port->enabled) continue; if (port->init == NULL) continue; rv = port->init(sc, port); if (rv != 0) { device_printf(dev, "Cannot init port '%s'\n", port->name); return (rv); } } return (0); } static device_method_t tegra_xusbpadctl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xusbpadctl_probe), DEVMETHOD(device_attach, xusbpadctl_attach), DEVMETHOD(device_detach, xusbpadctl_detach), DEVMETHOD_END }; static devclass_t tegra_xusbpadctl_devclass; static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver, tegra_xusbpadctl_methods, sizeof(struct padctl_softc)); EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, tegra_xusbpadctl_devclass, NULL, NULL, 73); diff --git a/sys/arm64/rockchip/clk/rk3288_cru.c b/sys/arm64/rockchip/clk/rk3288_cru.c index ed7ee0f12b32..6a8c63137df7 100644 --- a/sys/arm64/rockchip/clk/rk3288_cru.c +++ b/sys/arm64/rockchip/clk/rk3288_cru.c @@ -1,1026 +1,1026 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #define CRU_SOFTRST_SIZE 12 #define CRU_APLL_CON(x) (0x000 + (x) * 0x4) #define CRU_DPLL_CON(x) (0x010 + (x) * 0x4) #define CRU_CPLL_CON(x) (0x020 + (x) * 0x4) #define CRU_GPLL_CON(x) (0x030 + (x) * 0x4) #define CRU_NPLL_CON(x) (0x040 + (x) * 0x4) #define CRU_MODE_CON 0x050 #define CRU_CLKSEL_CON(x) (0x060 + (x) * 0x4) #define CRU_CLKGATE_CON(x) (0x160 + (x) * 0x4) #define CRU_GLB_SRST_FST_VALUE 0x1b0 #define CRU_GLB_SRST_SND_VALUE 0x1b4 #define CRU_SOFTRST_CON(x) (0x1b8 + (x) * 0x4) #define CRU_MISC_CON 0x1e8 #define CRU_GLB_CNT_TH 0x1ec #define CRU_GLB_RST_CON 0x1f0 #define CRU_GLB_RST_ST 0x1f8 #define CRU_SDMMC_CON0 0x200 #define CRU_SDMMC_CON1 0x204 #define CRU_SDIO0_CON0 0x208 #define CRU_SDIO0_CON1 0x20c #define CRU_SDIO1_CON0 0x210 #define CRU_SDIO1_CON1 0x214 #define CRU_EMMC_CON0 0x218 #define CRU_EMMC_CON1 0x21c /* GATES */ #define GATE(_idx, _clkname, _pname, _o, _s) \ { \ .id = _idx, \ .name = _clkname, \ .parent_name = _pname, \ .offset = CRU_CLKGATE_CON(_o), \ .shift = _s, \ } static struct rk_cru_gate rk3288_gates[] = { /* CRU_CLKGATE_CON0 */ GATE(0, "sclk_acc_efuse", "xin24m", 0, 12), GATE(0, "cpll_aclk_cpu", "cpll", 0, 11), GATE(0, "gpll_aclk_cpu", "gpll", 0, 10), GATE(0, "gpll_ddr", "gpll", 0, 9), GATE(0, "dpll_ddr", "dpll", 0, 8), GATE(0, "aclk_bus_2pmu", "aclk_cpu_pre", 0, 7), GATE(PCLK_CPU, "pclk_cpu", "pclk_cpu_s", 0, 5), GATE(HCLK_CPU, "hclk_cpu", "hclk_cpu_s", 0, 4), GATE(ACLK_CPU, "aclk_cpu", "aclk_cpu_pre", 0, 3), GATE(0, "gpll_core", "gpll", 0, 2), GATE(0, "apll_core", "apll", 0, 1), /* CRU_CLKGATE_CON1 */ GATE(0, "uart3_frac", "uart3_frac_s", 1, 15), GATE(0, "uart3_src", "uart3_src_s", 1, 14), GATE(0, "uart2_frac", "uart2_frac_s", 1, 13), GATE(0, "uart2_src", "uart2_src_s", 1, 12), GATE(0, "uart1_frac", "uart1_frac_s", 1, 11), GATE(0, "uart1_src", "uart1_src_s", 1, 10), GATE(0, "uart0_frac", "uart0_frac_s", 1, 9), GATE(0, "uart0_src", "uart0_src_s", 1, 8), GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 1, 5), GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 1, 4), GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 1, 3), GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 1, 2), GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 1, 1), GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 1, 0), /* CRU_CLKGATE_CON2 */ GATE(0, "uart4_frac", "uart4_frac_s", 2, 13), GATE(0, "uart4_src", "uart4_src_s", 2, 12), GATE(SCLK_SPI2, "sclk_spi2", "sclk_spi2_s", 2, 11), GATE(SCLK_SPI1, "sclk_spi1", "sclk_spi1_s", 2, 10), GATE(SCLK_SPI0, "sclk_spi0", "sclk_spi0_s", 2, 9), GATE(SCLK_SARADC, "sclk_saradc", "sclk_saradc_s", 2, 8), GATE(SCLK_TSADC, "sclk_tsadc", "sclk_tsadc_s", 2, 7), GATE(0, "hsadc_src", "hsadc_src_s", 2, 6), GATE(0, "mac_pll_src", "mac_pll_src_s", 2, 5), GATE(PCLK_PERI, "pclk_peri", "pclk_peri_s", 2, 3), GATE(HCLK_PERI, "hclk_peri", "hclk_peri_s", 2, 2), GATE(ACLK_PERI, "aclk_peri", "aclk_peri_src", 2, 1), GATE(0, "aclk_peri_src", "aclk_peri_src_s", 2, 0), /* CRU_CLKGATE_CON3 */ GATE(SCLK_ISP_JPE, "sclk_isp_jpe", "sclk_isp_jpe_s", 3, 15), GATE(SCLK_ISP, "sclk_isp", "sclk_isp_s", 3, 14), GATE(SCLK_EDP, "sclk_edp", "sclk_edp_s", 3, 13), GATE(SCLK_EDP_24M, "sclk_edp_24m", "sclk_edp_24m_s", 3, 12), GATE(0, "aclk_vdpu", "aclk_vdpu_s", 3, 11), GATE(0, "hclk_vcodec_pre", "hclk_vcodec_pre_s", 3, 10), GATE(0, "aclk_vepu", "aclk_vepu_s", 3, 9), GATE(0, "vip_src", "vip_src_s", 3, 7), /* 6 - Not in TRM, sclk_hsicphy480m in Linux */ GATE(0, "aclk_rga_pre", "aclk_rga_pre_s", 3, 5), GATE(SCLK_RGA, "sclk_rga", "sclk_rga_s", 3, 4), GATE(DCLK_VOP1, "dclk_vop1", "dclk_vop1_s", 3, 3), GATE(0, "aclk_vio1", "aclk_vio1_s", 3, 2), GATE(DCLK_VOP0, "dclk_vop0", "dclk_vop0_s", 3, 1), GATE(0, "aclk_vio0", "aclk_vio0_s", 3, 0), /* CRU_CLKGATE_CON4 */ /* 15 - Test clock generator */ GATE(0, "jtag", "ext_jtag", 4, 14), GATE(0, "sclk_ddrphy1", "ddrphy", 4, 13), GATE(0, "sclk_ddrphy0", "ddrphy", 4, 12), GATE(0, "sclk_tspout", "sclk_tspout_s", 4, 11), GATE(0, "sclk_tsp", "sclk_tsp_s", 4, 10), GATE(SCLK_SPDIF8CH, "sclk_spdif_8ch", "spdif_8ch_mux", 4, 9), GATE(0, "spdif_8ch_frac", "spdif_8ch_frac_s", 4, 8), GATE(0, "spdif_8ch_pre", "spdif_8ch_pre_s", 4, 7), GATE(SCLK_SPDIF, "sclk_spdif", "spdif_mux", 4, 6), GATE(0, "spdif_frac", "spdif_frac_s", 4, 5), GATE(0, "spdif_pre", "spdif_pre_s", 4, 4), GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 4, 3), GATE(0, "i2s_frac", "i2s_frac_s", 4, 2), GATE(0, "i2s_src", "i2s_src_s", 4, 1), GATE(SCLK_I2S0_OUT, "i2s0_clkout", "i2s0_clkout_s", 4, 1), /* CRU_CLKGATE_CON5 */ GATE(SCLK_MIPIDSI_24M, "sclk_mipidsi_24m", "xin24m", 5, 15), GATE(SCLK_USBPHY480M_SRC, "usbphy480m_src", "usbphy480m_src_s", 5, 14), GATE(SCLK_PS2C, "sclk_ps2c", "xin24m", 5, 13), GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 5, 12), GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 5, 11), GATE(SCLK_PVTM_GPU, "sclk_pvtm_gpu", "xin24m", 5, 10), GATE(SCLK_PVTM_CORE, "sclk_pvtm_core", "xin24m", 5, 9), GATE(0, "pclk_pd_pmu", "pclk_pd_pmu_s", 5, 8), GATE(SCLK_GPU, "sclk_gpu", "sclk_gpu_s", 5, 7), GATE(SCLK_NANDC1, "sclk_nandc1", "sclk_nandc1_s", 5, 6), GATE(SCLK_NANDC0, "sclk_nandc0", "sclk_nandc0_s", 5, 5), GATE(SCLK_CRYPTO, "crypto", "crypto_s", 5, 4), GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 5, 3), GATE(SCLK_MACREF, "sclk_macref", "mac_clk", 5, 2), GATE(SCLK_MAC_TX, "sclk_mac_tx", "mac_clk", 5, 1), GATE(SCLK_MAC_RX, "sclk_mac_rx", "mac_clk", 5, 0), /* CRU_CLKGATE_CON6 */ GATE(PCLK_I2C4, "pclk_i2c4", "pclk_peri", 6, 15), GATE(PCLK_I2C3, "pclk_i2c3", "pclk_peri", 6, 14), GATE(PCLK_I2C1, "pclk_i2c1", "pclk_peri", 6, 13), GATE(PCLK_UART4, "pclk_uart4", "pclk_peri", 6, 12), GATE(PCLK_UART3, "pclk_uart3", "pclk_peri", 6, 11), GATE(PCLK_UART1, "pclk_uart1", "pclk_peri", 6, 9), GATE(PCLK_UART0, "pclk_uart0", "pclk_peri", 6, 8), GATE(PCLK_PS2C, "pclk_ps2c", "pclk_peri", 6, 7), GATE(PCLK_SPI2, "pclk_spi2", "pclk_peri", 6, 6), GATE(PCLK_SPI1, "pclk_spi1", "pclk_peri", 6, 5), GATE(PCLK_SPI0, "pclk_spi0", "pclk_peri", 6, 4), GATE(ACLK_DMAC2, "aclk_dmac2", "aclk_peri", 6, 3), GATE(0, "aclk_peri_axi_matrix", "aclk_peri", 6, 2), GATE(0, "pclk_peri_matrix", "pclk_peri", 6, 1), GATE(0, "hclk_peri_matrix", "hclk_peri", 6, 0), /* CRU_CLKGATE_CON7 */ GATE(HCLK_NANDC1, "hclk_nandc1", "hclk_peri", 7, 15), GATE(HCLK_NANDC0, "hclk_nandc0", "hclk_peri", 7, 14), GATE(0, "hclk_mem", "hclk_peri", 7, 13), GATE(0, "hclk_emem", "hclk_peri", 7, 12), GATE(0, "aclk_peri_niu", "aclk_peri", 7, 11), GATE(0, "hclk_peri_ahb_arbi", "hclk_peri", 7, 10), GATE(0, "hclk_usb_peri", "hclk_peri", 7, 9), /* 8 - Not in TRM - hclk_hsic in Linux */ GATE(HCLK_USBHOST1, "hclk_host1", "hclk_peri", 7, 7), GATE(HCLK_USBHOST0, "hclk_host0", "hclk_peri", 7, 6), GATE(0, "pmu_hclk_otg0", "hclk_peri", 7, 5), GATE(HCLK_OTG0, "hclk_otg0", "hclk_peri", 7, 4), GATE(PCLK_SIM, "pclk_sim", "pclk_peri", 7, 3), GATE(PCLK_TSADC, "pclk_tsadc", "pclk_peri", 7, 2), GATE(PCLK_SARADC, "pclk_saradc", "pclk_peri", 7, 1), GATE(PCLK_I2C5, "pclk_i2c5", "pclk_peri", 7, 0), /* CRU_CLKGATE_CON8 */ GATE(ACLK_MMU, "aclk_mmu", "aclk_peri", 8, 12), /* 11 - 9 27m_tsp, hsadc_1_tsp, hsadc_1_tsp */ GATE(HCLK_TSP, "hclk_tsp", "hclk_peri", 8, 8), GATE(HCLK_HSADC, "hclk_hsadc", "hclk_peri", 8, 7), GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 8, 6), GATE(HCLK_SDIO1, "hclk_sdio1", "hclk_peri", 8, 5), GATE(HCLK_SDIO0, "hclk_sdio0", "hclk_peri", 8, 4), GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 8, 3), GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 8, 2), GATE(PCLK_GMAC, "pclk_gmac", "pclk_peri", 8, 1), GATE(ACLK_GMAC, "aclk_gmac", "aclk_peri", 8, 0), /* CRU_CLKGATE_CON9 */ GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 9, 1), GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vcodec_pre", 9, 0), /* CRU_CLKGATE_CON10 */ GATE(PCLK_PUBL0, "pclk_publ0", "pclk_cpu", 10, 15), GATE(PCLK_DDRUPCTL0, "pclk_ddrupctl0", "pclk_cpu", 10, 14), GATE(0, "aclk_strc_sys", "aclk_cpu", 10, 13), GATE(ACLK_DMAC1, "aclk_dmac1", "aclk_cpu", 10, 12), GATE(HCLK_SPDIF8CH, "hclk_spdif_8ch", "hclk_cpu", 10, 11), GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpu", 10, 10), GATE(HCLK_ROM, "hclk_rom", "hclk_cpu", 10, 9), GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpu", 10, 8), GATE(0, "sclk_intmem2", "aclk_cpu", 10, 7), GATE(0, "sclk_intmem1", "aclk_cpu", 10, 6), GATE(0, "sclk_intmem0", "aclk_cpu", 10, 5), GATE(0, "aclk_intmem", "aclk_cpu", 10, 4), GATE(PCLK_I2C2, "pclk_i2c2", "pclk_cpu", 10, 3), GATE(PCLK_I2C0, "pclk_i2c0", "pclk_cpu", 10, 2), GATE(PCLK_TIMER, "pclk_timer", "pclk_cpu", 10, 1), GATE(PCLK_PWM, "pclk_pwm", "pclk_cpu", 10, 0), /* CRU_CLKGATE_CON11 */ GATE(PCLK_RKPWM, "pclk_rkpwm", "pclk_cpu", 11, 11), GATE(PCLK_EFUSE256, "pclk_efuse_256", "pclk_cpu", 11, 10), GATE(PCLK_UART2, "pclk_uart2", "pclk_cpu", 11, 9), GATE(0, "aclk_ccp", "aclk_cpu", 11, 8), GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_cpu", 11, 7), GATE(ACLK_CRYPTO, "aclk_crypto", "aclk_cpu", 11, 6), GATE(0, "nclk_ddrupctl1", "ddrphy", 11, 5), GATE(0, "nclk_ddrupctl0", "ddrphy", 11, 4), GATE(PCLK_TZPC, "pclk_tzpc", "pclk_cpu", 11, 3), GATE(PCLK_EFUSE1024, "pclk_efuse_1024", "pclk_cpu", 11, 2), GATE(PCLK_PUBL1, "pclk_publ1", "pclk_cpu", 11, 1), GATE(PCLK_DDRUPCTL1, "pclk_ddrupctl1", "pclk_cpu", 11, 0), /* CRU_CLKGATE_CON12 */ GATE(0, "pclk_core_niu", "pclk_dbg_pre", 12, 11), GATE(0, "cs_dbg", "pclk_dbg_pre", 12, 10), GATE(0, "pclk_dbg", "pclk_dbg_pre", 12, 9), GATE(0, "armcore0", "armcore0_s", 12, 8), GATE(0, "armcore1", "armcore1_s", 12, 7), GATE(0, "armcore2", "armcore2_s", 12, 6), GATE(0, "armcore3", "armcore3_s", 12, 5), GATE(0, "l2ram", "l2ram_s", 12, 4), GATE(0, "aclk_core_m0", "aclk_core_m0_s", 12, 3), GATE(0, "aclk_core_mp", "aclk_core_mp_s", 12, 2), GATE(0, "atclk", "atclk_s", 12, 1), GATE(0, "pclk_dbg_pre", "pclk_dbg_pre_s", 12, 0), /* CRU_CLKGATE_CON13 */ GATE(SCLK_HEVC_CORE, "sclk_hevc_core", "sclk_hevc_core_s", 13, 15), GATE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", "sclk_hevc_cabac_s", 13, 14), GATE(ACLK_HEVC, "aclk_hevc", "aclk_hevc_s", 13, 13), GATE(0, "wii", "wifi_frac_s", 13, 12), GATE(SCLK_LCDC_PWM1, "sclk_lcdc_pwm1", "xin24m", 13, 11), GATE(SCLK_LCDC_PWM0, "sclk_lcdc_pwm0", "xin24m", 13, 10), /* 9 - Not in TRM - hsicphy12m_xin12m in Linux */ GATE(0, "c2c_host", "aclk_cpu_src", 13, 8), GATE(SCLK_OTG_ADP, "sclk_otg_adp", "xin32k", 13, 7), GATE(SCLK_OTGPHY2, "sclk_otgphy2", "xin24m", 13, 6), GATE(SCLK_OTGPHY1, "sclk_otgphy1", "xin24m", 13, 5), GATE(SCLK_OTGPHY0, "sclk_otgphy0", "xin24m", 13, 4), GATE(SCLK_EMMC, "sclk_emmc", "sclk_emmc_s", 13, 3), GATE(SCLK_SDIO1, "sclk_sdio1", "sclk_sdio1_s", 13, 2), GATE(SCLK_SDIO0, "sclk_sdio0", "sclk_sdio0_s", 13, 1), GATE(SCLK_SDMMC, "sclk_sdmmc", "sclk_sdmmc_s", 13, 0), /* CRU_CLKGATE_CON14 */ GATE(0, "pclk_alive_niu", "pclk_pd_alive", 14, 12), GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", 14, 11), GATE(PCLK_GPIO8, "pclk_gpio8", "pclk_pd_alive", 14, 8), GATE(PCLK_GPIO7, "pclk_gpio7", "pclk_pd_alive", 14, 7), GATE(PCLK_GPIO6, "pclk_gpio6", "pclk_pd_alive", 14, 6), GATE(PCLK_GPIO5, "pclk_gpio5", "pclk_pd_alive", 14, 5), GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_pd_alive", 14, 4), GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_pd_alive", 14, 3), GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 14, 2), GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 14, 1), /* CRU_CLKGATE_CON15*/ GATE(HCLK_VIP, "hclk_vip", "hclk_vio", 15, 15), GATE(ACLK_VIP, "aclk_vip", "aclk_vio0", 15, 14), GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", 15, 13), GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", 15, 12), GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", 15, 11), GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", 15, 10), GATE(HCLK_VIO_AHB_ARBI, "hclk_vio_ahb_arbi", "hclk_vio",15, 9), GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 15, 8), GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 15, 7), GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 15, 6), GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 15, 5), /* 4 - aclk_lcdc_iep */ GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 15, 3), GATE(ACLK_IEP, "aclk_iep", "aclk_vio0", 15, 2), GATE(HCLK_RGA, "hclk_rga", "hclk_vio", 15, 1), GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 15, 0), /* CRU_CLKGATE_CON16 */ GATE(PCLK_VIO2_H2P, "pclk_vio2_h2p", "hclk_vio", 16, 11), GATE(HCLK_VIO2_H2P, "hclk_vio2_h2p", "hclk_vio", 16, 10), GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "hclk_vio", 16, 9), GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "hclk_vio", 16, 8), GATE(PCLK_LVDS_PHY, "pclk_lvds_phy", "hclk_vio", 16, 7), GATE(PCLK_MIPI_CSI, "pclk_mipi_csi", "hclk_vio", 16, 6), GATE(PCLK_MIPI_DSI1, "pclk_mipi_dsi1", "hclk_vio", 16, 5), GATE(PCLK_MIPI_DSI0, "pclk_mipi_dsi0", "hclk_vio", 16, 4), GATE(PCLK_ISP_IN, "pclk_isp_in", "ext_isp", 16, 3), GATE(ACLK_ISP, "aclk_isp", "aclk_vio1", 16, 2), GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 16, 1), GATE(0, "pclk_vip_in", "ext_vip", 16, 0), /* CRU_CLKGATE_CON17 */ GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pd_pmu", 17, 4), GATE(PCLK_SGRF, "pclk_sgrf", "pclk_pd_pmu", 17, 3), GATE(0, "pclk_pmu_niu", "pclk_pd_pmu", 17, 2), GATE(0, "pclk_intmem1", "pclk_pd_pmu", 17, 1), GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", 17, 0), /* CRU_CLKGATE_CON18 */ GATE(ACLK_GPU, "aclk_gpu", "sclk_gpu", 18, 0), }; /* * PLLs */ #define PLL_RATE_BA(_hz, _ref, _fb, _post, _ba) \ { \ .freq = _hz, \ .refdiv = _ref, \ .fbdiv = _fb, \ .postdiv1 = _post, \ .bwadj = _ba, \ } #define PLL_RATE(_mhz, _ref, _fb, _post) \ PLL_RATE_BA(_mhz, _ref, _fb, _post, ((_fb < 2) ? 1 : _fb >> 1)) static struct rk_clk_pll_rate rk3288_pll_rates[] = { PLL_RATE( 2208000000, 1, 92, 1), PLL_RATE( 2184000000, 1, 91, 1), PLL_RATE( 2160000000, 1, 90, 1), PLL_RATE( 2136000000, 1, 89, 1), PLL_RATE( 2112000000, 1, 88, 1), PLL_RATE( 2088000000, 1, 87, 1), PLL_RATE( 2064000000, 1, 86, 1), PLL_RATE( 2040000000, 1, 85, 1), PLL_RATE( 2016000000, 1, 84, 1), PLL_RATE( 1992000000, 1, 83, 1), PLL_RATE( 1968000000, 1, 82, 1), PLL_RATE( 1944000000, 1, 81, 1), PLL_RATE( 1920000000, 1, 80, 1), PLL_RATE( 1896000000, 1, 79, 1), PLL_RATE( 1872000000, 1, 78, 1), PLL_RATE( 1848000000, 1, 77, 1), PLL_RATE( 1824000000, 1, 76, 1), PLL_RATE( 1800000000, 1, 75, 1), PLL_RATE( 1776000000, 1, 74, 1), PLL_RATE( 1752000000, 1, 73, 1), PLL_RATE( 1728000000, 1, 72, 1), PLL_RATE( 1704000000, 1, 71, 1), PLL_RATE( 1680000000, 1, 70, 1), PLL_RATE( 1656000000, 1, 69, 1), PLL_RATE( 1632000000, 1, 68, 1), PLL_RATE( 1608000000, 1, 67, 1), PLL_RATE( 1560000000, 1, 65, 1), PLL_RATE( 1512000000, 1, 63, 1), PLL_RATE( 1488000000, 1, 62, 1), PLL_RATE( 1464000000, 1, 61, 1), PLL_RATE( 1440000000, 1, 60, 1), PLL_RATE( 1416000000, 1, 59, 1), PLL_RATE( 1392000000, 1, 58, 1), PLL_RATE( 1368000000, 1, 57, 1), PLL_RATE( 1344000000, 1, 56, 1), PLL_RATE( 1320000000, 1, 55, 1), PLL_RATE( 1296000000, 1, 54, 1), PLL_RATE( 1272000000, 1, 53, 1), PLL_RATE( 1248000000, 1, 52, 1), PLL_RATE( 1224000000, 1, 51, 1), PLL_RATE( 1200000000, 1, 50, 1), PLL_RATE( 1188000000, 2, 99, 1), PLL_RATE( 1176000000, 1, 49, 1), PLL_RATE( 1128000000, 1, 47, 1), PLL_RATE( 1104000000, 1, 46, 1), PLL_RATE( 1008000000, 1, 84, 2), PLL_RATE( 912000000, 1, 76, 2), PLL_RATE( 891000000, 8, 594, 2), PLL_RATE( 888000000, 1, 74, 2), PLL_RATE( 816000000, 1, 68, 2), PLL_RATE( 798000000, 2, 133, 2), PLL_RATE( 792000000, 1, 66, 2), PLL_RATE( 768000000, 1, 64, 2), PLL_RATE( 742500000, 8, 495, 2), PLL_RATE( 696000000, 1, 58, 2), PLL_RATE_BA( 621000000, 1, 207, 8, 1), PLL_RATE( 600000000, 1, 50, 2), PLL_RATE_BA( 594000000, 1, 198, 8, 1), PLL_RATE( 552000000, 1, 46, 2), PLL_RATE( 504000000, 1, 84, 4), PLL_RATE( 500000000, 3, 125, 2), PLL_RATE( 456000000, 1, 76, 4), PLL_RATE( 428000000, 1, 107, 6), PLL_RATE( 408000000, 1, 68, 4), PLL_RATE( 400000000, 3, 100, 2), PLL_RATE_BA( 394000000, 1, 197, 12, 1), PLL_RATE( 384000000, 2, 128, 4), PLL_RATE( 360000000, 1, 60, 4), PLL_RATE_BA( 356000000, 1, 178, 12, 1), PLL_RATE_BA( 324000000, 1, 189, 14, 1), PLL_RATE( 312000000, 1, 52, 4), PLL_RATE_BA( 308000000, 1, 154, 12, 1), PLL_RATE_BA( 303000000, 1, 202, 16, 1), PLL_RATE( 300000000, 1, 75, 6), PLL_RATE_BA( 297750000, 2, 397, 16, 1), PLL_RATE_BA( 293250000, 2, 391, 16, 1), PLL_RATE_BA( 292500000, 1, 195, 16, 1), PLL_RATE( 273600000, 1, 114, 10), PLL_RATE_BA( 273000000, 1, 182, 16, 1), PLL_RATE_BA( 270000000, 1, 180, 16, 1), PLL_RATE_BA( 266250000, 2, 355, 16, 1), PLL_RATE_BA( 256500000, 1, 171, 16, 1), PLL_RATE( 252000000, 1, 84, 8), PLL_RATE_BA( 250500000, 1, 167, 16, 1), PLL_RATE_BA( 243428571, 1, 142, 14, 1), PLL_RATE( 238000000, 1, 119, 12), PLL_RATE_BA( 219750000, 2, 293, 16, 1), PLL_RATE_BA( 216000000, 1, 144, 16, 1), PLL_RATE_BA( 213000000, 1, 142, 16, 1), PLL_RATE( 195428571, 1, 114, 14), PLL_RATE( 160000000, 1, 80, 12), PLL_RATE( 157500000, 1, 105, 16), PLL_RATE( 126000000, 1, 84, 16), PLL_RATE( 48000000, 1, 64, 32), {}, }; static struct rk_clk_armclk_rates rk3288_armclk_rates[] = { { 1800000000, 1}, { 1704000000, 1}, { 1608000000, 1}, { 1512000000, 1}, { 1416000000, 1}, { 1200000000, 1}, { 1008000000, 1}, { 816000000, 1}, { 696000000, 1}, { 600000000, 1}, { 408000000, 1}, { 312000000, 1}, { 216000000, 1}, { 126000000, 1}, }; /* Fixed rate clock. */ #define FRATE(_id, _name, _freq) \ { \ .type = RK_CLK_FIXED, \ .clk.fixed = &(struct clk_fixed_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = NULL, \ .clkdef.parent_cnt = 0, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .freq = _freq, \ }, \ } /* Fixed rate multipier/divider. */ #define FACT(_id, _name, _pname, _mult, _div) \ { \ .type = RK_CLK_FIXED, \ .clk.fixed = &(struct clk_fixed_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = (const char *[]){_pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .mult = _mult, \ .div = _div, \ }, \ } /* Standard PLL. */ #define PLL(_id, _name, _base, _shift) \ { \ .type = RK3066_CLK_PLL, \ .clk.pll = &(struct rk_clk_pll_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = pll_src_p, \ .clkdef.parent_cnt = nitems(pll_src_p), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .base_offset = _base, \ .mode_reg = CRU_MODE_CON, \ .mode_shift = _shift, \ .rates = rk3288_pll_rates, \ }, \ } /* Multiplexer. */ #define MUX(_id, _name, _pn, _f, _mo, _ms, _mw) \ { \ .type = RK_CLK_MUX, \ .clk.mux = &(struct rk_clk_mux_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = _pn, \ .clkdef.parent_cnt = nitems(_pn), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = CRU_CLKSEL_CON(_mo), \ .shift = _ms, \ .width = _mw, \ .mux_flags = _f, \ }, \ } #define ARMDIV(_id, _name, _pn, _r, _o, _ds, _dw, _ms, _mw, _mp, _ap) \ { \ .type = RK_CLK_ARMCLK, \ .clk.armclk = &(struct rk_clk_armclk_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = _pn, \ .clkdef.parent_cnt = nitems(_pn), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .muxdiv_offset = CRU_CLKSEL_CON(_o), \ .mux_shift = _ms, \ .mux_width = _mw, \ .div_shift = _ds, \ .div_width = _dw, \ .main_parent = _mp, \ .alt_parent = _ap, \ .rates = _r, \ .nrates = nitems(_r), \ }, \ } /* Fixed rate multipier/divider. */ #define FRACT(_id, _name, _pname, _f, _o) \ { \ .type = RK_CLK_FRACT, \ .clk.fract = &(struct rk_clk_fract_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = (const char *[]){_pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = CRU_CLKSEL_CON(_o), \ .flags = _f, \ }, \ } /* Full composite clock. */ #define COMP(_id, _name, _pnames, _f, _o, _ds, _dw, _ms, _mw) \ { \ .type = RK_CLK_COMPOSITE, \ .clk.composite = &(struct rk_clk_composite_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = _pnames, \ .clkdef.parent_cnt = nitems(_pnames), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .muxdiv_offset = CRU_CLKSEL_CON(_o), \ .mux_shift = _ms, \ .mux_width = _mw, \ .div_shift = _ds, \ .div_width = _dw, \ .flags = RK_CLK_COMPOSITE_HAVE_MUX | _f, \ }, \ } /* Composite clock without mux (divider olnly). */ #define CDIV(_id, _name, _pname, _f, _o, _ds, _dw) \ { \ .type = RK_CLK_COMPOSITE, \ .clk.composite = &(struct rk_clk_composite_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = (const char *[]){_pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .muxdiv_offset = CRU_CLKSEL_CON(_o), \ .div_shift = _ds, \ .div_width = _dw, \ .flags = _f, \ }, \ } #define PLIST(_name) static const char *_name[] PLIST(pll_src_p) = {"xin24m", "xin24m", "xin32k"}; PLIST(armclk_p)= {"apll_core", "gpll_core"}; PLIST(ddrphy_p) = {"dpll_ddr", "gpll_ddr"}; PLIST(aclk_cpu_p) = {"cpll_aclk_cpu", "gpll_aclk_cpu"}; PLIST(cpll_gpll_p) = {"cpll", "gpll"}; PLIST(npll_cpll_gpll_p) = {"npll", "cpll", "gpll"}; PLIST(cpll_gpll_npll_p) = {"cpll", "gpll", "npll"}; PLIST(cpll_gpll_usb480m_p)= {"cpll", "gpll", "usbphy480m_src"}; PLIST(cpll_gpll_usb480m_npll_p) = {"cpll", "gpll", "usbphy480m_src", "npll"}; PLIST(mmc_p) = {"cpll", "gpll", "xin24m", "xin24m"}; PLIST(i2s_pre_p) = {"i2s_src", "i2s_frac", "ext_i2s", "xin12m"}; PLIST(i2s_clkout_p) = {"i2s_pre", "xin12m"}; PLIST(spdif_p) = {"spdif_pre", "spdif_frac", "xin12m"}; PLIST(spdif_8ch_p) = {"spdif_8ch_pre", "spdif_8ch_frac", "xin12m"}; PLIST(uart0_p) = {"uart0_src", "uart0_frac", "xin24m"}; PLIST(uart1_p) = {"uart1_src", "uart1_frac", "xin24m"}; PLIST(uart2_p) = {"uart2_src", "uart2_frac", "xin24m"}; PLIST(uart3_p) = {"uart3_src", "uart3_frac", "xin24m"}; PLIST(uart4_p) = {"uart4_src", "uart4_frac", "xin24m"}; PLIST(vip_out_p) = {"vip_src", "xin24m"}; PLIST(mac_p) = {"mac_pll_src", "ext_gmac"}; PLIST(hsadcout_p) = {"hsadc_src", "ext_hsadc"}; PLIST(edp_24m_p) = {"ext_edp_24m", "xin24m"}; PLIST(tspout_p) = {"cpll", "gpll", "npll", "xin27m"}; PLIST(wifi_p) = {"cpll", "gpll"}; PLIST(usbphy480m_p) = {"sclk_otgphy1_480m", "sclk_otgphy2_480m", "sclk_otgphy0_480m"}; /* PLIST(aclk_vcodec_pre_p) = {"aclk_vepu", "aclk_vdpu"}; */ static struct rk_clk rk3288_clks[] = { /* External clocks */ LINK("xin24m"), FRATE(0, "xin32k", 32000), FRATE(0, "xin27m", 27000000), FRATE(0, "ext_hsadc", 0), FRATE(0, "ext_jtag", 0), FRATE(0, "ext_isp", 0), FRATE(0, "ext_vip", 0), FRATE(0, "ext_i2s", 0), FRATE(0, "ext_edp_24m", 0), FRATE(0, "sclk_otgphy0_480m", 0), FRATE(0, "sclk_otgphy1_480m", 0), FRATE(0, "sclk_otgphy2_480m", 0), FRATE(0, "aclk_vcodec_pre", 0), /* Fixed dividers */ FACT(0, "xin12m", "xin24m", 1, 2), FACT(0, "hclk_vcodec_pre_s", "aclk_vcodec_pre", 1, 4), PLL(PLL_APLL, "apll", CRU_APLL_CON(0), 0), PLL(PLL_DPLL, "dpll", CRU_DPLL_CON(0), 4), PLL(PLL_CPLL, "cpll", CRU_CPLL_CON(0), 8), PLL(PLL_GPLL, "gpll", CRU_GPLL_CON(0), 12), PLL(PLL_NPLL, "npll", CRU_NPLL_CON(0), 14), /* CRU_CLKSEL0_CON */ ARMDIV(ARMCLK, "armclk", armclk_p, rk3288_armclk_rates, 0, 8, 5, 15, 1, 0, 1), CDIV(0, "aclk_core_mp_s", "armclk", 0, 0, 4, 4), CDIV(0, "aclk_core_m0_s", "armclk", 0, 0, 0, 4), /* CRU_CLKSEL1_CON */ CDIV(0, "pclk_cpu_s", "aclk_cpu_pre", 0, 1, 12, 3), CDIV(0, "hclk_cpu_s", "aclk_cpu_pre", RK_CLK_COMPOSITE_DIV_EXP, 1, 8, 2), COMP(0, "aclk_cpu_src", aclk_cpu_p, 0, 1, 3, 5, 15, 1), CDIV(0, "aclk_cpu_pre", "aclk_cpu_src", 0, 1, 0, 3), /* CRU_CLKSEL2_CON */ /* 12:8 testout_div */ CDIV(0, "sclk_tsadc_s", "xin32k", 0, 2, 0, 6), /* CRU_CLKSEL3_CON */ MUX(SCLK_UART4, "sclk_uart4", uart4_p, 0, 3, 8, 2), CDIV(0, "uart4_src_s", "uart_src", 0, 3, 0, 7), /* CRU_CLKSEL4_CON */ MUX(0, "i2s_pre", i2s_pre_p, 0, 4, 8, 2), MUX(0, "i2s0_clkout_s", i2s_clkout_p, 0, 4, 12, 1), COMP(0, "i2s_src_s", cpll_gpll_p, 0, 4, 0, 7, 15, 1), /* CRU_CLKSEL5_CON */ MUX(0, "spdif_src", cpll_gpll_p, 0, 5, 15, 1), MUX(0, "spdif_mux", spdif_p, 0, 5, 8, 2), CDIV(0, "spdif_pre_s", "spdif_src", 0, 5, 0, 7), /* CRU_CLKSEL6_CON */ COMP(0, "sclk_isp_jpe_s", cpll_gpll_npll_p, 0, 6, 8, 6, 14, 2), COMP(0, "sclk_isp_s", cpll_gpll_npll_p, 0, 6, 0, 6, 6, 2), /* CRU_CLKSEL7_CON */ FRACT(0, "uart4_frac_s", "uart4_src", 0, 7), /* CRU_CLKSEL8_CON */ FRACT(0, "i2s_frac_s", "i2s_src", 0, 8), /* CRU_CLKSEL9_CON */ FRACT(0, "spdif_frac_s", "spdif_src", 0, 9), /* CRU_CLKSEL10_CON */ CDIV(0, "pclk_peri_s", "aclk_peri_src", RK_CLK_COMPOSITE_DIV_EXP, 10, 12, 2), CDIV(0, "hclk_peri_s", "aclk_peri_src", RK_CLK_COMPOSITE_DIV_EXP, 10, 8, 2), COMP(0, "aclk_peri_src_s", cpll_gpll_p, 0, 10, 0, 5, 15, 1), /* CRU_CLKSEL11_CON */ COMP(0, "sclk_sdmmc_s", mmc_p, 0, 11, 0, 6, 6, 2), /* CRU_CLKSEL12_CON */ COMP(0, "sclk_emmc_s", mmc_p, 0, 12, 8, 6, 14, 2), COMP(0, "sclk_sdio0_s", mmc_p, 0, 12, 0, 6, 6, 2), /* CRU_CLKSEL13_CON */ MUX(0, "uart_src", cpll_gpll_p, 0, 13, 15, 1), MUX(0, "usbphy480m_src_s", usbphy480m_p, 0, 13, 11, 2), MUX(SCLK_UART0, "sclk_uart0", uart0_p, 0, 13, 8, 2), COMP(0, "uart0_src_s", cpll_gpll_usb480m_npll_p, 0, 13, 0, 7, 13, 2), /* CRU_CLKSEL14_CON */ MUX(SCLK_UART1, "sclk_uart1", uart1_p, 0, 14, 8, 2), CDIV(0, "uart1_src_s", "uart_src", 0, 14, 0, 7), /* CRU_CLKSEL15_CON */ MUX(SCLK_UART2, "sclk_uart2", uart2_p, 0, 15, 8, 2), CDIV(0, "uart2_src_s", "uart_src", 0, 15, 0, 7), /* CRU_CLKSEL16_CON */ MUX(SCLK_UART3, "sclk_uart3", uart3_p, 0, 16, 8, 2), CDIV(0, "uart3_src_s", "uart_src", 0, 16, 0, 7), /* CRU_CLKSEL17_CON */ FRACT(0, "uart0_frac_s", "uart0_src", 0, 17), /* CRU_CLKSEL18_CON */ FRACT(0, "uart1_frac_s", "uart1_src", 0, 18), /* CRU_CLKSEL19_CON */ FRACT(0, "uart2_frac_s", "uart2_src", 0, 19), /* CRU_CLKSEL20_CON */ FRACT(0, "uart3_frac_s", "uart3_src", 0, 20), /* CRU_CLKSEL21_CON */ COMP(0, "mac_pll_src_s", npll_cpll_gpll_p, 0, 21, 8, 5, 0, 2), MUX(SCLK_MAC, "mac_clk", mac_p, 0, 21, 4, 1), /* CRU_CLKSEL22_CON */ MUX(0, "sclk_hsadc_out", hsadcout_p, 0, 22, 4, 1), COMP(0, "hsadc_src_s", cpll_gpll_p, 0, 22, 8, 8, 0, 1), MUX(0, "wifi_src", wifi_p, 0, 22, 1, 1), /* 7 - inverter "sclk_hsadc", "sclk_hsadc_out" */ /* CRU_CLKSEL23_CON */ FRACT(0, "wifi_frac_s", "wifi_src", 0, 23), /* CRU_CLKSEL24_CON */ CDIV(0, "sclk_saradc_s", "xin24m", 0, 24, 8, 8), /* CRU_CLKSEL25_CON */ COMP(0, "sclk_spi1_s", cpll_gpll_p, 0, 25, 8, 7, 15, 1), COMP(0, "sclk_spi0_s", cpll_gpll_p, 0, 25, 0, 7, 7, 1), /* CRU_CLKSEL26_CON */ COMP(SCLK_VIP_OUT, "sclk_vip_out", vip_out_p, 0, 26, 9, 5, 15, 1), MUX(0, "vip_src_s", cpll_gpll_p, 0, 26, 8, 1), CDIV(0, "crypto_s", "aclk_cpu_pre", 0, 26, 6, 2), COMP(0, "ddrphy", ddrphy_p, RK_CLK_COMPOSITE_DIV_EXP, 26, 0, 2, 2, 1), /* CRU_CLKSEL27_CON */ COMP(0, "dclk_vop0_s", cpll_gpll_npll_p, 0, 27, 8, 8, 0, 2), MUX(0, "sclk_edp_24m_s", edp_24m_p, 0, 28, 15, 1), CDIV(0, "hclk_vio", "aclk_vio0", 0, 28, 8, 5), COMP(0, "sclk_edp_s", cpll_gpll_npll_p, 0, 28, 0, 6, 6, 2), /* CRU_CLKSEL29_CON */ COMP(0, "dclk_vop1_s", cpll_gpll_npll_p, 0, 29, 8, 8, 6, 2), /* 4 - inverter "pclk_vip" "pclk_vip_in" */ /* 3 - inverter "pclk_isp", "pclk_isp_in" */ /* CRU_CLKSEL30_CON */ COMP(0, "sclk_rga_s", cpll_gpll_usb480m_p, 0, 30, 8, 5, 14, 2), COMP(0, "aclk_rga_pre_s", cpll_gpll_usb480m_p, 0, 30, 0, 5, 6, 2), /* CRU_CLKSEL31_CON */ COMP(0, "aclk_vio1_s", cpll_gpll_usb480m_p, 0, 31, 8, 5, 14, 2), COMP(0, "aclk_vio0_s", cpll_gpll_usb480m_p, 0, 31, 0, 5, 6, 2), /* CRU_CLKSEL32_CON */ COMP(0, "aclk_vdpu_s", cpll_gpll_usb480m_p, 0, 32, 8, 5, 14, 2), COMP(0, "aclk_vepu_s", cpll_gpll_usb480m_p, 0, 32, 0, 5, 6, 2), /* CRU_CLKSEL33_CON */ CDIV(0, "pclk_pd_alive", "gpll", 0, 33, 8, 5), CDIV(0, "pclk_pd_pmu_s", "gpll", 0, 33, 0, 5), /* CRU_CLKSEL34_CON */ COMP(0, "sclk_sdio1_s", mmc_p, 0, 34, 8, 6, 14, 2), COMP(0, "sclk_gpu_s", cpll_gpll_usb480m_npll_p, 0, 34, 0, 5, 6, 2), /* CRU_CLKSEL35_CON */ COMP(0, "sclk_tspout_s", tspout_p, 0, 35, 8, 5, 14, 2), COMP(0, "sclk_tsp_s", cpll_gpll_npll_p, 0, 35, 0, 5, 6, 2), /* CRU_CLKSEL36_CON */ CDIV(0, "armcore3_s", "armclk", 0, 36, 12, 3), CDIV(0, "armcore2_s", "armclk", 0, 36, 8, 3), CDIV(0, "armcore1_s", "armclk", 0, 36, 4, 3), CDIV(0, "armcore0_s", "armclk", 0, 36, 0, 3), /* CRU_CLKSEL37_CON */ CDIV(0, "pclk_dbg_pre_s", "armclk", 0, 37, 9, 5), CDIV(0, "atclk_s", "armclk", 0, 37, 4, 5), CDIV(0, "l2ram_s", "armclk", 0, 37, 0, 3), /* CRU_CLKSEL38_CON */ COMP(0, "sclk_nandc1_s", cpll_gpll_p, 0, 38, 8, 5, 15, 1), COMP(0, "sclk_nandc0_s", cpll_gpll_p, 0, 38, 0, 5, 7, 1), /* CRU_CLKSEL39_CON */ COMP(0, "aclk_hevc_s", cpll_gpll_npll_p, 0, 39, 8, 5, 14, 2), COMP(0, "sclk_spi2_s", cpll_gpll_p, 0, 39, 0, 7, 7, 1), /* CRU_CLKSEL40_CON */ CDIV(HCLK_HEVC, "hclk_hevc", "aclk_hevc", 0, 40, 12, 2), MUX(0, "spdif_8ch_mux", spdif_8ch_p, 0, 40, 8, 2), CDIV(0, "spdif_8ch_pre_s", "spdif_src", 0, 40, 0, 7), /* CRU_CLKSEL41_CON */ FRACT(0, "spdif_8ch_frac_s", "spdif_8ch_pre", 0, 41), /* CRU_CLKSEL42_CON */ COMP(0, "sclk_hevc_core_s", cpll_gpll_npll_p, 0, 42, 8, 5, 14, 2), COMP(0, "sclk_hevc_cabac_s", cpll_gpll_npll_p, 0, 42, 0, 5, 6, 2), /* * not yet implemented MMC clocks * id name src reg * SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RK3288_SDMMC_CON0 * SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3288_SDMMC_CON1, * SCLK_SDIO0_DRV, "sdio0_drv", "sclk_sdio0", RK3288_SDIO0_CON0, 1), * SCLK_SDIO0_SAMPLE, "sdio0_sample", "sclk_sdio0", RK3288_SDIO0_CON1, 0), * SCLK_SDIO1_DRV, "sdio1_drv", "sclk_sdio1", RK3288_SDIO1_CON0, 1), * SCLK_SDIO1_SAMPLE, "sdio1_sample", "sclk_sdio1", RK3288_SDIO1_CON1, 0), * SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", RK3288_EMMC_CON0, 1), * SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK3288_EMMC_CON1, 0), * * and GFR based mux for "aclk_vcodec_pre" */ }; static int rk3288_cru_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "rockchip,rk3288-cru")) { device_set_desc(dev, "Rockchip RK3288 Clock and Reset Unit"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int rk3288_cru_attach(device_t dev) { struct rk_cru_softc *sc; sc = device_get_softc(dev); sc->dev = dev; sc->gates = rk3288_gates; sc->ngates = nitems(rk3288_gates); sc->clks = rk3288_clks; sc->nclks = nitems(rk3288_clks); sc->reset_num = CRU_SOFTRST_SIZE * 16; sc->reset_offset = CRU_SOFTRST_CON(0); return (rk_cru_attach(dev)); } static device_method_t rk3288_cru_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk3288_cru_probe), DEVMETHOD(device_attach, rk3288_cru_attach), DEVMETHOD_END }; static devclass_t rk3288_cru_devclass; DEFINE_CLASS_1(rk3288_cru, rk3288_cru_driver, rk3288_cru_methods, sizeof(struct rk_cru_softc), rk_cru_driver); EARLY_DRIVER_MODULE(rk3288_cru, simplebus, rk3288_cru_driver, rk3288_cru_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 1);