1 /*- 2 * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com> 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 /* 28 * Vybrid Family Clock Controller Module (CCM) 29 * Chapter 10, Vybrid Reference Manual, Rev. 5, 07/2013 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/bus.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 #include <sys/malloc.h> 41 #include <sys/rman.h> 42 #include <sys/timeet.h> 43 #include <sys/timetc.h> 44 #include <sys/watchdog.h> 45 46 #include <dev/fdt/fdt_common.h> 47 #include <dev/ofw/openfirm.h> 48 #include <dev/ofw/ofw_bus.h> 49 #include <dev/ofw/ofw_bus_subr.h> 50 51 #include <machine/bus.h> 52 #include <machine/cpu.h> 53 #include <machine/intr.h> 54 55 #include <arm/freescale/vybrid/vf_common.h> 56 57 #define CCM_CCR 0x00 /* Control Register */ 58 #define CCM_CSR 0x04 /* Status Register */ 59 #define CCM_CCSR 0x08 /* Clock Switcher Register */ 60 #define CCM_CACRR 0x0C /* ARM Clock Root Register */ 61 #define CCM_CSCMR1 0x10 /* Serial Clock Multiplexer Register 1 */ 62 #define CCM_CSCDR1 0x14 /* Serial Clock Divider Register 1 */ 63 #define CCM_CSCDR2 0x18 /* Serial Clock Divider Register 2 */ 64 #define CCM_CSCDR3 0x1C /* Serial Clock Divider Register 3 */ 65 #define CCM_CSCMR2 0x20 /* Serial Clock Multiplexer Register 2 */ 66 #define CCM_CTOR 0x28 /* Testing Observability Register */ 67 #define CCM_CLPCR 0x2C /* Low Power Control Register */ 68 #define CCM_CISR 0x30 /* Interrupt Status Register */ 69 #define CCM_CIMR 0x34 /* Interrupt Mask Register */ 70 #define CCM_CCOSR 0x38 /* Clock Output Source Register */ 71 #define CCM_CGPR 0x3C /* General Purpose Register */ 72 73 #define CCM_CCGRN 12 74 #define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */ 75 #define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */ 76 #define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */ 77 78 #define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */ 79 #define CCM_CCOWR 0x8C /* CORE Wakeup Register */ 80 81 #define PLL3_PFD4_EN (1U << 31) 82 #define PLL3_PFD3_EN (1 << 30) 83 #define PLL3_PFD2_EN (1 << 29) 84 #define PLL3_PFD1_EN (1 << 28) 85 #define PLL2_PFD4_EN (1 << 15) 86 #define PLL2_PFD3_EN (1 << 14) 87 #define PLL2_PFD2_EN (1 << 13) 88 #define PLL2_PFD1_EN (1 << 12) 89 #define PLL1_PFD4_EN (1 << 11) 90 #define PLL1_PFD3_EN (1 << 10) 91 #define PLL1_PFD2_EN (1 << 9) 92 #define PLL1_PFD1_EN (1 << 8) 93 94 /* CCM_CCR */ 95 #define FIRC_EN (1 << 16) 96 #define FXOSC_EN (1 << 12) 97 #define FXOSC_RDY (1 << 5) 98 99 /* CCM_CSCDR1 */ 100 #define ENET_TS_EN (1 << 23) 101 #define RMII_CLK_EN (1 << 24) 102 #define SAI3_EN (1 << 19) 103 104 /* CCM_CSCDR2 */ 105 #define ESAI_EN (1 << 30) 106 #define ESDHC1_EN (1 << 29) 107 #define ESDHC0_EN (1 << 28) 108 #define NFC_EN (1 << 9) 109 #define ESDHC1_DIV_S 20 110 #define ESDHC1_DIV_M 0xf 111 #define ESDHC0_DIV_S 16 112 #define ESDHC0_DIV_M 0xf 113 114 /* CCM_CSCDR3 */ 115 #define DCU0_EN (1 << 19) 116 117 #define QSPI1_EN (1 << 12) 118 #define QSPI1_DIV (1 << 11) 119 #define QSPI1_X2_DIV (1 << 10) 120 #define QSPI1_X4_DIV_M 0x3 121 #define QSPI1_X4_DIV_S 8 122 123 #define QSPI0_EN (1 << 4) 124 #define QSPI0_DIV (1 << 3) 125 #define QSPI0_X2_DIV (1 << 2) 126 #define QSPI0_X4_DIV_M 0x3 127 #define QSPI0_X4_DIV_S 0 128 129 #define SAI3_DIV_SHIFT 12 130 #define SAI3_DIV_MASK 0xf 131 #define ESAI_DIV_SHIFT 24 132 #define ESAI_DIV_MASK 0xf 133 134 #define PLL4_CLK_DIV_SHIFT 6 135 #define PLL4_CLK_DIV_MASK 0x7 136 137 #define IPG_CLK_DIV_SHIFT 11 138 #define IPG_CLK_DIV_MASK 0x3 139 140 #define ESAI_CLK_SEL_SHIFT 20 141 #define ESAI_CLK_SEL_MASK 0x3 142 143 #define SAI3_CLK_SEL_SHIFT 6 144 #define SAI3_CLK_SEL_MASK 0x3 145 146 #define CKO1_EN (1 << 10) 147 #define CKO1_DIV_MASK 0xf 148 #define CKO1_DIV_SHIFT 6 149 #define CKO1_SEL_MASK 0x3f 150 #define CKO1_SEL_SHIFT 0 151 #define CKO1_PLL4_MAIN 0x6 152 #define CKO1_PLL4_DIVD 0x7 153 154 struct clk { 155 uint32_t reg; 156 uint32_t enable_reg; 157 uint32_t div_mask; 158 uint32_t div_shift; 159 uint32_t div_val; 160 uint32_t sel_reg; 161 uint32_t sel_mask; 162 uint32_t sel_shift; 163 uint32_t sel_val; 164 }; 165 166 static struct clk ipg_clk = { 167 .reg = CCM_CACRR, 168 .enable_reg = 0, 169 .div_mask = IPG_CLK_DIV_MASK, 170 .div_shift = IPG_CLK_DIV_SHIFT, 171 .div_val = 1, /* Divide by 2 */ 172 .sel_reg = 0, 173 .sel_mask = 0, 174 .sel_shift = 0, 175 .sel_val = 0, 176 }; 177 178 /* 179 PLL4 clock divider (before switching the clocks should be gated) 180 000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz) 181 001 Divide by 4 182 010 Divide by 6 183 011 Divide by 8 184 100 Divide by 10 185 101 Divide by 12 186 110 Divide by 14 187 111 Divide by 16 188 */ 189 190 static struct clk pll4_clk = { 191 .reg = CCM_CACRR, 192 .enable_reg = 0, 193 .div_mask = PLL4_CLK_DIV_MASK, 194 .div_shift = PLL4_CLK_DIV_SHIFT, 195 .div_val = 5, /* Divide by 12 */ 196 .sel_reg = 0, 197 .sel_mask = 0, 198 .sel_shift = 0, 199 .sel_val = 0, 200 }; 201 202 static struct clk sai3_clk = { 203 .reg = CCM_CSCDR1, 204 .enable_reg = SAI3_EN, 205 .div_mask = SAI3_DIV_MASK, 206 .div_shift = SAI3_DIV_SHIFT, 207 .div_val = 1, 208 .sel_reg = CCM_CSCMR1, 209 .sel_mask = SAI3_CLK_SEL_MASK, 210 .sel_shift = SAI3_CLK_SEL_SHIFT, 211 .sel_val = 0x3, /* Divided PLL4 main clock */ 212 }; 213 214 static struct clk cko1_clk = { 215 .reg = CCM_CCOSR, 216 .enable_reg = CKO1_EN, 217 .div_mask = CKO1_DIV_MASK, 218 .div_shift = CKO1_DIV_SHIFT, 219 .div_val = 1, 220 .sel_reg = CCM_CCOSR, 221 .sel_mask = CKO1_SEL_MASK, 222 .sel_shift = CKO1_SEL_SHIFT, 223 .sel_val = CKO1_PLL4_DIVD, 224 }; 225 226 static struct clk esdhc0_clk = { 227 .reg = CCM_CSCDR2, 228 .enable_reg = ESDHC0_EN, 229 .div_mask = ESDHC0_DIV_M, 230 .div_shift = ESDHC0_DIV_S, 231 .div_val = 0x9, 232 .sel_reg = 0, 233 .sel_mask = 0, 234 .sel_shift = 0, 235 .sel_val = 0, 236 }; 237 238 static struct clk esdhc1_clk = { 239 .reg = CCM_CSCDR2, 240 .enable_reg = ESDHC1_EN, 241 .div_mask = ESDHC1_DIV_M, 242 .div_shift = ESDHC1_DIV_S, 243 .div_val = 0x9, 244 .sel_reg = 0, 245 .sel_mask = 0, 246 .sel_shift = 0, 247 .sel_val = 0, 248 }; 249 250 static struct clk qspi0_clk = { 251 .reg = CCM_CSCDR3, 252 .enable_reg = QSPI0_EN, 253 .div_mask = 0, 254 .div_shift = 0, 255 .div_val = 0, 256 .sel_reg = 0, 257 .sel_mask = 0, 258 .sel_shift = 0, 259 .sel_val = 0, 260 }; 261 262 static struct clk dcu0_clk = { 263 .reg = CCM_CSCDR3, 264 .enable_reg = DCU0_EN, 265 .div_mask = 0x7, 266 .div_shift = 16, /* DCU0_DIV */ 267 .div_val = 0, /* divide by 1 */ 268 .sel_reg = 0, 269 .sel_mask = 0, 270 .sel_shift = 0, 271 .sel_val = 0, 272 }; 273 274 static struct clk enet_clk = { 275 .reg = CCM_CSCDR1, 276 .enable_reg = (ENET_TS_EN | RMII_CLK_EN), 277 .div_mask = 0, 278 .div_shift = 0, 279 .div_val = 0, 280 .sel_reg = 0, 281 .sel_mask = 0, 282 .sel_shift = 0, 283 .sel_val = 0, 284 }; 285 286 static struct clk nand_clk = { 287 .reg = CCM_CSCDR2, 288 .enable_reg = NFC_EN, 289 .div_mask = 0, 290 .div_shift = 0, 291 .div_val = 0, 292 .sel_reg = 0, 293 .sel_mask = 0, 294 .sel_shift = 0, 295 .sel_val = 0, 296 }; 297 298 /* 299 Divider to generate ESAI clock 300 0000 Divide by 1 301 0001 Divide by 2 302 ... ... 303 1111 Divide by 16 304 */ 305 306 static struct clk esai_clk = { 307 .reg = CCM_CSCDR2, 308 .enable_reg = ESAI_EN, 309 .div_mask = ESAI_DIV_MASK, 310 .div_shift = ESAI_DIV_SHIFT, 311 .div_val = 3, /* Divide by 4 */ 312 .sel_reg = CCM_CSCMR1, 313 .sel_mask = ESAI_CLK_SEL_MASK, 314 .sel_shift = ESAI_CLK_SEL_SHIFT, 315 .sel_val = 0x3, /* Divided PLL4 main clock */ 316 }; 317 318 struct clock_entry { 319 char *name; 320 struct clk *clk; 321 }; 322 323 static struct clock_entry clock_map[] = { 324 {"ipg", &ipg_clk}, 325 {"pll4", &pll4_clk}, 326 {"sai3", &sai3_clk}, 327 {"cko1", &cko1_clk}, 328 {"esdhc0", &esdhc0_clk}, 329 {"esdhc1", &esdhc1_clk}, 330 {"qspi0", &qspi0_clk}, 331 {"dcu0", &dcu0_clk}, 332 {"enet", &enet_clk}, 333 {"nand", &nand_clk}, 334 {"esai", &esai_clk}, 335 {NULL, NULL} 336 }; 337 338 struct ccm_softc { 339 struct resource *res[1]; 340 bus_space_tag_t bst; 341 bus_space_handle_t bsh; 342 device_t dev; 343 }; 344 345 static struct resource_spec ccm_spec[] = { 346 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 347 { -1, 0 } 348 }; 349 350 static int 351 ccm_probe(device_t dev) 352 { 353 354 if (!ofw_bus_status_okay(dev)) 355 return (ENXIO); 356 357 if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm")) 358 return (ENXIO); 359 360 device_set_desc(dev, "Vybrid Family CCM Unit"); 361 return (BUS_PROBE_DEFAULT); 362 } 363 364 static int 365 set_clock(struct ccm_softc *sc, char *name) 366 { 367 struct clk *clk; 368 int reg; 369 int i; 370 371 for (i = 0; clock_map[i].name != NULL; i++) { 372 if (strcmp(clock_map[i].name, name) == 0) { 373 #if 0 374 device_printf(sc->dev, "Configuring %s clk\n", name); 375 #endif 376 clk = clock_map[i].clk; 377 if (clk->sel_reg != 0) { 378 reg = READ4(sc, clk->sel_reg); 379 reg &= ~(clk->sel_mask << clk->sel_shift); 380 reg |= (clk->sel_val << clk->sel_shift); 381 WRITE4(sc, clk->sel_reg, reg); 382 }; 383 384 reg = READ4(sc, clk->reg); 385 reg |= clk->enable_reg; 386 reg &= ~(clk->div_mask << clk->div_shift); 387 reg |= (clk->div_val << clk->div_shift); 388 WRITE4(sc, clk->reg, reg); 389 }; 390 }; 391 392 return (0); 393 } 394 395 static int 396 ccm_fdt_set(struct ccm_softc *sc) 397 { 398 phandle_t child, parent, root; 399 int len; 400 char *fdt_config, *name; 401 402 root = OF_finddevice("/"); 403 len = 0; 404 parent = root; 405 406 /* Find 'clock_names' prop in the tree */ 407 for (child = OF_child(parent); child != 0; child = OF_peer(child)) { 408 409 /* Find a 'leaf'. Start the search from this node. */ 410 while (OF_child(child)) { 411 parent = child; 412 child = OF_child(child); 413 } 414 415 if (!fdt_is_enabled(child)) 416 continue; 417 418 if ((len = OF_getproplen(child, "clock_names")) > 0) { 419 len = OF_getproplen(child, "clock_names"); 420 OF_getprop_alloc(child, "clock_names", 1, 421 (void **)&fdt_config); 422 423 while (len > 0) { 424 name = fdt_config; 425 fdt_config += strlen(name) + 1; 426 len -= strlen(name) + 1; 427 set_clock(sc, name); 428 }; 429 }; 430 431 if (OF_peer(child) == 0) { 432 /* No more siblings. */ 433 child = parent; 434 parent = OF_parent(child); 435 } 436 } 437 438 return (0); 439 } 440 441 static int 442 ccm_attach(device_t dev) 443 { 444 struct ccm_softc *sc; 445 int reg; 446 int i; 447 448 sc = device_get_softc(dev); 449 sc->dev = dev; 450 451 if (bus_alloc_resources(dev, ccm_spec, sc->res)) { 452 device_printf(dev, "could not allocate resources\n"); 453 return (ENXIO); 454 } 455 456 /* Memory interface */ 457 sc->bst = rman_get_bustag(sc->res[0]); 458 sc->bsh = rman_get_bushandle(sc->res[0]); 459 460 /* Enable oscillator */ 461 reg = READ4(sc, CCM_CCR); 462 reg |= (FIRC_EN | FXOSC_EN); 463 WRITE4(sc, CCM_CCR, reg); 464 465 /* Wait 10 times */ 466 for (i = 0; i < 10; i++) { 467 if (READ4(sc, CCM_CSR) & FXOSC_RDY) { 468 device_printf(sc->dev, "On board oscillator is ready.\n"); 469 break; 470 } 471 472 cpufunc_nullop(); 473 } 474 475 /* Clock is on during all modes, except stop mode. */ 476 for (i = 0; i < CCM_CCGRN; i++) { 477 WRITE4(sc, CCM_CCGR(i), 0xffffffff); 478 } 479 480 /* Take and apply FDT clocks */ 481 ccm_fdt_set(sc); 482 483 return (0); 484 } 485 486 static device_method_t ccm_methods[] = { 487 DEVMETHOD(device_probe, ccm_probe), 488 DEVMETHOD(device_attach, ccm_attach), 489 { 0, 0 } 490 }; 491 492 static driver_t ccm_driver = { 493 "ccm", 494 ccm_methods, 495 sizeof(struct ccm_softc), 496 }; 497 498 static devclass_t ccm_devclass; 499 500 DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0); 501