Path: blob/main/sys/arm/freescale/vybrid/vf_ccm.c
108106 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2013-2014 Ruslan Bukin <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728/*29* Vybrid Family Clock Controller Module (CCM)30* Chapter 10, Vybrid Reference Manual, Rev. 5, 07/201331*/3233#include <sys/param.h>34#include <sys/systm.h>35#include <sys/bus.h>36#include <sys/kernel.h>37#include <sys/module.h>38#include <sys/malloc.h>39#include <sys/rman.h>40#include <sys/timeet.h>41#include <sys/timetc.h>42#include <sys/watchdog.h>4344#include <dev/fdt/fdt_common.h>45#include <dev/ofw/openfirm.h>46#include <dev/ofw/ofw_bus.h>47#include <dev/ofw/ofw_bus_subr.h>4849#include <machine/bus.h>50#include <machine/cpu.h>51#include <machine/intr.h>5253#include <arm/freescale/vybrid/vf_common.h>5455#define CCM_CCR 0x00 /* Control Register */56#define CCM_CSR 0x04 /* Status Register */57#define CCM_CCSR 0x08 /* Clock Switcher Register */58#define CCM_CACRR 0x0C /* ARM Clock Root Register */59#define CCM_CSCMR1 0x10 /* Serial Clock Multiplexer Register 1 */60#define CCM_CSCDR1 0x14 /* Serial Clock Divider Register 1 */61#define CCM_CSCDR2 0x18 /* Serial Clock Divider Register 2 */62#define CCM_CSCDR3 0x1C /* Serial Clock Divider Register 3 */63#define CCM_CSCMR2 0x20 /* Serial Clock Multiplexer Register 2 */64#define CCM_CTOR 0x28 /* Testing Observability Register */65#define CCM_CLPCR 0x2C /* Low Power Control Register */66#define CCM_CISR 0x30 /* Interrupt Status Register */67#define CCM_CIMR 0x34 /* Interrupt Mask Register */68#define CCM_CCOSR 0x38 /* Clock Output Source Register */69#define CCM_CGPR 0x3C /* General Purpose Register */7071#define CCM_CCGRN 1272#define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */73#define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */74#define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */7576#define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */77#define CCM_CCOWR 0x8C /* CORE Wakeup Register */7879#define PLL3_PFD4_EN (1U << 31)80#define PLL3_PFD3_EN (1 << 30)81#define PLL3_PFD2_EN (1 << 29)82#define PLL3_PFD1_EN (1 << 28)83#define PLL2_PFD4_EN (1 << 15)84#define PLL2_PFD3_EN (1 << 14)85#define PLL2_PFD2_EN (1 << 13)86#define PLL2_PFD1_EN (1 << 12)87#define PLL1_PFD4_EN (1 << 11)88#define PLL1_PFD3_EN (1 << 10)89#define PLL1_PFD2_EN (1 << 9)90#define PLL1_PFD1_EN (1 << 8)9192/* CCM_CCR */93#define FIRC_EN (1 << 16)94#define FXOSC_EN (1 << 12)95#define FXOSC_RDY (1 << 5)9697/* CCM_CSCDR1 */98#define ENET_TS_EN (1 << 23)99#define RMII_CLK_EN (1 << 24)100#define SAI3_EN (1 << 19)101102/* CCM_CSCDR2 */103#define ESAI_EN (1 << 30)104#define ESDHC1_EN (1 << 29)105#define ESDHC0_EN (1 << 28)106#define NFC_EN (1 << 9)107#define ESDHC1_DIV_S 20108#define ESDHC1_DIV_M 0xf109#define ESDHC0_DIV_S 16110#define ESDHC0_DIV_M 0xf111112/* CCM_CSCDR3 */113#define DCU0_EN (1 << 19)114115#define QSPI1_EN (1 << 12)116#define QSPI1_DIV (1 << 11)117#define QSPI1_X2_DIV (1 << 10)118#define QSPI1_X4_DIV_M 0x3119#define QSPI1_X4_DIV_S 8120121#define QSPI0_EN (1 << 4)122#define QSPI0_DIV (1 << 3)123#define QSPI0_X2_DIV (1 << 2)124#define QSPI0_X4_DIV_M 0x3125#define QSPI0_X4_DIV_S 0126127#define SAI3_DIV_SHIFT 12128#define SAI3_DIV_MASK 0xf129#define ESAI_DIV_SHIFT 24130#define ESAI_DIV_MASK 0xf131132#define PLL4_CLK_DIV_SHIFT 6133#define PLL4_CLK_DIV_MASK 0x7134135#define IPG_CLK_DIV_SHIFT 11136#define IPG_CLK_DIV_MASK 0x3137138#define ESAI_CLK_SEL_SHIFT 20139#define ESAI_CLK_SEL_MASK 0x3140141#define SAI3_CLK_SEL_SHIFT 6142#define SAI3_CLK_SEL_MASK 0x3143144#define CKO1_EN (1 << 10)145#define CKO1_DIV_MASK 0xf146#define CKO1_DIV_SHIFT 6147#define CKO1_SEL_MASK 0x3f148#define CKO1_SEL_SHIFT 0149#define CKO1_PLL4_MAIN 0x6150#define CKO1_PLL4_DIVD 0x7151152struct clk {153uint32_t reg;154uint32_t enable_reg;155uint32_t div_mask;156uint32_t div_shift;157uint32_t div_val;158uint32_t sel_reg;159uint32_t sel_mask;160uint32_t sel_shift;161uint32_t sel_val;162};163164static struct clk ipg_clk = {165.reg = CCM_CACRR,166.enable_reg = 0,167.div_mask = IPG_CLK_DIV_MASK,168.div_shift = IPG_CLK_DIV_SHIFT,169.div_val = 1, /* Divide by 2 */170.sel_reg = 0,171.sel_mask = 0,172.sel_shift = 0,173.sel_val = 0,174};175176/*177PLL4 clock divider (before switching the clocks should be gated)178000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz)179001 Divide by 4180010 Divide by 6181011 Divide by 8182100 Divide by 10183101 Divide by 12184110 Divide by 14185111 Divide by 16186*/187188static struct clk pll4_clk = {189.reg = CCM_CACRR,190.enable_reg = 0,191.div_mask = PLL4_CLK_DIV_MASK,192.div_shift = PLL4_CLK_DIV_SHIFT,193.div_val = 5, /* Divide by 12 */194.sel_reg = 0,195.sel_mask = 0,196.sel_shift = 0,197.sel_val = 0,198};199200static struct clk sai3_clk = {201.reg = CCM_CSCDR1,202.enable_reg = SAI3_EN,203.div_mask = SAI3_DIV_MASK,204.div_shift = SAI3_DIV_SHIFT,205.div_val = 1,206.sel_reg = CCM_CSCMR1,207.sel_mask = SAI3_CLK_SEL_MASK,208.sel_shift = SAI3_CLK_SEL_SHIFT,209.sel_val = 0x3, /* Divided PLL4 main clock */210};211212static struct clk cko1_clk = {213.reg = CCM_CCOSR,214.enable_reg = CKO1_EN,215.div_mask = CKO1_DIV_MASK,216.div_shift = CKO1_DIV_SHIFT,217.div_val = 1,218.sel_reg = CCM_CCOSR,219.sel_mask = CKO1_SEL_MASK,220.sel_shift = CKO1_SEL_SHIFT,221.sel_val = CKO1_PLL4_DIVD,222};223224static struct clk esdhc0_clk = {225.reg = CCM_CSCDR2,226.enable_reg = ESDHC0_EN,227.div_mask = ESDHC0_DIV_M,228.div_shift = ESDHC0_DIV_S,229.div_val = 0x9,230.sel_reg = 0,231.sel_mask = 0,232.sel_shift = 0,233.sel_val = 0,234};235236static struct clk esdhc1_clk = {237.reg = CCM_CSCDR2,238.enable_reg = ESDHC1_EN,239.div_mask = ESDHC1_DIV_M,240.div_shift = ESDHC1_DIV_S,241.div_val = 0x9,242.sel_reg = 0,243.sel_mask = 0,244.sel_shift = 0,245.sel_val = 0,246};247248static struct clk qspi0_clk = {249.reg = CCM_CSCDR3,250.enable_reg = QSPI0_EN,251.div_mask = 0,252.div_shift = 0,253.div_val = 0,254.sel_reg = 0,255.sel_mask = 0,256.sel_shift = 0,257.sel_val = 0,258};259260static struct clk dcu0_clk = {261.reg = CCM_CSCDR3,262.enable_reg = DCU0_EN,263.div_mask = 0x7,264.div_shift = 16, /* DCU0_DIV */265.div_val = 0, /* divide by 1 */266.sel_reg = 0,267.sel_mask = 0,268.sel_shift = 0,269.sel_val = 0,270};271272static struct clk enet_clk = {273.reg = CCM_CSCDR1,274.enable_reg = (ENET_TS_EN | RMII_CLK_EN),275.div_mask = 0,276.div_shift = 0,277.div_val = 0,278.sel_reg = 0,279.sel_mask = 0,280.sel_shift = 0,281.sel_val = 0,282};283284static struct clk nand_clk = {285.reg = CCM_CSCDR2,286.enable_reg = NFC_EN,287.div_mask = 0,288.div_shift = 0,289.div_val = 0,290.sel_reg = 0,291.sel_mask = 0,292.sel_shift = 0,293.sel_val = 0,294};295296/*297Divider to generate ESAI clock2980000 Divide by 12990001 Divide by 2300... ...3011111 Divide by 16302*/303304static struct clk esai_clk = {305.reg = CCM_CSCDR2,306.enable_reg = ESAI_EN,307.div_mask = ESAI_DIV_MASK,308.div_shift = ESAI_DIV_SHIFT,309.div_val = 3, /* Divide by 4 */310.sel_reg = CCM_CSCMR1,311.sel_mask = ESAI_CLK_SEL_MASK,312.sel_shift = ESAI_CLK_SEL_SHIFT,313.sel_val = 0x3, /* Divided PLL4 main clock */314};315316struct clock_entry {317char *name;318struct clk *clk;319};320321static struct clock_entry clock_map[] = {322{"ipg", &ipg_clk},323{"pll4", &pll4_clk},324{"sai3", &sai3_clk},325{"cko1", &cko1_clk},326{"esdhc0", &esdhc0_clk},327{"esdhc1", &esdhc1_clk},328{"qspi0", &qspi0_clk},329{"dcu0", &dcu0_clk},330{"enet", &enet_clk},331{"nand", &nand_clk},332{"esai", &esai_clk},333{NULL, NULL}334};335336struct ccm_softc {337struct resource *res[1];338bus_space_tag_t bst;339bus_space_handle_t bsh;340device_t dev;341};342343static struct resource_spec ccm_spec[] = {344{ SYS_RES_MEMORY, 0, RF_ACTIVE },345{ -1, 0 }346};347348static int349ccm_probe(device_t dev)350{351352if (!ofw_bus_status_okay(dev))353return (ENXIO);354355if (!ofw_bus_is_compatible(dev, "fsl,mvf600-ccm"))356return (ENXIO);357358device_set_desc(dev, "Vybrid Family CCM Unit");359return (BUS_PROBE_DEFAULT);360}361362static int363set_clock(struct ccm_softc *sc, char *name)364{365struct clk *clk;366int reg;367int i;368369for (i = 0; clock_map[i].name != NULL; i++) {370if (strcmp(clock_map[i].name, name) == 0) {371#if 0372device_printf(sc->dev, "Configuring %s clk\n", name);373#endif374clk = clock_map[i].clk;375if (clk->sel_reg != 0) {376reg = READ4(sc, clk->sel_reg);377reg &= ~(clk->sel_mask << clk->sel_shift);378reg |= (clk->sel_val << clk->sel_shift);379WRITE4(sc, clk->sel_reg, reg);380}381382reg = READ4(sc, clk->reg);383reg |= clk->enable_reg;384reg &= ~(clk->div_mask << clk->div_shift);385reg |= (clk->div_val << clk->div_shift);386WRITE4(sc, clk->reg, reg);387}388}389390return (0);391}392393static int394ccm_fdt_set(struct ccm_softc *sc)395{396phandle_t child, parent, root;397int len;398char *fdt_config, *name;399400root = OF_finddevice("/");401len = 0;402parent = root;403404/* Find 'clock_names' prop in the tree */405for (child = OF_child(parent); child != 0; child = OF_peer(child)) {406/* Find a 'leaf'. Start the search from this node. */407while (OF_child(child)) {408parent = child;409child = OF_child(child);410}411412if (!ofw_bus_node_status_okay(child))413continue;414415if ((len = OF_getproplen(child, "clock_names")) > 0) {416len = OF_getproplen(child, "clock_names");417OF_getprop_alloc(child, "clock_names",418(void **)&fdt_config);419420while (len > 0) {421name = fdt_config;422fdt_config += strlen(name) + 1;423len -= strlen(name) + 1;424set_clock(sc, name);425}426}427428if (OF_peer(child) == 0) {429/* No more siblings. */430child = parent;431parent = OF_parent(child);432}433}434435return (0);436}437438static int439ccm_attach(device_t dev)440{441struct ccm_softc *sc;442int reg;443int i;444445sc = device_get_softc(dev);446sc->dev = dev;447448if (bus_alloc_resources(dev, ccm_spec, sc->res)) {449device_printf(dev, "could not allocate resources\n");450return (ENXIO);451}452453/* Memory interface */454sc->bst = rman_get_bustag(sc->res[0]);455sc->bsh = rman_get_bushandle(sc->res[0]);456457/* Enable oscillator */458reg = READ4(sc, CCM_CCR);459reg |= (FIRC_EN | FXOSC_EN);460WRITE4(sc, CCM_CCR, reg);461462/* Wait 10 times */463for (i = 0; i < 10; i++) {464if (READ4(sc, CCM_CSR) & FXOSC_RDY) {465device_printf(sc->dev, "On board oscillator is ready.\n");466break;467}468469cpufunc_nullop();470}471472/* Clock is on during all modes, except stop mode. */473for (i = 0; i < CCM_CCGRN; i++) {474WRITE4(sc, CCM_CCGR(i), 0xffffffff);475}476477/* Take and apply FDT clocks */478ccm_fdt_set(sc);479480return (0);481}482483static device_method_t ccm_methods[] = {484DEVMETHOD(device_probe, ccm_probe),485DEVMETHOD(device_attach, ccm_attach),486{ 0, 0 }487};488489static driver_t ccm_driver = {490"ccm",491ccm_methods,492sizeof(struct ccm_softc),493};494495DRIVER_MODULE(ccm, simplebus, ccm_driver, 0, 0);496497498