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