hc
2023-12-06 08f87f769b595151be1afeff53e144f543faa614
kernel/drivers/clk/sunxi-ng/ccu_mp.c
....@@ -1,14 +1,11 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Copyright (C) 2016 Maxime Ripard
34 * Maxime Ripard <maxime.ripard@free-electrons.com>
4
- *
5
- * This program is free software; you can redistribute it and/or
6
- * modify it under the terms of the GNU General Public License as
7
- * published by the Free Software Foundation; either version 2 of
8
- * the License, or (at your option) any later version.
95 */
106
117 #include <linux/clk-provider.h>
8
+#include <linux/io.h>
129
1310 #include "ccu_gate.h"
1411 #include "ccu_mp.h"
....@@ -40,6 +37,61 @@
4037 *p = best_p;
4138 }
4239
40
+static unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw,
41
+ unsigned long *parent,
42
+ unsigned long rate,
43
+ unsigned int max_m,
44
+ unsigned int max_p)
45
+{
46
+ unsigned long parent_rate_saved;
47
+ unsigned long parent_rate, now;
48
+ unsigned long best_rate = 0;
49
+ unsigned int _m, _p, div;
50
+ unsigned long maxdiv;
51
+
52
+ parent_rate_saved = *parent;
53
+
54
+ /*
55
+ * The maximum divider we can use without overflowing
56
+ * unsigned long in rate * m * p below
57
+ */
58
+ maxdiv = max_m * max_p;
59
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
60
+
61
+ for (_p = 1; _p <= max_p; _p <<= 1) {
62
+ for (_m = 1; _m <= max_m; _m++) {
63
+ div = _m * _p;
64
+
65
+ if (div > maxdiv)
66
+ break;
67
+
68
+ if (rate * div == parent_rate_saved) {
69
+ /*
70
+ * It's the most ideal case if the requested
71
+ * rate can be divided from parent clock without
72
+ * needing to change parent rate, so return the
73
+ * divider immediately.
74
+ */
75
+ *parent = parent_rate_saved;
76
+ return rate;
77
+ }
78
+
79
+ parent_rate = clk_hw_round_rate(hw, rate * div);
80
+ now = parent_rate / div;
81
+
82
+ if (now <= rate && now > best_rate) {
83
+ best_rate = now;
84
+ *parent = parent_rate;
85
+
86
+ if (now == rate)
87
+ return rate;
88
+ }
89
+ }
90
+ }
91
+
92
+ return best_rate;
93
+}
94
+
4395 static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
4496 struct clk_hw *hw,
4597 unsigned long *parent_rate,
....@@ -56,8 +108,13 @@
56108 max_m = cmp->m.max ?: 1 << cmp->m.width;
57109 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
58110
59
- ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p);
60
- rate = *parent_rate / p / m;
111
+ if (!clk_hw_can_set_rate_parent(&cmp->common.hw)) {
112
+ ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p);
113
+ rate = *parent_rate / p / m;
114
+ } else {
115
+ rate = ccu_mp_find_best_with_parent_adj(hw, parent_rate, rate,
116
+ max_m, max_p);
117
+ }
61118
62119 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
63120 rate /= cmp->fixed_post_div;