vf_ccm.c revision 266146
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: stable/10/sys/arm/freescale/vybrid/vf_ccm.c 266146 2014-05-15 15:43:33Z ian $"); 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 167266146Sian/* 168266146Sian PLL4 clock divider (before switching the clocks should be gated) 169266146Sian 000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz) 170266146Sian 001 Divide by 4 171266146Sian 010 Divide by 6 172266146Sian 011 Divide by 8 173266146Sian 100 Divide by 10 174266146Sian 101 Divide by 12 175266146Sian 110 Divide by 14 176266146Sian 111 Divide by 16 177266146Sian*/ 178266146Sian 179266146Sianstatic struct clk pll4_clk = { 180266146Sian .reg = CCM_CACRR, 181266146Sian .enable_reg = 0, 182266146Sian .div_mask = PLL4_CLK_DIV_MASK, 183266146Sian .div_shift = PLL4_CLK_DIV_SHIFT, 184266146Sian .div_val = 5, /* Divide by 12 */ 185266146Sian .sel_reg = 0, 186266146Sian .sel_mask = 0, 187266146Sian .sel_shift = 0, 188266146Sian .sel_val = 0, 189266146Sian}; 190266146Sian 191266146Sianstatic struct clk sai3_clk = { 192266146Sian .reg = CCM_CSCDR1, 193266146Sian .enable_reg = SAI3_EN, 194266146Sian .div_mask = SAI3_DIV_MASK, 195266146Sian .div_shift = SAI3_DIV_SHIFT, 196266146Sian .div_val = 1, 197266146Sian .sel_reg = CCM_CSCMR1, 198266146Sian .sel_mask = SAI3_CLK_SEL_MASK, 199266146Sian .sel_shift = SAI3_CLK_SEL_SHIFT, 200266146Sian .sel_val = 0x3, /* Divided PLL4 main clock */ 201266146Sian}; 202266146Sian 203266146Sianstatic struct clk cko1_clk = { 204266146Sian .reg = CCM_CCOSR, 205266146Sian .enable_reg = CKO1_EN, 206266146Sian .div_mask = CKO1_DIV_MASK, 207266146Sian .div_shift = CKO1_DIV_SHIFT, 208266146Sian .div_val = 1, 209266146Sian .sel_reg = CCM_CCOSR, 210266146Sian .sel_mask = CKO1_SEL_MASK, 211266146Sian .sel_shift = CKO1_SEL_SHIFT, 212266146Sian .sel_val = CKO1_PLL4_DIVD, 213266146Sian}; 214266146Sian 215266146Sianstatic struct clk esdhc0_clk = { 216266146Sian .reg = CCM_CSCDR2, 217266146Sian .enable_reg = ESDHC0_EN, 218266146Sian .div_mask = ESDHC0_DIV_M, 219266146Sian .div_shift = ESDHC0_DIV_S, 220266146Sian .div_val = 0x9, 221266146Sian .sel_reg = 0, 222266146Sian .sel_mask = 0, 223266146Sian .sel_shift = 0, 224266146Sian .sel_val = 0, 225266146Sian}; 226266146Sian 227266146Sianstatic struct clk esdhc1_clk = { 228266146Sian .reg = CCM_CSCDR2, 229266146Sian .enable_reg = ESDHC1_EN, 230266146Sian .div_mask = ESDHC1_DIV_M, 231266146Sian .div_shift = ESDHC1_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 qspi0_clk = { 240266146Sian .reg = CCM_CSCDR3, 241266146Sian .enable_reg = QSPI0_EN, 242266146Sian .div_mask = 0, 243266146Sian .div_shift = 0, 244266146Sian .div_val = 0, 245266146Sian .sel_reg = 0, 246266146Sian .sel_mask = 0, 247266146Sian .sel_shift = 0, 248266146Sian .sel_val = 0, 249266146Sian}; 250266146Sian 251266146Sianstatic struct clk dcu0_clk = { 252266146Sian .reg = CCM_CSCDR3, 253266146Sian .enable_reg = DCU0_EN, 254266146Sian .div_mask = 0x7, 255266146Sian .div_shift = 16, /* DCU0_DIV */ 256266146Sian .div_val = 0, /* divide by 1 */ 257266146Sian .sel_reg = 0, 258266146Sian .sel_mask = 0, 259266146Sian .sel_shift = 0, 260266146Sian .sel_val = 0, 261266146Sian}; 262266146Sian 263266146Sianstatic struct clk enet_clk = { 264266146Sian .reg = CCM_CSCDR1, 265266146Sian .enable_reg = (ENET_TS_EN | RMII_CLK_EN), 266266146Sian .div_mask = 0, 267266146Sian .div_shift = 0, 268266146Sian .div_val = 0, 269266146Sian .sel_reg = 0, 270266146Sian .sel_mask = 0, 271266146Sian .sel_shift = 0, 272266146Sian .sel_val = 0, 273266146Sian}; 274266146Sian 275266146Sianstatic struct clk nand_clk = { 276266146Sian .reg = CCM_CSCDR2, 277266146Sian .enable_reg = NFC_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 287266146Sian/* 288266146Sian Divider to generate ESAI clock 289266146Sian 0000 Divide by 1 290266146Sian 0001 Divide by 2 291266146Sian ... ... 292266146Sian 1111 Divide by 16 293266146Sian*/ 294266146Sian 295266146Sianstatic struct clk esai_clk = { 296266146Sian .reg = CCM_CSCDR2, 297266146Sian .enable_reg = ESAI_EN, 298266146Sian .div_mask = ESAI_DIV_MASK, 299266146Sian .div_shift = ESAI_DIV_SHIFT, 300266146Sian .div_val = 3, /* Divide by 4 */ 301266146Sian .sel_reg = CCM_CSCMR1, 302266146Sian .sel_mask = ESAI_CLK_SEL_MASK, 303266146Sian .sel_shift = ESAI_CLK_SEL_SHIFT, 304266146Sian .sel_val = 0x3, /* Divided PLL4 main clock */ 305266146Sian}; 306266146Sian 307266146Sianstruct clock_entry { 308266146Sian char *name; 309266146Sian struct clk *clk; 310266146Sian}; 311266146Sian 312266146Sianstatic struct clock_entry clock_map[] = { 313266146Sian {"pll4", &pll4_clk}, 314266146Sian {"sai3", &sai3_clk}, 315266146Sian {"cko1", &cko1_clk}, 316266146Sian {"esdhc0", &esdhc0_clk}, 317266146Sian {"esdhc1", &esdhc1_clk}, 318266146Sian {"qspi0", &qspi0_clk}, 319266146Sian {"dcu0", &dcu0_clk}, 320266146Sian {"enet", &enet_clk}, 321266146Sian {"nand", &nand_clk}, 322266146Sian {"esai", &esai_clk}, 323266146Sian {NULL, NULL} 324266146Sian}; 325266146Sian 326258057Sbrstruct ccm_softc { 327258057Sbr struct resource *res[1]; 328258057Sbr bus_space_tag_t bst; 329258057Sbr bus_space_handle_t bsh; 330258057Sbr device_t dev; 331258057Sbr}; 332258057Sbr 333258057Sbrstatic struct resource_spec ccm_spec[] = { 334258057Sbr { SYS_RES_MEMORY, 0, RF_ACTIVE }, 335258057Sbr { -1, 0 } 336258057Sbr}; 337258057Sbr 338258057Sbrstatic int 339258057Sbrccm_probe(device_t dev) 340258057Sbr{ 341258057Sbr 342258057Sbr if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm")) 343258057Sbr return (ENXIO); 344258057Sbr 345258057Sbr device_set_desc(dev, "Vybrid Family CCM Unit"); 346258057Sbr return (BUS_PROBE_DEFAULT); 347258057Sbr} 348258057Sbr 349258057Sbrstatic int 350266146Sianset_clock(struct ccm_softc *sc, char *name) 351266146Sian{ 352266146Sian struct clk *clk; 353266146Sian int reg; 354266146Sian int i; 355266146Sian 356266146Sian for (i = 0; clock_map[i].name != NULL; i++) { 357266146Sian if (strcmp(clock_map[i].name, name) == 0) { 358266146Sian#if 0 359266146Sian device_printf(sc->dev, "Configuring %s clk\n", name); 360266146Sian#endif 361266146Sian clk = clock_map[i].clk; 362266146Sian if (clk->sel_reg != 0) { 363266146Sian reg = READ4(sc, clk->sel_reg); 364266146Sian reg &= ~(clk->sel_mask << clk->sel_shift); 365266146Sian reg |= (clk->sel_val << clk->sel_shift); 366266146Sian WRITE4(sc, clk->sel_reg, reg); 367266146Sian }; 368266146Sian 369266146Sian reg = READ4(sc, clk->reg); 370266146Sian reg |= clk->enable_reg; 371266146Sian reg &= ~(clk->div_mask << clk->div_shift); 372266146Sian reg |= (clk->div_val << clk->div_shift); 373266146Sian WRITE4(sc, clk->reg, reg); 374266146Sian }; 375266146Sian }; 376266146Sian 377266146Sian return (0); 378266146Sian} 379266146Sian 380266146Sianstatic int 381266146Sianccm_fdt_set(struct ccm_softc *sc) 382266146Sian{ 383266146Sian phandle_t child, parent, root; 384266146Sian int len; 385266146Sian char *fdt_config, *name; 386266146Sian 387266146Sian root = OF_finddevice("/"); 388266146Sian len = 0; 389266146Sian parent = root; 390266146Sian 391266146Sian /* Find 'clock_names' prop in the tree */ 392266146Sian for (child = OF_child(parent); child != 0; child = OF_peer(child)) { 393266146Sian 394266146Sian /* Find a 'leaf'. Start the search from this node. */ 395266146Sian while (OF_child(child)) { 396266146Sian parent = child; 397266146Sian child = OF_child(child); 398266146Sian } 399266146Sian 400266146Sian if (!fdt_is_enabled(child)) 401266146Sian continue; 402266146Sian 403266146Sian if ((len = OF_getproplen(child, "clock_names")) > 0) { 404266146Sian len = OF_getproplen(child, "clock_names"); 405266146Sian OF_getprop_alloc(child, "clock_names", 1, 406266146Sian (void **)&fdt_config); 407266146Sian 408266146Sian while (len > 0) { 409266146Sian name = fdt_config; 410266146Sian fdt_config += strlen(name) + 1; 411266146Sian len -= strlen(name) + 1; 412266146Sian set_clock(sc, name); 413266146Sian }; 414266146Sian }; 415266146Sian 416266146Sian if (OF_peer(child) == 0) { 417266146Sian /* No more siblings. */ 418266146Sian child = parent; 419266146Sian parent = OF_parent(child); 420266146Sian } 421266146Sian } 422266146Sian 423266146Sian return (0); 424266146Sian} 425266146Sian 426266146Sianstatic int 427258057Sbrccm_attach(device_t dev) 428258057Sbr{ 429258057Sbr struct ccm_softc *sc; 430258057Sbr int reg; 431258057Sbr int i; 432258057Sbr 433258057Sbr sc = device_get_softc(dev); 434258057Sbr sc->dev = dev; 435258057Sbr 436258057Sbr if (bus_alloc_resources(dev, ccm_spec, sc->res)) { 437258057Sbr device_printf(dev, "could not allocate resources\n"); 438258057Sbr return (ENXIO); 439258057Sbr } 440258057Sbr 441258057Sbr /* Memory interface */ 442258057Sbr sc->bst = rman_get_bustag(sc->res[0]); 443258057Sbr sc->bsh = rman_get_bushandle(sc->res[0]); 444258057Sbr 445258057Sbr /* Enable oscillator */ 446258057Sbr reg = READ4(sc, CCM_CCR); 447258057Sbr reg |= (FIRC_EN | FXOSC_EN); 448258057Sbr WRITE4(sc, CCM_CCR, reg); 449258057Sbr 450258057Sbr /* Wait 10 times */ 451258057Sbr for (i = 0; i < 10; i++) { 452258057Sbr if (READ4(sc, CCM_CSR) & FXOSC_RDY) { 453258057Sbr device_printf(sc->dev, "On board oscillator is ready.\n"); 454258057Sbr break; 455258057Sbr } 456258057Sbr 457258057Sbr cpufunc_nullop(); 458258057Sbr } 459258057Sbr 460258057Sbr /* Clock is on during all modes, except stop mode. */ 461258057Sbr for (i = 0; i < CCM_CCGRN; i++) { 462258057Sbr WRITE4(sc, CCM_CCGR(i), 0xffffffff); 463258057Sbr } 464258057Sbr 465266146Sian /* Take and apply FDT clocks */ 466266146Sian ccm_fdt_set(sc); 467258057Sbr 468258057Sbr return (0); 469258057Sbr} 470258057Sbr 471258057Sbrstatic device_method_t ccm_methods[] = { 472258057Sbr DEVMETHOD(device_probe, ccm_probe), 473258057Sbr DEVMETHOD(device_attach, ccm_attach), 474258057Sbr { 0, 0 } 475258057Sbr}; 476258057Sbr 477258057Sbrstatic driver_t ccm_driver = { 478258057Sbr "ccm", 479258057Sbr ccm_methods, 480258057Sbr sizeof(struct ccm_softc), 481258057Sbr}; 482258057Sbr 483258057Sbrstatic devclass_t ccm_devclass; 484258057Sbr 485258057SbrDRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0); 486