1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * JZ47xx SoCs TCU clocks driver |
4 | * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> |
5 | */ |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/clockchips.h> |
10 | #include <linux/mfd/ingenic-tcu.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/syscore_ops.h> |
15 | |
16 | #include <dt-bindings/clock/ingenic,tcu.h> |
17 | |
18 | /* 8 channels max + watchdog + OST */ |
19 | #define TCU_CLK_COUNT 10 |
20 | |
21 | #undef pr_fmt |
22 | #define pr_fmt(fmt) "ingenic-tcu-clk: " fmt |
23 | |
24 | enum tcu_clk_parent { |
25 | TCU_PARENT_PCLK, |
26 | TCU_PARENT_RTC, |
27 | TCU_PARENT_EXT, |
28 | }; |
29 | |
30 | struct ingenic_soc_info { |
31 | unsigned int num_channels; |
32 | bool has_ost; |
33 | bool has_tcu_clk; |
34 | bool allow_missing_tcu_clk; |
35 | }; |
36 | |
37 | struct ingenic_tcu_clk_info { |
38 | struct clk_init_data init_data; |
39 | u8 gate_bit; |
40 | u8 tcsr_reg; |
41 | }; |
42 | |
43 | struct ingenic_tcu_clk { |
44 | struct clk_hw hw; |
45 | unsigned int idx; |
46 | struct ingenic_tcu *tcu; |
47 | const struct ingenic_tcu_clk_info *info; |
48 | }; |
49 | |
50 | struct ingenic_tcu { |
51 | const struct ingenic_soc_info *soc_info; |
52 | struct regmap *map; |
53 | struct clk *clk; |
54 | |
55 | struct clk_hw_onecell_data *clocks; |
56 | }; |
57 | |
58 | static struct ingenic_tcu *ingenic_tcu; |
59 | |
60 | static inline struct ingenic_tcu_clk *to_tcu_clk(struct clk_hw *hw) |
61 | { |
62 | return container_of(hw, struct ingenic_tcu_clk, hw); |
63 | } |
64 | |
65 | static int ingenic_tcu_enable(struct clk_hw *hw) |
66 | { |
67 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
68 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
69 | struct ingenic_tcu *tcu = tcu_clk->tcu; |
70 | |
71 | regmap_write(map: tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | static void ingenic_tcu_disable(struct clk_hw *hw) |
77 | { |
78 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
79 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
80 | struct ingenic_tcu *tcu = tcu_clk->tcu; |
81 | |
82 | regmap_write(map: tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); |
83 | } |
84 | |
85 | static int ingenic_tcu_is_enabled(struct clk_hw *hw) |
86 | { |
87 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
88 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
89 | unsigned int value; |
90 | |
91 | regmap_read(map: tcu_clk->tcu->map, TCU_REG_TSR, val: &value); |
92 | |
93 | return !(value & BIT(info->gate_bit)); |
94 | } |
95 | |
96 | static bool ingenic_tcu_enable_regs(struct clk_hw *hw) |
97 | { |
98 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
99 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
100 | struct ingenic_tcu *tcu = tcu_clk->tcu; |
101 | bool enabled = false; |
102 | |
103 | /* |
104 | * According to the programming manual, a timer channel's registers can |
105 | * only be accessed when the channel's stop bit is clear. |
106 | */ |
107 | enabled = !!ingenic_tcu_is_enabled(hw); |
108 | regmap_write(map: tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); |
109 | |
110 | return enabled; |
111 | } |
112 | |
113 | static void ingenic_tcu_disable_regs(struct clk_hw *hw) |
114 | { |
115 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
116 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
117 | struct ingenic_tcu *tcu = tcu_clk->tcu; |
118 | |
119 | regmap_write(map: tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); |
120 | } |
121 | |
122 | static u8 ingenic_tcu_get_parent(struct clk_hw *hw) |
123 | { |
124 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
125 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
126 | unsigned int val = 0; |
127 | int ret; |
128 | |
129 | ret = regmap_read(map: tcu_clk->tcu->map, reg: info->tcsr_reg, val: &val); |
130 | WARN_ONCE(ret < 0, "Unable to read TCSR %d" , tcu_clk->idx); |
131 | |
132 | return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1; |
133 | } |
134 | |
135 | static int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx) |
136 | { |
137 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
138 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
139 | bool was_enabled; |
140 | int ret; |
141 | |
142 | was_enabled = ingenic_tcu_enable_regs(hw); |
143 | |
144 | ret = regmap_update_bits(map: tcu_clk->tcu->map, reg: info->tcsr_reg, |
145 | TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx)); |
146 | WARN_ONCE(ret < 0, "Unable to update TCSR %d" , tcu_clk->idx); |
147 | |
148 | if (!was_enabled) |
149 | ingenic_tcu_disable_regs(hw); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | static unsigned long ingenic_tcu_recalc_rate(struct clk_hw *hw, |
155 | unsigned long parent_rate) |
156 | { |
157 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
158 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
159 | unsigned int prescale; |
160 | int ret; |
161 | |
162 | ret = regmap_read(map: tcu_clk->tcu->map, reg: info->tcsr_reg, val: &prescale); |
163 | WARN_ONCE(ret < 0, "Unable to read TCSR %d" , tcu_clk->idx); |
164 | |
165 | prescale = (prescale & TCU_TCSR_PRESCALE_MASK) >> TCU_TCSR_PRESCALE_LSB; |
166 | |
167 | return parent_rate >> (prescale * 2); |
168 | } |
169 | |
170 | static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate) |
171 | { |
172 | u8 prescale; |
173 | |
174 | for (prescale = 0; prescale < 5; prescale++) |
175 | if ((rate >> (prescale * 2)) <= req_rate) |
176 | return prescale; |
177 | |
178 | return 5; /* /1024 divider */ |
179 | } |
180 | |
181 | static int ingenic_tcu_determine_rate(struct clk_hw *hw, |
182 | struct clk_rate_request *req) |
183 | { |
184 | unsigned long rate = req->best_parent_rate; |
185 | u8 prescale; |
186 | |
187 | if (req->rate > rate) { |
188 | req->rate = rate; |
189 | return 0; |
190 | } |
191 | |
192 | prescale = ingenic_tcu_get_prescale(rate, req_rate: req->rate); |
193 | |
194 | req->rate = rate >> (prescale * 2); |
195 | return 0; |
196 | } |
197 | |
198 | static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate, |
199 | unsigned long parent_rate) |
200 | { |
201 | struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw); |
202 | const struct ingenic_tcu_clk_info *info = tcu_clk->info; |
203 | u8 prescale = ingenic_tcu_get_prescale(rate: parent_rate, req_rate); |
204 | bool was_enabled; |
205 | int ret; |
206 | |
207 | was_enabled = ingenic_tcu_enable_regs(hw); |
208 | |
209 | ret = regmap_update_bits(map: tcu_clk->tcu->map, reg: info->tcsr_reg, |
210 | TCU_TCSR_PRESCALE_MASK, |
211 | val: prescale << TCU_TCSR_PRESCALE_LSB); |
212 | WARN_ONCE(ret < 0, "Unable to update TCSR %d" , tcu_clk->idx); |
213 | |
214 | if (!was_enabled) |
215 | ingenic_tcu_disable_regs(hw); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static const struct clk_ops ingenic_tcu_clk_ops = { |
221 | .get_parent = ingenic_tcu_get_parent, |
222 | .set_parent = ingenic_tcu_set_parent, |
223 | |
224 | .recalc_rate = ingenic_tcu_recalc_rate, |
225 | .determine_rate = ingenic_tcu_determine_rate, |
226 | .set_rate = ingenic_tcu_set_rate, |
227 | |
228 | .enable = ingenic_tcu_enable, |
229 | .disable = ingenic_tcu_disable, |
230 | .is_enabled = ingenic_tcu_is_enabled, |
231 | }; |
232 | |
233 | static const char * const ingenic_tcu_timer_parents[] = { |
234 | [TCU_PARENT_PCLK] = "pclk" , |
235 | [TCU_PARENT_RTC] = "rtc" , |
236 | [TCU_PARENT_EXT] = "ext" , |
237 | }; |
238 | |
239 | #define DEF_TIMER(_name, _gate_bit, _tcsr) \ |
240 | { \ |
241 | .init_data = { \ |
242 | .name = _name, \ |
243 | .parent_names = ingenic_tcu_timer_parents, \ |
244 | .num_parents = ARRAY_SIZE(ingenic_tcu_timer_parents),\ |
245 | .ops = &ingenic_tcu_clk_ops, \ |
246 | .flags = CLK_SET_RATE_UNGATE, \ |
247 | }, \ |
248 | .gate_bit = _gate_bit, \ |
249 | .tcsr_reg = _tcsr, \ |
250 | } |
251 | static const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = { |
252 | [TCU_CLK_TIMER0] = DEF_TIMER("timer0" , 0, TCU_REG_TCSRc(0)), |
253 | [TCU_CLK_TIMER1] = DEF_TIMER("timer1" , 1, TCU_REG_TCSRc(1)), |
254 | [TCU_CLK_TIMER2] = DEF_TIMER("timer2" , 2, TCU_REG_TCSRc(2)), |
255 | [TCU_CLK_TIMER3] = DEF_TIMER("timer3" , 3, TCU_REG_TCSRc(3)), |
256 | [TCU_CLK_TIMER4] = DEF_TIMER("timer4" , 4, TCU_REG_TCSRc(4)), |
257 | [TCU_CLK_TIMER5] = DEF_TIMER("timer5" , 5, TCU_REG_TCSRc(5)), |
258 | [TCU_CLK_TIMER6] = DEF_TIMER("timer6" , 6, TCU_REG_TCSRc(6)), |
259 | [TCU_CLK_TIMER7] = DEF_TIMER("timer7" , 7, TCU_REG_TCSRc(7)), |
260 | }; |
261 | |
262 | static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info = |
263 | DEF_TIMER("wdt" , 16, TCU_REG_WDT_TCSR); |
264 | static const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info = |
265 | DEF_TIMER("ost" , 15, TCU_REG_OST_TCSR); |
266 | #undef DEF_TIMER |
267 | |
268 | static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu, |
269 | unsigned int idx, enum tcu_clk_parent parent, |
270 | const struct ingenic_tcu_clk_info *info, |
271 | struct clk_hw_onecell_data *clocks) |
272 | { |
273 | struct ingenic_tcu_clk *tcu_clk; |
274 | int err; |
275 | |
276 | tcu_clk = kzalloc(size: sizeof(*tcu_clk), GFP_KERNEL); |
277 | if (!tcu_clk) |
278 | return -ENOMEM; |
279 | |
280 | tcu_clk->hw.init = &info->init_data; |
281 | tcu_clk->idx = idx; |
282 | tcu_clk->info = info; |
283 | tcu_clk->tcu = tcu; |
284 | |
285 | /* Reset channel and clock divider, set default parent */ |
286 | ingenic_tcu_enable_regs(hw: &tcu_clk->hw); |
287 | regmap_update_bits(map: tcu->map, reg: info->tcsr_reg, mask: 0xffff, BIT(parent)); |
288 | ingenic_tcu_disable_regs(hw: &tcu_clk->hw); |
289 | |
290 | err = clk_hw_register(NULL, hw: &tcu_clk->hw); |
291 | if (err) { |
292 | kfree(objp: tcu_clk); |
293 | return err; |
294 | } |
295 | |
296 | clocks->hws[idx] = &tcu_clk->hw; |
297 | |
298 | return 0; |
299 | } |
300 | |
301 | static const struct ingenic_soc_info jz4740_soc_info = { |
302 | .num_channels = 8, |
303 | .has_ost = false, |
304 | .has_tcu_clk = true, |
305 | }; |
306 | |
307 | static const struct ingenic_soc_info jz4725b_soc_info = { |
308 | .num_channels = 6, |
309 | .has_ost = true, |
310 | .has_tcu_clk = true, |
311 | }; |
312 | |
313 | static const struct ingenic_soc_info jz4770_soc_info = { |
314 | .num_channels = 8, |
315 | .has_ost = true, |
316 | .has_tcu_clk = false, |
317 | }; |
318 | |
319 | static const struct ingenic_soc_info x1000_soc_info = { |
320 | .num_channels = 8, |
321 | .has_ost = false, /* X1000 has OST, but it not belong TCU */ |
322 | .has_tcu_clk = true, |
323 | .allow_missing_tcu_clk = true, |
324 | }; |
325 | |
326 | static const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = { |
327 | { .compatible = "ingenic,jz4740-tcu" , .data = &jz4740_soc_info, }, |
328 | { .compatible = "ingenic,jz4725b-tcu" , .data = &jz4725b_soc_info, }, |
329 | { .compatible = "ingenic,jz4760-tcu" , .data = &jz4770_soc_info, }, |
330 | { .compatible = "ingenic,jz4770-tcu" , .data = &jz4770_soc_info, }, |
331 | { .compatible = "ingenic,x1000-tcu" , .data = &x1000_soc_info, }, |
332 | { /* sentinel */ } |
333 | }; |
334 | |
335 | static int __init ingenic_tcu_probe(struct device_node *np) |
336 | { |
337 | const struct of_device_id *id = of_match_node(matches: ingenic_tcu_of_match, node: np); |
338 | struct ingenic_tcu *tcu; |
339 | struct regmap *map; |
340 | unsigned int i; |
341 | int ret; |
342 | |
343 | map = device_node_to_regmap(np); |
344 | if (IS_ERR(ptr: map)) |
345 | return PTR_ERR(ptr: map); |
346 | |
347 | tcu = kzalloc(size: sizeof(*tcu), GFP_KERNEL); |
348 | if (!tcu) |
349 | return -ENOMEM; |
350 | |
351 | tcu->map = map; |
352 | tcu->soc_info = id->data; |
353 | |
354 | if (tcu->soc_info->has_tcu_clk) { |
355 | tcu->clk = of_clk_get_by_name(np, name: "tcu" ); |
356 | if (IS_ERR(ptr: tcu->clk)) { |
357 | ret = PTR_ERR(ptr: tcu->clk); |
358 | |
359 | /* |
360 | * Old device trees for some SoCs did not include the |
361 | * TCU clock because this driver (incorrectly) didn't |
362 | * use it. In this case we complain loudly and attempt |
363 | * to continue without the clock, which might work if |
364 | * booting with workarounds like "clk_ignore_unused". |
365 | */ |
366 | if (tcu->soc_info->allow_missing_tcu_clk && ret == -EINVAL) { |
367 | pr_warn("TCU clock missing from device tree, please update your device tree\n" ); |
368 | tcu->clk = NULL; |
369 | } else { |
370 | pr_crit("Cannot get TCU clock from device tree\n" ); |
371 | goto err_free_tcu; |
372 | } |
373 | } else { |
374 | ret = clk_prepare_enable(clk: tcu->clk); |
375 | if (ret) { |
376 | pr_crit("Unable to enable TCU clock\n" ); |
377 | goto err_put_clk; |
378 | } |
379 | } |
380 | } |
381 | |
382 | tcu->clocks = kzalloc(struct_size(tcu->clocks, hws, TCU_CLK_COUNT), |
383 | GFP_KERNEL); |
384 | if (!tcu->clocks) { |
385 | ret = -ENOMEM; |
386 | goto err_clk_disable; |
387 | } |
388 | |
389 | tcu->clocks->num = TCU_CLK_COUNT; |
390 | |
391 | for (i = 0; i < tcu->soc_info->num_channels; i++) { |
392 | ret = ingenic_tcu_register_clock(tcu, idx: i, parent: TCU_PARENT_EXT, |
393 | info: &ingenic_tcu_clk_info[i], |
394 | clocks: tcu->clocks); |
395 | if (ret) { |
396 | pr_crit("cannot register clock %d\n" , i); |
397 | goto err_unregister_timer_clocks; |
398 | } |
399 | } |
400 | |
401 | /* |
402 | * We set EXT as the default parent clock for all the TCU clocks |
403 | * except for the watchdog one, where we set the RTC clock as the |
404 | * parent. Since the EXT and PCLK are much faster than the RTC clock, |
405 | * the watchdog would kick after a maximum time of 5s, and we might |
406 | * want a slower kicking time. |
407 | */ |
408 | ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, parent: TCU_PARENT_RTC, |
409 | info: &ingenic_tcu_watchdog_clk_info, |
410 | clocks: tcu->clocks); |
411 | if (ret) { |
412 | pr_crit("cannot register watchdog clock\n" ); |
413 | goto err_unregister_timer_clocks; |
414 | } |
415 | |
416 | if (tcu->soc_info->has_ost) { |
417 | ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST, |
418 | parent: TCU_PARENT_EXT, |
419 | info: &ingenic_tcu_ost_clk_info, |
420 | clocks: tcu->clocks); |
421 | if (ret) { |
422 | pr_crit("cannot register ost clock\n" ); |
423 | goto err_unregister_watchdog_clock; |
424 | } |
425 | } |
426 | |
427 | ret = of_clk_add_hw_provider(np, get: of_clk_hw_onecell_get, data: tcu->clocks); |
428 | if (ret) { |
429 | pr_crit("cannot add OF clock provider\n" ); |
430 | goto err_unregister_ost_clock; |
431 | } |
432 | |
433 | ingenic_tcu = tcu; |
434 | |
435 | return 0; |
436 | |
437 | err_unregister_ost_clock: |
438 | if (tcu->soc_info->has_ost) |
439 | clk_hw_unregister(hw: tcu->clocks->hws[i + 1]); |
440 | err_unregister_watchdog_clock: |
441 | clk_hw_unregister(hw: tcu->clocks->hws[i]); |
442 | err_unregister_timer_clocks: |
443 | for (i = 0; i < tcu->clocks->num; i++) |
444 | if (tcu->clocks->hws[i]) |
445 | clk_hw_unregister(hw: tcu->clocks->hws[i]); |
446 | kfree(objp: tcu->clocks); |
447 | err_clk_disable: |
448 | if (tcu->clk) |
449 | clk_disable_unprepare(clk: tcu->clk); |
450 | err_put_clk: |
451 | if (tcu->clk) |
452 | clk_put(clk: tcu->clk); |
453 | err_free_tcu: |
454 | kfree(objp: tcu); |
455 | return ret; |
456 | } |
457 | |
458 | static int __maybe_unused tcu_pm_suspend(void) |
459 | { |
460 | struct ingenic_tcu *tcu = ingenic_tcu; |
461 | |
462 | if (tcu->clk) |
463 | clk_disable(clk: tcu->clk); |
464 | |
465 | return 0; |
466 | } |
467 | |
468 | static void __maybe_unused tcu_pm_resume(void) |
469 | { |
470 | struct ingenic_tcu *tcu = ingenic_tcu; |
471 | |
472 | if (tcu->clk) |
473 | clk_enable(clk: tcu->clk); |
474 | } |
475 | |
476 | static struct syscore_ops __maybe_unused tcu_pm_ops = { |
477 | .suspend = tcu_pm_suspend, |
478 | .resume = tcu_pm_resume, |
479 | }; |
480 | |
481 | static void __init ingenic_tcu_init(struct device_node *np) |
482 | { |
483 | int ret = ingenic_tcu_probe(np); |
484 | |
485 | if (ret) |
486 | pr_crit("Failed to initialize TCU clocks: %d\n" , ret); |
487 | |
488 | if (IS_ENABLED(CONFIG_PM_SLEEP)) |
489 | register_syscore_ops(ops: &tcu_pm_ops); |
490 | } |
491 | |
492 | CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu" , ingenic_tcu_init); |
493 | CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu" , ingenic_tcu_init); |
494 | CLK_OF_DECLARE_DRIVER(jz4760_cgu, "ingenic,jz4760-tcu" , ingenic_tcu_init); |
495 | CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu" , ingenic_tcu_init); |
496 | CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu" , ingenic_tcu_init); |
497 | |