1 | // SPDX-License-Identifier: GPL-2.0-or-later |
---|---|
2 | /* |
3 | * Clock control for Cirrus EP93xx chips. |
4 | * Copyright (C) 2021 Nikita Shubin <nikita.shubin@maquefel.me> |
5 | * |
6 | * Based on a rewrite of arch/arm/mach-ep93xx/clock.c: |
7 | * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> |
8 | */ |
9 | #define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/bits.h> |
12 | #include <linux/cleanup.h> |
13 | #include <linux/clk-provider.h> |
14 | #include <linux/math.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/spinlock.h> |
18 | |
19 | #include <linux/soc/cirrus/ep93xx.h> |
20 | #include <dt-bindings/clock/cirrus,ep9301-syscon.h> |
21 | |
22 | #include <asm/div64.h> |
23 | |
24 | #define EP93XX_EXT_CLK_RATE 14745600 |
25 | #define EP93XX_EXT_RTC_RATE 32768 |
26 | |
27 | #define EP93XX_SYSCON_POWER_STATE 0x00 |
28 | #define EP93XX_SYSCON_PWRCNT 0x04 |
29 | #define EP93XX_SYSCON_PWRCNT_UARTBAUD BIT(29) |
30 | #define EP93XX_SYSCON_PWRCNT_USH_EN 28 |
31 | #define EP93XX_SYSCON_PWRCNT_DMA_M2M1 27 |
32 | #define EP93XX_SYSCON_PWRCNT_DMA_M2M0 26 |
33 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P8 25 |
34 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P9 24 |
35 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P6 23 |
36 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P7 22 |
37 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P4 21 |
38 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P5 20 |
39 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P2 19 |
40 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P3 18 |
41 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P0 17 |
42 | #define EP93XX_SYSCON_PWRCNT_DMA_M2P1 16 |
43 | #define EP93XX_SYSCON_CLKSET1 0x20 |
44 | #define EP93XX_SYSCON_CLKSET1_NBYP1 BIT(23) |
45 | #define EP93XX_SYSCON_CLKSET2 0x24 |
46 | #define EP93XX_SYSCON_CLKSET2_NBYP2 BIT(19) |
47 | #define EP93XX_SYSCON_CLKSET2_PLL2_EN BIT(18) |
48 | #define EP93XX_SYSCON_DEVCFG 0x80 |
49 | #define EP93XX_SYSCON_DEVCFG_U3EN 24 |
50 | #define EP93XX_SYSCON_DEVCFG_U2EN 20 |
51 | #define EP93XX_SYSCON_DEVCFG_U1EN 18 |
52 | #define EP93XX_SYSCON_VIDCLKDIV 0x84 |
53 | #define EP93XX_SYSCON_CLKDIV_ENABLE 15 |
54 | #define EP93XX_SYSCON_CLKDIV_ESEL BIT(14) |
55 | #define EP93XX_SYSCON_CLKDIV_PSEL BIT(13) |
56 | #define EP93XX_SYSCON_CLKDIV_MASK GENMASK(14, 13) |
57 | #define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8 |
58 | #define EP93XX_SYSCON_I2SCLKDIV 0x8c |
59 | #define EP93XX_SYSCON_I2SCLKDIV_SENA 31 |
60 | #define EP93XX_SYSCON_I2SCLKDIV_ORIDE BIT(29) |
61 | #define EP93XX_SYSCON_I2SCLKDIV_SPOL BIT(19) |
62 | #define EP93XX_SYSCON_KEYTCHCLKDIV 0x90 |
63 | #define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN 31 |
64 | #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV 16 |
65 | #define EP93XX_SYSCON_KEYTCHCLKDIV_KEN 15 |
66 | #define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV 0 |
67 | #define EP93XX_SYSCON_CHIPID 0x94 |
68 | #define EP93XX_SYSCON_CHIPID_ID 0x9213 |
69 | |
70 | #define EP93XX_FIXED_CLK_COUNT 21 |
71 | |
72 | static const char ep93xx_adc_divisors[] = { 16, 4 }; |
73 | static const char ep93xx_sclk_divisors[] = { 2, 4 }; |
74 | static const char ep93xx_lrclk_divisors[] = { 32, 64, 128 }; |
75 | |
76 | struct ep93xx_clk { |
77 | struct clk_hw hw; |
78 | u16 idx; |
79 | u16 reg; |
80 | u32 mask; |
81 | u8 bit_idx; |
82 | u8 shift; |
83 | u8 width; |
84 | u8 num_div; |
85 | const char *div; |
86 | }; |
87 | |
88 | struct ep93xx_clk_priv { |
89 | spinlock_t lock; |
90 | struct ep93xx_regmap_adev *aux_dev; |
91 | struct device *dev; |
92 | void __iomem *base; |
93 | struct regmap *map; |
94 | struct clk_hw *fixed[EP93XX_FIXED_CLK_COUNT]; |
95 | struct ep93xx_clk reg[]; |
96 | }; |
97 | |
98 | static struct ep93xx_clk *ep93xx_clk_from(struct clk_hw *hw) |
99 | { |
100 | return container_of(hw, struct ep93xx_clk, hw); |
101 | } |
102 | |
103 | static struct ep93xx_clk_priv *ep93xx_priv_from(struct ep93xx_clk *clk) |
104 | { |
105 | return container_of(clk, struct ep93xx_clk_priv, reg[clk->idx]); |
106 | } |
107 | |
108 | static void ep93xx_clk_write(struct ep93xx_clk_priv *priv, unsigned int reg, unsigned int val) |
109 | { |
110 | struct ep93xx_regmap_adev *aux = priv->aux_dev; |
111 | |
112 | aux->write(aux->map, aux->lock, reg, val); |
113 | } |
114 | |
115 | static int ep93xx_clk_is_enabled(struct clk_hw *hw) |
116 | { |
117 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
118 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
119 | u32 val; |
120 | |
121 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
122 | |
123 | return !!(val & BIT(clk->bit_idx)); |
124 | } |
125 | |
126 | static int ep93xx_clk_enable(struct clk_hw *hw) |
127 | { |
128 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
129 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
130 | u32 val; |
131 | |
132 | guard(spinlock_irqsave)(l: &priv->lock); |
133 | |
134 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
135 | val |= BIT(clk->bit_idx); |
136 | |
137 | ep93xx_clk_write(priv, reg: clk->reg, val); |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | static void ep93xx_clk_disable(struct clk_hw *hw) |
143 | { |
144 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
145 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
146 | u32 val; |
147 | |
148 | guard(spinlock_irqsave)(l: &priv->lock); |
149 | |
150 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
151 | val &= ~BIT(clk->bit_idx); |
152 | |
153 | ep93xx_clk_write(priv, reg: clk->reg, val); |
154 | } |
155 | |
156 | static const struct clk_ops clk_ep93xx_gate_ops = { |
157 | .enable = ep93xx_clk_enable, |
158 | .disable = ep93xx_clk_disable, |
159 | .is_enabled = ep93xx_clk_is_enabled, |
160 | }; |
161 | |
162 | static int ep93xx_clk_register_gate(struct ep93xx_clk *clk, |
163 | const char *name, |
164 | struct clk_parent_data *parent_data, |
165 | unsigned long flags, |
166 | unsigned int reg, |
167 | u8 bit_idx) |
168 | { |
169 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
170 | struct clk_init_data init = { }; |
171 | |
172 | init.name = name; |
173 | init.ops = &clk_ep93xx_gate_ops; |
174 | init.flags = flags; |
175 | init.parent_data = parent_data; |
176 | init.num_parents = 1; |
177 | |
178 | clk->reg = reg; |
179 | clk->bit_idx = bit_idx; |
180 | clk->hw.init = &init; |
181 | |
182 | return devm_clk_hw_register(dev: priv->dev, hw: &clk->hw); |
183 | } |
184 | |
185 | static u8 ep93xx_mux_get_parent(struct clk_hw *hw) |
186 | { |
187 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
188 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
189 | u32 val; |
190 | |
191 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
192 | |
193 | val &= EP93XX_SYSCON_CLKDIV_MASK; |
194 | |
195 | switch (val) { |
196 | case EP93XX_SYSCON_CLKDIV_ESEL: |
197 | return 1; /* PLL1 */ |
198 | case EP93XX_SYSCON_CLKDIV_MASK: |
199 | return 2; /* PLL2 */ |
200 | default: |
201 | return 0; /* XTALI */ |
202 | }; |
203 | } |
204 | |
205 | static int ep93xx_mux_set_parent_lock(struct clk_hw *hw, u8 index) |
206 | { |
207 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
208 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
209 | u32 val; |
210 | |
211 | if (index >= 3) |
212 | return -EINVAL; |
213 | |
214 | guard(spinlock_irqsave)(l: &priv->lock); |
215 | |
216 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
217 | val &= ~(EP93XX_SYSCON_CLKDIV_MASK); |
218 | val |= index > 0 ? EP93XX_SYSCON_CLKDIV_ESEL : 0; |
219 | val |= index > 1 ? EP93XX_SYSCON_CLKDIV_PSEL : 0; |
220 | |
221 | ep93xx_clk_write(priv, reg: clk->reg, val); |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | static bool is_best(unsigned long rate, unsigned long now, |
227 | unsigned long best) |
228 | { |
229 | return abs_diff(rate, now) < abs_diff(rate, best); |
230 | } |
231 | |
232 | static int ep93xx_mux_determine_rate(struct clk_hw *hw, |
233 | struct clk_rate_request *req) |
234 | { |
235 | unsigned long best_rate = 0, actual_rate, mclk_rate; |
236 | unsigned long rate = req->rate; |
237 | struct clk_hw *parent_best = NULL; |
238 | unsigned long parent_rate_best; |
239 | unsigned long parent_rate; |
240 | int div, pdiv; |
241 | unsigned int i; |
242 | |
243 | /* |
244 | * Try the two pll's and the external clock, |
245 | * because the valid predividers are 2, 2.5 and 3, we multiply |
246 | * all the clocks by 2 to avoid floating point math. |
247 | * |
248 | * This is based on the algorithm in the ep93xx raster guide: |
249 | * http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf |
250 | * |
251 | */ |
252 | for (i = 0; i < clk_hw_get_num_parents(hw); i++) { |
253 | struct clk_hw *parent = clk_hw_get_parent_by_index(hw, index: i); |
254 | |
255 | parent_rate = clk_hw_get_rate(hw: parent); |
256 | mclk_rate = parent_rate * 2; |
257 | |
258 | /* Try each predivider value */ |
259 | for (pdiv = 4; pdiv <= 6; pdiv++) { |
260 | div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv); |
261 | if (!in_range(div, 1, 127)) |
262 | continue; |
263 | |
264 | actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div); |
265 | if (is_best(rate, now: actual_rate, best: best_rate)) { |
266 | best_rate = actual_rate; |
267 | parent_rate_best = parent_rate; |
268 | parent_best = parent; |
269 | } |
270 | } |
271 | } |
272 | |
273 | if (!parent_best) |
274 | return -EINVAL; |
275 | |
276 | req->best_parent_rate = parent_rate_best; |
277 | req->best_parent_hw = parent_best; |
278 | req->rate = best_rate; |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | static unsigned long ep93xx_ddiv_recalc_rate(struct clk_hw *hw, |
284 | unsigned long parent_rate) |
285 | { |
286 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
287 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
288 | unsigned int pdiv, div; |
289 | u32 val; |
290 | |
291 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
292 | pdiv = (val >> EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) & GENMASK(1, 0); |
293 | div = val & GENMASK(6, 0); |
294 | if (!div) |
295 | return 0; |
296 | |
297 | return DIV_ROUND_CLOSEST(parent_rate * 2, (pdiv + 3) * div); |
298 | } |
299 | |
300 | static int ep93xx_ddiv_set_rate(struct clk_hw *hw, unsigned long rate, |
301 | unsigned long parent_rate) |
302 | { |
303 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
304 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
305 | int pdiv, div, npdiv, ndiv; |
306 | unsigned long actual_rate, mclk_rate, rate_err = ULONG_MAX; |
307 | u32 val; |
308 | |
309 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
310 | mclk_rate = parent_rate * 2; |
311 | |
312 | for (pdiv = 4; pdiv <= 6; pdiv++) { |
313 | div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv); |
314 | if (!in_range(div, 1, 127)) |
315 | continue; |
316 | |
317 | actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div); |
318 | if (abs(actual_rate - rate) < rate_err) { |
319 | npdiv = pdiv - 3; |
320 | ndiv = div; |
321 | rate_err = abs(actual_rate - rate); |
322 | } |
323 | } |
324 | |
325 | if (rate_err == ULONG_MAX) |
326 | return -EINVAL; |
327 | |
328 | /* |
329 | * Clear old dividers. |
330 | * Bit 7 is reserved bit in all ClkDiv registers. |
331 | */ |
332 | val &= ~(GENMASK(9, 0) & ~BIT(7)); |
333 | |
334 | /* Set the new pdiv and div bits for the new clock rate */ |
335 | val |= (npdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | ndiv; |
336 | |
337 | ep93xx_clk_write(priv, reg: clk->reg, val); |
338 | |
339 | return 0; |
340 | } |
341 | |
342 | static const struct clk_ops clk_ddiv_ops = { |
343 | .enable = ep93xx_clk_enable, |
344 | .disable = ep93xx_clk_disable, |
345 | .is_enabled = ep93xx_clk_is_enabled, |
346 | .get_parent = ep93xx_mux_get_parent, |
347 | .set_parent = ep93xx_mux_set_parent_lock, |
348 | .determine_rate = ep93xx_mux_determine_rate, |
349 | .recalc_rate = ep93xx_ddiv_recalc_rate, |
350 | .set_rate = ep93xx_ddiv_set_rate, |
351 | }; |
352 | |
353 | static int ep93xx_clk_register_ddiv(struct ep93xx_clk *clk, |
354 | const char *name, |
355 | struct clk_parent_data *parent_data, |
356 | u8 num_parents, |
357 | unsigned int reg, |
358 | u8 bit_idx) |
359 | { |
360 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
361 | struct clk_init_data init = { }; |
362 | |
363 | init.name = name; |
364 | init.ops = &clk_ddiv_ops; |
365 | init.flags = 0; |
366 | init.parent_data = parent_data; |
367 | init.num_parents = num_parents; |
368 | |
369 | clk->reg = reg; |
370 | clk->bit_idx = bit_idx; |
371 | clk->hw.init = &init; |
372 | |
373 | return devm_clk_hw_register(dev: priv->dev, hw: &clk->hw); |
374 | } |
375 | |
376 | static unsigned long ep93xx_div_recalc_rate(struct clk_hw *hw, |
377 | unsigned long parent_rate) |
378 | { |
379 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
380 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
381 | u32 val; |
382 | u8 index; |
383 | |
384 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
385 | index = (val & clk->mask) >> clk->shift; |
386 | if (index >= clk->num_div) |
387 | return 0; |
388 | |
389 | return DIV_ROUND_CLOSEST(parent_rate, clk->div[index]); |
390 | } |
391 | |
392 | static long ep93xx_div_round_rate(struct clk_hw *hw, unsigned long rate, |
393 | unsigned long *parent_rate) |
394 | { |
395 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
396 | unsigned long best = 0, now; |
397 | unsigned int i; |
398 | |
399 | for (i = 0; i < clk->num_div; i++) { |
400 | if ((rate * clk->div[i]) == *parent_rate) |
401 | return rate; |
402 | |
403 | now = DIV_ROUND_CLOSEST(*parent_rate, clk->div[i]); |
404 | if (!best || is_best(rate, now, best)) |
405 | best = now; |
406 | } |
407 | |
408 | return best; |
409 | } |
410 | |
411 | static int ep93xx_div_set_rate(struct clk_hw *hw, unsigned long rate, |
412 | unsigned long parent_rate) |
413 | { |
414 | struct ep93xx_clk *clk = ep93xx_clk_from(hw); |
415 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
416 | unsigned int i; |
417 | u32 val; |
418 | |
419 | regmap_read(map: priv->map, reg: clk->reg, val: &val); |
420 | val &= ~clk->mask; |
421 | for (i = 0; i < clk->num_div; i++) |
422 | if (rate == DIV_ROUND_CLOSEST(parent_rate, clk->div[i])) |
423 | break; |
424 | |
425 | if (i == clk->num_div) |
426 | return -EINVAL; |
427 | |
428 | val |= i << clk->shift; |
429 | |
430 | ep93xx_clk_write(priv, reg: clk->reg, val); |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | static const struct clk_ops ep93xx_div_ops = { |
436 | .enable = ep93xx_clk_enable, |
437 | .disable = ep93xx_clk_disable, |
438 | .is_enabled = ep93xx_clk_is_enabled, |
439 | .recalc_rate = ep93xx_div_recalc_rate, |
440 | .round_rate = ep93xx_div_round_rate, |
441 | .set_rate = ep93xx_div_set_rate, |
442 | }; |
443 | |
444 | static int ep93xx_register_div(struct ep93xx_clk *clk, |
445 | const char *name, |
446 | const struct clk_parent_data *parent_data, |
447 | unsigned int reg, |
448 | u8 enable_bit, |
449 | u8 shift, |
450 | u8 width, |
451 | const char *clk_divisors, |
452 | u8 num_div) |
453 | { |
454 | struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); |
455 | struct clk_init_data init = { }; |
456 | |
457 | init.name = name; |
458 | init.ops = &ep93xx_div_ops; |
459 | init.flags = 0; |
460 | init.parent_data = parent_data; |
461 | init.num_parents = 1; |
462 | |
463 | clk->reg = reg; |
464 | clk->bit_idx = enable_bit; |
465 | clk->mask = GENMASK(shift + width - 1, shift); |
466 | clk->shift = shift; |
467 | clk->div = clk_divisors; |
468 | clk->num_div = num_div; |
469 | clk->hw.init = &init; |
470 | |
471 | return devm_clk_hw_register(dev: priv->dev, hw: &clk->hw); |
472 | } |
473 | |
474 | struct ep93xx_gate { |
475 | unsigned int idx; |
476 | unsigned int bit; |
477 | const char *name; |
478 | }; |
479 | |
480 | static const struct ep93xx_gate ep93xx_uarts[] = { |
481 | { EP93XX_CLK_UART1, EP93XX_SYSCON_DEVCFG_U1EN, "uart1"}, |
482 | { EP93XX_CLK_UART2, EP93XX_SYSCON_DEVCFG_U2EN, "uart2"}, |
483 | { EP93XX_CLK_UART3, EP93XX_SYSCON_DEVCFG_U3EN, "uart3"}, |
484 | }; |
485 | |
486 | static int ep93xx_uart_clock_init(struct ep93xx_clk_priv *priv) |
487 | { |
488 | struct clk_parent_data parent_data = { }; |
489 | unsigned int i, idx, ret, clk_uart_div; |
490 | struct ep93xx_clk *clk; |
491 | u32 val; |
492 | |
493 | regmap_read(map: priv->map, EP93XX_SYSCON_PWRCNT, val: &val); |
494 | if (val & EP93XX_SYSCON_PWRCNT_UARTBAUD) |
495 | clk_uart_div = 1; |
496 | else |
497 | clk_uart_div = 2; |
498 | |
499 | priv->fixed[EP93XX_CLK_UART] = |
500 | devm_clk_hw_register_fixed_factor_index(dev: priv->dev, name: "uart", |
501 | index: 0, /* XTALI external clock */ |
502 | flags: 0, mult: 1, div: clk_uart_div); |
503 | parent_data.hw = priv->fixed[EP93XX_CLK_UART]; |
504 | |
505 | /* parenting uart gate clocks to uart clock */ |
506 | for (i = 0; i < ARRAY_SIZE(ep93xx_uarts); i++) { |
507 | idx = ep93xx_uarts[i].idx - EP93XX_CLK_UART1; |
508 | clk = &priv->reg[idx]; |
509 | clk->idx = idx; |
510 | ret = ep93xx_clk_register_gate(clk, |
511 | name: ep93xx_uarts[i].name, |
512 | parent_data: &parent_data, CLK_SET_RATE_PARENT, |
513 | EP93XX_SYSCON_DEVCFG, |
514 | bit_idx: ep93xx_uarts[i].bit); |
515 | if (ret) |
516 | return dev_err_probe(dev: priv->dev, err: ret, |
517 | fmt: "failed to register uart[%d] clock\n", i); |
518 | } |
519 | |
520 | return 0; |
521 | } |
522 | |
523 | static const struct ep93xx_gate ep93xx_dmas[] = { |
524 | { EP93XX_CLK_M2M0, EP93XX_SYSCON_PWRCNT_DMA_M2M0, "m2m0"}, |
525 | { EP93XX_CLK_M2M1, EP93XX_SYSCON_PWRCNT_DMA_M2M1, "m2m1"}, |
526 | { EP93XX_CLK_M2P0, EP93XX_SYSCON_PWRCNT_DMA_M2P0, "m2p0"}, |
527 | { EP93XX_CLK_M2P1, EP93XX_SYSCON_PWRCNT_DMA_M2P1, "m2p1"}, |
528 | { EP93XX_CLK_M2P2, EP93XX_SYSCON_PWRCNT_DMA_M2P2, "m2p2"}, |
529 | { EP93XX_CLK_M2P3, EP93XX_SYSCON_PWRCNT_DMA_M2P3, "m2p3"}, |
530 | { EP93XX_CLK_M2P4, EP93XX_SYSCON_PWRCNT_DMA_M2P4, "m2p4"}, |
531 | { EP93XX_CLK_M2P5, EP93XX_SYSCON_PWRCNT_DMA_M2P5, "m2p5"}, |
532 | { EP93XX_CLK_M2P6, EP93XX_SYSCON_PWRCNT_DMA_M2P6, "m2p6"}, |
533 | { EP93XX_CLK_M2P7, EP93XX_SYSCON_PWRCNT_DMA_M2P7, "m2p7"}, |
534 | { EP93XX_CLK_M2P8, EP93XX_SYSCON_PWRCNT_DMA_M2P8, "m2p8"}, |
535 | { EP93XX_CLK_M2P9, EP93XX_SYSCON_PWRCNT_DMA_M2P9, "m2p9"}, |
536 | }; |
537 | |
538 | static int ep93xx_dma_clock_init(struct ep93xx_clk_priv *priv) |
539 | { |
540 | struct clk_parent_data parent_data = { }; |
541 | unsigned int i, idx; |
542 | |
543 | parent_data.hw = priv->fixed[EP93XX_CLK_HCLK]; |
544 | for (i = 0; i < ARRAY_SIZE(ep93xx_dmas); i++) { |
545 | idx = ep93xx_dmas[i].idx; |
546 | priv->fixed[idx] = devm_clk_hw_register_gate_parent_data(priv->dev, |
547 | ep93xx_dmas[i].name, |
548 | &parent_data, 0, |
549 | priv->base + EP93XX_SYSCON_PWRCNT, |
550 | ep93xx_dmas[i].bit, |
551 | 0, |
552 | &priv->lock); |
553 | if (IS_ERR(ptr: priv->fixed[idx])) |
554 | return PTR_ERR(ptr: priv->fixed[idx]); |
555 | } |
556 | |
557 | return 0; |
558 | } |
559 | |
560 | static struct clk_hw *of_clk_ep93xx_get(struct of_phandle_args *clkspec, void *data) |
561 | { |
562 | struct ep93xx_clk_priv *priv = data; |
563 | unsigned int idx = clkspec->args[0]; |
564 | |
565 | if (idx < EP93XX_CLK_UART1) |
566 | return priv->fixed[idx]; |
567 | |
568 | if (idx <= EP93XX_CLK_I2S_LRCLK) |
569 | return &priv->reg[idx - EP93XX_CLK_UART1].hw; |
570 | |
571 | return ERR_PTR(error: -EINVAL); |
572 | } |
573 | |
574 | /* |
575 | * PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) / 2^PS |
576 | */ |
577 | static unsigned long calc_pll_rate(u64 rate, u32 config_word) |
578 | { |
579 | rate *= ((config_word >> 11) & GENMASK(4, 0)) + 1; /* X1FBD */ |
580 | rate *= ((config_word >> 5) & GENMASK(5, 0)) + 1; /* X2FBD */ |
581 | do_div(rate, (config_word & GENMASK(4, 0)) + 1); /* X2IPD */ |
582 | rate >>= (config_word >> 16) & GENMASK(1, 0); /* PS */ |
583 | |
584 | return rate; |
585 | } |
586 | |
587 | static int ep93xx_plls_init(struct ep93xx_clk_priv *priv) |
588 | { |
589 | static const char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 }; |
590 | static const char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 }; |
591 | static const char pclk_divisors[] = { 1, 2, 4, 8 }; |
592 | struct clk_parent_data xtali = { .index = 0 }; |
593 | unsigned int clk_f_div, clk_h_div, clk_p_div; |
594 | unsigned long clk_pll1_rate, clk_pll2_rate; |
595 | struct device *dev = priv->dev; |
596 | struct clk_hw *hw, *pll1; |
597 | u32 value; |
598 | |
599 | /* Determine the bootloader configured pll1 rate */ |
600 | regmap_read(map: priv->map, EP93XX_SYSCON_CLKSET1, val: &value); |
601 | |
602 | if (value & EP93XX_SYSCON_CLKSET1_NBYP1) |
603 | clk_pll1_rate = calc_pll_rate(EP93XX_EXT_CLK_RATE, config_word: value); |
604 | else |
605 | clk_pll1_rate = EP93XX_EXT_CLK_RATE; |
606 | |
607 | pll1 = devm_clk_hw_register_fixed_rate_parent_data(dev, "pll1", &xtali, |
608 | 0, clk_pll1_rate); |
609 | if (IS_ERR(ptr: pll1)) |
610 | return PTR_ERR(ptr: pll1); |
611 | |
612 | priv->fixed[EP93XX_CLK_PLL1] = pll1; |
613 | |
614 | /* Initialize the pll1 derived clocks */ |
615 | clk_f_div = fclk_divisors[(value >> 25) & GENMASK(2, 0)]; |
616 | clk_h_div = hclk_divisors[(value >> 20) & GENMASK(2, 0)]; |
617 | clk_p_div = pclk_divisors[(value >> 18) & GENMASK(1, 0)]; |
618 | |
619 | hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, name: "fclk", parent_hw: pll1, flags: 0, mult: 1, div: clk_f_div); |
620 | if (IS_ERR(ptr: hw)) |
621 | return PTR_ERR(ptr: hw); |
622 | |
623 | priv->fixed[EP93XX_CLK_FCLK] = hw; |
624 | |
625 | hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, name: "hclk", parent_hw: pll1, flags: 0, mult: 1, div: clk_h_div); |
626 | if (IS_ERR(ptr: hw)) |
627 | return PTR_ERR(ptr: hw); |
628 | |
629 | priv->fixed[EP93XX_CLK_HCLK] = hw; |
630 | |
631 | hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, name: "pclk", parent_hw: hw, flags: 0, mult: 1, div: clk_p_div); |
632 | if (IS_ERR(ptr: hw)) |
633 | return PTR_ERR(ptr: hw); |
634 | |
635 | priv->fixed[EP93XX_CLK_PCLK] = hw; |
636 | |
637 | /* Determine the bootloader configured pll2 rate */ |
638 | regmap_read(map: priv->map, EP93XX_SYSCON_CLKSET2, val: &value); |
639 | if (!(value & EP93XX_SYSCON_CLKSET2_NBYP2)) |
640 | clk_pll2_rate = EP93XX_EXT_CLK_RATE; |
641 | else if (value & EP93XX_SYSCON_CLKSET2_PLL2_EN) |
642 | clk_pll2_rate = calc_pll_rate(EP93XX_EXT_CLK_RATE, config_word: value); |
643 | else |
644 | clk_pll2_rate = 0; |
645 | |
646 | hw = devm_clk_hw_register_fixed_rate_parent_data(dev, "pll2", &xtali, |
647 | 0, clk_pll2_rate); |
648 | if (IS_ERR(ptr: hw)) |
649 | return PTR_ERR(ptr: hw); |
650 | |
651 | priv->fixed[EP93XX_CLK_PLL2] = hw; |
652 | |
653 | return 0; |
654 | } |
655 | |
656 | static int ep93xx_clk_probe(struct auxiliary_device *adev, |
657 | const struct auxiliary_device_id *id) |
658 | { |
659 | struct ep93xx_regmap_adev *rdev = to_ep93xx_regmap_adev(adev); |
660 | struct clk_parent_data xtali = { .index = 0 }; |
661 | struct clk_parent_data ddiv_pdata[3] = { }; |
662 | unsigned int clk_spi_div, clk_usb_div; |
663 | struct clk_parent_data pdata = {}; |
664 | struct device *dev = &adev->dev; |
665 | struct ep93xx_clk_priv *priv; |
666 | struct ep93xx_clk *clk; |
667 | struct clk_hw *hw; |
668 | unsigned int idx; |
669 | int ret; |
670 | u32 value; |
671 | |
672 | priv = devm_kzalloc(dev, struct_size(priv, reg, 10), GFP_KERNEL); |
673 | if (!priv) |
674 | return -ENOMEM; |
675 | |
676 | spin_lock_init(&priv->lock); |
677 | priv->dev = dev; |
678 | priv->aux_dev = rdev; |
679 | priv->map = rdev->map; |
680 | priv->base = rdev->base; |
681 | |
682 | ret = ep93xx_plls_init(priv); |
683 | if (ret) |
684 | return ret; |
685 | |
686 | regmap_read(map: priv->map, EP93XX_SYSCON_CLKSET2, val: &value); |
687 | clk_usb_div = (value >> 28 & GENMASK(3, 0)) + 1; |
688 | hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, name: "usb_clk", |
689 | parent_hw: priv->fixed[EP93XX_CLK_PLL2], flags: 0, mult: 1, |
690 | div: clk_usb_div); |
691 | if (IS_ERR(ptr: hw)) |
692 | return PTR_ERR(ptr: hw); |
693 | |
694 | priv->fixed[EP93XX_CLK_USB] = hw; |
695 | |
696 | ret = ep93xx_uart_clock_init(priv); |
697 | if (ret) |
698 | return ret; |
699 | |
700 | ret = ep93xx_dma_clock_init(priv); |
701 | if (ret) |
702 | return ret; |
703 | |
704 | clk_spi_div = id->driver_data; |
705 | hw = devm_clk_hw_register_fixed_factor_index(dev, name: "ep93xx-spi.0", |
706 | index: 0, /* XTALI external clock */ |
707 | flags: 0, mult: 1, div: clk_spi_div); |
708 | if (IS_ERR(ptr: hw)) |
709 | return PTR_ERR(ptr: hw); |
710 | |
711 | priv->fixed[EP93XX_CLK_SPI] = hw; |
712 | |
713 | /* PWM clock */ |
714 | hw = devm_clk_hw_register_fixed_factor_index(dev, name: "pwm_clk", index: 0, /* XTALI external clock */ |
715 | flags: 0, mult: 1, div: 1); |
716 | if (IS_ERR(ptr: hw)) |
717 | return PTR_ERR(ptr: hw); |
718 | |
719 | priv->fixed[EP93XX_CLK_PWM] = hw; |
720 | |
721 | /* USB clock */ |
722 | pdata.hw = priv->fixed[EP93XX_CLK_USB]; |
723 | hw = devm_clk_hw_register_gate_parent_data(priv->dev, "ohci-platform", &pdata, |
724 | 0, priv->base + EP93XX_SYSCON_PWRCNT, |
725 | EP93XX_SYSCON_PWRCNT_USH_EN, 0, |
726 | &priv->lock); |
727 | if (IS_ERR(ptr: hw)) |
728 | return PTR_ERR(ptr: hw); |
729 | |
730 | priv->fixed[EP93XX_CLK_USB] = hw; |
731 | |
732 | ddiv_pdata[0].index = 0; /* XTALI external clock */ |
733 | ddiv_pdata[1].hw = priv->fixed[EP93XX_CLK_PLL1]; |
734 | ddiv_pdata[2].hw = priv->fixed[EP93XX_CLK_PLL2]; |
735 | |
736 | /* touchscreen/ADC clock */ |
737 | idx = EP93XX_CLK_ADC - EP93XX_CLK_UART1; |
738 | clk = &priv->reg[idx]; |
739 | clk->idx = idx; |
740 | ret = ep93xx_register_div(clk, name: "ep93xx-adc", parent_data: &xtali, |
741 | EP93XX_SYSCON_KEYTCHCLKDIV, |
742 | EP93XX_SYSCON_KEYTCHCLKDIV_TSEN, |
743 | EP93XX_SYSCON_KEYTCHCLKDIV_ADIV, |
744 | width: 1, |
745 | clk_divisors: ep93xx_adc_divisors, |
746 | ARRAY_SIZE(ep93xx_adc_divisors)); |
747 | |
748 | |
749 | /* keypad clock */ |
750 | idx = EP93XX_CLK_KEYPAD - EP93XX_CLK_UART1; |
751 | clk = &priv->reg[idx]; |
752 | clk->idx = idx; |
753 | ret = ep93xx_register_div(clk, name: "ep93xx-keypad", parent_data: &xtali, |
754 | EP93XX_SYSCON_KEYTCHCLKDIV, |
755 | EP93XX_SYSCON_KEYTCHCLKDIV_KEN, |
756 | EP93XX_SYSCON_KEYTCHCLKDIV_KDIV, |
757 | width: 1, |
758 | clk_divisors: ep93xx_adc_divisors, |
759 | ARRAY_SIZE(ep93xx_adc_divisors)); |
760 | |
761 | /* |
762 | * On reset PDIV and VDIV is set to zero, while PDIV zero |
763 | * means clock disable, VDIV shouldn't be zero. |
764 | * So we set both video and i2s dividers to minimum. |
765 | * ENA - Enable CLK divider. |
766 | * PDIV - 00 - Disable clock |
767 | * VDIV - at least 2 |
768 | */ |
769 | |
770 | /* Check and enable video clk registers */ |
771 | regmap_read(map: priv->map, EP93XX_SYSCON_VIDCLKDIV, val: &value); |
772 | value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2; |
773 | ep93xx_clk_write(priv, EP93XX_SYSCON_VIDCLKDIV, val: value); |
774 | |
775 | /* Check and enable i2s clk registers */ |
776 | regmap_read(map: priv->map, EP93XX_SYSCON_I2SCLKDIV, val: &value); |
777 | value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2; |
778 | |
779 | /* |
780 | * Override the SAI_MSTR_CLK_CFG from the I2S block and use the |
781 | * I2SClkDiv Register settings. LRCLK transitions on the falling SCLK |
782 | * edge. |
783 | */ |
784 | value |= EP93XX_SYSCON_I2SCLKDIV_ORIDE | EP93XX_SYSCON_I2SCLKDIV_SPOL; |
785 | ep93xx_clk_write(priv, EP93XX_SYSCON_I2SCLKDIV, val: value); |
786 | |
787 | /* video clk */ |
788 | idx = EP93XX_CLK_VIDEO - EP93XX_CLK_UART1; |
789 | clk = &priv->reg[idx]; |
790 | clk->idx = idx; |
791 | ret = ep93xx_clk_register_ddiv(clk, name: "ep93xx-fb", |
792 | parent_data: ddiv_pdata, ARRAY_SIZE(ddiv_pdata), |
793 | EP93XX_SYSCON_VIDCLKDIV, |
794 | EP93XX_SYSCON_CLKDIV_ENABLE); |
795 | |
796 | /* i2s clk */ |
797 | idx = EP93XX_CLK_I2S_MCLK - EP93XX_CLK_UART1; |
798 | clk = &priv->reg[idx]; |
799 | clk->idx = idx; |
800 | ret = ep93xx_clk_register_ddiv(clk, name: "mclk", |
801 | parent_data: ddiv_pdata, ARRAY_SIZE(ddiv_pdata), |
802 | EP93XX_SYSCON_I2SCLKDIV, |
803 | EP93XX_SYSCON_CLKDIV_ENABLE); |
804 | |
805 | /* i2s sclk */ |
806 | idx = EP93XX_CLK_I2S_SCLK - EP93XX_CLK_UART1; |
807 | clk = &priv->reg[idx]; |
808 | clk->idx = idx; |
809 | pdata.hw = &priv->reg[EP93XX_CLK_I2S_MCLK - EP93XX_CLK_UART1].hw; |
810 | ret = ep93xx_register_div(clk, name: "sclk", parent_data: &pdata, |
811 | EP93XX_SYSCON_I2SCLKDIV, |
812 | EP93XX_SYSCON_I2SCLKDIV_SENA, |
813 | shift: 16, /* EP93XX_I2SCLKDIV_SDIV_SHIFT */ |
814 | width: 1, /* EP93XX_I2SCLKDIV_SDIV_WIDTH */ |
815 | clk_divisors: ep93xx_sclk_divisors, |
816 | ARRAY_SIZE(ep93xx_sclk_divisors)); |
817 | |
818 | /* i2s lrclk */ |
819 | idx = EP93XX_CLK_I2S_LRCLK - EP93XX_CLK_UART1; |
820 | clk = &priv->reg[idx]; |
821 | clk->idx = idx; |
822 | pdata.hw = &priv->reg[EP93XX_CLK_I2S_SCLK - EP93XX_CLK_UART1].hw; |
823 | ret = ep93xx_register_div(clk, name: "lrclk", parent_data: &pdata, |
824 | EP93XX_SYSCON_I2SCLKDIV, |
825 | EP93XX_SYSCON_I2SCLKDIV_SENA, |
826 | shift: 17, /* EP93XX_I2SCLKDIV_LRDIV32_SHIFT */ |
827 | width: 2, /* EP93XX_I2SCLKDIV_LRDIV32_WIDTH */ |
828 | clk_divisors: ep93xx_lrclk_divisors, |
829 | ARRAY_SIZE(ep93xx_lrclk_divisors)); |
830 | |
831 | /* IrDa clk uses same pattern but no init code presents in original clock driver */ |
832 | return devm_of_clk_add_hw_provider(dev: priv->dev, get: of_clk_ep93xx_get, data: priv); |
833 | } |
834 | |
835 | static const struct auxiliary_device_id ep93xx_clk_ids[] = { |
836 | { .name = "soc_ep93xx.clk-ep93xx", .driver_data = 2, }, |
837 | { .name = "soc_ep93xx.clk-ep93xx.e2", .driver_data = 1, }, |
838 | { /* sentinel */ } |
839 | }; |
840 | MODULE_DEVICE_TABLE(auxiliary, ep93xx_clk_ids); |
841 | |
842 | static struct auxiliary_driver ep93xx_clk_driver = { |
843 | .probe = ep93xx_clk_probe, |
844 | .id_table = ep93xx_clk_ids, |
845 | }; |
846 | module_auxiliary_driver(ep93xx_clk_driver); |
847 | |
848 | MODULE_LICENSE("GPL"); |
849 | MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>"); |
850 | MODULE_DESCRIPTION("Clock control for Cirrus EP93xx chips"); |
851 |
Definitions
- ep93xx_adc_divisors
- ep93xx_sclk_divisors
- ep93xx_lrclk_divisors
- ep93xx_clk
- ep93xx_clk_priv
- ep93xx_clk_from
- ep93xx_priv_from
- ep93xx_clk_write
- ep93xx_clk_is_enabled
- ep93xx_clk_enable
- ep93xx_clk_disable
- clk_ep93xx_gate_ops
- ep93xx_clk_register_gate
- ep93xx_mux_get_parent
- ep93xx_mux_set_parent_lock
- is_best
- ep93xx_mux_determine_rate
- ep93xx_ddiv_recalc_rate
- ep93xx_ddiv_set_rate
- clk_ddiv_ops
- ep93xx_clk_register_ddiv
- ep93xx_div_recalc_rate
- ep93xx_div_round_rate
- ep93xx_div_set_rate
- ep93xx_div_ops
- ep93xx_register_div
- ep93xx_gate
- ep93xx_uarts
- ep93xx_uart_clock_init
- ep93xx_dmas
- ep93xx_dma_clock_init
- of_clk_ep93xx_get
- calc_pll_rate
- ep93xx_plls_init
- ep93xx_clk_probe
- ep93xx_clk_ids
Improve your Profiling and Debugging skills
Find out more