1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/clk-provider.h> |
3 | #include <linux/mfd/syscon.h> |
4 | #include <linux/slab.h> |
5 | |
6 | #include <dt-bindings/clock/at91.h> |
7 | |
8 | #include "pmc.h" |
9 | |
10 | static DEFINE_SPINLOCK(mck_lock); |
11 | |
12 | static const struct clk_master_characteristics mck_characteristics = { |
13 | .output = { .min = 124000000, .max = 166000000 }, |
14 | .divisors = { 1, 2, 4, 3 }, |
15 | }; |
16 | |
17 | static u8 plla_out[] = { 0 }; |
18 | |
19 | static u16 plla_icpll[] = { 0 }; |
20 | |
21 | static const struct clk_range plla_outputs[] = { |
22 | { .min = 600000000, .max = 1200000000 }, |
23 | }; |
24 | |
25 | static const struct clk_pll_characteristics plla_characteristics = { |
26 | .input = { .min = 12000000, .max = 24000000 }, |
27 | .num_output = ARRAY_SIZE(plla_outputs), |
28 | .output = plla_outputs, |
29 | .icpll = plla_icpll, |
30 | .out = plla_out, |
31 | }; |
32 | |
33 | static const struct clk_pcr_layout sama5d2_pcr_layout = { |
34 | .offset = 0x10c, |
35 | .cmd = BIT(12), |
36 | .gckcss_mask = GENMASK(10, 8), |
37 | .pid_mask = GENMASK(6, 0), |
38 | }; |
39 | |
40 | static const struct { |
41 | char *n; |
42 | char *p; |
43 | unsigned long flags; |
44 | u8 id; |
45 | } sama5d2_systemck[] = { |
46 | /* |
47 | * ddrck feeds DDR controller and is enabled by bootloader thus we need |
48 | * to keep it enabled in case there is no Linux consumer for it. |
49 | */ |
50 | { .n = "ddrck" , .p = "masterck_div" , .id = 2, .flags = CLK_IS_CRITICAL }, |
51 | { .n = "lcdck" , .p = "masterck_div" , .id = 3 }, |
52 | { .n = "uhpck" , .p = "usbck" , .id = 6 }, |
53 | { .n = "udpck" , .p = "usbck" , .id = 7 }, |
54 | { .n = "pck0" , .p = "prog0" , .id = 8 }, |
55 | { .n = "pck1" , .p = "prog1" , .id = 9 }, |
56 | { .n = "pck2" , .p = "prog2" , .id = 10 }, |
57 | { .n = "iscck" , .p = "masterck_div" , .id = 18 }, |
58 | }; |
59 | |
60 | static const struct { |
61 | char *n; |
62 | u8 id; |
63 | struct clk_range r; |
64 | } sama5d2_periph32ck[] = { |
65 | { .n = "macb0_clk" , .id = 5, .r = { .min = 0, .max = 83000000 }, }, |
66 | { .n = "tdes_clk" , .id = 11, .r = { .min = 0, .max = 83000000 }, }, |
67 | { .n = "matrix1_clk" , .id = 14, }, |
68 | { .n = "hsmc_clk" , .id = 17, }, |
69 | { .n = "pioA_clk" , .id = 18, .r = { .min = 0, .max = 83000000 }, }, |
70 | { .n = "flx0_clk" , .id = 19, .r = { .min = 0, .max = 83000000 }, }, |
71 | { .n = "flx1_clk" , .id = 20, .r = { .min = 0, .max = 83000000 }, }, |
72 | { .n = "flx2_clk" , .id = 21, .r = { .min = 0, .max = 83000000 }, }, |
73 | { .n = "flx3_clk" , .id = 22, .r = { .min = 0, .max = 83000000 }, }, |
74 | { .n = "flx4_clk" , .id = 23, .r = { .min = 0, .max = 83000000 }, }, |
75 | { .n = "uart0_clk" , .id = 24, .r = { .min = 0, .max = 83000000 }, }, |
76 | { .n = "uart1_clk" , .id = 25, .r = { .min = 0, .max = 83000000 }, }, |
77 | { .n = "uart2_clk" , .id = 26, .r = { .min = 0, .max = 83000000 }, }, |
78 | { .n = "uart3_clk" , .id = 27, .r = { .min = 0, .max = 83000000 }, }, |
79 | { .n = "uart4_clk" , .id = 28, .r = { .min = 0, .max = 83000000 }, }, |
80 | { .n = "twi0_clk" , .id = 29, .r = { .min = 0, .max = 83000000 }, }, |
81 | { .n = "twi1_clk" , .id = 30, .r = { .min = 0, .max = 83000000 }, }, |
82 | { .n = "spi0_clk" , .id = 33, .r = { .min = 0, .max = 83000000 }, }, |
83 | { .n = "spi1_clk" , .id = 34, .r = { .min = 0, .max = 83000000 }, }, |
84 | { .n = "tcb0_clk" , .id = 35, .r = { .min = 0, .max = 83000000 }, }, |
85 | { .n = "tcb1_clk" , .id = 36, .r = { .min = 0, .max = 83000000 }, }, |
86 | { .n = "pwm_clk" , .id = 38, .r = { .min = 0, .max = 83000000 }, }, |
87 | { .n = "adc_clk" , .id = 40, .r = { .min = 0, .max = 83000000 }, }, |
88 | { .n = "uhphs_clk" , .id = 41, .r = { .min = 0, .max = 83000000 }, }, |
89 | { .n = "udphs_clk" , .id = 42, .r = { .min = 0, .max = 83000000 }, }, |
90 | { .n = "ssc0_clk" , .id = 43, .r = { .min = 0, .max = 83000000 }, }, |
91 | { .n = "ssc1_clk" , .id = 44, .r = { .min = 0, .max = 83000000 }, }, |
92 | { .n = "trng_clk" , .id = 47, .r = { .min = 0, .max = 83000000 }, }, |
93 | { .n = "pdmic_clk" , .id = 48, .r = { .min = 0, .max = 83000000 }, }, |
94 | { .n = "securam_clk" , .id = 51, }, |
95 | { .n = "i2s0_clk" , .id = 54, .r = { .min = 0, .max = 83000000 }, }, |
96 | { .n = "i2s1_clk" , .id = 55, .r = { .min = 0, .max = 83000000 }, }, |
97 | { .n = "can0_clk" , .id = 56, .r = { .min = 0, .max = 83000000 }, }, |
98 | { .n = "can1_clk" , .id = 57, .r = { .min = 0, .max = 83000000 }, }, |
99 | { .n = "ptc_clk" , .id = 58, .r = { .min = 0, .max = 83000000 }, }, |
100 | { .n = "classd_clk" , .id = 59, .r = { .min = 0, .max = 83000000 }, }, |
101 | }; |
102 | |
103 | static const struct { |
104 | char *n; |
105 | unsigned long flags; |
106 | u8 id; |
107 | } sama5d2_periphck[] = { |
108 | { .n = "dma0_clk" , .id = 6, }, |
109 | { .n = "dma1_clk" , .id = 7, }, |
110 | { .n = "aes_clk" , .id = 9, }, |
111 | { .n = "aesb_clk" , .id = 10, }, |
112 | { .n = "sha_clk" , .id = 12, }, |
113 | /* |
114 | * mpddr_clk feeds DDR controller and is enabled by bootloader thus we |
115 | * need to keep it enabled in case there is no Linux consumer for it. |
116 | */ |
117 | { .n = "mpddr_clk" , .id = 13, .flags = CLK_IS_CRITICAL }, |
118 | { .n = "matrix0_clk" , .id = 15, }, |
119 | { .n = "sdmmc0_hclk" , .id = 31, }, |
120 | { .n = "sdmmc1_hclk" , .id = 32, }, |
121 | { .n = "lcdc_clk" , .id = 45, }, |
122 | { .n = "isc_clk" , .id = 46, }, |
123 | { .n = "qspi0_clk" , .id = 52, }, |
124 | { .n = "qspi1_clk" , .id = 53, }, |
125 | }; |
126 | |
127 | static const struct { |
128 | char *n; |
129 | u8 id; |
130 | struct clk_range r; |
131 | int chg_pid; |
132 | } sama5d2_gck[] = { |
133 | { .n = "flx0_gclk" , .id = 19, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
134 | { .n = "flx1_gclk" , .id = 20, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
135 | { .n = "flx2_gclk" , .id = 21, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
136 | { .n = "flx3_gclk" , .id = 22, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
137 | { .n = "flx4_gclk" , .id = 23, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
138 | { .n = "uart0_gclk" , .id = 24, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
139 | { .n = "uart1_gclk" , .id = 25, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
140 | { .n = "uart2_gclk" , .id = 26, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
141 | { .n = "uart3_gclk" , .id = 27, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
142 | { .n = "uart4_gclk" , .id = 28, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, |
143 | { .n = "sdmmc0_gclk" , .id = 31, .chg_pid = INT_MIN, }, |
144 | { .n = "sdmmc1_gclk" , .id = 32, .chg_pid = INT_MIN, }, |
145 | { .n = "tcb0_gclk" , .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, }, |
146 | { .n = "tcb1_gclk" , .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, }, |
147 | { .n = "pwm_gclk" , .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, }, |
148 | { .n = "isc_gclk" , .id = 46, .chg_pid = INT_MIN, }, |
149 | { .n = "pdmic_gclk" , .id = 48, .chg_pid = INT_MIN, }, |
150 | { .n = "i2s0_gclk" , .id = 54, .chg_pid = 5, }, |
151 | { .n = "i2s1_gclk" , .id = 55, .chg_pid = 5, }, |
152 | { .n = "can0_gclk" , .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, }, |
153 | { .n = "can1_gclk" , .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, }, |
154 | { .n = "classd_gclk" , .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, }, |
155 | }; |
156 | |
157 | static const struct clk_programmable_layout sama5d2_programmable_layout = { |
158 | .pres_mask = 0xff, |
159 | .pres_shift = 4, |
160 | .css_mask = 0x7, |
161 | .have_slck_mck = 0, |
162 | .is_pres_direct = 1, |
163 | }; |
164 | |
165 | static void __init sama5d2_pmc_setup(struct device_node *np) |
166 | { |
167 | struct clk_range range = CLK_RANGE(0, 0); |
168 | const char *slck_name, *mainxtal_name; |
169 | struct pmc_data *sama5d2_pmc; |
170 | const char *parent_names[6]; |
171 | struct regmap *regmap, *regmap_sfr; |
172 | struct clk_hw *hw; |
173 | int i; |
174 | bool bypass; |
175 | |
176 | i = of_property_match_string(np, propname: "clock-names" , string: "slow_clk" ); |
177 | if (i < 0) |
178 | return; |
179 | |
180 | slck_name = of_clk_get_parent_name(np, index: i); |
181 | |
182 | i = of_property_match_string(np, propname: "clock-names" , string: "main_xtal" ); |
183 | if (i < 0) |
184 | return; |
185 | mainxtal_name = of_clk_get_parent_name(np, index: i); |
186 | |
187 | regmap = device_node_to_regmap(np); |
188 | if (IS_ERR(ptr: regmap)) |
189 | return; |
190 | |
191 | sama5d2_pmc = pmc_data_allocate(PMC_AUDIOPINCK + 1, |
192 | nck(sama5d2_systemck), |
193 | nck(sama5d2_periph32ck), |
194 | nck(sama5d2_gck), npck: 3); |
195 | if (!sama5d2_pmc) |
196 | return; |
197 | |
198 | hw = at91_clk_register_main_rc_osc(regmap, name: "main_rc_osc" , frequency: 12000000, |
199 | accuracy: 100000000); |
200 | if (IS_ERR(ptr: hw)) |
201 | goto err_free; |
202 | |
203 | bypass = of_property_read_bool(np, propname: "atmel,osc-bypass" ); |
204 | |
205 | hw = at91_clk_register_main_osc(regmap, name: "main_osc" , parent_name: mainxtal_name, NULL, |
206 | bypass); |
207 | if (IS_ERR(ptr: hw)) |
208 | goto err_free; |
209 | |
210 | parent_names[0] = "main_rc_osc" ; |
211 | parent_names[1] = "main_osc" ; |
212 | hw = at91_clk_register_sam9x5_main(regmap, name: "mainck" , parent_names, NULL, num_parents: 2); |
213 | if (IS_ERR(ptr: hw)) |
214 | goto err_free; |
215 | |
216 | sama5d2_pmc->chws[PMC_MAIN] = hw; |
217 | |
218 | hw = at91_clk_register_pll(regmap, name: "pllack" , parent_name: "mainck" , id: 0, |
219 | layout: &sama5d3_pll_layout, characteristics: &plla_characteristics); |
220 | if (IS_ERR(ptr: hw)) |
221 | goto err_free; |
222 | |
223 | hw = at91_clk_register_plldiv(regmap, name: "plladivck" , parent_name: "pllack" ); |
224 | if (IS_ERR(ptr: hw)) |
225 | goto err_free; |
226 | |
227 | sama5d2_pmc->chws[PMC_PLLACK] = hw; |
228 | |
229 | hw = at91_clk_register_audio_pll_frac(regmap, name: "audiopll_fracck" , |
230 | parent_name: "mainck" ); |
231 | if (IS_ERR(ptr: hw)) |
232 | goto err_free; |
233 | |
234 | hw = at91_clk_register_audio_pll_pad(regmap, name: "audiopll_padck" , |
235 | parent_name: "audiopll_fracck" ); |
236 | if (IS_ERR(ptr: hw)) |
237 | goto err_free; |
238 | |
239 | sama5d2_pmc->chws[PMC_AUDIOPINCK] = hw; |
240 | |
241 | hw = at91_clk_register_audio_pll_pmc(regmap, name: "audiopll_pmcck" , |
242 | parent_name: "audiopll_fracck" ); |
243 | if (IS_ERR(ptr: hw)) |
244 | goto err_free; |
245 | |
246 | sama5d2_pmc->chws[PMC_AUDIOPLLCK] = hw; |
247 | |
248 | regmap_sfr = syscon_regmap_lookup_by_compatible(s: "atmel,sama5d2-sfr" ); |
249 | if (IS_ERR(ptr: regmap_sfr)) |
250 | regmap_sfr = NULL; |
251 | |
252 | hw = at91_clk_register_utmi(regmap_pmc: regmap, regmap_sfr, name: "utmick" , parent_name: "mainck" , NULL); |
253 | if (IS_ERR(ptr: hw)) |
254 | goto err_free; |
255 | |
256 | sama5d2_pmc->chws[PMC_UTMI] = hw; |
257 | |
258 | parent_names[0] = slck_name; |
259 | parent_names[1] = "mainck" ; |
260 | parent_names[2] = "plladivck" ; |
261 | parent_names[3] = "utmick" ; |
262 | hw = at91_clk_register_master_pres(regmap, name: "masterck_pres" , num_parents: 4, |
263 | parent_names, NULL, |
264 | layout: &at91sam9x5_master_layout, |
265 | characteristics: &mck_characteristics, lock: &mck_lock); |
266 | if (IS_ERR(ptr: hw)) |
267 | goto err_free; |
268 | |
269 | hw = at91_clk_register_master_div(regmap, name: "masterck_div" , |
270 | parent_names: "masterck_pres" , NULL, |
271 | layout: &at91sam9x5_master_layout, |
272 | characteristics: &mck_characteristics, lock: &mck_lock, |
273 | CLK_SET_RATE_GATE, safe_div: 0); |
274 | if (IS_ERR(ptr: hw)) |
275 | goto err_free; |
276 | |
277 | sama5d2_pmc->chws[PMC_MCK] = hw; |
278 | |
279 | hw = at91_clk_register_h32mx(regmap, name: "h32mxck" , parent_name: "masterck_div" ); |
280 | if (IS_ERR(ptr: hw)) |
281 | goto err_free; |
282 | |
283 | sama5d2_pmc->chws[PMC_MCK2] = hw; |
284 | |
285 | parent_names[0] = "plladivck" ; |
286 | parent_names[1] = "utmick" ; |
287 | hw = at91sam9x5_clk_register_usb(regmap, name: "usbck" , parent_names, num_parents: 2); |
288 | if (IS_ERR(ptr: hw)) |
289 | goto err_free; |
290 | |
291 | parent_names[0] = slck_name; |
292 | parent_names[1] = "mainck" ; |
293 | parent_names[2] = "plladivck" ; |
294 | parent_names[3] = "utmick" ; |
295 | parent_names[4] = "masterck_div" ; |
296 | parent_names[5] = "audiopll_pmcck" ; |
297 | for (i = 0; i < 3; i++) { |
298 | char name[6]; |
299 | |
300 | snprintf(buf: name, size: sizeof(name), fmt: "prog%d" , i); |
301 | |
302 | hw = at91_clk_register_programmable(regmap, name, |
303 | parent_names, NULL, num_parents: 6, id: i, |
304 | layout: &sama5d2_programmable_layout, |
305 | NULL); |
306 | if (IS_ERR(ptr: hw)) |
307 | goto err_free; |
308 | |
309 | sama5d2_pmc->pchws[i] = hw; |
310 | } |
311 | |
312 | for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) { |
313 | hw = at91_clk_register_system(regmap, name: sama5d2_systemck[i].n, |
314 | parent_name: sama5d2_systemck[i].p, NULL, |
315 | id: sama5d2_systemck[i].id, |
316 | flags: sama5d2_systemck[i].flags); |
317 | if (IS_ERR(ptr: hw)) |
318 | goto err_free; |
319 | |
320 | sama5d2_pmc->shws[sama5d2_systemck[i].id] = hw; |
321 | } |
322 | |
323 | for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) { |
324 | hw = at91_clk_register_sam9x5_peripheral(regmap, lock: &pmc_pcr_lock, |
325 | layout: &sama5d2_pcr_layout, |
326 | name: sama5d2_periphck[i].n, |
327 | parent_name: "masterck_div" , NULL, |
328 | id: sama5d2_periphck[i].id, |
329 | range: &range, INT_MIN, |
330 | flags: sama5d2_periphck[i].flags); |
331 | if (IS_ERR(ptr: hw)) |
332 | goto err_free; |
333 | |
334 | sama5d2_pmc->phws[sama5d2_periphck[i].id] = hw; |
335 | } |
336 | |
337 | for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) { |
338 | hw = at91_clk_register_sam9x5_peripheral(regmap, lock: &pmc_pcr_lock, |
339 | layout: &sama5d2_pcr_layout, |
340 | name: sama5d2_periph32ck[i].n, |
341 | parent_name: "h32mxck" , NULL, |
342 | id: sama5d2_periph32ck[i].id, |
343 | range: &sama5d2_periph32ck[i].r, |
344 | INT_MIN, flags: 0); |
345 | if (IS_ERR(ptr: hw)) |
346 | goto err_free; |
347 | |
348 | sama5d2_pmc->phws[sama5d2_periph32ck[i].id] = hw; |
349 | } |
350 | |
351 | parent_names[0] = slck_name; |
352 | parent_names[1] = "mainck" ; |
353 | parent_names[2] = "plladivck" ; |
354 | parent_names[3] = "utmick" ; |
355 | parent_names[4] = "masterck_div" ; |
356 | parent_names[5] = "audiopll_pmcck" ; |
357 | for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) { |
358 | hw = at91_clk_register_generated(regmap, lock: &pmc_pcr_lock, |
359 | layout: &sama5d2_pcr_layout, |
360 | name: sama5d2_gck[i].n, |
361 | parent_names, NULL, NULL, num_parents: 6, |
362 | id: sama5d2_gck[i].id, |
363 | range: &sama5d2_gck[i].r, |
364 | chg_pid: sama5d2_gck[i].chg_pid); |
365 | if (IS_ERR(ptr: hw)) |
366 | goto err_free; |
367 | |
368 | sama5d2_pmc->ghws[sama5d2_gck[i].id] = hw; |
369 | } |
370 | |
371 | if (regmap_sfr) { |
372 | parent_names[0] = "i2s0_clk" ; |
373 | parent_names[1] = "i2s0_gclk" ; |
374 | hw = at91_clk_i2s_mux_register(regmap: regmap_sfr, name: "i2s0_muxclk" , |
375 | parent_names, num_parents: 2, bus_id: 0); |
376 | if (IS_ERR(ptr: hw)) |
377 | goto err_free; |
378 | |
379 | sama5d2_pmc->chws[PMC_I2S0_MUX] = hw; |
380 | |
381 | parent_names[0] = "i2s1_clk" ; |
382 | parent_names[1] = "i2s1_gclk" ; |
383 | hw = at91_clk_i2s_mux_register(regmap: regmap_sfr, name: "i2s1_muxclk" , |
384 | parent_names, num_parents: 2, bus_id: 1); |
385 | if (IS_ERR(ptr: hw)) |
386 | goto err_free; |
387 | |
388 | sama5d2_pmc->chws[PMC_I2S1_MUX] = hw; |
389 | } |
390 | |
391 | of_clk_add_hw_provider(np, get: of_clk_hw_pmc_get, data: sama5d2_pmc); |
392 | |
393 | return; |
394 | |
395 | err_free: |
396 | kfree(objp: sama5d2_pmc); |
397 | } |
398 | |
399 | CLK_OF_DECLARE(sama5d2_pmc, "atmel,sama5d2-pmc" , sama5d2_pmc_setup); |
400 | |