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