hc
2024-09-20 a36159eec6ca17402b0e146b86efaf76568dc353
kernel/drivers/clk/rockchip/clk-cpu.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * Copyright (c) 2014 MundoReader S.L.
34 * Author: Heiko Stuebner <heiko@sntech.de>
....@@ -5,10 +6,6 @@
56 * based on clk/samsung/clk-cpu.c
67 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
78 * Author: Thomas Abraham <thomas.ab@samsung.com>
8
- *
9
- * This program is free software; you can redistribute it and/or modify
10
- * it under the terms of the GNU General Public License version 2 as
11
- * published by the Free Software Foundation.
129 *
1310 * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs.
1411 * The CPU clock is typically derived from a hierarchy of clock
....@@ -196,6 +193,13 @@
196193 alt_div = reg_data->div_core_mask[0];
197194 }
198195
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
+ */
199203 pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
200204 __func__, alt_div, alt_prate, ndata->old_rate);
201205
....@@ -209,13 +213,19 @@
209213 if (IS_ENABLED(CONFIG_ROCKCHIP_CLK_BOOST))
210214 rockchip_boost_add_core_div(cpuclk->pll_hw, alt_prate);
211215
212
- /* select alternate parent */
213
- writel(HIWORD_UPDATE(reg_data->mux_core_alt,
214
- reg_data->mux_core_mask,
215
- reg_data->mux_core_shift),
216
- cpuclk->reg_base + reg_data->core_reg[0]);
217
-
218216 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]);
219229
220230 spin_unlock_irqrestore(cpuclk->lock, flags);
221231 return 0;
....@@ -241,11 +251,23 @@
241251 if (ndata->old_rate < ndata->new_rate)
242252 rockchip_cpuclk_set_dividers(cpuclk, rate);
243253
244
- /* re-mux to primary parent */
245
- writel(HIWORD_UPDATE(reg_data->mux_core_main,
246
- reg_data->mux_core_mask,
247
- reg_data->mux_core_shift),
248
- cpuclk->reg_base + reg_data->core_reg[0]);
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]);
249271
250272 rockchip_cpuclk_set_post_muxs(cpuclk, rate);
251273
....@@ -290,14 +312,16 @@
290312 }
291313
292314 struct clk *rockchip_clk_register_cpuclk(const char *name,
293
- const char *const *parent_names, u8 num_parents,
315
+ u8 num_parents,
316
+ struct clk *parent, struct clk *alt_parent,
294317 const struct rockchip_cpuclk_reg_data *reg_data,
295318 const struct rockchip_cpuclk_rate_table *rates,
296319 int nrates, void __iomem *reg_base, spinlock_t *lock)
297320 {
298321 struct rockchip_cpuclk *cpuclk;
299
- struct clk_init_data init = {};
322
+ struct clk_init_data init;
300323 struct clk *clk, *cclk, *pll_clk;
324
+ const char *parent_name;
301325 int ret;
302326
303327 if (num_parents < 2) {
....@@ -305,12 +329,18 @@
305329 return ERR_PTR(-EINVAL);
306330 }
307331
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
+
308337 cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
309338 if (!cpuclk)
310339 return ERR_PTR(-ENOMEM);
311340
341
+ parent_name = clk_hw_get_name(__clk_get_hw(parent));
312342 init.name = name;
313
- init.parent_names = &parent_names[reg_data->mux_core_main];
343
+ init.parent_names = &parent_name;
314344 init.num_parents = 1;
315345 init.ops = &rockchip_cpuclk_ops;
316346
....@@ -328,7 +358,7 @@
328358 cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
329359 cpuclk->hw.init = &init;
330360 if (IS_ENABLED(CONFIG_ROCKCHIP_CLK_BOOST) && reg_data->pll_name) {
331
- pll_clk = __clk_lookup(reg_data->pll_name);
361
+ pll_clk = clk_get_parent(parent);
332362 if (!pll_clk) {
333363 pr_err("%s: could not lookup pll clock: (%s)\n",
334364 __func__, reg_data->pll_name);
....@@ -339,7 +369,7 @@
339369 rockchip_boost_init(cpuclk->pll_hw);
340370 }
341371
342
- cpuclk->alt_parent = __clk_lookup(parent_names[reg_data->mux_core_alt]);
372
+ cpuclk->alt_parent = alt_parent;
343373 if (!cpuclk->alt_parent) {
344374 pr_err("%s: could not lookup alternate parent: (%d)\n",
345375 __func__, reg_data->mux_core_alt);
....@@ -354,11 +384,11 @@
354384 goto free_cpuclk;
355385 }
356386
357
- clk = __clk_lookup(parent_names[reg_data->mux_core_main]);
387
+ clk = parent;
358388 if (!clk) {
359389 pr_err("%s: could not lookup parent clock: (%d) %s\n",
360390 __func__, reg_data->mux_core_main,
361
- parent_names[reg_data->mux_core_main]);
391
+ parent_name);
362392 ret = -EINVAL;
363393 goto free_alt_parent;
364394 }
....@@ -400,3 +430,168 @@
400430 kfree(cpuclk);
401431 return ERR_PTR(ret);
402432 }
433
+
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
+
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
+
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
+
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
+}