1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (C) 2016 Freescale Semiconductor, Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <div64.h>
9*4882a593Smuzhiyun #include <asm/io.h>
10*4882a593Smuzhiyun #include <errno.h>
11*4882a593Smuzhiyun #include <asm/arch/imx-regs.h>
12*4882a593Smuzhiyun #include <asm/arch/pcc.h>
13*4882a593Smuzhiyun #include <asm/arch/sys_proto.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #define PCC_CLKSRC_TYPES 2
18*4882a593Smuzhiyun #define PCC_CLKSRC_NUM 7
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun static enum scg_clk pcc_clksrc[PCC_CLKSRC_TYPES][PCC_CLKSRC_NUM] = {
21*4882a593Smuzhiyun { SCG_NIC1_BUS_CLK,
22*4882a593Smuzhiyun SCG_NIC1_CLK,
23*4882a593Smuzhiyun SCG_DDR_CLK,
24*4882a593Smuzhiyun SCG_APLL_PFD2_CLK,
25*4882a593Smuzhiyun SCG_APLL_PFD1_CLK,
26*4882a593Smuzhiyun SCG_APLL_PFD0_CLK,
27*4882a593Smuzhiyun USB_PLL_OUT,
28*4882a593Smuzhiyun },
29*4882a593Smuzhiyun { SCG_SOSC_DIV2_CLK, /* SOSC BUS clock */
30*4882a593Smuzhiyun MIPI_PLL_OUT,
31*4882a593Smuzhiyun SCG_FIRC_DIV2_CLK, /* FIRC BUS clock */
32*4882a593Smuzhiyun SCG_ROSC_CLK,
33*4882a593Smuzhiyun SCG_NIC1_BUS_CLK,
34*4882a593Smuzhiyun SCG_NIC1_CLK,
35*4882a593Smuzhiyun SCG_APLL_PFD3_CLK,
36*4882a593Smuzhiyun },
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun static struct pcc_entry pcc_arrays[] = {
40*4882a593Smuzhiyun {PCC2_RBASE, DMA1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
41*4882a593Smuzhiyun {PCC2_RBASE, RGPIO1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
42*4882a593Smuzhiyun {PCC2_RBASE, FLEXBUS0_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
43*4882a593Smuzhiyun {PCC2_RBASE, SEMA42_1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
44*4882a593Smuzhiyun {PCC2_RBASE, DMA1_CH_MUX0_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
45*4882a593Smuzhiyun {PCC2_RBASE, SNVS_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
46*4882a593Smuzhiyun {PCC2_RBASE, CAAM_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
47*4882a593Smuzhiyun {PCC2_RBASE, LPTPM4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
48*4882a593Smuzhiyun {PCC2_RBASE, LPTPM5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
49*4882a593Smuzhiyun {PCC2_RBASE, LPIT1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
50*4882a593Smuzhiyun {PCC2_RBASE, LPSPI2_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
51*4882a593Smuzhiyun {PCC2_RBASE, LPSPI3_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
52*4882a593Smuzhiyun {PCC2_RBASE, LPI2C4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
53*4882a593Smuzhiyun {PCC2_RBASE, LPI2C5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
54*4882a593Smuzhiyun {PCC2_RBASE, LPUART4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
55*4882a593Smuzhiyun {PCC2_RBASE, LPUART5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
56*4882a593Smuzhiyun {PCC2_RBASE, FLEXIO1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
57*4882a593Smuzhiyun {PCC2_RBASE, USBOTG0_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV},
58*4882a593Smuzhiyun {PCC2_RBASE, USBOTG1_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV},
59*4882a593Smuzhiyun {PCC2_RBASE, USBPHY_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
60*4882a593Smuzhiyun {PCC2_RBASE, USB_PL301_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
61*4882a593Smuzhiyun {PCC2_RBASE, USDHC0_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV},
62*4882a593Smuzhiyun {PCC2_RBASE, USDHC1_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV},
63*4882a593Smuzhiyun {PCC2_RBASE, WDG1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV},
64*4882a593Smuzhiyun {PCC2_RBASE, WDG2_PCC2_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV},
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun {PCC3_RBASE, LPTPM6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
67*4882a593Smuzhiyun {PCC3_RBASE, LPTPM7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
68*4882a593Smuzhiyun {PCC3_RBASE, LPI2C6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
69*4882a593Smuzhiyun {PCC3_RBASE, LPI2C7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
70*4882a593Smuzhiyun {PCC3_RBASE, LPUART6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
71*4882a593Smuzhiyun {PCC3_RBASE, LPUART7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV},
72*4882a593Smuzhiyun {PCC3_RBASE, VIU0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
73*4882a593Smuzhiyun {PCC3_RBASE, DSI0_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV},
74*4882a593Smuzhiyun {PCC3_RBASE, LCDIF0_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV},
75*4882a593Smuzhiyun {PCC3_RBASE, MMDC0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
76*4882a593Smuzhiyun {PCC3_RBASE, PORTC_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
77*4882a593Smuzhiyun {PCC3_RBASE, PORTD_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
78*4882a593Smuzhiyun {PCC3_RBASE, PORTE_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
79*4882a593Smuzhiyun {PCC3_RBASE, PORTF_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV},
80*4882a593Smuzhiyun {PCC3_RBASE, GPU3D_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_NO_DIV},
81*4882a593Smuzhiyun {PCC3_RBASE, GPU2D_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_NO_DIV},
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun
pcc_clock_enable(enum pcc_clk clk,bool enable)84*4882a593Smuzhiyun int pcc_clock_enable(enum pcc_clk clk, bool enable)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun u32 reg, val;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (clk >= ARRAY_SIZE(pcc_arrays))
89*4882a593Smuzhiyun return -EINVAL;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun val = readl(reg);
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun clk_debug("pcc_clock_enable: clk %d, reg 0x%x, val 0x%x, enable %d\n",
96*4882a593Smuzhiyun clk, reg, val, enable);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK))
99*4882a593Smuzhiyun return -EPERM;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun if (enable)
102*4882a593Smuzhiyun val |= PCC_CGC_MASK;
103*4882a593Smuzhiyun else
104*4882a593Smuzhiyun val &= ~PCC_CGC_MASK;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun writel(val, reg);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun clk_debug("pcc_clock_enable: val 0x%x\n", val);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun return 0;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun /* The clock source select needs clock is disabled */
pcc_clock_sel(enum pcc_clk clk,enum scg_clk src)114*4882a593Smuzhiyun int pcc_clock_sel(enum pcc_clk clk, enum scg_clk src)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun u32 reg, val, i, clksrc_type;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (clk >= ARRAY_SIZE(pcc_arrays))
119*4882a593Smuzhiyun return -EINVAL;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun clksrc_type = pcc_arrays[clk].clksrc;
122*4882a593Smuzhiyun if (clksrc_type >= CLKSRC_NO_PCS) {
123*4882a593Smuzhiyun printf("No PCS field for the PCC %d, clksrc type %d\n",
124*4882a593Smuzhiyun clk, clksrc_type);
125*4882a593Smuzhiyun return -EPERM;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun for (i = 0; i < PCC_CLKSRC_NUM; i++) {
129*4882a593Smuzhiyun if (pcc_clksrc[clksrc_type][i] == src) {
130*4882a593Smuzhiyun /* Find the clock src, then set it to PCS */
131*4882a593Smuzhiyun break;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun if (i == PCC_CLKSRC_NUM) {
136*4882a593Smuzhiyun printf("Not find the parent scg_clk in PCS of PCC %d, invalid scg_clk %d\n", clk, src);
137*4882a593Smuzhiyun return -EINVAL;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun val = readl(reg);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun clk_debug("pcc_clock_sel: clk %d, reg 0x%x, val 0x%x, clksrc_type %d\n",
145*4882a593Smuzhiyun clk, reg, val, clksrc_type);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
148*4882a593Smuzhiyun (val & PCC_CGC_MASK)) {
149*4882a593Smuzhiyun printf("Not permit to select clock source val = 0x%x\n", val);
150*4882a593Smuzhiyun return -EPERM;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun val &= ~PCC_PCS_MASK;
154*4882a593Smuzhiyun val |= ((i + 1) << PCC_PCS_OFFSET);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun writel(val, reg);
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun clk_debug("pcc_clock_sel: val 0x%x\n", val);
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun return 0;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
pcc_clock_div_config(enum pcc_clk clk,bool frac,u8 div)163*4882a593Smuzhiyun int pcc_clock_div_config(enum pcc_clk clk, bool frac, u8 div)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun u32 reg, val;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun if (clk >= ARRAY_SIZE(pcc_arrays) || div > 8 ||
168*4882a593Smuzhiyun (div == 1 && frac != 0))
169*4882a593Smuzhiyun return -EINVAL;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun if (pcc_arrays[clk].div >= PCC_NO_DIV) {
172*4882a593Smuzhiyun printf("No DIV/FRAC field for the PCC %d\n", clk);
173*4882a593Smuzhiyun return -EPERM;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun val = readl(reg);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
181*4882a593Smuzhiyun (val & PCC_CGC_MASK)) {
182*4882a593Smuzhiyun printf("Not permit to set div/frac val = 0x%x\n", val);
183*4882a593Smuzhiyun return -EPERM;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun if (frac)
187*4882a593Smuzhiyun val |= PCC_FRAC_MASK;
188*4882a593Smuzhiyun else
189*4882a593Smuzhiyun val &= ~PCC_FRAC_MASK;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun val &= ~PCC_PCD_MASK;
192*4882a593Smuzhiyun val |= (div - 1) & PCC_PCD_MASK;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun writel(val, reg);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun return 0;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
pcc_clock_is_enable(enum pcc_clk clk)199*4882a593Smuzhiyun bool pcc_clock_is_enable(enum pcc_clk clk)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun u32 reg, val;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun if (clk >= ARRAY_SIZE(pcc_arrays))
204*4882a593Smuzhiyun return -EINVAL;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
207*4882a593Smuzhiyun val = readl(reg);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK))
210*4882a593Smuzhiyun return true;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun return false;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
pcc_clock_get_clksrc(enum pcc_clk clk,enum scg_clk * src)215*4882a593Smuzhiyun int pcc_clock_get_clksrc(enum pcc_clk clk, enum scg_clk *src)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun u32 reg, val, clksrc_type;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (clk >= ARRAY_SIZE(pcc_arrays))
220*4882a593Smuzhiyun return -EINVAL;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun clksrc_type = pcc_arrays[clk].clksrc;
223*4882a593Smuzhiyun if (clksrc_type >= CLKSRC_NO_PCS) {
224*4882a593Smuzhiyun printf("No PCS field for the PCC %d, clksrc type %d\n",
225*4882a593Smuzhiyun clk, clksrc_type);
226*4882a593Smuzhiyun return -EPERM;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun val = readl(reg);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun clk_debug("pcc_clock_get_clksrc: clk %d, reg 0x%x, val 0x%x, type %d\n",
234*4882a593Smuzhiyun clk, reg, val, clksrc_type);
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun if (!(val & PCC_PR_MASK)) {
237*4882a593Smuzhiyun printf("This pcc slot is not present = 0x%x\n", val);
238*4882a593Smuzhiyun return -EPERM;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun val &= PCC_PCS_MASK;
242*4882a593Smuzhiyun val = (val >> PCC_PCS_OFFSET);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun if (!val) {
245*4882a593Smuzhiyun printf("Clock source is off\n");
246*4882a593Smuzhiyun return -EIO;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun *src = pcc_clksrc[clksrc_type][val - 1];
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun clk_debug("pcc_clock_get_clksrc: parent scg clk %d\n", *src);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun return 0;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
pcc_clock_get_rate(enum pcc_clk clk)256*4882a593Smuzhiyun u32 pcc_clock_get_rate(enum pcc_clk clk)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun u32 reg, val, rate, frac, div;
259*4882a593Smuzhiyun enum scg_clk parent;
260*4882a593Smuzhiyun int ret;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun ret = pcc_clock_get_clksrc(clk, &parent);
263*4882a593Smuzhiyun if (ret)
264*4882a593Smuzhiyun return 0;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun rate = scg_clk_get_rate(parent);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun clk_debug("pcc_clock_get_rate: parent rate %u\n", rate);
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun if (pcc_arrays[clk].div == PCC_HAS_DIV) {
271*4882a593Smuzhiyun reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
272*4882a593Smuzhiyun val = readl(reg);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET;
275*4882a593Smuzhiyun div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun /*
278*4882a593Smuzhiyun * Theoretically don't have overflow in the calc,
279*4882a593Smuzhiyun * the rate won't exceed 2G
280*4882a593Smuzhiyun */
281*4882a593Smuzhiyun rate = rate * (frac + 1) / (div + 1);
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun clk_debug("pcc_clock_get_rate: rate %u\n", rate);
285*4882a593Smuzhiyun return rate;
286*4882a593Smuzhiyun }
287