Index: stable/10/sys/arm/freescale/imx/imx6_ccm.c =================================================================== --- stable/10/sys/arm/freescale/imx/imx6_ccm.c (revision 278731) +++ stable/10/sys/arm/freescale/imx/imx6_ccm.c (revision 278732) @@ -1,290 +1,290 @@ /*- * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Clocks and power control driver for Freescale i.MX6 family of SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef CCGR_CLK_MODE_ALWAYS #define CCGR_CLK_MODE_OFF 0 #define CCGR_CLK_MODE_RUNMODE 1 #define CCGR_CLK_MODE_ALWAYS 3 #endif struct ccm_softc { device_t dev; struct resource *mem_res; }; static struct ccm_softc *ccm_sc; static inline uint32_t RD4(struct ccm_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct ccm_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } /* * Until we have a fully functional ccm driver which implements the fdt_clock * interface, use the age-old workaround of unconditionally enabling the clocks * for devices we might need to use. The SoC defaults to most clocks enabled, * but the rom boot code and u-boot disable a few of them. We turn on only * what's needed to run the chip plus devices we have drivers for, and turn off * devices we don't yet have drivers for. (Note that USB is not turned on here * because that is one we do when the driver asks for it.) */ static void ccm_init_gates(struct ccm_softc *sc) { /* Turns on... */ WR4(sc, CCM_CCGR0, 0x0000003f); /* ahpbdma, aipstz 1 & 2 busses */ WR4(sc, CCM_CCGR1, 0x00300c00); /* gpt, enet */ WR4(sc, CCM_CCGR2, 0x0fffffc0); /* ipmux & ipsync (bridges), iomux, i2c */ WR4(sc, CCM_CCGR3, 0x3ff00000); /* DDR memory controller */ WR4(sc, CCM_CCGR4, 0x0000f300); /* pl301 bus crossbar */ WR4(sc, CCM_CCGR5, 0x0f000000); /* uarts */ - WR4(sc, CCM_CCGR6, 0x000000cc); /* usdhc 1 & 3 */ + WR4(sc, CCM_CCGR6, 0x000000ff); /* usdhc 1-4 */ } static int ccm_detach(device_t dev) { struct ccm_softc *sc; sc = device_get_softc(dev); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (0); } static int ccm_attach(device_t dev) { struct ccm_softc *sc; int err, rid; uint32_t reg; sc = device_get_softc(dev); err = 0; /* Allocate bus_space resources. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); err = ENXIO; goto out; } ccm_sc = sc; /* * Configure the Low Power Mode setting to leave the ARM core power on * when a WFI instruction is executed. This lets the MPCore timers and * GIC continue to run, which is helpful when the only thing that can * wake you up is an MPCore Private Timer interrupt delivered via GIC. * * XXX Based on the docs, setting CCM_CGPR_INT_MEM_CLK_LPM shouldn't be * required when the LPM bits are set to LPM_RUN. But experimentally * I've experienced a fairly rare lockup when not setting it. I was * unable to prove conclusively that the lockup was related to power * management or that this definitively fixes it. Revisit this. */ reg = RD4(sc, CCM_CGPR); reg |= CCM_CGPR_INT_MEM_CLK_LPM; WR4(sc, CCM_CGPR, reg); reg = RD4(sc, CCM_CLPCR); reg = (reg & ~CCM_CLPCR_LPM_MASK) | CCM_CLPCR_LPM_RUN; WR4(sc, CCM_CLPCR, reg); ccm_init_gates(sc); err = 0; out: if (err != 0) ccm_detach(dev); return (err); } static int ccm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "fsl,imx6q-ccm") == 0) return (ENXIO); device_set_desc(dev, "Freescale i.MX6 Clock Control Module"); return (BUS_PROBE_DEFAULT); } void imx_ccm_usb_enable(device_t _usbdev) { /* * For imx6, the USBOH3 clock gate is bits 0-1 of CCGR6, so no need for * shifting and masking here, just set the low-order two bits to ALWAYS. */ WR4(ccm_sc, CCM_CCGR6, RD4(ccm_sc, CCM_CCGR6) | CCGR_CLK_MODE_ALWAYS); } void imx_ccm_usbphy_enable(device_t _phydev) { /* * XXX Which unit? * Right now it's not clear how to figure from fdt data which phy unit * we're supposed to operate on. Until this is worked out, just enable * both PHYs. */ #if 0 int phy_num, regoff; phy_num = 0; /* XXX */ switch (phy_num) { case 0: regoff = 0; break; case 1: regoff = 0x10; break; default: device_printf(ccm_sc->dev, "Bad PHY number %u,\n", phy_num); return; } imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + regoff, IMX6_ANALOG_CCM_PLL_USB_ENABLE | IMX6_ANALOG_CCM_PLL_USB_POWER | IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); #else imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0, IMX6_ANALOG_CCM_PLL_USB_ENABLE | IMX6_ANALOG_CCM_PLL_USB_POWER | IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0x10, IMX6_ANALOG_CCM_PLL_USB_ENABLE | IMX6_ANALOG_CCM_PLL_USB_POWER | IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); #endif } uint32_t imx_ccm_ipg_hz(void) { return (66000000); } uint32_t imx_ccm_perclk_hz(void) { return (66000000); } uint32_t imx_ccm_sdhci_hz(void) { return (200000000); } uint32_t imx_ccm_uart_hz(void) { return (80000000); } uint32_t imx_ccm_ahb_hz(void) { return (132000000); } static device_method_t ccm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ccm_probe), DEVMETHOD(device_attach, ccm_attach), DEVMETHOD(device_detach, ccm_detach), DEVMETHOD_END }; static driver_t ccm_driver = { "ccm", ccm_methods, sizeof(struct ccm_softc) }; static devclass_t ccm_devclass; EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); Index: stable/10/sys/arm/freescale/imx/imx_iomux.c =================================================================== --- stable/10/sys/arm/freescale/imx/imx_iomux.c (revision 278731) +++ stable/10/sys/arm/freescale/imx/imx_iomux.c (revision 278732) @@ -1,300 +1,325 @@ /*- * Copyright (c) 2014 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Pin mux and pad control driver for imx5 and imx6. * * This driver implements the fdt_pinctrl interface for configuring the gpio and * peripheral pins based on fdt configuration data. * * When the driver attaches, it walks the entire fdt tree and automatically * configures the pins for each device which has a pinctrl-0 property and whose * status is "okay". In addition it implements the fdt_pinctrl_configure() * method which any other driver can call at any time to reconfigure its pins. * * The nature of the fsl,pins property in fdt data makes this driver's job very * easy. Instead of representing each pin and pad configuration using symbolic * properties such as pullup-enable="true" and so on, the data simply contains * the addresses of the registers that control the pins, and the raw values to * store in those registers. * * The imx5 and imx6 SoCs also have a small number of "general purpose * registers" in the iomuxc device which are used to control an assortment * of completely unrelated aspects of SoC behavior. This driver provides other * drivers with direct access to those registers via simple accessor functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct iomux_softc { device_t dev; struct resource *mem_res; u_int last_gpreg; }; static struct iomux_softc *iomux_sc; static struct ofw_compat_data compat_data[] = { {"fsl,imx6dl-iomuxc", true}, {"fsl,imx6q-iomuxc", true}, {"fsl,imx6sl-iomuxc", true}, {"fsl,imx6sx-iomuxc", true}, {"fsl,imx53-iomuxc", true}, {"fsl,imx51-iomuxc", true}, {NULL, false}, }; /* * Each tuple in an fsl,pins property contains these fields. */ struct pincfg { uint32_t mux_reg; uint32_t padconf_reg; uint32_t input_reg; uint32_t mux_val; uint32_t input_val; uint32_t padconf_val; }; #define PADCONF_NONE (1U << 31) /* Do not configure pad. */ #define PADCONF_SION (1U << 30) /* Force SION bit in mux register. */ #define PADMUX_SION (1U << 4) /* The SION bit in the mux register. */ static inline uint32_t RD4(struct iomux_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct iomux_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } +static void +iomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val) +{ + u_int select, mask, shift, width; + + /* If register and value are zero, there is nothing to configure. */ + if (reg == 0 && val == 0) + return; + + /* + * If the config value has 0xff in the high byte it is encoded: + * 31 23 15 7 0 + * | 0xff | shift | width | select | + * We need to mask out the old select value and OR in the new, using a + * mask of the given width and shifting the values up by shift. + */ + if ((val & 0xff000000) == 0xff000000) { + select = val & 0x000000ff; + width = (val & 0x0000ff00) >> 8; + shift = (val & 0x00ff0000) >> 16; + mask = ((1u << width) - 1) << shift; + val = (RD4(sc, reg) & ~mask) | (select << shift); + } + WR4(sc, reg, val); +} + static int iomux_configure_pins(device_t dev, phandle_t cfgxref) { - struct iomux_softc * sc; + struct iomux_softc *sc; struct pincfg *cfgtuples, *cfg; phandle_t cfgnode; int i, ntuples; uint32_t sion; sc = device_get_softc(dev); cfgnode = OF_node_from_xref(cfgxref); ntuples = OF_getencprop_alloc(cfgnode, "fsl,pins", sizeof(*cfgtuples), (void **)&cfgtuples); if (ntuples < 0) return (ENOENT); if (ntuples == 0) return (0); /* Empty property is not an error. */ for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) { sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0; WR4(sc, cfg->mux_reg, cfg->mux_val | sion); - if (cfg->input_reg != 0) - WR4(sc, cfg->input_reg, cfg->input_val); + iomux_configure_input(sc, cfg->input_reg, cfg->input_val); if ((cfg->padconf_val & PADCONF_NONE) == 0) WR4(sc, cfg->padconf_reg, cfg->padconf_val); if (bootverbose) { char name[32]; OF_getprop(cfgnode, "name", &name, sizeof(name)); printf("%16s: muxreg 0x%04x muxval 0x%02x " "inpreg 0x%04x inpval 0x%02x " "padreg 0x%04x padval 0x%08x\n", name, cfg->mux_reg, cfg->mux_val | sion, cfg->input_reg, cfg->input_val, cfg->padconf_reg, cfg->padconf_val); } } free(cfgtuples, M_OFWPROP); return (0); } static int iomux_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, "Freescale i.MX pin configuration"); return (BUS_PROBE_DEFAULT); } static int iomux_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int iomux_attach(device_t dev) { struct iomux_softc * sc; int rid; sc = device_get_softc(dev); sc->dev = dev; switch (imx_soc_type()) { case IMXSOC_51: sc->last_gpreg = 1; break; case IMXSOC_53: sc->last_gpreg = 2; break; case IMXSOC_6DL: case IMXSOC_6S: case IMXSOC_6SL: case IMXSOC_6Q: sc->last_gpreg = 13; break; default: device_printf(dev, "Unknown SoC type\n"); return (ENXIO); } rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); return (ENXIO); } iomux_sc = sc; /* * Register as a pinctrl device, and call the convenience function that * walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any * pinctrl-0 property cells whose xref phandle refers to a configuration * that is a child node of our node in the tree. * * The pinctrl bindings documentation specifically mentions that the * pinctrl device itself may have a pinctrl-0 property which contains * static configuration to be applied at device init time. The tree * walk will automatically handle this for us when it passes through our * node in the tree. */ fdt_pinctrl_register(dev, "fsl,pins"); fdt_pinctrl_configure_tree(dev); return (0); } uint32_t imx_iomux_gpr_get(u_int regnum) { struct iomux_softc * sc; sc = iomux_sc; KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); return (RD4(iomux_sc, regnum * 4)); } void imx_iomux_gpr_set(u_int regnum, uint32_t val) { struct iomux_softc * sc; sc = iomux_sc; KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); WR4(iomux_sc, regnum * 4, val); } void imx_iomux_gpr_set_masked(u_int regnum, uint32_t clrbits, uint32_t setbits) { struct iomux_softc * sc; uint32_t val; sc = iomux_sc; KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); val = RD4(iomux_sc, regnum * 4); val = (val & ~clrbits) | setbits; WR4(iomux_sc, regnum * 4, val); } static device_method_t imx_iomux_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iomux_probe), DEVMETHOD(device_attach, iomux_attach), DEVMETHOD(device_detach, iomux_detach), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins), DEVMETHOD_END }; static driver_t imx_iomux_driver = { "imx_iomux", imx_iomux_methods, sizeof(struct iomux_softc), }; static devclass_t imx_iomux_devclass; EARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver, imx_iomux_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); Index: stable/10 =================================================================== --- stable/10 (revision 278731) +++ stable/10 (revision 278732) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r277555,277568