1258057Sbr/*- 2266146Sian * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com> 3258057Sbr * All rights reserved. 4258057Sbr * 5258057Sbr * Redistribution and use in source and binary forms, with or without 6258057Sbr * modification, are permitted provided that the following conditions 7258057Sbr * are met: 8258057Sbr * 1. Redistributions of source code must retain the above copyright 9258057Sbr * notice, this list of conditions and the following disclaimer. 10258057Sbr * 2. Redistributions in binary form must reproduce the above copyright 11258057Sbr * notice, this list of conditions and the following disclaimer in the 12258057Sbr * documentation and/or other materials provided with the distribution. 13258057Sbr * 14258057Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15258057Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16258057Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17258057Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18258057Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19258057Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20258057Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21258057Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22258057Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23258057Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24258057Sbr * SUCH DAMAGE. 25258057Sbr */ 26258057Sbr 27258057Sbr/* 28258057Sbr * Vybrid Family Clock Controller Module (CCM) 29258057Sbr * Chapter 10, Vybrid Reference Manual, Rev. 5, 07/2013 30258057Sbr */ 31258057Sbr 32258057Sbr#include <sys/cdefs.h> 33258057Sbr__FBSDID("$FreeBSD$"); 34258057Sbr 35258057Sbr#include <sys/param.h> 36258057Sbr#include <sys/systm.h> 37258057Sbr#include <sys/bus.h> 38258057Sbr#include <sys/kernel.h> 39258057Sbr#include <sys/module.h> 40258057Sbr#include <sys/malloc.h> 41258057Sbr#include <sys/rman.h> 42258057Sbr#include <sys/timeet.h> 43258057Sbr#include <sys/timetc.h> 44258057Sbr#include <sys/watchdog.h> 45258057Sbr 46258057Sbr#include <dev/fdt/fdt_common.h> 47258057Sbr#include <dev/ofw/openfirm.h> 48258057Sbr#include <dev/ofw/ofw_bus.h> 49258057Sbr#include <dev/ofw/ofw_bus_subr.h> 50258057Sbr 51258057Sbr#include <machine/bus.h> 52258057Sbr#include <machine/fdt.h> 53258057Sbr#include <machine/cpu.h> 54258057Sbr#include <machine/intr.h> 55258057Sbr 56258057Sbr#include <arm/freescale/vybrid/vf_common.h> 57258057Sbr 58258057Sbr#define CCM_CCR 0x00 /* Control Register */ 59258057Sbr#define CCM_CSR 0x04 /* Status Register */ 60258057Sbr#define CCM_CCSR 0x08 /* Clock Switcher Register */ 61258057Sbr#define CCM_CACRR 0x0C /* ARM Clock Root Register */ 62258057Sbr#define CCM_CSCMR1 0x10 /* Serial Clock Multiplexer Register 1 */ 63258057Sbr#define CCM_CSCDR1 0x14 /* Serial Clock Divider Register 1 */ 64258057Sbr#define CCM_CSCDR2 0x18 /* Serial Clock Divider Register 2 */ 65258057Sbr#define CCM_CSCDR3 0x1C /* Serial Clock Divider Register 3 */ 66258057Sbr#define CCM_CSCMR2 0x20 /* Serial Clock Multiplexer Register 2 */ 67258057Sbr#define CCM_CTOR 0x28 /* Testing Observability Register */ 68258057Sbr#define CCM_CLPCR 0x2C /* Low Power Control Register */ 69258057Sbr#define CCM_CISR 0x30 /* Interrupt Status Register */ 70258057Sbr#define CCM_CIMR 0x34 /* Interrupt Mask Register */ 71258057Sbr#define CCM_CCOSR 0x38 /* Clock Output Source Register */ 72258057Sbr#define CCM_CGPR 0x3C /* General Purpose Register */ 73258057Sbr 74258057Sbr#define CCM_CCGRN 12 75258057Sbr#define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */ 76266146Sian#define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */ 77266146Sian#define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */ 78258057Sbr 79258057Sbr#define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */ 80258057Sbr#define CCM_CCOWR 0x8C /* CORE Wakeup Register */ 81258057Sbr 82258057Sbr#define PLL3_PFD4_EN (1 << 31) 83258057Sbr#define PLL3_PFD3_EN (1 << 30) 84258057Sbr#define PLL3_PFD2_EN (1 << 29) 85258057Sbr#define PLL3_PFD1_EN (1 << 28) 86258057Sbr#define PLL2_PFD4_EN (1 << 15) 87258057Sbr#define PLL2_PFD3_EN (1 << 14) 88258057Sbr#define PLL2_PFD2_EN (1 << 13) 89258057Sbr#define PLL2_PFD1_EN (1 << 12) 90258057Sbr#define PLL1_PFD4_EN (1 << 11) 91258057Sbr#define PLL1_PFD3_EN (1 << 10) 92258057Sbr#define PLL1_PFD2_EN (1 << 9) 93258057Sbr#define PLL1_PFD1_EN (1 << 8) 94258057Sbr 95258057Sbr/* CCM_CCR */ 96258057Sbr#define FIRC_EN (1 << 16) 97258057Sbr#define FXOSC_EN (1 << 12) 98258057Sbr#define FXOSC_RDY (1 << 5) 99258057Sbr 100258057Sbr/* CCM_CSCDR1 */ 101258057Sbr#define ENET_TS_EN (1 << 23) 102258057Sbr#define RMII_CLK_EN (1 << 24) 103266146Sian#define SAI3_EN (1 << 19) 104258057Sbr 105266146Sian/* CCM_CSCDR2 */ 106266146Sian#define ESAI_EN (1 << 30) 107266146Sian#define ESDHC1_EN (1 << 29) 108266146Sian#define ESDHC0_EN (1 << 28) 109266146Sian#define NFC_EN (1 << 9) 110266146Sian#define ESDHC1_DIV_S 20 111266146Sian#define ESDHC1_DIV_M 0xf 112266146Sian#define ESDHC0_DIV_S 16 113266146Sian#define ESDHC0_DIV_M 0xf 114266146Sian 115266146Sian/* CCM_CSCDR3 */ 116266146Sian#define DCU0_EN (1 << 19) 117266146Sian 118266146Sian#define QSPI1_EN (1 << 12) 119266146Sian#define QSPI1_DIV (1 << 11) 120266146Sian#define QSPI1_X2_DIV (1 << 10) 121266146Sian#define QSPI1_X4_DIV_M 0x3 122266146Sian#define QSPI1_X4_DIV_S 8 123266146Sian 124266146Sian#define QSPI0_EN (1 << 4) 125266146Sian#define QSPI0_DIV (1 << 3) 126266146Sian#define QSPI0_X2_DIV (1 << 2) 127266146Sian#define QSPI0_X4_DIV_M 0x3 128266146Sian#define QSPI0_X4_DIV_S 0 129266146Sian 130266146Sian#define SAI3_DIV_SHIFT 12 131266146Sian#define SAI3_DIV_MASK 0xf 132266146Sian#define ESAI_DIV_SHIFT 24 133266146Sian#define ESAI_DIV_MASK 0xf 134266146Sian 135266146Sian#define PLL4_CLK_DIV_SHIFT 6 136266146Sian#define PLL4_CLK_DIV_MASK 0x7 137266146Sian 138266146Sian#define IPG_CLK_DIV_SHIFT 11 139266146Sian#define IPG_CLK_DIV_MASK 0x3 140266146Sian 141266146Sian#define ESAI_CLK_SEL_SHIFT 20 142266146Sian#define ESAI_CLK_SEL_MASK 0x3 143266146Sian 144266146Sian#define SAI3_CLK_SEL_SHIFT 6 145266146Sian#define SAI3_CLK_SEL_MASK 0x3 146266146Sian 147266146Sian#define CKO1_EN (1 << 10) 148266146Sian#define CKO1_DIV_MASK 0xf 149266146Sian#define CKO1_DIV_SHIFT 6 150266146Sian#define CKO1_SEL_MASK 0x3f 151266146Sian#define CKO1_SEL_SHIFT 0 152266146Sian#define CKO1_PLL4_MAIN 0x6 153266146Sian#define CKO1_PLL4_DIVD 0x7 154266146Sian 155266146Sianstruct clk { 156266146Sian uint32_t reg; 157266146Sian uint32_t enable_reg; 158266146Sian uint32_t div_mask; 159266146Sian uint32_t div_shift; 160266146Sian uint32_t div_val; 161266146Sian uint32_t sel_reg; 162266146Sian uint32_t sel_mask; 163266146Sian uint32_t sel_shift; 164266146Sian uint32_t sel_val; 165266146Sian}; 166266146Sian 167266203Sianstatic struct clk ipg_clk = { 168266203Sian .reg = CCM_CACRR, 169266203Sian .enable_reg = 0, 170266203Sian .div_mask = IPG_CLK_DIV_MASK, 171266203Sian .div_shift = IPG_CLK_DIV_SHIFT, 172266203Sian .div_val = 1, /* Divide by 2 */ 173266203Sian .sel_reg = 0, 174266203Sian .sel_mask = 0, 175266203Sian .sel_shift = 0, 176266203Sian .sel_val = 0, 177266203Sian}; 178266203Sian 179266146Sian/* 180266146Sian PLL4 clock divider (before switching the clocks should be gated) 181266146Sian 000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz) 182266146Sian 001 Divide by 4 183266146Sian 010 Divide by 6 184266146Sian 011 Divide by 8 185266146Sian 100 Divide by 10 186266146Sian 101 Divide by 12 187266146Sian 110 Divide by 14 188266146Sian 111 Divide by 16 189266146Sian*/ 190266146Sian 191266146Sianstatic struct clk pll4_clk = { 192266146Sian .reg = CCM_CACRR, 193266146Sian .enable_reg = 0, 194266146Sian .div_mask = PLL4_CLK_DIV_MASK, 195266146Sian .div_shift = PLL4_CLK_DIV_SHIFT, 196266146Sian .div_val = 5, /* Divide by 12 */ 197266146Sian .sel_reg = 0, 198266146Sian .sel_mask = 0, 199266146Sian .sel_shift = 0, 200266146Sian .sel_val = 0, 201266146Sian}; 202266146Sian 203266146Sianstatic struct clk sai3_clk = { 204266146Sian .reg = CCM_CSCDR1, 205266146Sian .enable_reg = SAI3_EN, 206266146Sian .div_mask = SAI3_DIV_MASK, 207266146Sian .div_shift = SAI3_DIV_SHIFT, 208266146Sian .div_val = 1, 209266146Sian .sel_reg = CCM_CSCMR1, 210266146Sian .sel_mask = SAI3_CLK_SEL_MASK, 211266146Sian .sel_shift = SAI3_CLK_SEL_SHIFT, 212266146Sian .sel_val = 0x3, /* Divided PLL4 main clock */ 213266146Sian}; 214266146Sian 215266146Sianstatic struct clk cko1_clk = { 216266146Sian .reg = CCM_CCOSR, 217266146Sian .enable_reg = CKO1_EN, 218266146Sian .div_mask = CKO1_DIV_MASK, 219266146Sian .div_shift = CKO1_DIV_SHIFT, 220266146Sian .div_val = 1, 221266146Sian .sel_reg = CCM_CCOSR, 222266146Sian .sel_mask = CKO1_SEL_MASK, 223266146Sian .sel_shift = CKO1_SEL_SHIFT, 224266146Sian .sel_val = CKO1_PLL4_DIVD, 225266146Sian}; 226266146Sian 227266146Sianstatic struct clk esdhc0_clk = { 228266146Sian .reg = CCM_CSCDR2, 229266146Sian .enable_reg = ESDHC0_EN, 230266146Sian .div_mask = ESDHC0_DIV_M, 231266146Sian .div_shift = ESDHC0_DIV_S, 232266146Sian .div_val = 0x9, 233266146Sian .sel_reg = 0, 234266146Sian .sel_mask = 0, 235266146Sian .sel_shift = 0, 236266146Sian .sel_val = 0, 237266146Sian}; 238266146Sian 239266146Sianstatic struct clk esdhc1_clk = { 240266146Sian .reg = CCM_CSCDR2, 241266146Sian .enable_reg = ESDHC1_EN, 242266146Sian .div_mask = ESDHC1_DIV_M, 243266146Sian .div_shift = ESDHC1_DIV_S, 244266146Sian .div_val = 0x9, 245266146Sian .sel_reg = 0, 246266146Sian .sel_mask = 0, 247266146Sian .sel_shift = 0, 248266146Sian .sel_val = 0, 249266146Sian}; 250266146Sian 251266146Sianstatic struct clk qspi0_clk = { 252266146Sian .reg = CCM_CSCDR3, 253266146Sian .enable_reg = QSPI0_EN, 254266146Sian .div_mask = 0, 255266146Sian .div_shift = 0, 256266146Sian .div_val = 0, 257266146Sian .sel_reg = 0, 258266146Sian .sel_mask = 0, 259266146Sian .sel_shift = 0, 260266146Sian .sel_val = 0, 261266146Sian}; 262266146Sian 263266146Sianstatic struct clk dcu0_clk = { 264266146Sian .reg = CCM_CSCDR3, 265266146Sian .enable_reg = DCU0_EN, 266266146Sian .div_mask = 0x7, 267266146Sian .div_shift = 16, /* DCU0_DIV */ 268266146Sian .div_val = 0, /* divide by 1 */ 269266146Sian .sel_reg = 0, 270266146Sian .sel_mask = 0, 271266146Sian .sel_shift = 0, 272266146Sian .sel_val = 0, 273266146Sian}; 274266146Sian 275266146Sianstatic struct clk enet_clk = { 276266146Sian .reg = CCM_CSCDR1, 277266146Sian .enable_reg = (ENET_TS_EN | RMII_CLK_EN), 278266146Sian .div_mask = 0, 279266146Sian .div_shift = 0, 280266146Sian .div_val = 0, 281266146Sian .sel_reg = 0, 282266146Sian .sel_mask = 0, 283266146Sian .sel_shift = 0, 284266146Sian .sel_val = 0, 285266146Sian}; 286266146Sian 287266146Sianstatic struct clk nand_clk = { 288266146Sian .reg = CCM_CSCDR2, 289266146Sian .enable_reg = NFC_EN, 290266146Sian .div_mask = 0, 291266146Sian .div_shift = 0, 292266146Sian .div_val = 0, 293266146Sian .sel_reg = 0, 294266146Sian .sel_mask = 0, 295266146Sian .sel_shift = 0, 296266146Sian .sel_val = 0, 297266146Sian}; 298266146Sian 299266146Sian/* 300266146Sian Divider to generate ESAI clock 301266146Sian 0000 Divide by 1 302266146Sian 0001 Divide by 2 303266146Sian ... ... 304266146Sian 1111 Divide by 16 305266146Sian*/ 306266146Sian 307266146Sianstatic struct clk esai_clk = { 308266146Sian .reg = CCM_CSCDR2, 309266146Sian .enable_reg = ESAI_EN, 310266146Sian .div_mask = ESAI_DIV_MASK, 311266146Sian .div_shift = ESAI_DIV_SHIFT, 312266146Sian .div_val = 3, /* Divide by 4 */ 313266146Sian .sel_reg = CCM_CSCMR1, 314266146Sian .sel_mask = ESAI_CLK_SEL_MASK, 315266146Sian .sel_shift = ESAI_CLK_SEL_SHIFT, 316266146Sian .sel_val = 0x3, /* Divided PLL4 main clock */ 317266146Sian}; 318266146Sian 319266146Sianstruct clock_entry { 320266146Sian char *name; 321266146Sian struct clk *clk; 322266146Sian}; 323266146Sian 324266146Sianstatic struct clock_entry clock_map[] = { 325266203Sian {"ipg", &ipg_clk}, 326266146Sian {"pll4", &pll4_clk}, 327266146Sian {"sai3", &sai3_clk}, 328266146Sian {"cko1", &cko1_clk}, 329266146Sian {"esdhc0", &esdhc0_clk}, 330266146Sian {"esdhc1", &esdhc1_clk}, 331266146Sian {"qspi0", &qspi0_clk}, 332266146Sian {"dcu0", &dcu0_clk}, 333266146Sian {"enet", &enet_clk}, 334266146Sian {"nand", &nand_clk}, 335266146Sian {"esai", &esai_clk}, 336266146Sian {NULL, NULL} 337266146Sian}; 338266146Sian 339258057Sbrstruct ccm_softc { 340258057Sbr struct resource *res[1]; 341258057Sbr bus_space_tag_t bst; 342258057Sbr bus_space_handle_t bsh; 343258057Sbr device_t dev; 344258057Sbr}; 345258057Sbr 346258057Sbrstatic struct resource_spec ccm_spec[] = { 347258057Sbr { SYS_RES_MEMORY, 0, RF_ACTIVE }, 348258057Sbr { -1, 0 } 349258057Sbr}; 350258057Sbr 351258057Sbrstatic int 352258057Sbrccm_probe(device_t dev) 353258057Sbr{ 354258057Sbr 355266152Sian if (!ofw_bus_status_okay(dev)) 356266152Sian return (ENXIO); 357266152Sian 358258057Sbr if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm")) 359258057Sbr return (ENXIO); 360258057Sbr 361258057Sbr device_set_desc(dev, "Vybrid Family CCM Unit"); 362258057Sbr return (BUS_PROBE_DEFAULT); 363258057Sbr} 364258057Sbr 365258057Sbrstatic int 366266146Sianset_clock(struct ccm_softc *sc, char *name) 367266146Sian{ 368266146Sian struct clk *clk; 369266146Sian int reg; 370266146Sian int i; 371266146Sian 372266146Sian for (i = 0; clock_map[i].name != NULL; i++) { 373266146Sian if (strcmp(clock_map[i].name, name) == 0) { 374266146Sian#if 0 375266146Sian device_printf(sc->dev, "Configuring %s clk\n", name); 376266146Sian#endif 377266146Sian clk = clock_map[i].clk; 378266146Sian if (clk->sel_reg != 0) { 379266146Sian reg = READ4(sc, clk->sel_reg); 380266146Sian reg &= ~(clk->sel_mask << clk->sel_shift); 381266146Sian reg |= (clk->sel_val << clk->sel_shift); 382266146Sian WRITE4(sc, clk->sel_reg, reg); 383266146Sian }; 384266146Sian 385266146Sian reg = READ4(sc, clk->reg); 386266146Sian reg |= clk->enable_reg; 387266146Sian reg &= ~(clk->div_mask << clk->div_shift); 388266146Sian reg |= (clk->div_val << clk->div_shift); 389266146Sian WRITE4(sc, clk->reg, reg); 390266146Sian }; 391266146Sian }; 392266146Sian 393266146Sian return (0); 394266146Sian} 395266146Sian 396266146Sianstatic int 397266146Sianccm_fdt_set(struct ccm_softc *sc) 398266146Sian{ 399266146Sian phandle_t child, parent, root; 400266146Sian int len; 401266146Sian char *fdt_config, *name; 402266146Sian 403266146Sian root = OF_finddevice("/"); 404266146Sian len = 0; 405266146Sian parent = root; 406266146Sian 407266146Sian /* Find 'clock_names' prop in the tree */ 408266146Sian for (child = OF_child(parent); child != 0; child = OF_peer(child)) { 409266146Sian 410266146Sian /* Find a 'leaf'. Start the search from this node. */ 411266146Sian while (OF_child(child)) { 412266146Sian parent = child; 413266146Sian child = OF_child(child); 414266146Sian } 415266146Sian 416266146Sian if (!fdt_is_enabled(child)) 417266146Sian continue; 418266146Sian 419266146Sian if ((len = OF_getproplen(child, "clock_names")) > 0) { 420266146Sian len = OF_getproplen(child, "clock_names"); 421266146Sian OF_getprop_alloc(child, "clock_names", 1, 422266146Sian (void **)&fdt_config); 423266146Sian 424266146Sian while (len > 0) { 425266146Sian name = fdt_config; 426266146Sian fdt_config += strlen(name) + 1; 427266146Sian len -= strlen(name) + 1; 428266146Sian set_clock(sc, name); 429266146Sian }; 430266146Sian }; 431266146Sian 432266146Sian if (OF_peer(child) == 0) { 433266146Sian /* No more siblings. */ 434266146Sian child = parent; 435266146Sian parent = OF_parent(child); 436266146Sian } 437266146Sian } 438266146Sian 439266146Sian return (0); 440266146Sian} 441266146Sian 442266146Sianstatic int 443258057Sbrccm_attach(device_t dev) 444258057Sbr{ 445258057Sbr struct ccm_softc *sc; 446258057Sbr int reg; 447258057Sbr int i; 448258057Sbr 449258057Sbr sc = device_get_softc(dev); 450258057Sbr sc->dev = dev; 451258057Sbr 452258057Sbr if (bus_alloc_resources(dev, ccm_spec, sc->res)) { 453258057Sbr device_printf(dev, "could not allocate resources\n"); 454258057Sbr return (ENXIO); 455258057Sbr } 456258057Sbr 457258057Sbr /* Memory interface */ 458258057Sbr sc->bst = rman_get_bustag(sc->res[0]); 459258057Sbr sc->bsh = rman_get_bushandle(sc->res[0]); 460258057Sbr 461258057Sbr /* Enable oscillator */ 462258057Sbr reg = READ4(sc, CCM_CCR); 463258057Sbr reg |= (FIRC_EN | FXOSC_EN); 464258057Sbr WRITE4(sc, CCM_CCR, reg); 465258057Sbr 466258057Sbr /* Wait 10 times */ 467258057Sbr for (i = 0; i < 10; i++) { 468258057Sbr if (READ4(sc, CCM_CSR) & FXOSC_RDY) { 469258057Sbr device_printf(sc->dev, "On board oscillator is ready.\n"); 470258057Sbr break; 471258057Sbr } 472258057Sbr 473258057Sbr cpufunc_nullop(); 474258057Sbr } 475258057Sbr 476258057Sbr /* Clock is on during all modes, except stop mode. */ 477258057Sbr for (i = 0; i < CCM_CCGRN; i++) { 478258057Sbr WRITE4(sc, CCM_CCGR(i), 0xffffffff); 479258057Sbr } 480258057Sbr 481266146Sian /* Take and apply FDT clocks */ 482266146Sian ccm_fdt_set(sc); 483258057Sbr 484258057Sbr return (0); 485258057Sbr} 486258057Sbr 487258057Sbrstatic device_method_t ccm_methods[] = { 488258057Sbr DEVMETHOD(device_probe, ccm_probe), 489258057Sbr DEVMETHOD(device_attach, ccm_attach), 490258057Sbr { 0, 0 } 491258057Sbr}; 492258057Sbr 493258057Sbrstatic driver_t ccm_driver = { 494258057Sbr "ccm", 495258057Sbr ccm_methods, 496258057Sbr sizeof(struct ccm_softc), 497258057Sbr}; 498258057Sbr 499258057Sbrstatic devclass_t ccm_devclass; 500258057Sbr 501258057SbrDRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0); 502