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 58struct ccm_softc { 59 device_t dev; 60 struct resource *mem_res; 61}; 62 63static struct ccm_softc *ccm_sc; 64 65static inline uint32_t 66RD4(struct ccm_softc *sc, bus_size_t off) 67{ 68 69 return (bus_read_4(sc->mem_res, off)); 70} 71 72static inline void 73WR4(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 */ 88static void 89ccm_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 101static int 102ccm_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 114static int 115ccm_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 159out: 160 161 if (err != 0) 162 ccm_detach(dev); 163 164 return (err); 165} 166 167static int 168ccm_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 182void 183imx_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 234void 235imx_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 245void 246imx_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 289uint32_t 290imx_ccm_ipg_hz(void) 291{ 292 293 return (66000000); 294} 295 296uint32_t 297imx_ccm_perclk_hz(void) 298{ 299 300 return (66000000); 301} 302 303uint32_t 304imx_ccm_sdhci_hz(void) 305{ 306 307 return (200000000); 308} 309 310uint32_t 311imx_ccm_uart_hz(void) 312{ 313 314 return (80000000); 315} 316 317uint32_t 318imx_ccm_ahb_hz(void) 319{ 320 return (132000000); 321} 322 323uint32_t 324imx_ccm_get_cacrr(void) 325{ 326 327 return (RD4(ccm_sc, CCM_CACCR)); 328} 329 330void 331imx_ccm_set_cacrr(uint32_t divisor) 332{ 333 334 WR4(ccm_sc, CCM_CACCR, divisor); 335} 336 337static 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 346static driver_t ccm_driver = { 347 "ccm", 348 ccm_methods, 349 sizeof(struct ccm_softc) 350}; 351 352static devclass_t ccm_devclass; 353 354EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0, 355 BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); 356 357