Skip to content

Commit 8289aaf

Browse files
jbrun3tsuperna9999
authored andcommitted
clk: meson: improve pll driver results with frac
Finding the appropriate settings of meson plls is too tricky to be done entirely at runtime, using calculation only. Many combination of m, n and od won't lock which is why we are using a table for this. However, for plls having a fractional parameters, it is possible to improve on the result provided by the table by calculating the frac parameter. This change adds the calculation of frac when the parameter is available and the rate provided by the table is not an exact match for the requested rate. Signed-off-by: Jerome Brunet <[email protected]> Signed-off-by: Neil Armstrong <[email protected]>
1 parent c178b00 commit 8289aaf

File tree

2 files changed

+91
-59
lines changed

2 files changed

+91
-59
lines changed

drivers/clk/meson/clk-pll.c

Lines changed: 90 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
* Copyright (c) 2015 Endless Mobile, Inc.
33
* Author: Carlo Caione <[email protected]>
44
*
5+
* Copyright (c) 2018 Baylibre, SAS.
6+
* Author: Jerome Brunet <[email protected]>
7+
*
58
* This program is free software; you can redistribute it and/or modify it
69
* under the terms and conditions of the GNU General Public License,
710
* version 2, as published by the Free Software Foundation.
@@ -27,7 +30,7 @@
2730
* | |
2831
* FREF VCO
2932
*
30-
* out = (in * M / N) >> OD
33+
* out = in * (m + frac / frac_max) / (n << sum(ods))
3134
*/
3235

3336
#include <linux/clk-provider.h>
@@ -48,73 +51,110 @@ meson_clk_pll_data(struct clk_regmap *clk)
4851
return (struct meson_clk_pll_data *)clk->data;
4952
}
5053

54+
static unsigned long __pll_params_to_rate(unsigned long parent_rate,
55+
const struct pll_rate_table *pllt,
56+
u16 frac,
57+
struct meson_clk_pll_data *pll)
58+
{
59+
u64 rate = (u64)parent_rate * pllt->m;
60+
unsigned int od = pllt->od + pllt->od2 + pllt->od3;
61+
62+
if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
63+
u64 frac_rate = (u64)parent_rate * frac;
64+
65+
rate += DIV_ROUND_UP_ULL(frac_rate,
66+
(1 << pll->frac.width));
67+
}
68+
69+
return DIV_ROUND_UP_ULL(rate, pllt->n << od);
70+
}
71+
5172
static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
5273
unsigned long parent_rate)
5374
{
5475
struct clk_regmap *clk = to_clk_regmap(hw);
5576
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
56-
u64 rate;
57-
u16 n, m, frac = 0, od, od2 = 0, od3 = 0;
58-
59-
n = meson_parm_read(clk->map, &pll->n);
60-
m = meson_parm_read(clk->map, &pll->m);
61-
od = meson_parm_read(clk->map, &pll->od);
62-
63-
if (MESON_PARM_APPLICABLE(&pll->od2))
64-
od2 = meson_parm_read(clk->map, &pll->od2);
77+
struct pll_rate_table pllt;
78+
u16 frac;
6579

66-
if (MESON_PARM_APPLICABLE(&pll->od3))
67-
od3 = meson_parm_read(clk->map, &pll->od3);
80+
pllt.n = meson_parm_read(clk->map, &pll->n);
81+
pllt.m = meson_parm_read(clk->map, &pll->m);
82+
pllt.od = meson_parm_read(clk->map, &pll->od);
6883

69-
rate = (u64)m * parent_rate;
84+
pllt.od2 = MESON_PARM_APPLICABLE(&pll->od2) ?
85+
meson_parm_read(clk->map, &pll->od2) :
86+
0;
7087

71-
if (MESON_PARM_APPLICABLE(&pll->frac)) {
72-
frac = meson_parm_read(clk->map, &pll->frac);
88+
pllt.od3 = MESON_PARM_APPLICABLE(&pll->od3) ?
89+
meson_parm_read(clk->map, &pll->od3) :
90+
0;
7391

74-
rate += mul_u64_u32_shr(parent_rate, frac, pll->frac.width);
75-
}
92+
frac = MESON_PARM_APPLICABLE(&pll->frac) ?
93+
meson_parm_read(clk->map, &pll->frac) :
94+
0;
7695

77-
return div_u64(rate, n) >> od >> od2 >> od3;
96+
return __pll_params_to_rate(parent_rate, &pllt, frac, pll);
7897
}
7998

80-
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
81-
unsigned long *parent_rate)
99+
static u16 __pll_params_with_frac(unsigned long rate,
100+
unsigned long parent_rate,
101+
const struct pll_rate_table *pllt,
102+
struct meson_clk_pll_data *pll)
82103
{
83-
struct clk_regmap *clk = to_clk_regmap(hw);
84-
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
85-
const struct pll_rate_table *pllt;
86-
87-
/*
88-
* if the table is missing, just return the current rate
89-
* since we don't have the other available frequencies
90-
*/
91-
if (!pll->table)
92-
return meson_clk_pll_recalc_rate(hw, *parent_rate);
104+
u16 frac_max = (1 << pll->frac.width);
105+
u64 val = (u64)rate * pllt->n;
93106

94-
for (pllt = pll->table; pllt->rate; pllt++) {
95-
if (rate <= pllt->rate)
96-
return pllt->rate;
97-
}
107+
val <<= pllt->od + pllt->od2 + pllt->od3;
108+
val = div_u64(val * frac_max, parent_rate);
109+
val -= pllt->m * frac_max;
98110

99-
/* else return the smallest value */
100-
return pll->table[0].rate;
111+
return min((u16)val, (u16)(frac_max - 1));
101112
}
102113

103114
static const struct pll_rate_table *
104-
meson_clk_get_pll_settings(const struct pll_rate_table *table,
105-
unsigned long rate)
115+
meson_clk_get_pll_settings(unsigned long rate,
116+
struct meson_clk_pll_data *pll)
106117
{
107-
const struct pll_rate_table *pllt;
118+
const struct pll_rate_table *table = pll->table;
119+
unsigned int i = 0;
108120

109121
if (!table)
110122
return NULL;
111123

112-
for (pllt = table; pllt->rate; pllt++) {
113-
if (rate == pllt->rate)
114-
return pllt;
115-
}
124+
/* Find the first table element exceeding rate */
125+
while (table[i].rate && table[i].rate <= rate)
126+
i++;
127+
128+
/* Select the setting of the rounded down rate */
129+
if (i != 0)
130+
i--;
131+
132+
return (struct pll_rate_table *)&table[i];
133+
}
134+
135+
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
136+
unsigned long *parent_rate)
137+
{
138+
struct clk_regmap *clk = to_clk_regmap(hw);
139+
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
140+
const struct pll_rate_table *pllt =
141+
meson_clk_get_pll_settings(rate, pll);
142+
u16 frac;
143+
144+
if (!pllt)
145+
return meson_clk_pll_recalc_rate(hw, *parent_rate);
146+
147+
if (!MESON_PARM_APPLICABLE(&pll->frac)
148+
|| rate == pllt->rate)
149+
return pllt->rate;
116150

117-
return NULL;
151+
/*
152+
* The rate provided by the setting is not an exact match, let's
153+
* try to improve the result using the fractional parameter
154+
*/
155+
frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll);
156+
157+
return __pll_params_to_rate(*parent_rate, pllt, frac, pll);
118158
}
119159

120160
static int meson_clk_pll_wait_lock(struct clk_hw *hw)
@@ -154,13 +194,14 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
154194
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
155195
const struct pll_rate_table *pllt;
156196
unsigned long old_rate;
197+
u16 frac = 0;
157198

158199
if (parent_rate == 0 || rate == 0)
159200
return -EINVAL;
160201

161202
old_rate = rate;
162203

163-
pllt = meson_clk_get_pll_settings(pll->table, rate);
204+
pllt = meson_clk_get_pll_settings(rate, pll);
164205
if (!pllt)
165206
return -EINVAL;
166207

@@ -177,8 +218,10 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
177218
if (MESON_PARM_APPLICABLE(&pll->od3))
178219
meson_parm_write(clk->map, &pll->od3, pllt->od3);
179220

180-
if (MESON_PARM_APPLICABLE(&pll->frac))
181-
meson_parm_write(clk->map, &pll->frac, pllt->frac);
221+
if (MESON_PARM_APPLICABLE(&pll->frac)) {
222+
frac = __pll_params_with_frac(rate, parent_rate, pllt, pll);
223+
meson_parm_write(clk->map, &pll->frac, frac);
224+
}
182225

183226
/* make sure the reset is cleared at this point */
184227
meson_parm_write(clk->map, &pll->rst, 0);

drivers/clk/meson/clkc.h

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ struct pll_rate_table {
6161
u16 od;
6262
u16 od2;
6363
u16 od3;
64-
u16 frac;
6564
};
6665

6766
#define PLL_RATE(_r, _m, _n, _od) \
@@ -70,17 +69,7 @@ struct pll_rate_table {
7069
.m = (_m), \
7170
.n = (_n), \
7271
.od = (_od), \
73-
} \
74-
75-
#define PLL_FRAC_RATE(_r, _m, _n, _od, _od2, _frac) \
76-
{ \
77-
.rate = (_r), \
78-
.m = (_m), \
79-
.n = (_n), \
80-
.od = (_od), \
81-
.od2 = (_od2), \
82-
.frac = (_frac), \
83-
} \
72+
}
8473

8574
struct meson_clk_pll_data {
8675
struct parm m;

0 commit comments

Comments
 (0)