Skip to content

Commit da1eb4e

Browse files
zonquebebarino
authored andcommitted
clk: cs2000-cp: add support for dynamic mode
The CS2000 chip features two input clocks, REF_CLK and CLK_IN. In static mode, the output clock (CLK_OUT) is directly derived from REF_CLK, and CLK_IN is ignored. In dynamic mode, CLK_IN is used by the digital PLL. In dynamic mode, a low-frequency ratio configuration that uses a higher multiplier factor. Until now, only the static mode and high-frequency divider rations of the hardware was supported by the driver. This patch adds support for dynamic mode and both ratios: * Parse a new OF property 'cirrus,dynamic-mode' to determine the mode * In dynamic mode, present CLK_IN as parent clock, else use REF_CLK * The low-frequency ratio mode is automatically selected, depending on the mode of operation and the given input and output rates Signed-off-by: Daniel Mack <daniel@zonque.org> Link: https://lore.kernel.org/r/20220125093336.226787-7-daniel@zonque.org Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent a6e11bb commit da1eb4e

File tree

1 file changed

+74
-37
lines changed

1 file changed

+74
-37
lines changed

drivers/clk/clk-cs2000-cp.c

Lines changed: 74 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
#define LOCKCLK_MASK LOCKCLK(0x3)
5050
#define FRACNSRC_MASK (1 << 0)
5151
#define FRACNSRC_STATIC (0 << 0)
52-
#define FRACNSRC_DYNAMIC (1 << 1)
52+
#define FRACNSRC_DYNAMIC (1 << 0)
5353

5454
/* GLOBAL_CFG */
5555
#define ENDEV2 (0x1)
@@ -79,6 +79,9 @@ struct cs2000_priv {
7979
struct clk *clk_in;
8080
struct clk *ref_clk;
8181

82+
bool dynamic_mode;
83+
bool lf_ratio;
84+
8285
/* suspend/resume */
8386
unsigned long saved_rate;
8487
unsigned long saved_parent_rate;
@@ -134,17 +137,11 @@ static int cs2000_enable_dev_config(struct cs2000_priv *priv, bool enable)
134137
if (ret < 0)
135138
return ret;
136139

137-
/* FIXME: for Static ratio mode */
138-
ret = cs2000_bset(priv, FUNC_CFG2, LFRATIO_MASK,
139-
LFRATIO_12_20);
140-
if (ret < 0)
141-
return ret;
142-
143140
return 0;
144141
}
145142

146-
static int cs2000_clk_in_bound_rate(struct cs2000_priv *priv,
147-
u32 rate_in)
143+
static int cs2000_ref_clk_bound_rate(struct cs2000_priv *priv,
144+
u32 rate_in)
148145
{
149146
u32 val;
150147

@@ -191,35 +188,37 @@ static int cs2000_clk_out_enable(struct cs2000_priv *priv, bool enable)
191188
(AUXOUTDIS | CLKOUTDIS));
192189
}
193190

194-
static u32 cs2000_rate_to_ratio(u32 rate_in, u32 rate_out)
191+
static u32 cs2000_rate_to_ratio(u32 rate_in, u32 rate_out, bool lf_ratio)
195192
{
196193
u64 ratio;
194+
u32 multiplier = lf_ratio ? 12 : 20;
197195

198196
/*
199-
* ratio = rate_out / rate_in * 2^20
197+
* ratio = rate_out / rate_in * 2^multiplier
200198
*
201199
* To avoid over flow, rate_out is u64.
202200
* The result should be u32.
203201
*/
204-
ratio = (u64)rate_out << 20;
202+
ratio = (u64)rate_out << multiplier;
205203
do_div(ratio, rate_in);
206204

207205
return ratio;
208206
}
209207

210-
static unsigned long cs2000_ratio_to_rate(u32 ratio, u32 rate_in)
208+
static unsigned long cs2000_ratio_to_rate(u32 ratio, u32 rate_in, bool lf_ratio)
211209
{
212210
u64 rate_out;
211+
u32 multiplier = lf_ratio ? 12 : 20;
213212

214213
/*
215-
* ratio = rate_out / rate_in * 2^20
214+
* ratio = rate_out / rate_in * 2^multiplier
216215
*
217216
* To avoid over flow, rate_out is u64.
218217
* The result should be u32 or unsigned long.
219218
*/
220219

221220
rate_out = (u64)ratio * rate_in;
222-
return rate_out >> 20;
221+
return rate_out >> multiplier;
223222
}
224223

225224
static int cs2000_ratio_set(struct cs2000_priv *priv,
@@ -232,7 +231,7 @@ static int cs2000_ratio_set(struct cs2000_priv *priv,
232231
if (CH_SIZE_ERR(ch))
233232
return -EINVAL;
234233

235-
val = cs2000_rate_to_ratio(rate_in, rate_out);
234+
val = cs2000_rate_to_ratio(rate_in, rate_out, priv->lf_ratio);
236235
for (i = 0; i < RATIO_REG_SIZE; i++) {
237236
ret = cs2000_write(priv,
238237
Ratio_Add(ch, i),
@@ -265,22 +264,20 @@ static u32 cs2000_ratio_get(struct cs2000_priv *priv, int ch)
265264
static int cs2000_ratio_select(struct cs2000_priv *priv, int ch)
266265
{
267266
int ret;
267+
u8 fracnsrc;
268268

269269
if (CH_SIZE_ERR(ch))
270270
return -EINVAL;
271271

272-
/*
273-
* FIXME
274-
*
275-
* this driver supports static ratio mode only at this point.
276-
*/
277272
ret = cs2000_bset(priv, DEVICE_CFG1, RSEL_MASK, RSEL(ch));
278273
if (ret < 0)
279274
return ret;
280275

276+
fracnsrc = priv->dynamic_mode ? FRACNSRC_DYNAMIC : FRACNSRC_STATIC;
277+
281278
ret = cs2000_bset(priv, DEVICE_CFG2,
282-
(AUTORMOD | LOCKCLK_MASK | FRACNSRC_MASK),
283-
(LOCKCLK(ch) | FRACNSRC_STATIC));
279+
AUTORMOD | LOCKCLK_MASK | FRACNSRC_MASK,
280+
LOCKCLK(ch) | fracnsrc);
284281
if (ret < 0)
285282
return ret;
286283

@@ -296,17 +293,39 @@ static unsigned long cs2000_recalc_rate(struct clk_hw *hw,
296293

297294
ratio = cs2000_ratio_get(priv, ch);
298295

299-
return cs2000_ratio_to_rate(ratio, parent_rate);
296+
return cs2000_ratio_to_rate(ratio, parent_rate, priv->lf_ratio);
300297
}
301298

302299
static long cs2000_round_rate(struct clk_hw *hw, unsigned long rate,
303300
unsigned long *parent_rate)
304301
{
302+
struct cs2000_priv *priv = hw_to_priv(hw);
305303
u32 ratio;
306304

307-
ratio = cs2000_rate_to_ratio(*parent_rate, rate);
305+
ratio = cs2000_rate_to_ratio(*parent_rate, rate, priv->lf_ratio);
306+
307+
return cs2000_ratio_to_rate(ratio, *parent_rate, priv->lf_ratio);
308+
}
309+
310+
static int cs2000_select_ratio_mode(struct cs2000_priv *priv,
311+
unsigned long rate,
312+
unsigned long parent_rate)
313+
{
314+
/*
315+
* From the datasheet:
316+
*
317+
* | It is recommended that the 12.20 High-Resolution format be
318+
* | utilized whenever the desired ratio is less than 4096 since
319+
* | the output frequency accuracy of the PLL is directly proportional
320+
* | to the accuracy of the timing reference clock and the resolution
321+
* | of the R_UD.
322+
*
323+
* This mode is only available in dynamic mode.
324+
*/
325+
priv->lf_ratio = priv->dynamic_mode && ((rate / parent_rate) > 4096);
308326

309-
return cs2000_ratio_to_rate(ratio, *parent_rate);
327+
return cs2000_bset(priv, FUNC_CFG2, LFRATIO_MASK,
328+
priv->lf_ratio ? LFRATIO_20_12 : LFRATIO_12_20);
310329
}
311330

312331
static int __cs2000_set_rate(struct cs2000_priv *priv, int ch,
@@ -315,7 +334,7 @@ static int __cs2000_set_rate(struct cs2000_priv *priv, int ch,
315334
{
316335
int ret;
317336

318-
ret = cs2000_clk_in_bound_rate(priv, parent_rate);
337+
ret = cs2000_select_ratio_mode(priv, rate, parent_rate);
319338
if (ret < 0)
320339
return ret;
321340

@@ -382,8 +401,13 @@ static void cs2000_disable(struct clk_hw *hw)
382401

383402
static u8 cs2000_get_parent(struct clk_hw *hw)
384403
{
385-
/* always return REF_CLK */
386-
return REF_CLK;
404+
struct cs2000_priv *priv = hw_to_priv(hw);
405+
406+
/*
407+
* In dynamic mode, output rates are derived from CLK_IN.
408+
* In static mode, CLK_IN is ignored, so we return REF_CLK instead.
409+
*/
410+
return priv->dynamic_mode ? CLK_IN : REF_CLK;
387411
}
388412

389413
static const struct clk_ops cs2000_ops = {
@@ -424,28 +448,41 @@ static int cs2000_clk_register(struct cs2000_priv *priv)
424448
const char *name = np->name;
425449
static const char *parent_names[CLK_MAX];
426450
u32 aux_out = 0;
451+
int ref_clk_rate;
427452
int ch = 0; /* it uses ch0 only at this point */
428-
int rate;
429453
int ret;
430454

431455
of_property_read_string(np, "clock-output-names", &name);
432456

457+
priv->dynamic_mode = of_property_read_bool(np, "cirrus,dynamic-mode");
458+
dev_info(dev, "operating in %s mode\n",
459+
priv->dynamic_mode ? "dynamic" : "static");
460+
433461
of_property_read_u32(np, "cirrus,aux-output-source", &aux_out);
434462
ret = cs2000_bset(priv, DEVICE_CFG1,
435463
AUXOUTSRC_MASK, AUXOUTSRC(aux_out));
436464
if (ret < 0)
437465
return ret;
438466

439-
/*
440-
* set default rate as 1/1.
441-
* otherwise .set_rate which setup ratio
442-
* is never called if user requests 1/1 rate
443-
*/
444-
rate = clk_get_rate(priv->ref_clk);
445-
ret = __cs2000_set_rate(priv, ch, rate, rate);
467+
ref_clk_rate = clk_get_rate(priv->ref_clk);
468+
ret = cs2000_ref_clk_bound_rate(priv, ref_clk_rate);
446469
if (ret < 0)
447470
return ret;
448471

472+
if (priv->dynamic_mode) {
473+
/* Default to low-frequency mode to allow for large ratios */
474+
priv->lf_ratio = true;
475+
} else {
476+
/*
477+
* set default rate as 1/1.
478+
* otherwise .set_rate which setup ratio
479+
* is never called if user requests 1/1 rate
480+
*/
481+
ret = __cs2000_set_rate(priv, ch, ref_clk_rate, ref_clk_rate);
482+
if (ret < 0)
483+
return ret;
484+
}
485+
449486
parent_names[CLK_IN] = __clk_get_name(priv->clk_in);
450487
parent_names[REF_CLK] = __clk_get_name(priv->ref_clk);
451488

0 commit comments

Comments
 (0)