1 /*- 2 * Copyright (c) 2013 Ian Lepore <ian@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 /* 31 * Clocks and power control driver for Freescale i.MX6 family of SoCs. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/bus.h> 39 #include <sys/rman.h> 40 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include <machine/bus.h> 45 46 #include <arm/freescale/imx/imx6_anatopreg.h> 47 #include <arm/freescale/imx/imx6_anatopvar.h> 48 #include <arm/freescale/imx/imx6_ccmreg.h> 49 #include <arm/freescale/imx/imx_machdep.h> 50 #include <arm/freescale/imx/imx_ccmvar.h> 51 52 #ifndef CCGR_CLK_MODE_ALWAYS 53 #define CCGR_CLK_MODE_OFF 0 54 #define CCGR_CLK_MODE_RUNMODE 1 55 #define CCGR_CLK_MODE_ALWAYS 3 56 #endif 57 58 struct ccm_softc { 59 device_t dev; 60 struct resource *mem_res; 61 }; 62 63 static struct ccm_softc *ccm_sc; 64 65 static inline uint32_t 66 RD4(struct ccm_softc *sc, bus_size_t off) 67 { 68 69 return (bus_read_4(sc->mem_res, off)); 70 } 71 72 static inline void 73 WR4(struct ccm_softc *sc, bus_size_t off, uint32_t val) 74 { 75 76 bus_write_4(sc->mem_res, off, val); 77 } 78 79 /* 80 * Until we have a fully functional ccm driver which implements the fdt_clock 81 * interface, use the age-old workaround of unconditionally enabling the clocks 82 * for devices we might need to use. The SoC defaults to most clocks enabled, 83 * but the rom boot code and u-boot disable a few of them. We turn on only 84 * what's needed to run the chip plus devices we have drivers for, and turn off 85 * devices we don't yet have drivers for. (Note that USB is not turned on here 86 * because that is one we do when the driver asks for it.) 87 */ 88 static void 89 ccm_init_gates(struct ccm_softc *sc) 90 { 91 /* Turns on... */ 92 WR4(sc, CCM_CCGR0, 0x0000003f); /* ahpbdma, aipstz 1 & 2 busses */ 93 WR4(sc, CCM_CCGR1, 0x00300c00); /* gpt, enet */ 94 WR4(sc, CCM_CCGR2, 0x0fffffc0); /* ipmux & ipsync (bridges), iomux, i2c */ 95 WR4(sc, CCM_CCGR3, 0x3ff00000); /* DDR memory controller */ 96 WR4(sc, CCM_CCGR4, 0x0000f300); /* pl301 bus crossbar */ 97 WR4(sc, CCM_CCGR5, 0x0f000000); /* uarts */ 98 WR4(sc, CCM_CCGR6, 0x000000cc); /* usdhc 1 & 3 */ 99 } 100 101 static int 102 ccm_detach(device_t dev) 103 { 104 struct ccm_softc *sc; 105 106 sc = device_get_softc(dev); 107 108 if (sc->mem_res != NULL) 109 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 110 111 return (0); 112 } 113 114 static int 115 ccm_attach(device_t dev) 116 { 117 struct ccm_softc *sc; 118 int err, rid; 119 uint32_t reg; 120 121 sc = device_get_softc(dev); 122 err = 0; 123 124 /* Allocate bus_space resources. */ 125 rid = 0; 126 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 127 RF_ACTIVE); 128 if (sc->mem_res == NULL) { 129 device_printf(dev, "Cannot allocate memory resources\n"); 130 err = ENXIO; 131 goto out; 132 } 133 134 ccm_sc = sc; 135 136 /* 137 * Configure the Low Power Mode setting to leave the ARM core power on 138 * when a WFI instruction is executed. This lets the MPCore timers and 139 * GIC continue to run, which is helpful when the only thing that can 140 * wake you up is an MPCore Private Timer interrupt delivered via GIC. 141 * 142 * XXX Based on the docs, setting CCM_CGPR_INT_MEM_CLK_LPM shouldn't be 143 * required when the LPM bits are set to LPM_RUN. But experimentally 144 * I've experienced a fairly rare lockup when not setting it. I was 145 * unable to prove conclusively that the lockup was related to power 146 * management or that this definitively fixes it. Revisit this. 147 */ 148 reg = RD4(sc, CCM_CGPR); 149 reg |= CCM_CGPR_INT_MEM_CLK_LPM; 150 WR4(sc, CCM_CGPR, reg); 151 reg = RD4(sc, CCM_CLPCR); 152 reg = (reg & ~CCM_CLPCR_LPM_MASK) | CCM_CLPCR_LPM_RUN; 153 WR4(sc, CCM_CLPCR, reg); 154 155 ccm_init_gates(sc); 156 157 err = 0; 158 159 out: 160 161 if (err != 0) 162 ccm_detach(dev); 163 164 return (err); 165 } 166 167 static int 168 ccm_probe(device_t dev) 169 { 170 171 if (!ofw_bus_status_okay(dev)) 172 return (ENXIO); 173 174 if (ofw_bus_is_compatible(dev, "fsl,imx6q-ccm") == 0) 175 return (ENXIO); 176 177 device_set_desc(dev, "Freescale i.MX6 Clock Control Module"); 178 179 return (BUS_PROBE_DEFAULT); 180 } 181 182 void 183 imx_ccm_usb_enable(device_t _usbdev) 184 { 185 186 /* 187 * For imx6, the USBOH3 clock gate is bits 0-1 of CCGR6, so no need for 188 * shifting and masking here, just set the low-order two bits to ALWAYS. 189 */ 190 WR4(ccm_sc, CCM_CCGR6, RD4(ccm_sc, CCM_CCGR6) | CCGR_CLK_MODE_ALWAYS); 191 } 192 193 void 194 imx_ccm_usbphy_enable(device_t _phydev) 195 { 196 /* 197 * XXX Which unit? 198 * Right now it's not clear how to figure from fdt data which phy unit 199 * we're supposed to operate on. Until this is worked out, just enable 200 * both PHYs. 201 */ 202 #if 0 203 int phy_num, regoff; 204 205 phy_num = 0; /* XXX */ 206 207 switch (phy_num) { 208 case 0: 209 regoff = 0; 210 break; 211 case 1: 212 regoff = 0x10; 213 break; 214 default: 215 device_printf(ccm_sc->dev, "Bad PHY number %u,\n", 216 phy_num); 217 return; 218 } 219 220 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + regoff, 221 IMX6_ANALOG_CCM_PLL_USB_ENABLE | 222 IMX6_ANALOG_CCM_PLL_USB_POWER | 223 IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); 224 #else 225 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0, 226 IMX6_ANALOG_CCM_PLL_USB_ENABLE | 227 IMX6_ANALOG_CCM_PLL_USB_POWER | 228 IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); 229 230 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0x10, 231 IMX6_ANALOG_CCM_PLL_USB_ENABLE | 232 IMX6_ANALOG_CCM_PLL_USB_POWER | 233 IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); 234 #endif 235 } 236 237 uint32_t 238 imx_ccm_ipg_hz(void) 239 { 240 241 return (66000000); 242 } 243 244 uint32_t 245 imx_ccm_perclk_hz(void) 246 { 247 248 return (66000000); 249 } 250 251 uint32_t 252 imx_ccm_sdhci_hz(void) 253 { 254 255 return (200000000); 256 } 257 258 uint32_t 259 imx_ccm_uart_hz(void) 260 { 261 262 return (80000000); 263 } 264 265 uint32_t 266 imx_ccm_ahb_hz(void) 267 { 268 return (132000000); 269 } 270 271 static device_method_t ccm_methods[] = { 272 /* Device interface */ 273 DEVMETHOD(device_probe, ccm_probe), 274 DEVMETHOD(device_attach, ccm_attach), 275 DEVMETHOD(device_detach, ccm_detach), 276 277 DEVMETHOD_END 278 }; 279 280 static driver_t ccm_driver = { 281 "ccm", 282 ccm_methods, 283 sizeof(struct ccm_softc) 284 }; 285 286 static devclass_t ccm_devclass; 287 288 EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0, 289 BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); 290 291