1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) STMicroelectronics 2022 - All Rights Reserved |
4 | * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics. |
5 | */ |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/device.h> |
10 | #include <linux/err.h> |
11 | #include <linux/io.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/spinlock.h> |
16 | |
17 | #include "clk-stm32-core.h" |
18 | #include "reset-stm32.h" |
19 | |
20 | static DEFINE_SPINLOCK(rlock); |
21 | |
22 | static int stm32_rcc_clock_init(struct device *dev, |
23 | const struct of_device_id *match, |
24 | void __iomem *base) |
25 | { |
26 | const struct stm32_rcc_match_data *data = match->data; |
27 | struct clk_hw_onecell_data *clk_data = data->hw_clks; |
28 | struct device_node *np = dev_of_node(dev); |
29 | struct clk_hw **hws; |
30 | int n, max_binding; |
31 | |
32 | max_binding = data->maxbinding; |
33 | |
34 | clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, max_binding), GFP_KERNEL); |
35 | if (!clk_data) |
36 | return -ENOMEM; |
37 | |
38 | clk_data->num = max_binding; |
39 | |
40 | hws = clk_data->hws; |
41 | |
42 | for (n = 0; n < max_binding; n++) |
43 | hws[n] = ERR_PTR(error: -ENOENT); |
44 | |
45 | for (n = 0; n < data->num_clocks; n++) { |
46 | const struct clock_config *cfg_clock = &data->tab_clocks[n]; |
47 | struct clk_hw *hw = ERR_PTR(error: -ENOENT); |
48 | |
49 | if (data->check_security && |
50 | data->check_security(base, cfg_clock)) |
51 | continue; |
52 | |
53 | if (cfg_clock->func) |
54 | hw = (*cfg_clock->func)(dev, data, base, &rlock, |
55 | cfg_clock); |
56 | |
57 | if (IS_ERR(ptr: hw)) { |
58 | dev_err(dev, "Can't register clk %d: %ld\n" , n, |
59 | PTR_ERR(hw)); |
60 | return PTR_ERR(ptr: hw); |
61 | } |
62 | |
63 | if (cfg_clock->id != NO_ID) |
64 | hws[cfg_clock->id] = hw; |
65 | } |
66 | |
67 | return of_clk_add_hw_provider(np, get: of_clk_hw_onecell_get, data: clk_data); |
68 | } |
69 | |
70 | int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data, |
71 | void __iomem *base) |
72 | { |
73 | const struct stm32_rcc_match_data *rcc_match_data; |
74 | const struct of_device_id *match; |
75 | int err; |
76 | |
77 | match = of_match_node(matches: match_data, node: dev_of_node(dev)); |
78 | if (!match) { |
79 | dev_err(dev, "match data not found\n" ); |
80 | return -ENODEV; |
81 | } |
82 | |
83 | rcc_match_data = match->data; |
84 | |
85 | /* RCC Reset Configuration */ |
86 | err = stm32_rcc_reset_init(dev, data: rcc_match_data->reset_data, base); |
87 | if (err) { |
88 | pr_err("stm32 reset failed to initialize\n" ); |
89 | return err; |
90 | } |
91 | |
92 | /* RCC Clock Configuration */ |
93 | err = stm32_rcc_clock_init(dev, match, base); |
94 | if (err) { |
95 | pr_err("stm32 clock failed to initialize\n" ); |
96 | return err; |
97 | } |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static u8 stm32_mux_get_parent(void __iomem *base, |
103 | struct clk_stm32_clock_data *data, |
104 | u16 mux_id) |
105 | { |
106 | const struct stm32_mux_cfg *mux = &data->muxes[mux_id]; |
107 | u32 mask = BIT(mux->width) - 1; |
108 | u32 val; |
109 | |
110 | val = readl(addr: base + mux->offset) >> mux->shift; |
111 | val &= mask; |
112 | |
113 | return val; |
114 | } |
115 | |
116 | static int stm32_mux_set_parent(void __iomem *base, |
117 | struct clk_stm32_clock_data *data, |
118 | u16 mux_id, u8 index) |
119 | { |
120 | const struct stm32_mux_cfg *mux = &data->muxes[mux_id]; |
121 | |
122 | u32 mask = BIT(mux->width) - 1; |
123 | u32 reg = readl(addr: base + mux->offset); |
124 | u32 val = index << mux->shift; |
125 | |
126 | reg &= ~(mask << mux->shift); |
127 | reg |= val; |
128 | |
129 | writel(val: reg, addr: base + mux->offset); |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | static void stm32_gate_endisable(void __iomem *base, |
135 | struct clk_stm32_clock_data *data, |
136 | u16 gate_id, int enable) |
137 | { |
138 | const struct stm32_gate_cfg *gate = &data->gates[gate_id]; |
139 | void __iomem *addr = base + gate->offset; |
140 | |
141 | if (enable) { |
142 | if (data->gate_cpt[gate_id]++ > 0) |
143 | return; |
144 | |
145 | if (gate->set_clr != 0) |
146 | writel(BIT(gate->bit_idx), addr); |
147 | else |
148 | writel(readl(addr) | BIT(gate->bit_idx), addr); |
149 | } else { |
150 | if (--data->gate_cpt[gate_id] > 0) |
151 | return; |
152 | |
153 | if (gate->set_clr != 0) |
154 | writel(BIT(gate->bit_idx), addr: addr + gate->set_clr); |
155 | else |
156 | writel(readl(addr) & ~BIT(gate->bit_idx), addr); |
157 | } |
158 | } |
159 | |
160 | static void stm32_gate_disable_unused(void __iomem *base, |
161 | struct clk_stm32_clock_data *data, |
162 | u16 gate_id) |
163 | { |
164 | const struct stm32_gate_cfg *gate = &data->gates[gate_id]; |
165 | void __iomem *addr = base + gate->offset; |
166 | |
167 | if (data->gate_cpt[gate_id] > 0) |
168 | return; |
169 | |
170 | if (gate->set_clr != 0) |
171 | writel(BIT(gate->bit_idx), addr: addr + gate->set_clr); |
172 | else |
173 | writel(readl(addr) & ~BIT(gate->bit_idx), addr); |
174 | } |
175 | |
176 | static int stm32_gate_is_enabled(void __iomem *base, |
177 | struct clk_stm32_clock_data *data, |
178 | u16 gate_id) |
179 | { |
180 | const struct stm32_gate_cfg *gate = &data->gates[gate_id]; |
181 | |
182 | return (readl(addr: base + gate->offset) & BIT(gate->bit_idx)) != 0; |
183 | } |
184 | |
185 | static unsigned int _get_table_div(const struct clk_div_table *table, |
186 | unsigned int val) |
187 | { |
188 | const struct clk_div_table *clkt; |
189 | |
190 | for (clkt = table; clkt->div; clkt++) |
191 | if (clkt->val == val) |
192 | return clkt->div; |
193 | return 0; |
194 | } |
195 | |
196 | static unsigned int _get_div(const struct clk_div_table *table, |
197 | unsigned int val, unsigned long flags, u8 width) |
198 | { |
199 | if (flags & CLK_DIVIDER_ONE_BASED) |
200 | return val; |
201 | if (flags & CLK_DIVIDER_POWER_OF_TWO) |
202 | return 1 << val; |
203 | if (table) |
204 | return _get_table_div(table, val); |
205 | return val + 1; |
206 | } |
207 | |
208 | static unsigned long stm32_divider_get_rate(void __iomem *base, |
209 | struct clk_stm32_clock_data *data, |
210 | u16 div_id, |
211 | unsigned long parent_rate) |
212 | { |
213 | const struct stm32_div_cfg *divider = &data->dividers[div_id]; |
214 | unsigned int val; |
215 | unsigned int div; |
216 | |
217 | val = readl(addr: base + divider->offset) >> divider->shift; |
218 | val &= clk_div_mask(divider->width); |
219 | div = _get_div(table: divider->table, val, flags: divider->flags, width: divider->width); |
220 | |
221 | if (!div) { |
222 | WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), |
223 | "%d: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n" , |
224 | div_id); |
225 | return parent_rate; |
226 | } |
227 | |
228 | return DIV_ROUND_UP_ULL((u64)parent_rate, div); |
229 | } |
230 | |
231 | static int stm32_divider_set_rate(void __iomem *base, |
232 | struct clk_stm32_clock_data *data, |
233 | u16 div_id, unsigned long rate, |
234 | unsigned long parent_rate) |
235 | { |
236 | const struct stm32_div_cfg *divider = &data->dividers[div_id]; |
237 | int value; |
238 | u32 val; |
239 | |
240 | value = divider_get_val(rate, parent_rate, table: divider->table, |
241 | width: divider->width, flags: divider->flags); |
242 | if (value < 0) |
243 | return value; |
244 | |
245 | if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { |
246 | val = clk_div_mask(divider->width) << (divider->shift + 16); |
247 | } else { |
248 | val = readl(addr: base + divider->offset); |
249 | val &= ~(clk_div_mask(divider->width) << divider->shift); |
250 | } |
251 | |
252 | val |= (u32)value << divider->shift; |
253 | |
254 | writel(val, addr: base + divider->offset); |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | static u8 clk_stm32_mux_get_parent(struct clk_hw *hw) |
260 | { |
261 | struct clk_stm32_mux *mux = to_clk_stm32_mux(hw); |
262 | |
263 | return stm32_mux_get_parent(base: mux->base, data: mux->clock_data, mux_id: mux->mux_id); |
264 | } |
265 | |
266 | static int clk_stm32_mux_set_parent(struct clk_hw *hw, u8 index) |
267 | { |
268 | struct clk_stm32_mux *mux = to_clk_stm32_mux(hw); |
269 | unsigned long flags = 0; |
270 | |
271 | spin_lock_irqsave(mux->lock, flags); |
272 | |
273 | stm32_mux_set_parent(base: mux->base, data: mux->clock_data, mux_id: mux->mux_id, index); |
274 | |
275 | spin_unlock_irqrestore(lock: mux->lock, flags); |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | const struct clk_ops clk_stm32_mux_ops = { |
281 | .determine_rate = __clk_mux_determine_rate, |
282 | .get_parent = clk_stm32_mux_get_parent, |
283 | .set_parent = clk_stm32_mux_set_parent, |
284 | }; |
285 | |
286 | static void clk_stm32_gate_endisable(struct clk_hw *hw, int enable) |
287 | { |
288 | struct clk_stm32_gate *gate = to_clk_stm32_gate(hw); |
289 | unsigned long flags = 0; |
290 | |
291 | spin_lock_irqsave(gate->lock, flags); |
292 | |
293 | stm32_gate_endisable(base: gate->base, data: gate->clock_data, gate_id: gate->gate_id, enable); |
294 | |
295 | spin_unlock_irqrestore(lock: gate->lock, flags); |
296 | } |
297 | |
298 | static int clk_stm32_gate_enable(struct clk_hw *hw) |
299 | { |
300 | clk_stm32_gate_endisable(hw, enable: 1); |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | static void clk_stm32_gate_disable(struct clk_hw *hw) |
306 | { |
307 | clk_stm32_gate_endisable(hw, enable: 0); |
308 | } |
309 | |
310 | static int clk_stm32_gate_is_enabled(struct clk_hw *hw) |
311 | { |
312 | struct clk_stm32_gate *gate = to_clk_stm32_gate(hw); |
313 | |
314 | return stm32_gate_is_enabled(base: gate->base, data: gate->clock_data, gate_id: gate->gate_id); |
315 | } |
316 | |
317 | static void clk_stm32_gate_disable_unused(struct clk_hw *hw) |
318 | { |
319 | struct clk_stm32_gate *gate = to_clk_stm32_gate(hw); |
320 | unsigned long flags = 0; |
321 | |
322 | spin_lock_irqsave(gate->lock, flags); |
323 | |
324 | stm32_gate_disable_unused(base: gate->base, data: gate->clock_data, gate_id: gate->gate_id); |
325 | |
326 | spin_unlock_irqrestore(lock: gate->lock, flags); |
327 | } |
328 | |
329 | const struct clk_ops clk_stm32_gate_ops = { |
330 | .enable = clk_stm32_gate_enable, |
331 | .disable = clk_stm32_gate_disable, |
332 | .is_enabled = clk_stm32_gate_is_enabled, |
333 | .disable_unused = clk_stm32_gate_disable_unused, |
334 | }; |
335 | |
336 | static int clk_stm32_divider_set_rate(struct clk_hw *hw, unsigned long rate, |
337 | unsigned long parent_rate) |
338 | { |
339 | struct clk_stm32_div *div = to_clk_stm32_divider(hw); |
340 | unsigned long flags = 0; |
341 | int ret; |
342 | |
343 | if (div->div_id == NO_STM32_DIV) |
344 | return rate; |
345 | |
346 | spin_lock_irqsave(div->lock, flags); |
347 | |
348 | ret = stm32_divider_set_rate(base: div->base, data: div->clock_data, div_id: div->div_id, rate, parent_rate); |
349 | |
350 | spin_unlock_irqrestore(lock: div->lock, flags); |
351 | |
352 | return ret; |
353 | } |
354 | |
355 | static long clk_stm32_divider_round_rate(struct clk_hw *hw, unsigned long rate, |
356 | unsigned long *prate) |
357 | { |
358 | struct clk_stm32_div *div = to_clk_stm32_divider(hw); |
359 | const struct stm32_div_cfg *divider; |
360 | |
361 | if (div->div_id == NO_STM32_DIV) |
362 | return rate; |
363 | |
364 | divider = &div->clock_data->dividers[div->div_id]; |
365 | |
366 | /* if read only, just return current value */ |
367 | if (divider->flags & CLK_DIVIDER_READ_ONLY) { |
368 | u32 val; |
369 | |
370 | val = readl(addr: div->base + divider->offset) >> divider->shift; |
371 | val &= clk_div_mask(divider->width); |
372 | |
373 | return divider_ro_round_rate(hw, rate, prate, table: divider->table, |
374 | width: divider->width, flags: divider->flags, |
375 | val); |
376 | } |
377 | |
378 | return divider_round_rate_parent(hw, parent: clk_hw_get_parent(hw), |
379 | rate, prate, table: divider->table, |
380 | width: divider->width, flags: divider->flags); |
381 | } |
382 | |
383 | static unsigned long clk_stm32_divider_recalc_rate(struct clk_hw *hw, |
384 | unsigned long parent_rate) |
385 | { |
386 | struct clk_stm32_div *div = to_clk_stm32_divider(hw); |
387 | |
388 | if (div->div_id == NO_STM32_DIV) |
389 | return parent_rate; |
390 | |
391 | return stm32_divider_get_rate(base: div->base, data: div->clock_data, div_id: div->div_id, parent_rate); |
392 | } |
393 | |
394 | const struct clk_ops clk_stm32_divider_ops = { |
395 | .recalc_rate = clk_stm32_divider_recalc_rate, |
396 | .round_rate = clk_stm32_divider_round_rate, |
397 | .set_rate = clk_stm32_divider_set_rate, |
398 | }; |
399 | |
400 | static int clk_stm32_composite_set_rate(struct clk_hw *hw, unsigned long rate, |
401 | unsigned long parent_rate) |
402 | { |
403 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
404 | unsigned long flags = 0; |
405 | int ret; |
406 | |
407 | if (composite->div_id == NO_STM32_DIV) |
408 | return rate; |
409 | |
410 | spin_lock_irqsave(composite->lock, flags); |
411 | |
412 | ret = stm32_divider_set_rate(base: composite->base, data: composite->clock_data, |
413 | div_id: composite->div_id, rate, parent_rate); |
414 | |
415 | spin_unlock_irqrestore(lock: composite->lock, flags); |
416 | |
417 | return ret; |
418 | } |
419 | |
420 | static unsigned long clk_stm32_composite_recalc_rate(struct clk_hw *hw, |
421 | unsigned long parent_rate) |
422 | { |
423 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
424 | |
425 | if (composite->div_id == NO_STM32_DIV) |
426 | return parent_rate; |
427 | |
428 | return stm32_divider_get_rate(base: composite->base, data: composite->clock_data, |
429 | div_id: composite->div_id, parent_rate); |
430 | } |
431 | |
432 | static int clk_stm32_composite_determine_rate(struct clk_hw *hw, |
433 | struct clk_rate_request *req) |
434 | { |
435 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
436 | const struct stm32_div_cfg *divider; |
437 | long rate; |
438 | |
439 | if (composite->div_id == NO_STM32_DIV) |
440 | return 0; |
441 | |
442 | divider = &composite->clock_data->dividers[composite->div_id]; |
443 | |
444 | /* if read only, just return current value */ |
445 | if (divider->flags & CLK_DIVIDER_READ_ONLY) { |
446 | u32 val; |
447 | |
448 | val = readl(addr: composite->base + divider->offset) >> divider->shift; |
449 | val &= clk_div_mask(divider->width); |
450 | |
451 | rate = divider_ro_round_rate(hw, rate: req->rate, prate: &req->best_parent_rate, |
452 | table: divider->table, width: divider->width, flags: divider->flags, |
453 | val); |
454 | if (rate < 0) |
455 | return rate; |
456 | |
457 | req->rate = rate; |
458 | return 0; |
459 | } |
460 | |
461 | rate = divider_round_rate_parent(hw, parent: clk_hw_get_parent(hw), |
462 | rate: req->rate, prate: &req->best_parent_rate, |
463 | table: divider->table, width: divider->width, flags: divider->flags); |
464 | if (rate < 0) |
465 | return rate; |
466 | |
467 | req->rate = rate; |
468 | return 0; |
469 | } |
470 | |
471 | static u8 clk_stm32_composite_get_parent(struct clk_hw *hw) |
472 | { |
473 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
474 | |
475 | return stm32_mux_get_parent(base: composite->base, data: composite->clock_data, mux_id: composite->mux_id); |
476 | } |
477 | |
478 | static int clk_stm32_composite_set_parent(struct clk_hw *hw, u8 index) |
479 | { |
480 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
481 | unsigned long flags = 0; |
482 | |
483 | spin_lock_irqsave(composite->lock, flags); |
484 | |
485 | stm32_mux_set_parent(base: composite->base, data: composite->clock_data, mux_id: composite->mux_id, index); |
486 | |
487 | spin_unlock_irqrestore(lock: composite->lock, flags); |
488 | |
489 | if (composite->clock_data->is_multi_mux) { |
490 | struct clk_hw *other_mux_hw = composite->clock_data->is_multi_mux(hw); |
491 | |
492 | if (other_mux_hw) { |
493 | struct clk_hw *hwp = clk_hw_get_parent_by_index(hw, index); |
494 | |
495 | clk_hw_reparent(hw: other_mux_hw, new_parent: hwp); |
496 | } |
497 | } |
498 | |
499 | return 0; |
500 | } |
501 | |
502 | static int clk_stm32_composite_is_enabled(struct clk_hw *hw) |
503 | { |
504 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
505 | |
506 | if (composite->gate_id == NO_STM32_GATE) |
507 | return (__clk_get_enable_count(clk: hw->clk) > 0); |
508 | |
509 | return stm32_gate_is_enabled(base: composite->base, data: composite->clock_data, gate_id: composite->gate_id); |
510 | } |
511 | |
512 | #define MUX_SAFE_POSITION 0 |
513 | |
514 | static int clk_stm32_has_safe_mux(struct clk_hw *hw) |
515 | { |
516 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
517 | const struct stm32_mux_cfg *mux = &composite->clock_data->muxes[composite->mux_id]; |
518 | |
519 | return !!(mux->flags & MUX_SAFE); |
520 | } |
521 | |
522 | static void clk_stm32_set_safe_position_mux(struct clk_hw *hw) |
523 | { |
524 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
525 | |
526 | if (!clk_stm32_composite_is_enabled(hw)) { |
527 | unsigned long flags = 0; |
528 | |
529 | if (composite->clock_data->is_multi_mux) { |
530 | struct clk_hw *other_mux_hw = NULL; |
531 | |
532 | other_mux_hw = composite->clock_data->is_multi_mux(hw); |
533 | |
534 | if (!other_mux_hw || clk_stm32_composite_is_enabled(hw: other_mux_hw)) |
535 | return; |
536 | } |
537 | |
538 | spin_lock_irqsave(composite->lock, flags); |
539 | |
540 | stm32_mux_set_parent(base: composite->base, data: composite->clock_data, |
541 | mux_id: composite->mux_id, MUX_SAFE_POSITION); |
542 | |
543 | spin_unlock_irqrestore(lock: composite->lock, flags); |
544 | } |
545 | } |
546 | |
547 | static void clk_stm32_safe_restore_position_mux(struct clk_hw *hw) |
548 | { |
549 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
550 | int sel = clk_hw_get_parent_index(hw); |
551 | unsigned long flags = 0; |
552 | |
553 | spin_lock_irqsave(composite->lock, flags); |
554 | |
555 | stm32_mux_set_parent(base: composite->base, data: composite->clock_data, mux_id: composite->mux_id, index: sel); |
556 | |
557 | spin_unlock_irqrestore(lock: composite->lock, flags); |
558 | } |
559 | |
560 | static void clk_stm32_composite_gate_endisable(struct clk_hw *hw, int enable) |
561 | { |
562 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
563 | unsigned long flags = 0; |
564 | |
565 | spin_lock_irqsave(composite->lock, flags); |
566 | |
567 | stm32_gate_endisable(base: composite->base, data: composite->clock_data, gate_id: composite->gate_id, enable); |
568 | |
569 | spin_unlock_irqrestore(lock: composite->lock, flags); |
570 | } |
571 | |
572 | static int clk_stm32_composite_gate_enable(struct clk_hw *hw) |
573 | { |
574 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
575 | |
576 | if (composite->gate_id == NO_STM32_GATE) |
577 | return 0; |
578 | |
579 | clk_stm32_composite_gate_endisable(hw, enable: 1); |
580 | |
581 | if (composite->mux_id != NO_STM32_MUX && clk_stm32_has_safe_mux(hw)) |
582 | clk_stm32_safe_restore_position_mux(hw); |
583 | |
584 | return 0; |
585 | } |
586 | |
587 | static void clk_stm32_composite_gate_disable(struct clk_hw *hw) |
588 | { |
589 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
590 | |
591 | if (composite->gate_id == NO_STM32_GATE) |
592 | return; |
593 | |
594 | clk_stm32_composite_gate_endisable(hw, enable: 0); |
595 | |
596 | if (composite->mux_id != NO_STM32_MUX && clk_stm32_has_safe_mux(hw)) |
597 | clk_stm32_set_safe_position_mux(hw); |
598 | } |
599 | |
600 | static void clk_stm32_composite_disable_unused(struct clk_hw *hw) |
601 | { |
602 | struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); |
603 | unsigned long flags = 0; |
604 | |
605 | if (composite->gate_id == NO_STM32_GATE) |
606 | return; |
607 | |
608 | spin_lock_irqsave(composite->lock, flags); |
609 | |
610 | stm32_gate_disable_unused(base: composite->base, data: composite->clock_data, gate_id: composite->gate_id); |
611 | |
612 | spin_unlock_irqrestore(lock: composite->lock, flags); |
613 | } |
614 | |
615 | const struct clk_ops clk_stm32_composite_ops = { |
616 | .set_rate = clk_stm32_composite_set_rate, |
617 | .recalc_rate = clk_stm32_composite_recalc_rate, |
618 | .determine_rate = clk_stm32_composite_determine_rate, |
619 | .get_parent = clk_stm32_composite_get_parent, |
620 | .set_parent = clk_stm32_composite_set_parent, |
621 | .enable = clk_stm32_composite_gate_enable, |
622 | .disable = clk_stm32_composite_gate_disable, |
623 | .is_enabled = clk_stm32_composite_is_enabled, |
624 | .disable_unused = clk_stm32_composite_disable_unused, |
625 | }; |
626 | |
627 | struct clk_hw *clk_stm32_mux_register(struct device *dev, |
628 | const struct stm32_rcc_match_data *data, |
629 | void __iomem *base, |
630 | spinlock_t *lock, |
631 | const struct clock_config *cfg) |
632 | { |
633 | struct clk_stm32_mux *mux = cfg->clock_cfg; |
634 | struct clk_hw *hw = &mux->hw; |
635 | int err; |
636 | |
637 | mux->base = base; |
638 | mux->lock = lock; |
639 | mux->clock_data = data->clock_data; |
640 | |
641 | err = clk_hw_register(dev, hw); |
642 | if (err) |
643 | return ERR_PTR(error: err); |
644 | |
645 | return hw; |
646 | } |
647 | |
648 | struct clk_hw *clk_stm32_gate_register(struct device *dev, |
649 | const struct stm32_rcc_match_data *data, |
650 | void __iomem *base, |
651 | spinlock_t *lock, |
652 | const struct clock_config *cfg) |
653 | { |
654 | struct clk_stm32_gate *gate = cfg->clock_cfg; |
655 | struct clk_hw *hw = &gate->hw; |
656 | int err; |
657 | |
658 | gate->base = base; |
659 | gate->lock = lock; |
660 | gate->clock_data = data->clock_data; |
661 | |
662 | err = clk_hw_register(dev, hw); |
663 | if (err) |
664 | return ERR_PTR(error: err); |
665 | |
666 | return hw; |
667 | } |
668 | |
669 | struct clk_hw *clk_stm32_div_register(struct device *dev, |
670 | const struct stm32_rcc_match_data *data, |
671 | void __iomem *base, |
672 | spinlock_t *lock, |
673 | const struct clock_config *cfg) |
674 | { |
675 | struct clk_stm32_div *div = cfg->clock_cfg; |
676 | struct clk_hw *hw = &div->hw; |
677 | int err; |
678 | |
679 | div->base = base; |
680 | div->lock = lock; |
681 | div->clock_data = data->clock_data; |
682 | |
683 | err = clk_hw_register(dev, hw); |
684 | if (err) |
685 | return ERR_PTR(error: err); |
686 | |
687 | return hw; |
688 | } |
689 | |
690 | struct clk_hw *clk_stm32_composite_register(struct device *dev, |
691 | const struct stm32_rcc_match_data *data, |
692 | void __iomem *base, |
693 | spinlock_t *lock, |
694 | const struct clock_config *cfg) |
695 | { |
696 | struct clk_stm32_composite *composite = cfg->clock_cfg; |
697 | struct clk_hw *hw = &composite->hw; |
698 | int err; |
699 | |
700 | composite->base = base; |
701 | composite->lock = lock; |
702 | composite->clock_data = data->clock_data; |
703 | |
704 | err = clk_hw_register(dev, hw); |
705 | if (err) |
706 | return ERR_PTR(error: err); |
707 | |
708 | return hw; |
709 | } |
710 | |