1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014 MediaTek Inc. |
4 | * Author: James Liao <jamesjj.liao@mediatek.com> |
5 | */ |
6 | |
7 | #include <linux/bitops.h> |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/err.h> |
10 | #include <linux/io.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/slab.h> |
18 | |
19 | #include "clk-mtk.h" |
20 | #include "clk-gate.h" |
21 | #include "clk-mux.h" |
22 | |
23 | const struct mtk_gate_regs cg_regs_dummy = { 0, 0, 0 }; |
24 | EXPORT_SYMBOL_GPL(cg_regs_dummy); |
25 | |
26 | static int mtk_clk_dummy_enable(struct clk_hw *hw) |
27 | { |
28 | return 0; |
29 | } |
30 | |
31 | static void mtk_clk_dummy_disable(struct clk_hw *hw) { } |
32 | |
33 | const struct clk_ops mtk_clk_dummy_ops = { |
34 | .enable = mtk_clk_dummy_enable, |
35 | .disable = mtk_clk_dummy_disable, |
36 | }; |
37 | EXPORT_SYMBOL_GPL(mtk_clk_dummy_ops); |
38 | |
39 | static void mtk_init_clk_data(struct clk_hw_onecell_data *clk_data, |
40 | unsigned int clk_num) |
41 | { |
42 | int i; |
43 | |
44 | clk_data->num = clk_num; |
45 | |
46 | for (i = 0; i < clk_num; i++) |
47 | clk_data->hws[i] = ERR_PTR(error: -ENOENT); |
48 | } |
49 | |
50 | struct clk_hw_onecell_data *mtk_devm_alloc_clk_data(struct device *dev, |
51 | unsigned int clk_num) |
52 | { |
53 | struct clk_hw_onecell_data *clk_data; |
54 | |
55 | clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, clk_num), |
56 | GFP_KERNEL); |
57 | if (!clk_data) |
58 | return NULL; |
59 | |
60 | mtk_init_clk_data(clk_data, clk_num); |
61 | |
62 | return clk_data; |
63 | } |
64 | EXPORT_SYMBOL_GPL(mtk_devm_alloc_clk_data); |
65 | |
66 | struct clk_hw_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) |
67 | { |
68 | struct clk_hw_onecell_data *clk_data; |
69 | |
70 | clk_data = kzalloc(struct_size(clk_data, hws, clk_num), GFP_KERNEL); |
71 | if (!clk_data) |
72 | return NULL; |
73 | |
74 | mtk_init_clk_data(clk_data, clk_num); |
75 | |
76 | return clk_data; |
77 | } |
78 | EXPORT_SYMBOL_GPL(mtk_alloc_clk_data); |
79 | |
80 | void mtk_free_clk_data(struct clk_hw_onecell_data *clk_data) |
81 | { |
82 | kfree(objp: clk_data); |
83 | } |
84 | EXPORT_SYMBOL_GPL(mtk_free_clk_data); |
85 | |
86 | int mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, int num, |
87 | struct clk_hw_onecell_data *clk_data) |
88 | { |
89 | int i; |
90 | struct clk_hw *hw; |
91 | |
92 | if (!clk_data) |
93 | return -ENOMEM; |
94 | |
95 | for (i = 0; i < num; i++) { |
96 | const struct mtk_fixed_clk *rc = &clks[i]; |
97 | |
98 | if (!IS_ERR_OR_NULL(ptr: clk_data->hws[rc->id])) { |
99 | pr_warn("Trying to register duplicate clock ID: %d\n" , rc->id); |
100 | continue; |
101 | } |
102 | |
103 | hw = clk_hw_register_fixed_rate(NULL, rc->name, rc->parent, 0, |
104 | rc->rate); |
105 | |
106 | if (IS_ERR(ptr: hw)) { |
107 | pr_err("Failed to register clk %s: %pe\n" , rc->name, |
108 | hw); |
109 | goto err; |
110 | } |
111 | |
112 | clk_data->hws[rc->id] = hw; |
113 | } |
114 | |
115 | return 0; |
116 | |
117 | err: |
118 | while (--i >= 0) { |
119 | const struct mtk_fixed_clk *rc = &clks[i]; |
120 | |
121 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[rc->id])) |
122 | continue; |
123 | |
124 | clk_hw_unregister_fixed_rate(hw: clk_data->hws[rc->id]); |
125 | clk_data->hws[rc->id] = ERR_PTR(error: -ENOENT); |
126 | } |
127 | |
128 | return PTR_ERR(ptr: hw); |
129 | } |
130 | EXPORT_SYMBOL_GPL(mtk_clk_register_fixed_clks); |
131 | |
132 | void mtk_clk_unregister_fixed_clks(const struct mtk_fixed_clk *clks, int num, |
133 | struct clk_hw_onecell_data *clk_data) |
134 | { |
135 | int i; |
136 | |
137 | if (!clk_data) |
138 | return; |
139 | |
140 | for (i = num; i > 0; i--) { |
141 | const struct mtk_fixed_clk *rc = &clks[i - 1]; |
142 | |
143 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[rc->id])) |
144 | continue; |
145 | |
146 | clk_hw_unregister_fixed_rate(hw: clk_data->hws[rc->id]); |
147 | clk_data->hws[rc->id] = ERR_PTR(error: -ENOENT); |
148 | } |
149 | } |
150 | EXPORT_SYMBOL_GPL(mtk_clk_unregister_fixed_clks); |
151 | |
152 | int mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, |
153 | struct clk_hw_onecell_data *clk_data) |
154 | { |
155 | int i; |
156 | struct clk_hw *hw; |
157 | |
158 | if (!clk_data) |
159 | return -ENOMEM; |
160 | |
161 | for (i = 0; i < num; i++) { |
162 | const struct mtk_fixed_factor *ff = &clks[i]; |
163 | |
164 | if (!IS_ERR_OR_NULL(ptr: clk_data->hws[ff->id])) { |
165 | pr_warn("Trying to register duplicate clock ID: %d\n" , ff->id); |
166 | continue; |
167 | } |
168 | |
169 | hw = clk_hw_register_fixed_factor(NULL, name: ff->name, parent_name: ff->parent_name, |
170 | flags: ff->flags, mult: ff->mult, div: ff->div); |
171 | |
172 | if (IS_ERR(ptr: hw)) { |
173 | pr_err("Failed to register clk %s: %pe\n" , ff->name, |
174 | hw); |
175 | goto err; |
176 | } |
177 | |
178 | clk_data->hws[ff->id] = hw; |
179 | } |
180 | |
181 | return 0; |
182 | |
183 | err: |
184 | while (--i >= 0) { |
185 | const struct mtk_fixed_factor *ff = &clks[i]; |
186 | |
187 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[ff->id])) |
188 | continue; |
189 | |
190 | clk_hw_unregister_fixed_factor(hw: clk_data->hws[ff->id]); |
191 | clk_data->hws[ff->id] = ERR_PTR(error: -ENOENT); |
192 | } |
193 | |
194 | return PTR_ERR(ptr: hw); |
195 | } |
196 | EXPORT_SYMBOL_GPL(mtk_clk_register_factors); |
197 | |
198 | void mtk_clk_unregister_factors(const struct mtk_fixed_factor *clks, int num, |
199 | struct clk_hw_onecell_data *clk_data) |
200 | { |
201 | int i; |
202 | |
203 | if (!clk_data) |
204 | return; |
205 | |
206 | for (i = num; i > 0; i--) { |
207 | const struct mtk_fixed_factor *ff = &clks[i - 1]; |
208 | |
209 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[ff->id])) |
210 | continue; |
211 | |
212 | clk_hw_unregister_fixed_factor(hw: clk_data->hws[ff->id]); |
213 | clk_data->hws[ff->id] = ERR_PTR(error: -ENOENT); |
214 | } |
215 | } |
216 | EXPORT_SYMBOL_GPL(mtk_clk_unregister_factors); |
217 | |
218 | static struct clk_hw *mtk_clk_register_composite(struct device *dev, |
219 | const struct mtk_composite *mc, void __iomem *base, spinlock_t *lock) |
220 | { |
221 | struct clk_hw *hw; |
222 | struct clk_mux *mux = NULL; |
223 | struct clk_gate *gate = NULL; |
224 | struct clk_divider *div = NULL; |
225 | struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; |
226 | const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; |
227 | const char * const *parent_names; |
228 | const char *parent; |
229 | int num_parents; |
230 | int ret; |
231 | |
232 | if (mc->mux_shift >= 0) { |
233 | mux = kzalloc(size: sizeof(*mux), GFP_KERNEL); |
234 | if (!mux) |
235 | return ERR_PTR(error: -ENOMEM); |
236 | |
237 | mux->reg = base + mc->mux_reg; |
238 | mux->mask = BIT(mc->mux_width) - 1; |
239 | mux->shift = mc->mux_shift; |
240 | mux->lock = lock; |
241 | mux->flags = mc->mux_flags; |
242 | mux_hw = &mux->hw; |
243 | mux_ops = &clk_mux_ops; |
244 | |
245 | parent_names = mc->parent_names; |
246 | num_parents = mc->num_parents; |
247 | } else { |
248 | parent = mc->parent; |
249 | parent_names = &parent; |
250 | num_parents = 1; |
251 | } |
252 | |
253 | if (mc->gate_shift >= 0) { |
254 | gate = kzalloc(size: sizeof(*gate), GFP_KERNEL); |
255 | if (!gate) { |
256 | ret = -ENOMEM; |
257 | goto err_out; |
258 | } |
259 | |
260 | gate->reg = base + mc->gate_reg; |
261 | gate->bit_idx = mc->gate_shift; |
262 | gate->flags = CLK_GATE_SET_TO_DISABLE; |
263 | gate->lock = lock; |
264 | |
265 | gate_hw = &gate->hw; |
266 | gate_ops = &clk_gate_ops; |
267 | } |
268 | |
269 | if (mc->divider_shift >= 0) { |
270 | div = kzalloc(size: sizeof(*div), GFP_KERNEL); |
271 | if (!div) { |
272 | ret = -ENOMEM; |
273 | goto err_out; |
274 | } |
275 | |
276 | div->reg = base + mc->divider_reg; |
277 | div->shift = mc->divider_shift; |
278 | div->width = mc->divider_width; |
279 | div->lock = lock; |
280 | |
281 | div_hw = &div->hw; |
282 | div_ops = &clk_divider_ops; |
283 | } |
284 | |
285 | hw = clk_hw_register_composite(dev, name: mc->name, parent_names, num_parents, |
286 | mux_hw, mux_ops, |
287 | rate_hw: div_hw, rate_ops: div_ops, |
288 | gate_hw, gate_ops, |
289 | flags: mc->flags); |
290 | |
291 | if (IS_ERR(ptr: hw)) { |
292 | ret = PTR_ERR(ptr: hw); |
293 | goto err_out; |
294 | } |
295 | |
296 | return hw; |
297 | err_out: |
298 | kfree(objp: div); |
299 | kfree(objp: gate); |
300 | kfree(objp: mux); |
301 | |
302 | return ERR_PTR(error: ret); |
303 | } |
304 | |
305 | static void mtk_clk_unregister_composite(struct clk_hw *hw) |
306 | { |
307 | struct clk_composite *composite; |
308 | struct clk_mux *mux = NULL; |
309 | struct clk_gate *gate = NULL; |
310 | struct clk_divider *div = NULL; |
311 | |
312 | if (!hw) |
313 | return; |
314 | |
315 | composite = to_clk_composite(hw); |
316 | if (composite->mux_hw) |
317 | mux = to_clk_mux(composite->mux_hw); |
318 | if (composite->gate_hw) |
319 | gate = to_clk_gate(composite->gate_hw); |
320 | if (composite->rate_hw) |
321 | div = to_clk_divider(composite->rate_hw); |
322 | |
323 | clk_hw_unregister_composite(hw); |
324 | kfree(objp: div); |
325 | kfree(objp: gate); |
326 | kfree(objp: mux); |
327 | } |
328 | |
329 | int mtk_clk_register_composites(struct device *dev, |
330 | const struct mtk_composite *mcs, int num, |
331 | void __iomem *base, spinlock_t *lock, |
332 | struct clk_hw_onecell_data *clk_data) |
333 | { |
334 | struct clk_hw *hw; |
335 | int i; |
336 | |
337 | if (!clk_data) |
338 | return -ENOMEM; |
339 | |
340 | for (i = 0; i < num; i++) { |
341 | const struct mtk_composite *mc = &mcs[i]; |
342 | |
343 | if (!IS_ERR_OR_NULL(ptr: clk_data->hws[mc->id])) { |
344 | pr_warn("Trying to register duplicate clock ID: %d\n" , |
345 | mc->id); |
346 | continue; |
347 | } |
348 | |
349 | hw = mtk_clk_register_composite(dev, mc, base, lock); |
350 | |
351 | if (IS_ERR(ptr: hw)) { |
352 | pr_err("Failed to register clk %s: %pe\n" , mc->name, |
353 | hw); |
354 | goto err; |
355 | } |
356 | |
357 | clk_data->hws[mc->id] = hw; |
358 | } |
359 | |
360 | return 0; |
361 | |
362 | err: |
363 | while (--i >= 0) { |
364 | const struct mtk_composite *mc = &mcs[i]; |
365 | |
366 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[mcs->id])) |
367 | continue; |
368 | |
369 | mtk_clk_unregister_composite(hw: clk_data->hws[mc->id]); |
370 | clk_data->hws[mc->id] = ERR_PTR(error: -ENOENT); |
371 | } |
372 | |
373 | return PTR_ERR(ptr: hw); |
374 | } |
375 | EXPORT_SYMBOL_GPL(mtk_clk_register_composites); |
376 | |
377 | void mtk_clk_unregister_composites(const struct mtk_composite *mcs, int num, |
378 | struct clk_hw_onecell_data *clk_data) |
379 | { |
380 | int i; |
381 | |
382 | if (!clk_data) |
383 | return; |
384 | |
385 | for (i = num; i > 0; i--) { |
386 | const struct mtk_composite *mc = &mcs[i - 1]; |
387 | |
388 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[mc->id])) |
389 | continue; |
390 | |
391 | mtk_clk_unregister_composite(hw: clk_data->hws[mc->id]); |
392 | clk_data->hws[mc->id] = ERR_PTR(error: -ENOENT); |
393 | } |
394 | } |
395 | EXPORT_SYMBOL_GPL(mtk_clk_unregister_composites); |
396 | |
397 | int mtk_clk_register_dividers(struct device *dev, |
398 | const struct mtk_clk_divider *mcds, int num, |
399 | void __iomem *base, spinlock_t *lock, |
400 | struct clk_hw_onecell_data *clk_data) |
401 | { |
402 | struct clk_hw *hw; |
403 | int i; |
404 | |
405 | if (!clk_data) |
406 | return -ENOMEM; |
407 | |
408 | for (i = 0; i < num; i++) { |
409 | const struct mtk_clk_divider *mcd = &mcds[i]; |
410 | |
411 | if (!IS_ERR_OR_NULL(ptr: clk_data->hws[mcd->id])) { |
412 | pr_warn("Trying to register duplicate clock ID: %d\n" , |
413 | mcd->id); |
414 | continue; |
415 | } |
416 | |
417 | hw = clk_hw_register_divider(dev, mcd->name, mcd->parent_name, |
418 | mcd->flags, base + mcd->div_reg, mcd->div_shift, |
419 | mcd->div_width, mcd->clk_divider_flags, lock); |
420 | |
421 | if (IS_ERR(ptr: hw)) { |
422 | pr_err("Failed to register clk %s: %pe\n" , mcd->name, |
423 | hw); |
424 | goto err; |
425 | } |
426 | |
427 | clk_data->hws[mcd->id] = hw; |
428 | } |
429 | |
430 | return 0; |
431 | |
432 | err: |
433 | while (--i >= 0) { |
434 | const struct mtk_clk_divider *mcd = &mcds[i]; |
435 | |
436 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[mcd->id])) |
437 | continue; |
438 | |
439 | clk_hw_unregister_divider(hw: clk_data->hws[mcd->id]); |
440 | clk_data->hws[mcd->id] = ERR_PTR(error: -ENOENT); |
441 | } |
442 | |
443 | return PTR_ERR(ptr: hw); |
444 | } |
445 | EXPORT_SYMBOL_GPL(mtk_clk_register_dividers); |
446 | |
447 | void mtk_clk_unregister_dividers(const struct mtk_clk_divider *mcds, int num, |
448 | struct clk_hw_onecell_data *clk_data) |
449 | { |
450 | int i; |
451 | |
452 | if (!clk_data) |
453 | return; |
454 | |
455 | for (i = num; i > 0; i--) { |
456 | const struct mtk_clk_divider *mcd = &mcds[i - 1]; |
457 | |
458 | if (IS_ERR_OR_NULL(ptr: clk_data->hws[mcd->id])) |
459 | continue; |
460 | |
461 | clk_hw_unregister_divider(hw: clk_data->hws[mcd->id]); |
462 | clk_data->hws[mcd->id] = ERR_PTR(error: -ENOENT); |
463 | } |
464 | } |
465 | EXPORT_SYMBOL_GPL(mtk_clk_unregister_dividers); |
466 | |
467 | static int __mtk_clk_simple_probe(struct platform_device *pdev, |
468 | struct device_node *node) |
469 | { |
470 | const struct platform_device_id *id; |
471 | const struct mtk_clk_desc *mcd; |
472 | struct clk_hw_onecell_data *clk_data; |
473 | void __iomem *base = NULL; |
474 | int num_clks, r; |
475 | |
476 | mcd = device_get_match_data(dev: &pdev->dev); |
477 | if (!mcd) { |
478 | /* Clock driver wasn't registered from devicetree */ |
479 | id = platform_get_device_id(pdev); |
480 | if (id) |
481 | mcd = (const struct mtk_clk_desc *)id->driver_data; |
482 | |
483 | if (!mcd) |
484 | return -EINVAL; |
485 | } |
486 | |
487 | /* Composite and divider clocks needs us to pass iomem pointer */ |
488 | if (mcd->composite_clks || mcd->divider_clks) { |
489 | if (!mcd->shared_io) |
490 | base = devm_platform_ioremap_resource(pdev, index: 0); |
491 | else |
492 | base = of_iomap(node, index: 0); |
493 | |
494 | if (IS_ERR_OR_NULL(ptr: base)) |
495 | return IS_ERR(ptr: base) ? PTR_ERR(ptr: base) : -ENOMEM; |
496 | } |
497 | |
498 | |
499 | devm_pm_runtime_enable(dev: &pdev->dev); |
500 | /* |
501 | * Do a pm_runtime_resume_and_get() to workaround a possible |
502 | * deadlock between clk_register() and the genpd framework. |
503 | */ |
504 | r = pm_runtime_resume_and_get(dev: &pdev->dev); |
505 | if (r) |
506 | return r; |
507 | |
508 | /* Calculate how many clk_hw_onecell_data entries to allocate */ |
509 | num_clks = mcd->num_clks + mcd->num_composite_clks; |
510 | num_clks += mcd->num_fixed_clks + mcd->num_factor_clks; |
511 | num_clks += mcd->num_mux_clks + mcd->num_divider_clks; |
512 | |
513 | clk_data = mtk_alloc_clk_data(num_clks); |
514 | if (!clk_data) { |
515 | r = -ENOMEM; |
516 | goto free_base; |
517 | } |
518 | |
519 | if (mcd->fixed_clks) { |
520 | r = mtk_clk_register_fixed_clks(mcd->fixed_clks, |
521 | mcd->num_fixed_clks, clk_data); |
522 | if (r) |
523 | goto free_data; |
524 | } |
525 | |
526 | if (mcd->factor_clks) { |
527 | r = mtk_clk_register_factors(mcd->factor_clks, |
528 | mcd->num_factor_clks, clk_data); |
529 | if (r) |
530 | goto unregister_fixed_clks; |
531 | } |
532 | |
533 | if (mcd->mux_clks) { |
534 | r = mtk_clk_register_muxes(dev: &pdev->dev, muxes: mcd->mux_clks, |
535 | num: mcd->num_mux_clks, node, |
536 | lock: mcd->clk_lock, clk_data); |
537 | if (r) |
538 | goto unregister_factors; |
539 | } |
540 | |
541 | if (mcd->composite_clks) { |
542 | /* We don't check composite_lock because it's optional */ |
543 | r = mtk_clk_register_composites(&pdev->dev, |
544 | mcd->composite_clks, |
545 | mcd->num_composite_clks, |
546 | base, mcd->clk_lock, clk_data); |
547 | if (r) |
548 | goto unregister_muxes; |
549 | } |
550 | |
551 | if (mcd->divider_clks) { |
552 | r = mtk_clk_register_dividers(&pdev->dev, |
553 | mcd->divider_clks, |
554 | mcd->num_divider_clks, |
555 | base, mcd->clk_lock, clk_data); |
556 | if (r) |
557 | goto unregister_composites; |
558 | } |
559 | |
560 | if (mcd->clks) { |
561 | r = mtk_clk_register_gates(dev: &pdev->dev, node, clks: mcd->clks, |
562 | num: mcd->num_clks, clk_data); |
563 | if (r) |
564 | goto unregister_dividers; |
565 | } |
566 | |
567 | if (mcd->clk_notifier_func) { |
568 | struct clk *mfg_mux = clk_data->hws[mcd->mfg_clk_idx]->clk; |
569 | |
570 | r = mcd->clk_notifier_func(&pdev->dev, mfg_mux); |
571 | if (r) |
572 | goto unregister_clks; |
573 | } |
574 | |
575 | r = of_clk_add_hw_provider(np: node, get: of_clk_hw_onecell_get, data: clk_data); |
576 | if (r) |
577 | goto unregister_clks; |
578 | |
579 | platform_set_drvdata(pdev, data: clk_data); |
580 | |
581 | if (mcd->rst_desc) { |
582 | r = mtk_register_reset_controller_with_dev(dev: &pdev->dev, |
583 | desc: mcd->rst_desc); |
584 | if (r) |
585 | goto unregister_clks; |
586 | } |
587 | |
588 | pm_runtime_put(dev: &pdev->dev); |
589 | |
590 | return r; |
591 | |
592 | unregister_clks: |
593 | if (mcd->clks) |
594 | mtk_clk_unregister_gates(clks: mcd->clks, num: mcd->num_clks, clk_data); |
595 | unregister_dividers: |
596 | if (mcd->divider_clks) |
597 | mtk_clk_unregister_dividers(mcd->divider_clks, |
598 | mcd->num_divider_clks, clk_data); |
599 | unregister_composites: |
600 | if (mcd->composite_clks) |
601 | mtk_clk_unregister_composites(mcd->composite_clks, |
602 | mcd->num_composite_clks, clk_data); |
603 | unregister_muxes: |
604 | if (mcd->mux_clks) |
605 | mtk_clk_unregister_muxes(muxes: mcd->mux_clks, |
606 | num: mcd->num_mux_clks, clk_data); |
607 | unregister_factors: |
608 | if (mcd->factor_clks) |
609 | mtk_clk_unregister_factors(mcd->factor_clks, |
610 | mcd->num_factor_clks, clk_data); |
611 | unregister_fixed_clks: |
612 | if (mcd->fixed_clks) |
613 | mtk_clk_unregister_fixed_clks(mcd->fixed_clks, |
614 | mcd->num_fixed_clks, clk_data); |
615 | free_data: |
616 | mtk_free_clk_data(clk_data); |
617 | free_base: |
618 | if (mcd->shared_io && base) |
619 | iounmap(addr: base); |
620 | |
621 | pm_runtime_put(dev: &pdev->dev); |
622 | return r; |
623 | } |
624 | |
625 | static void __mtk_clk_simple_remove(struct platform_device *pdev, |
626 | struct device_node *node) |
627 | { |
628 | struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev); |
629 | const struct mtk_clk_desc *mcd = device_get_match_data(dev: &pdev->dev); |
630 | |
631 | of_clk_del_provider(np: node); |
632 | if (mcd->clks) |
633 | mtk_clk_unregister_gates(clks: mcd->clks, num: mcd->num_clks, clk_data); |
634 | if (mcd->divider_clks) |
635 | mtk_clk_unregister_dividers(mcd->divider_clks, |
636 | mcd->num_divider_clks, clk_data); |
637 | if (mcd->composite_clks) |
638 | mtk_clk_unregister_composites(mcd->composite_clks, |
639 | mcd->num_composite_clks, clk_data); |
640 | if (mcd->mux_clks) |
641 | mtk_clk_unregister_muxes(muxes: mcd->mux_clks, |
642 | num: mcd->num_mux_clks, clk_data); |
643 | if (mcd->factor_clks) |
644 | mtk_clk_unregister_factors(mcd->factor_clks, |
645 | mcd->num_factor_clks, clk_data); |
646 | if (mcd->fixed_clks) |
647 | mtk_clk_unregister_fixed_clks(mcd->fixed_clks, |
648 | mcd->num_fixed_clks, clk_data); |
649 | mtk_free_clk_data(clk_data); |
650 | } |
651 | |
652 | int mtk_clk_pdev_probe(struct platform_device *pdev) |
653 | { |
654 | struct device *dev = &pdev->dev; |
655 | struct device_node *node = dev->parent->of_node; |
656 | |
657 | return __mtk_clk_simple_probe(pdev, node); |
658 | } |
659 | EXPORT_SYMBOL_GPL(mtk_clk_pdev_probe); |
660 | |
661 | int mtk_clk_simple_probe(struct platform_device *pdev) |
662 | { |
663 | struct device_node *node = pdev->dev.of_node; |
664 | |
665 | return __mtk_clk_simple_probe(pdev, node); |
666 | } |
667 | EXPORT_SYMBOL_GPL(mtk_clk_simple_probe); |
668 | |
669 | void mtk_clk_pdev_remove(struct platform_device *pdev) |
670 | { |
671 | struct device *dev = &pdev->dev; |
672 | struct device_node *node = dev->parent->of_node; |
673 | |
674 | __mtk_clk_simple_remove(pdev, node); |
675 | } |
676 | EXPORT_SYMBOL_GPL(mtk_clk_pdev_remove); |
677 | |
678 | void mtk_clk_simple_remove(struct platform_device *pdev) |
679 | { |
680 | __mtk_clk_simple_remove(pdev, node: pdev->dev.of_node); |
681 | } |
682 | EXPORT_SYMBOL_GPL(mtk_clk_simple_remove); |
683 | |
684 | MODULE_LICENSE("GPL" ); |
685 | |