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