Skip to content

Commit 4571780

Browse files
Andre-ARMwens
authored andcommitted
clk: sunxi-ng: mp: introduce dual-divider clock
The Allwinner A523 SoC introduces some new MP-style mod clock, where the second "P" divider is an actual numerical divider value, and not the numbers of bits to shift (1..32 instead of 1,2,4,8). The rest of the clock is the same as the existing MP clock, so enhance the existing code to accommodate for this. Introduce the new CCU feature bit CCU_FEATURE_DUAL_DIV to mark an MP clock as having two dividers, and change the dividing and encoding code to differentiate the two cases. Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Chen-Yu Tsai <wens@csie.org> Link: https://patch.msgid.link/20250307002628.10684-2-andre.przywara@arm.com Signed-off-by: Chen-Yu Tsai <wens@csie.org>
1 parent eb963d7 commit 4571780

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

drivers/clk/sunxi-ng/ccu_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7)
2020
#define CCU_FEATURE_KEY_FIELD BIT(8)
2121
#define CCU_FEATURE_CLOSEST_RATE BIT(9)
22+
#define CCU_FEATURE_DUAL_DIV BIT(10)
2223

2324
/* MMC timing mode switch bit */
2425
#define CCU_MMC_NEW_TIMING_MODE BIT(30)

drivers/clk/sunxi-ng/ccu_mp.c

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@
1010
#include "ccu_gate.h"
1111
#include "ccu_mp.h"
1212

13+
static unsigned int next_div(unsigned int div, bool shift)
14+
{
15+
if (shift)
16+
return div << 1;
17+
return div + 1;
18+
}
19+
1320
static unsigned long ccu_mp_find_best(unsigned long parent, unsigned long rate,
1421
unsigned int max_m, unsigned int max_p,
22+
bool shift,
1523
unsigned int *m, unsigned int *p)
1624
{
1725
unsigned long best_rate = 0;
1826
unsigned int best_m = 0, best_p = 0;
1927
unsigned int _m, _p;
2028

21-
for (_p = 1; _p <= max_p; _p <<= 1) {
29+
for (_p = 1; _p <= max_p; _p = next_div(_p, shift)) {
2230
for (_m = 1; _m <= max_m; _m++) {
2331
unsigned long tmp_rate = parent / _p / _m;
2432

@@ -43,7 +51,8 @@ static unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw,
4351
unsigned long *parent,
4452
unsigned long rate,
4553
unsigned int max_m,
46-
unsigned int max_p)
54+
unsigned int max_p,
55+
bool shift)
4756
{
4857
unsigned long parent_rate_saved;
4958
unsigned long parent_rate, now;
@@ -60,7 +69,7 @@ static unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw,
6069
maxdiv = max_m * max_p;
6170
maxdiv = min(ULONG_MAX / rate, maxdiv);
6271

63-
for (_p = 1; _p <= max_p; _p <<= 1) {
72+
for (_p = 1; _p <= max_p; _p = next_div(_p, shift)) {
6473
for (_m = 1; _m <= max_m; _m++) {
6574
div = _m * _p;
6675

@@ -103,18 +112,26 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
103112
struct ccu_mp *cmp = data;
104113
unsigned int max_m, max_p;
105114
unsigned int m, p;
115+
bool shift = true;
106116

107117
if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
108118
rate *= cmp->fixed_post_div;
109119

120+
if (cmp->common.features & CCU_FEATURE_DUAL_DIV)
121+
shift = false;
122+
110123
max_m = cmp->m.max ?: 1 << cmp->m.width;
111-
max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
124+
if (shift)
125+
max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
126+
else
127+
max_p = cmp->p.max ?: 1 << cmp->p.width;
112128

113129
if (!clk_hw_can_set_rate_parent(&cmp->common.hw)) {
114-
rate = ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p);
130+
rate = ccu_mp_find_best(*parent_rate, rate, max_m, max_p, shift,
131+
&m, &p);
115132
} else {
116133
rate = ccu_mp_find_best_with_parent_adj(hw, parent_rate, rate,
117-
max_m, max_p);
134+
max_m, max_p, shift);
118135
}
119136

120137
if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
@@ -167,7 +184,11 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw,
167184
p = reg >> cmp->p.shift;
168185
p &= (1 << cmp->p.width) - 1;
169186

170-
rate = (parent_rate >> p) / m;
187+
if (cmp->common.features & CCU_FEATURE_DUAL_DIV)
188+
rate = (parent_rate / p) / m;
189+
else
190+
rate = (parent_rate >> p) / m;
191+
171192
if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
172193
rate /= cmp->fixed_post_div;
173194

@@ -190,28 +211,38 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
190211
unsigned long flags;
191212
unsigned int max_m, max_p;
192213
unsigned int m, p;
214+
bool shift = true;
193215
u32 reg;
194216

217+
if (cmp->common.features & CCU_FEATURE_DUAL_DIV)
218+
shift = false;
219+
195220
/* Adjust parent_rate according to pre-dividers */
196221
parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1,
197222
parent_rate);
198223

199224
max_m = cmp->m.max ?: 1 << cmp->m.width;
200-
max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
225+
if (shift)
226+
max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
227+
else
228+
max_p = cmp->p.max ?: 1 << cmp->p.width;
201229

202230
/* Adjust target rate according to post-dividers */
203231
if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
204232
rate = rate * cmp->fixed_post_div;
205233

206-
ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
234+
ccu_mp_find_best(parent_rate, rate, max_m, max_p, shift, &m, &p);
207235

208236
spin_lock_irqsave(cmp->common.lock, flags);
209237

210238
reg = readl(cmp->common.base + cmp->common.reg);
211239
reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
212240
reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
213241
reg |= (m - cmp->m.offset) << cmp->m.shift;
214-
reg |= ilog2(p) << cmp->p.shift;
242+
if (shift)
243+
reg |= ilog2(p) << cmp->p.shift;
244+
else
245+
reg |= (p - cmp->p.offset) << cmp->p.shift;
215246

216247
writel(reg, cmp->common.base + cmp->common.reg);
217248

0 commit comments

Comments
 (0)