Commit | Line | Data |
---|---|---|
d40670dc RJ |
1 | /* |
2 | * Marvell PXA27x family clocks | |
3 | * | |
4 | * Copyright (C) 2014 Robert Jarzmik | |
5 | * | |
6 | * Heavily inspired from former arch/arm/mach-pxa/clock.c. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; version 2 of the License. | |
11 | * | |
12 | */ | |
13 | #include <linux/clk-provider.h> | |
14 | #include <mach/pxa2xx-regs.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/clk.h> | |
17 | #include <linux/clkdev.h> | |
18 | #include <linux/of.h> | |
19 | ||
20 | #include <dt-bindings/clock/pxa-clock.h> | |
21 | #include "clk-pxa.h" | |
22 | ||
23 | #define KHz 1000 | |
24 | #define MHz (1000 * 1000) | |
25 | ||
26 | enum { | |
27 | PXA_CORE_13Mhz = 0, | |
28 | PXA_CORE_RUN, | |
29 | PXA_CORE_TURBO, | |
30 | }; | |
31 | ||
32 | enum { | |
33 | PXA_BUS_13Mhz = 0, | |
34 | PXA_BUS_RUN, | |
35 | }; | |
36 | ||
37 | enum { | |
38 | PXA_LCD_13Mhz = 0, | |
39 | PXA_LCD_RUN, | |
40 | }; | |
41 | ||
42 | enum { | |
43 | PXA_MEM_13Mhz = 0, | |
44 | PXA_MEM_SYSTEM_BUS, | |
45 | PXA_MEM_RUN, | |
46 | }; | |
47 | ||
48 | static const char * const get_freq_khz[] = { | |
49 | "core", "run", "cpll", "memory", | |
50 | "system_bus" | |
51 | }; | |
52 | ||
53 | /* | |
54 | * Get the clock frequency as reflected by CCSR and the turbo flag. | |
55 | * We assume these values have been applied via a fcs. | |
56 | * If info is not 0 we also display the current settings. | |
57 | */ | |
58 | unsigned int pxa27x_get_clk_frequency_khz(int info) | |
59 | { | |
60 | struct clk *clk; | |
61 | unsigned long clks[5]; | |
62 | int i; | |
63 | ||
64 | for (i = 0; i < 5; i++) { | |
65 | clk = clk_get(NULL, get_freq_khz[i]); | |
66 | if (IS_ERR(clk)) { | |
67 | clks[i] = 0; | |
68 | } else { | |
69 | clks[i] = clk_get_rate(clk); | |
70 | clk_put(clk); | |
71 | } | |
72 | } | |
73 | if (info) { | |
74 | pr_info("Run Mode clock: %ld.%02ldMHz\n", | |
75 | clks[1] / 1000000, (clks[1] % 1000000) / 10000); | |
76 | pr_info("Turbo Mode clock: %ld.%02ldMHz\n", | |
77 | clks[2] / 1000000, (clks[2] % 1000000) / 10000); | |
78 | pr_info("Memory clock: %ld.%02ldMHz\n", | |
79 | clks[3] / 1000000, (clks[3] % 1000000) / 10000); | |
80 | pr_info("System bus clock: %ld.%02ldMHz\n", | |
81 | clks[4] / 1000000, (clks[4] % 1000000) / 10000); | |
82 | } | |
4b5fb7dc | 83 | return (unsigned int)clks[0] / KHz; |
d40670dc RJ |
84 | } |
85 | ||
86 | bool pxa27x_is_ppll_disabled(void) | |
87 | { | |
88 | unsigned long ccsr = CCSR; | |
89 | ||
90 | return ccsr & (1 << CCCR_PPDIS_BIT); | |
91 | } | |
92 | ||
93 | #define PXA27X_CKEN(dev_id, con_id, parents, mult_hp, div_hp, \ | |
94 | bit, is_lp, flags) \ | |
95 | PXA_CKEN(dev_id, con_id, bit, parents, 1, 1, mult_hp, div_hp, \ | |
96 | is_lp, &CKEN, CKEN_ ## bit, flags) | |
97 | #define PXA27X_PBUS_CKEN(dev_id, con_id, bit, mult_hp, div_hp, delay) \ | |
98 | PXA27X_CKEN(dev_id, con_id, pxa27x_pbus_parents, mult_hp, \ | |
99 | div_hp, bit, pxa27x_is_ppll_disabled, 0) | |
100 | ||
101 | PARENTS(pxa27x_pbus) = { "osc_13mhz", "ppll_312mhz" }; | |
102 | PARENTS(pxa27x_sbus) = { "system_bus", "system_bus" }; | |
103 | PARENTS(pxa27x_32Mhz_bus) = { "osc_32_768khz", "osc_32_768khz" }; | |
104 | PARENTS(pxa27x_lcd_bus) = { "lcd_base", "lcd_base" }; | |
105 | PARENTS(pxa27x_membus) = { "lcd_base", "lcd_base" }; | |
106 | ||
107 | #define PXA27X_CKEN_1RATE(dev_id, con_id, bit, parents, delay) \ | |
108 | PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \ | |
109 | &CKEN, CKEN_ ## bit, 0) | |
110 | #define PXA27X_CKEN_1RATE_AO(dev_id, con_id, bit, parents, delay) \ | |
111 | PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \ | |
112 | &CKEN, CKEN_ ## bit, CLK_IGNORE_UNUSED) | |
113 | ||
14dd5b01 | 114 | static struct desc_clk_cken pxa27x_clocks[] __initdata = { |
d40670dc RJ |
115 | PXA27X_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 2, 42, 1), |
116 | PXA27X_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 2, 42, 1), | |
117 | PXA27X_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 2, 42, 1), | |
118 | PXA27X_PBUS_CKEN("pxa2xx-i2s", NULL, I2S, 2, 51, 0), | |
119 | PXA27X_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 19, 0), | |
120 | PXA27X_PBUS_CKEN("pxa27x-udc", NULL, USB, 2, 13, 5), | |
121 | PXA27X_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC, 2, 32, 0), | |
122 | PXA27X_PBUS_CKEN("pxa2xx-ir", "FICPCLK", FICP, 2, 13, 0), | |
123 | PXA27X_PBUS_CKEN("pxa27x-ohci", NULL, USBHOST, 2, 13, 0), | |
124 | PXA27X_PBUS_CKEN("pxa2xx-i2c.1", NULL, PWRI2C, 1, 24, 0), | |
125 | PXA27X_PBUS_CKEN("pxa27x-ssp.0", NULL, SSP1, 1, 24, 0), | |
126 | PXA27X_PBUS_CKEN("pxa27x-ssp.1", NULL, SSP2, 1, 24, 0), | |
127 | PXA27X_PBUS_CKEN("pxa27x-ssp.2", NULL, SSP3, 1, 24, 0), | |
128 | PXA27X_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 24, 0), | |
129 | PXA27X_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 24, 0), | |
130 | PXA27X_PBUS_CKEN(NULL, "MSLCLK", MSL, 2, 13, 0), | |
131 | PXA27X_PBUS_CKEN(NULL, "USIMCLK", USIM, 2, 13, 0), | |
132 | PXA27X_PBUS_CKEN(NULL, "MSTKCLK", MEMSTK, 2, 32, 0), | |
133 | PXA27X_PBUS_CKEN(NULL, "AC97CLK", AC97, 1, 1, 0), | |
134 | PXA27X_PBUS_CKEN(NULL, "AC97CONFCLK", AC97CONF, 1, 1, 0), | |
135 | PXA27X_PBUS_CKEN(NULL, "OSTIMER0", OSTIMER, 1, 96, 0), | |
136 | ||
137 | PXA27X_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD, | |
138 | pxa27x_32Mhz_bus_parents, 0), | |
139 | PXA27X_CKEN_1RATE(NULL, "IMCLK", IM, pxa27x_sbus_parents, 0), | |
140 | PXA27X_CKEN_1RATE("pxa2xx-fb", NULL, LCD, pxa27x_lcd_bus_parents, 0), | |
141 | PXA27X_CKEN_1RATE("pxa27x-camera.0", NULL, CAMERA, | |
142 | pxa27x_lcd_bus_parents, 0), | |
143 | PXA27X_CKEN_1RATE_AO("pxa2xx-pcmcia", NULL, MEMC, | |
144 | pxa27x_membus_parents, 0), | |
145 | ||
146 | }; | |
147 | ||
148 | static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, | |
149 | unsigned long parent_rate) | |
150 | { | |
151 | unsigned long clkcfg; | |
152 | unsigned int t, ht; | |
153 | unsigned int l, L, n2, N; | |
154 | unsigned long ccsr = CCSR; | |
155 | ||
156 | asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); | |
157 | t = clkcfg & (1 << 0); | |
158 | ht = clkcfg & (1 << 2); | |
159 | ||
160 | l = ccsr & CCSR_L_MASK; | |
161 | n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT; | |
162 | L = l * parent_rate; | |
163 | N = (L * n2) / 2; | |
164 | ||
165 | return t ? N : L; | |
166 | } | |
167 | PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" }; | |
168 | RATE_RO_OPS(clk_pxa27x_cpll, "cpll"); | |
169 | ||
170 | static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw, | |
171 | unsigned long parent_rate) | |
172 | { | |
173 | unsigned int l, osc_forced; | |
174 | unsigned long ccsr = CCSR; | |
175 | unsigned long cccr = CCCR; | |
176 | ||
177 | l = ccsr & CCSR_L_MASK; | |
178 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
179 | if (osc_forced) { | |
180 | if (cccr & (1 << CCCR_LCD_26_BIT)) | |
181 | return parent_rate * 2; | |
182 | else | |
183 | return parent_rate; | |
184 | } | |
185 | ||
186 | if (l <= 7) | |
187 | return parent_rate; | |
188 | if (l <= 16) | |
189 | return parent_rate / 2; | |
190 | return parent_rate / 4; | |
191 | } | |
192 | ||
193 | static u8 clk_pxa27x_lcd_base_get_parent(struct clk_hw *hw) | |
194 | { | |
195 | unsigned int osc_forced; | |
196 | unsigned long ccsr = CCSR; | |
197 | ||
198 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
199 | if (osc_forced) | |
200 | return PXA_LCD_13Mhz; | |
201 | else | |
202 | return PXA_LCD_RUN; | |
203 | } | |
204 | ||
205 | PARENTS(clk_pxa27x_lcd_base) = { "osc_13mhz", "run" }; | |
206 | MUX_RO_RATE_RO_OPS(clk_pxa27x_lcd_base, "lcd_base"); | |
207 | ||
208 | static void __init pxa27x_register_plls(void) | |
209 | { | |
210 | clk_register_fixed_rate(NULL, "osc_13mhz", NULL, | |
211 | CLK_GET_RATE_NOCACHE | CLK_IS_ROOT, | |
212 | 13 * MHz); | |
213 | clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, | |
214 | CLK_GET_RATE_NOCACHE | CLK_IS_ROOT, | |
215 | 32768 * KHz); | |
216 | clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0); | |
217 | clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1); | |
218 | } | |
219 | ||
220 | static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw, | |
221 | unsigned long parent_rate) | |
222 | { | |
223 | unsigned long clkcfg; | |
224 | unsigned int t, ht, b, osc_forced; | |
225 | unsigned long ccsr = CCSR; | |
226 | ||
227 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
228 | asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); | |
229 | t = clkcfg & (1 << 0); | |
230 | ht = clkcfg & (1 << 2); | |
231 | b = clkcfg & (1 << 3); | |
232 | ||
233 | if (osc_forced) | |
234 | return parent_rate; | |
235 | if (ht) | |
236 | return parent_rate / 2; | |
237 | else | |
238 | return parent_rate; | |
239 | } | |
240 | ||
241 | static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) | |
242 | { | |
243 | unsigned long clkcfg; | |
244 | unsigned int t, ht, b, osc_forced; | |
245 | unsigned long ccsr = CCSR; | |
246 | ||
247 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
248 | if (osc_forced) | |
249 | return PXA_CORE_13Mhz; | |
250 | ||
251 | asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); | |
252 | t = clkcfg & (1 << 0); | |
253 | ht = clkcfg & (1 << 2); | |
254 | b = clkcfg & (1 << 3); | |
255 | ||
256 | if (ht || t) | |
257 | return PXA_CORE_TURBO; | |
258 | return PXA_CORE_RUN; | |
259 | } | |
260 | PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" }; | |
261 | MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core"); | |
262 | ||
263 | static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw, | |
264 | unsigned long parent_rate) | |
265 | { | |
266 | unsigned long ccsr = CCSR; | |
267 | unsigned int n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT; | |
268 | ||
269 | return (parent_rate / n2) * 2; | |
270 | } | |
271 | PARENTS(clk_pxa27x_run) = { "cpll" }; | |
272 | RATE_RO_OPS(clk_pxa27x_run, "run"); | |
273 | ||
274 | static void __init pxa27x_register_core(void) | |
275 | { | |
276 | clk_register_clk_pxa27x_cpll(); | |
277 | clk_register_clk_pxa27x_run(); | |
278 | ||
279 | clkdev_pxa_register(CLK_CORE, "core", NULL, | |
280 | clk_register_clk_pxa27x_core()); | |
281 | } | |
282 | ||
283 | static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw, | |
284 | unsigned long parent_rate) | |
285 | { | |
286 | unsigned long clkcfg; | |
287 | unsigned int b, osc_forced; | |
288 | unsigned long ccsr = CCSR; | |
289 | ||
290 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
291 | asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); | |
292 | b = clkcfg & (1 << 3); | |
293 | ||
294 | if (osc_forced) | |
295 | return parent_rate; | |
296 | if (b) | |
297 | return parent_rate / 2; | |
298 | else | |
299 | return parent_rate; | |
300 | } | |
301 | ||
302 | static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw) | |
303 | { | |
304 | unsigned int osc_forced; | |
305 | unsigned long ccsr = CCSR; | |
306 | ||
307 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
308 | if (osc_forced) | |
309 | return PXA_BUS_13Mhz; | |
310 | else | |
311 | return PXA_BUS_RUN; | |
312 | } | |
313 | ||
314 | PARENTS(clk_pxa27x_system_bus) = { "osc_13mhz", "run" }; | |
315 | MUX_RO_RATE_RO_OPS(clk_pxa27x_system_bus, "system_bus"); | |
316 | ||
317 | static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw, | |
318 | unsigned long parent_rate) | |
319 | { | |
320 | unsigned int a, l, osc_forced; | |
321 | unsigned long cccr = CCCR; | |
322 | unsigned long ccsr = CCSR; | |
323 | ||
324 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
dcf3d458 | 325 | a = cccr & (1 << CCCR_A_BIT); |
d40670dc RJ |
326 | l = ccsr & CCSR_L_MASK; |
327 | ||
328 | if (osc_forced || a) | |
329 | return parent_rate; | |
330 | if (l <= 10) | |
331 | return parent_rate; | |
332 | if (l <= 20) | |
333 | return parent_rate / 2; | |
334 | return parent_rate / 4; | |
335 | } | |
336 | ||
337 | static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw) | |
338 | { | |
339 | unsigned int osc_forced, a; | |
340 | unsigned long cccr = CCCR; | |
341 | unsigned long ccsr = CCSR; | |
342 | ||
343 | osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); | |
dcf3d458 | 344 | a = cccr & (1 << CCCR_A_BIT); |
d40670dc RJ |
345 | if (osc_forced) |
346 | return PXA_MEM_13Mhz; | |
347 | if (a) | |
348 | return PXA_MEM_SYSTEM_BUS; | |
349 | else | |
350 | return PXA_MEM_RUN; | |
351 | } | |
352 | ||
353 | PARENTS(clk_pxa27x_memory) = { "osc_13mhz", "system_bus", "run" }; | |
354 | MUX_RO_RATE_RO_OPS(clk_pxa27x_memory, "memory"); | |
355 | ||
8b6d1034 RJ |
356 | #define DUMMY_CLK(_con_id, _dev_id, _parent) \ |
357 | { .con_id = _con_id, .dev_id = _dev_id, .parent = _parent } | |
358 | struct dummy_clk { | |
359 | const char *con_id; | |
360 | const char *dev_id; | |
361 | const char *parent; | |
362 | }; | |
363 | static struct dummy_clk dummy_clks[] __initdata = { | |
364 | DUMMY_CLK(NULL, "pxa27x-gpio", "osc_32_768khz"), | |
365 | DUMMY_CLK(NULL, "sa1100-rtc", "osc_32_768khz"), | |
366 | DUMMY_CLK("UARTCLK", "pxa2xx-ir", "STUART"), | |
367 | }; | |
368 | ||
369 | static void __init pxa27x_dummy_clocks_init(void) | |
370 | { | |
371 | struct clk *clk; | |
372 | struct dummy_clk *d; | |
373 | const char *name; | |
374 | int i; | |
375 | ||
376 | for (i = 0; i < ARRAY_SIZE(dummy_clks); i++) { | |
377 | d = &dummy_clks[i]; | |
378 | name = d->dev_id ? d->dev_id : d->con_id; | |
379 | clk = clk_register_fixed_factor(NULL, name, d->parent, 0, 1, 1); | |
380 | clk_register_clkdev(clk, d->con_id, d->dev_id); | |
381 | } | |
382 | } | |
383 | ||
d40670dc RJ |
384 | static void __init pxa27x_base_clocks_init(void) |
385 | { | |
386 | pxa27x_register_plls(); | |
387 | pxa27x_register_core(); | |
388 | clk_register_clk_pxa27x_system_bus(); | |
389 | clk_register_clk_pxa27x_memory(); | |
390 | clk_register_clk_pxa27x_lcd_base(); | |
391 | } | |
392 | ||
5e1d0128 | 393 | int __init pxa27x_clocks_init(void) |
d40670dc RJ |
394 | { |
395 | pxa27x_base_clocks_init(); | |
8b6d1034 | 396 | pxa27x_dummy_clocks_init(); |
d40670dc RJ |
397 | return clk_pxa_cken_init(pxa27x_clocks, ARRAY_SIZE(pxa27x_clocks)); |
398 | } | |
6f8a444a RJ |
399 | |
400 | static void __init pxa27x_dt_clocks_init(struct device_node *np) | |
401 | { | |
402 | pxa27x_clocks_init(); | |
403 | clk_pxa_dt_common_init(np); | |
404 | } | |
405 | CLK_OF_DECLARE(pxa_clks, "marvell,pxa270-clocks", pxa27x_dt_clocks_init); |