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, 0x0ffc00c0); /* uarts, ssi, sdma */ 98 WR4(sc, CCM_CCGR6, 0x000000ff); /* usdhc 1-4 */ 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_ssi_configure(device_t _ssidev) 184 { 185 struct ccm_softc *sc; 186 uint32_t reg; 187 188 sc = ccm_sc; 189 190 /* 191 * Select PLL4 (Audio PLL) clock multiplexer as source. 192 * PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM). 193 */ 194 195 reg = RD4(sc, CCM_CSCMR1); 196 reg &= ~(SSI_CLK_SEL_M << SSI1_CLK_SEL_S); 197 reg |= (SSI_CLK_SEL_PLL4 << SSI1_CLK_SEL_S); 198 reg &= ~(SSI_CLK_SEL_M << SSI2_CLK_SEL_S); 199 reg |= (SSI_CLK_SEL_PLL4 << SSI2_CLK_SEL_S); 200 reg &= ~(SSI_CLK_SEL_M << SSI3_CLK_SEL_S); 201 reg |= (SSI_CLK_SEL_PLL4 << SSI3_CLK_SEL_S); 202 WR4(sc, CCM_CSCMR1, reg); 203 204 /* 205 * Ensure we have set hardware-default values 206 * for pre and post dividers. 207 */ 208 209 /* SSI1 and SSI3 */ 210 reg = RD4(sc, CCM_CS1CDR); 211 /* Divide by 2 */ 212 reg &= ~(SSI_CLK_PODF_MASK << SSI1_CLK_PODF_SHIFT); 213 reg &= ~(SSI_CLK_PODF_MASK << SSI3_CLK_PODF_SHIFT); 214 reg |= (0x1 << SSI1_CLK_PODF_SHIFT); 215 reg |= (0x1 << SSI3_CLK_PODF_SHIFT); 216 /* Divide by 4 */ 217 reg &= ~(SSI_CLK_PRED_MASK << SSI1_CLK_PRED_SHIFT); 218 reg &= ~(SSI_CLK_PRED_MASK << SSI3_CLK_PRED_SHIFT); 219 reg |= (0x3 << SSI1_CLK_PRED_SHIFT); 220 reg |= (0x3 << SSI3_CLK_PRED_SHIFT); 221 WR4(sc, CCM_CS1CDR, reg); 222 223 /* SSI2 */ 224 reg = RD4(sc, CCM_CS2CDR); 225 /* Divide by 2 */ 226 reg &= ~(SSI_CLK_PODF_MASK << SSI2_CLK_PODF_SHIFT); 227 reg |= (0x1 << SSI2_CLK_PODF_SHIFT); 228 /* Divide by 4 */ 229 reg &= ~(SSI_CLK_PRED_MASK << SSI2_CLK_PRED_SHIFT); 230 reg |= (0x3 << SSI2_CLK_PRED_SHIFT); 231 WR4(sc, CCM_CS2CDR, reg); 232 } 233 234 void 235 imx_ccm_usb_enable(device_t _usbdev) 236 { 237 238 /* 239 * For imx6, the USBOH3 clock gate is bits 0-1 of CCGR6, so no need for 240 * shifting and masking here, just set the low-order two bits to ALWAYS. 241 */ 242 WR4(ccm_sc, CCM_CCGR6, RD4(ccm_sc, CCM_CCGR6) | CCGR_CLK_MODE_ALWAYS); 243 } 244 245 void 246 imx_ccm_usbphy_enable(device_t _phydev) 247 { 248 /* 249 * XXX Which unit? 250 * Right now it's not clear how to figure from fdt data which phy unit 251 * we're supposed to operate on. Until this is worked out, just enable 252 * both PHYs. 253 */ 254 #if 0 255 int phy_num, regoff; 256 257 phy_num = 0; /* XXX */ 258 259 switch (phy_num) { 260 case 0: 261 regoff = 0; 262 break; 263 case 1: 264 regoff = 0x10; 265 break; 266 default: 267 device_printf(ccm_sc->dev, "Bad PHY number %u,\n", 268 phy_num); 269 return; 270 } 271 272 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + regoff, 273 IMX6_ANALOG_CCM_PLL_USB_ENABLE | 274 IMX6_ANALOG_CCM_PLL_USB_POWER | 275 IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); 276 #else 277 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0, 278 IMX6_ANALOG_CCM_PLL_USB_ENABLE | 279 IMX6_ANALOG_CCM_PLL_USB_POWER | 280 IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); 281 282 imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0x10, 283 IMX6_ANALOG_CCM_PLL_USB_ENABLE | 284 IMX6_ANALOG_CCM_PLL_USB_POWER | 285 IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); 286 #endif 287 } 288 289 uint32_t 290 imx_ccm_ipg_hz(void) 291 { 292 293 return (66000000); 294 } 295 296 uint32_t 297 imx_ccm_perclk_hz(void) 298 { 299 300 return (66000000); 301 } 302 303 uint32_t 304 imx_ccm_sdhci_hz(void) 305 { 306 307 return (200000000); 308 } 309 310 uint32_t 311 imx_ccm_uart_hz(void) 312 { 313 314 return (80000000); 315 } 316 317 uint32_t 318 imx_ccm_ahb_hz(void) 319 { 320 return (132000000); 321 } 322 323 uint32_t 324 imx_ccm_get_cacrr(void) 325 { 326 327 return (RD4(ccm_sc, CCM_CACCR)); 328 } 329 330 void 331 imx_ccm_set_cacrr(uint32_t divisor) 332 { 333 334 WR4(ccm_sc, CCM_CACCR, divisor); 335 } 336 337 static device_method_t ccm_methods[] = { 338 /* Device interface */ 339 DEVMETHOD(device_probe, ccm_probe), 340 DEVMETHOD(device_attach, ccm_attach), 341 DEVMETHOD(device_detach, ccm_detach), 342 343 DEVMETHOD_END 344 }; 345 346 static driver_t ccm_driver = { 347 "ccm", 348 ccm_methods, 349 sizeof(struct ccm_softc) 350 }; 351 352 static devclass_t ccm_devclass; 353 354 EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0, 355 BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); 356 357