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