1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2014 MundoReader S.L.
4 * Author: Heiko Stuebner <heiko@sntech.de>
5 *
6 * based on clk/samsung/clk-cpu.c
7 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
8 * Author: Thomas Abraham <thomas.ab@samsung.com>
9 *
10 * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs.
11 * The CPU clock is typically derived from a hierarchy of clock
12 * blocks which includes mux and divider blocks. There are a number of other
13 * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
14 * clock for CPU domain. The rates of these auxiliary clocks are related to the
15 * CPU clock rate and this relation is usually specified in the hardware manual
16 * of the SoC or supplied after the SoC characterization.
17 *
18 * The below implementation of the CPU clock allows the rate changes of the CPU
19 * clock and the corresponding rate changes of the auxillary clocks of the CPU
20 * domain. The platform clock driver provides a clock register configuration
21 * for each configurable rate which is then used to program the clock hardware
22 * registers to acheive a fast co-oridinated rate change for all the CPU domain
23 * clocks.
24 *
25 * On a rate change request for the CPU clock, the rate change is propagated
26 * upto the PLL supplying the clock to the CPU domain clock blocks. While the
27 * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
28 * alternate clock source. If required, the alternate clock source is divided
29 * down in order to keep the output clock rate within the previous OPP limits.
30 */
31
32 #include <linux/of.h>
33 #include <linux/slab.h>
34 #include <linux/io.h>
35 #include <linux/clk.h>
36 #include <linux/clk-provider.h>
37 #include "clk.h"
38
39 /**
40 * struct rockchip_cpuclk: information about clock supplied to a CPU core.
41 * @hw: handle between ccf and cpu clock.
42 * @alt_parent: alternate parent clock to use when switching the speed
43 * of the primary parent clock.
44 * @reg_base: base register for cpu-clock values.
45 * @clk_nb: clock notifier registered for changes in clock speed of the
46 * primary parent clock.
47 * @rate_count: number of rates in the rate_table
48 * @rate_table: pll-rates and their associated dividers
49 * @reg_data: cpu-specific register settings
50 * @lock: clock lock
51 */
52 struct rockchip_cpuclk {
53 struct clk_hw hw;
54 struct clk_hw *pll_hw;
55
56 struct clk_mux cpu_mux;
57 const struct clk_ops *cpu_mux_ops;
58
59 struct clk *alt_parent;
60 void __iomem *reg_base;
61 struct notifier_block clk_nb;
62 unsigned int rate_count;
63 struct rockchip_cpuclk_rate_table *rate_table;
64 const struct rockchip_cpuclk_reg_data *reg_data;
65 spinlock_t *lock;
66 };
67
68 #define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw)
69 #define to_rockchip_cpuclk_nb(nb) \
70 container_of(nb, struct rockchip_cpuclk, clk_nb)
71
rockchip_get_cpuclk_settings(struct rockchip_cpuclk * cpuclk,unsigned long rate)72 static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings(
73 struct rockchip_cpuclk *cpuclk, unsigned long rate)
74 {
75 const struct rockchip_cpuclk_rate_table *rate_table =
76 cpuclk->rate_table;
77 int i;
78
79 for (i = 0; i < cpuclk->rate_count; i++) {
80 if (rate == rate_table[i].prate)
81 return &rate_table[i];
82 }
83
84 return NULL;
85 }
86
rockchip_cpuclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)87 static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw,
88 unsigned long parent_rate)
89 {
90 struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw);
91 const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
92 u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg[0]);
93
94 clksel0 >>= reg_data->div_core_shift[0];
95 clksel0 &= reg_data->div_core_mask[0];
96 return parent_rate / (clksel0 + 1);
97 }
98
99 static const struct clk_ops rockchip_cpuclk_ops = {
100 .recalc_rate = rockchip_cpuclk_recalc_rate,
101 };
102
rockchip_cpuclk_set_dividers(struct rockchip_cpuclk * cpuclk,const struct rockchip_cpuclk_rate_table * rate)103 static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
104 const struct rockchip_cpuclk_rate_table *rate)
105 {
106 int i;
107
108 /* alternate parent is active now. set the dividers */
109 for (i = 0; i < ARRAY_SIZE(rate->divs); i++) {
110 const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i];
111
112 if (!clksel->reg)
113 continue;
114
115 pr_debug("%s: setting reg 0x%x to 0x%x\n",
116 __func__, clksel->reg, clksel->val);
117 writel(clksel->val, cpuclk->reg_base + clksel->reg);
118 }
119 }
120
rockchip_cpuclk_set_pre_muxs(struct rockchip_cpuclk * cpuclk,const struct rockchip_cpuclk_rate_table * rate)121 static void rockchip_cpuclk_set_pre_muxs(struct rockchip_cpuclk *cpuclk,
122 const struct rockchip_cpuclk_rate_table *rate)
123 {
124 int i;
125
126 /* alternate parent is active now. set the pre_muxs */
127 for (i = 0; i < ARRAY_SIZE(rate->pre_muxs); i++) {
128 const struct rockchip_cpuclk_clksel *clksel = &rate->pre_muxs[i];
129
130 if (!clksel->reg)
131 break;
132
133 pr_debug("%s: setting reg 0x%x to 0x%x\n",
134 __func__, clksel->reg, clksel->val);
135 writel(clksel->val, cpuclk->reg_base + clksel->reg);
136 }
137 }
138
rockchip_cpuclk_set_post_muxs(struct rockchip_cpuclk * cpuclk,const struct rockchip_cpuclk_rate_table * rate)139 static void rockchip_cpuclk_set_post_muxs(struct rockchip_cpuclk *cpuclk,
140 const struct rockchip_cpuclk_rate_table *rate)
141 {
142 int i;
143
144 /* alternate parent is active now. set the muxs */
145 for (i = 0; i < ARRAY_SIZE(rate->post_muxs); i++) {
146 const struct rockchip_cpuclk_clksel *clksel = &rate->post_muxs[i];
147
148 if (!clksel->reg)
149 break;
150
151 pr_debug("%s: setting reg 0x%x to 0x%x\n",
152 __func__, clksel->reg, clksel->val);
153 writel(clksel->val, cpuclk->reg_base + clksel->reg);
154 }
155 }
156
rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk * cpuclk,struct clk_notifier_data * ndata)157 static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
158 struct clk_notifier_data *ndata)
159 {
160 const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
161 const struct rockchip_cpuclk_rate_table *rate;
162 unsigned long alt_prate, alt_div;
163 unsigned long flags;
164 int i = 0;
165
166 /* check validity of the new rate */
167 rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
168 if (!rate) {
169 pr_err("%s: Invalid rate : %lu for cpuclk\n",
170 __func__, ndata->new_rate);
171 return -EINVAL;
172 }
173
174 if (IS_ENABLED(CONFIG_ROCKCHIP_CLK_BOOST))
175 rockchip_boost_enable_recovery_sw_low(cpuclk->pll_hw);
176
177 alt_prate = clk_get_rate(cpuclk->alt_parent);
178
179 spin_lock_irqsave(cpuclk->lock, flags);
180
181 /*
182 * If the old parent clock speed is less than the clock speed
183 * of the alternate parent, then it should be ensured that at no point
184 * the armclk speed is more than the old_rate until the dividers are
185 * set.
186 */
187 if (alt_prate > ndata->old_rate) {
188 /* calculate dividers */
189 alt_div = DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1;
190 if (alt_div > reg_data->div_core_mask[0]) {
191 pr_warn("%s: limiting alt-divider %lu to %d\n",
192 __func__, alt_div, reg_data->div_core_mask[0]);
193 alt_div = reg_data->div_core_mask[0];
194 }
195
196 /*
197 * Change parents and add dividers in a single transaction.
198 *
199 * NOTE: we do this in a single transaction so we're never
200 * dividing the primary parent by the extra dividers that were
201 * needed for the alt.
202 */
203 pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
204 __func__, alt_div, alt_prate, ndata->old_rate);
205
206 for (i = 0; i < reg_data->num_cores; i++) {
207 writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask[i],
208 reg_data->div_core_shift[i]),
209 cpuclk->reg_base + reg_data->core_reg[i]);
210 }
211 }
212
213 if (IS_ENABLED(CONFIG_ROCKCHIP_CLK_BOOST))
214 rockchip_boost_add_core_div(cpuclk->pll_hw, alt_prate);
215
216 rockchip_cpuclk_set_pre_muxs(cpuclk, rate);
217
218 /* select alternate parent */
219 if (reg_data->mux_core_reg)
220 writel(HIWORD_UPDATE(reg_data->mux_core_alt,
221 reg_data->mux_core_mask,
222 reg_data->mux_core_shift),
223 cpuclk->reg_base + reg_data->mux_core_reg);
224 else
225 writel(HIWORD_UPDATE(reg_data->mux_core_alt,
226 reg_data->mux_core_mask,
227 reg_data->mux_core_shift),
228 cpuclk->reg_base + reg_data->core_reg[0]);
229
230 spin_unlock_irqrestore(cpuclk->lock, flags);
231 return 0;
232 }
233
rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk * cpuclk,struct clk_notifier_data * ndata)234 static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
235 struct clk_notifier_data *ndata)
236 {
237 const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
238 const struct rockchip_cpuclk_rate_table *rate;
239 unsigned long flags;
240 int i = 0;
241
242 rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
243 if (!rate) {
244 pr_err("%s: Invalid rate : %lu for cpuclk\n",
245 __func__, ndata->new_rate);
246 return -EINVAL;
247 }
248
249 spin_lock_irqsave(cpuclk->lock, flags);
250
251 if (ndata->old_rate < ndata->new_rate)
252 rockchip_cpuclk_set_dividers(cpuclk, rate);
253
254 /*
255 * post-rate change event, re-mux to primary parent and remove dividers.
256 *
257 * NOTE: we do this in a single transaction so we're never dividing the
258 * primary parent by the extra dividers that were needed for the alt.
259 */
260
261 if (reg_data->mux_core_reg)
262 writel(HIWORD_UPDATE(reg_data->mux_core_main,
263 reg_data->mux_core_mask,
264 reg_data->mux_core_shift),
265 cpuclk->reg_base + reg_data->mux_core_reg);
266 else
267 writel(HIWORD_UPDATE(reg_data->mux_core_main,
268 reg_data->mux_core_mask,
269 reg_data->mux_core_shift),
270 cpuclk->reg_base + reg_data->core_reg[0]);
271
272 rockchip_cpuclk_set_post_muxs(cpuclk, rate);
273
274 /* remove dividers */
275 for (i = 0; i < reg_data->num_cores; i++) {
276 writel(HIWORD_UPDATE(0, reg_data->div_core_mask[i],
277 reg_data->div_core_shift[i]),
278 cpuclk->reg_base + reg_data->core_reg[i]);
279 }
280
281 if (ndata->old_rate > ndata->new_rate)
282 rockchip_cpuclk_set_dividers(cpuclk, rate);
283
284 if (IS_ENABLED(CONFIG_ROCKCHIP_CLK_BOOST))
285 rockchip_boost_disable_recovery_sw(cpuclk->pll_hw);
286
287 spin_unlock_irqrestore(cpuclk->lock, flags);
288 return 0;
289 }
290
291 /*
292 * This clock notifier is called when the frequency of the parent clock
293 * of cpuclk is to be changed. This notifier handles the setting up all
294 * the divider clocks, remux to temporary parent and handling the safe
295 * frequency levels when using temporary parent.
296 */
rockchip_cpuclk_notifier_cb(struct notifier_block * nb,unsigned long event,void * data)297 static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
298 unsigned long event, void *data)
299 {
300 struct clk_notifier_data *ndata = data;
301 struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
302 int ret = 0;
303
304 pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
305 __func__, event, ndata->old_rate, ndata->new_rate);
306 if (event == PRE_RATE_CHANGE)
307 ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata);
308 else if (event == POST_RATE_CHANGE)
309 ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata);
310
311 return notifier_from_errno(ret);
312 }
313
rockchip_clk_register_cpuclk(const char * name,u8 num_parents,struct clk * parent,struct clk * alt_parent,const struct rockchip_cpuclk_reg_data * reg_data,const struct rockchip_cpuclk_rate_table * rates,int nrates,void __iomem * reg_base,spinlock_t * lock)314 struct clk *rockchip_clk_register_cpuclk(const char *name,
315 u8 num_parents,
316 struct clk *parent, struct clk *alt_parent,
317 const struct rockchip_cpuclk_reg_data *reg_data,
318 const struct rockchip_cpuclk_rate_table *rates,
319 int nrates, void __iomem *reg_base, spinlock_t *lock)
320 {
321 struct rockchip_cpuclk *cpuclk;
322 struct clk_init_data init;
323 struct clk *clk, *cclk, *pll_clk;
324 const char *parent_name;
325 int ret;
326
327 if (num_parents < 2) {
328 pr_err("%s: needs at least two parent clocks\n", __func__);
329 return ERR_PTR(-EINVAL);
330 }
331
332 if (IS_ERR(parent) || IS_ERR(alt_parent)) {
333 pr_err("%s: invalid parent clock(s)\n", __func__);
334 return ERR_PTR(-EINVAL);
335 }
336
337 cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
338 if (!cpuclk)
339 return ERR_PTR(-ENOMEM);
340
341 parent_name = clk_hw_get_name(__clk_get_hw(parent));
342 init.name = name;
343 init.parent_names = &parent_name;
344 init.num_parents = 1;
345 init.ops = &rockchip_cpuclk_ops;
346
347 /* only allow rate changes when we have a rate table */
348 init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0;
349
350 /* disallow automatic parent changes by ccf */
351 init.flags |= CLK_SET_RATE_NO_REPARENT;
352
353 init.flags |= CLK_GET_RATE_NOCACHE;
354
355 cpuclk->reg_base = reg_base;
356 cpuclk->lock = lock;
357 cpuclk->reg_data = reg_data;
358 cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
359 cpuclk->hw.init = &init;
360 if (IS_ENABLED(CONFIG_ROCKCHIP_CLK_BOOST) && reg_data->pll_name) {
361 pll_clk = clk_get_parent(parent);
362 if (!pll_clk) {
363 pr_err("%s: could not lookup pll clock: (%s)\n",
364 __func__, reg_data->pll_name);
365 ret = -EINVAL;
366 goto free_cpuclk;
367 }
368 cpuclk->pll_hw = __clk_get_hw(pll_clk);
369 rockchip_boost_init(cpuclk->pll_hw);
370 }
371
372 cpuclk->alt_parent = alt_parent;
373 if (!cpuclk->alt_parent) {
374 pr_err("%s: could not lookup alternate parent: (%d)\n",
375 __func__, reg_data->mux_core_alt);
376 ret = -EINVAL;
377 goto free_cpuclk;
378 }
379
380 ret = clk_prepare_enable(cpuclk->alt_parent);
381 if (ret) {
382 pr_err("%s: could not enable alternate parent\n",
383 __func__);
384 goto free_cpuclk;
385 }
386
387 clk = parent;
388 if (!clk) {
389 pr_err("%s: could not lookup parent clock: (%d) %s\n",
390 __func__, reg_data->mux_core_main,
391 parent_name);
392 ret = -EINVAL;
393 goto free_alt_parent;
394 }
395
396 ret = clk_notifier_register(clk, &cpuclk->clk_nb);
397 if (ret) {
398 pr_err("%s: failed to register clock notifier for %s\n",
399 __func__, name);
400 goto free_alt_parent;
401 }
402
403 if (nrates > 0) {
404 cpuclk->rate_count = nrates;
405 cpuclk->rate_table = kmemdup(rates,
406 sizeof(*rates) * nrates,
407 GFP_KERNEL);
408 if (!cpuclk->rate_table) {
409 ret = -ENOMEM;
410 goto unregister_notifier;
411 }
412 }
413
414 cclk = clk_register(NULL, &cpuclk->hw);
415 if (IS_ERR(cclk)) {
416 pr_err("%s: could not register cpuclk %s\n", __func__, name);
417 ret = PTR_ERR(cclk);
418 goto free_rate_table;
419 }
420
421 return cclk;
422
423 free_rate_table:
424 kfree(cpuclk->rate_table);
425 unregister_notifier:
426 clk_notifier_unregister(clk, &cpuclk->clk_nb);
427 free_alt_parent:
428 clk_disable_unprepare(cpuclk->alt_parent);
429 free_cpuclk:
430 kfree(cpuclk);
431 return ERR_PTR(ret);
432 }
433
rockchip_cpuclk_v2_pre_rate_change(struct rockchip_cpuclk * cpuclk,struct clk_notifier_data * ndata)434 static int rockchip_cpuclk_v2_pre_rate_change(struct rockchip_cpuclk *cpuclk,
435 struct clk_notifier_data *ndata)
436 {
437 unsigned long new_rate = roundup(ndata->new_rate, 1000);
438 const struct rockchip_cpuclk_rate_table *rate;
439 unsigned long flags;
440
441 rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
442 if (!rate) {
443 pr_err("%s: Invalid rate : %lu for cpuclk\n",
444 __func__, new_rate);
445 return -EINVAL;
446 }
447
448 if (new_rate > ndata->old_rate) {
449 spin_lock_irqsave(cpuclk->lock, flags);
450 rockchip_cpuclk_set_dividers(cpuclk, rate);
451 spin_unlock_irqrestore(cpuclk->lock, flags);
452 }
453
454 return 0;
455 }
456
rockchip_cpuclk_v2_post_rate_change(struct rockchip_cpuclk * cpuclk,struct clk_notifier_data * ndata)457 static int rockchip_cpuclk_v2_post_rate_change(struct rockchip_cpuclk *cpuclk,
458 struct clk_notifier_data *ndata)
459 {
460 unsigned long new_rate = roundup(ndata->new_rate, 1000);
461 const struct rockchip_cpuclk_rate_table *rate;
462 unsigned long flags;
463
464 rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
465 if (!rate) {
466 pr_err("%s: Invalid rate : %lu for cpuclk\n",
467 __func__, new_rate);
468 return -EINVAL;
469 }
470
471 if (new_rate < ndata->old_rate) {
472 spin_lock_irqsave(cpuclk->lock, flags);
473 rockchip_cpuclk_set_dividers(cpuclk, rate);
474 spin_unlock_irqrestore(cpuclk->lock, flags);
475 }
476
477 return 0;
478 }
479
rockchip_cpuclk_v2_notifier_cb(struct notifier_block * nb,unsigned long event,void * data)480 static int rockchip_cpuclk_v2_notifier_cb(struct notifier_block *nb,
481 unsigned long event, void *data)
482 {
483 struct clk_notifier_data *ndata = data;
484 struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
485 int ret = 0;
486
487 pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
488 __func__, event, ndata->old_rate, ndata->new_rate);
489 if (event == PRE_RATE_CHANGE)
490 ret = rockchip_cpuclk_v2_pre_rate_change(cpuclk, ndata);
491 else if (event == POST_RATE_CHANGE)
492 ret = rockchip_cpuclk_v2_post_rate_change(cpuclk, ndata);
493
494 return notifier_from_errno(ret);
495 }
496
rockchip_clk_register_cpuclk_v2(const char * name,const char * const * parent_names,u8 num_parents,void __iomem * base,int muxdiv_offset,u8 mux_shift,u8 mux_width,u8 mux_flags,int div_offset,u8 div_shift,u8 div_width,u8 div_flags,unsigned long flags,spinlock_t * lock,const struct rockchip_cpuclk_rate_table * rates,int nrates)497 struct clk *rockchip_clk_register_cpuclk_v2(const char *name,
498 const char *const *parent_names,
499 u8 num_parents, void __iomem *base,
500 int muxdiv_offset, u8 mux_shift,
501 u8 mux_width, u8 mux_flags,
502 int div_offset, u8 div_shift,
503 u8 div_width, u8 div_flags,
504 unsigned long flags, spinlock_t *lock,
505 const struct rockchip_cpuclk_rate_table *rates,
506 int nrates)
507 {
508 struct rockchip_cpuclk *cpuclk;
509 struct clk_hw *hw;
510 struct clk_mux *mux = NULL;
511 struct clk_divider *div = NULL;
512 const struct clk_ops *mux_ops = NULL, *div_ops = NULL;
513 int ret;
514
515 if (num_parents > 1) {
516 mux = kzalloc(sizeof(*mux), GFP_KERNEL);
517 if (!mux)
518 return ERR_PTR(-ENOMEM);
519
520 mux->reg = base + muxdiv_offset;
521 mux->shift = mux_shift;
522 mux->mask = BIT(mux_width) - 1;
523 mux->flags = mux_flags;
524 mux->lock = lock;
525 mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
526 : &clk_mux_ops;
527 }
528
529 if (div_width > 0) {
530 div = kzalloc(sizeof(*div), GFP_KERNEL);
531 if (!div) {
532 ret = -ENOMEM;
533 goto free_mux;
534 }
535
536 div->flags = div_flags;
537 if (div_offset)
538 div->reg = base + div_offset;
539 else
540 div->reg = base + muxdiv_offset;
541 div->shift = div_shift;
542 div->width = div_width;
543 div->lock = lock;
544 div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
545 ? &clk_divider_ro_ops
546 : &clk_divider_ops;
547 }
548
549 hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
550 mux ? &mux->hw : NULL, mux_ops,
551 div ? &div->hw : NULL, div_ops,
552 NULL, NULL, flags);
553 if (IS_ERR(hw)) {
554 ret = PTR_ERR(hw);
555 goto free_div;
556 }
557
558 cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
559 if (!cpuclk) {
560 ret = -ENOMEM;
561 goto unregister_clk;
562 }
563
564 cpuclk->reg_base = base;
565 cpuclk->lock = lock;
566 cpuclk->clk_nb.notifier_call = rockchip_cpuclk_v2_notifier_cb;
567 ret = clk_notifier_register(hw->clk, &cpuclk->clk_nb);
568 if (ret) {
569 pr_err("%s: failed to register clock notifier for %s\n",
570 __func__, name);
571 goto free_cpuclk;
572 }
573
574 if (nrates > 0) {
575 cpuclk->rate_count = nrates;
576 cpuclk->rate_table = kmemdup(rates,
577 sizeof(*rates) * nrates,
578 GFP_KERNEL);
579 if (!cpuclk->rate_table) {
580 ret = -ENOMEM;
581 goto free_cpuclk;
582 }
583 }
584
585 return hw->clk;
586
587 free_cpuclk:
588 kfree(cpuclk);
589 unregister_clk:
590 clk_hw_unregister_composite(hw);
591 free_div:
592 kfree(div);
593 free_mux:
594 kfree(mux);
595
596 return ERR_PTR(ret);
597 }
598