49
49
#define LOCKCLK_MASK LOCKCLK(0x3)
50
50
#define FRACNSRC_MASK (1 << 0)
51
51
#define FRACNSRC_STATIC (0 << 0)
52
- #define FRACNSRC_DYNAMIC (1 << 1 )
52
+ #define FRACNSRC_DYNAMIC (1 << 0 )
53
53
54
54
/* GLOBAL_CFG */
55
55
#define ENDEV2 (0x1)
@@ -79,6 +79,9 @@ struct cs2000_priv {
79
79
struct clk * clk_in ;
80
80
struct clk * ref_clk ;
81
81
82
+ bool dynamic_mode ;
83
+ bool lf_ratio ;
84
+
82
85
/* suspend/resume */
83
86
unsigned long saved_rate ;
84
87
unsigned long saved_parent_rate ;
@@ -134,17 +137,11 @@ static int cs2000_enable_dev_config(struct cs2000_priv *priv, bool enable)
134
137
if (ret < 0 )
135
138
return ret ;
136
139
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
-
143
140
return 0 ;
144
141
}
145
142
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 )
148
145
{
149
146
u32 val ;
150
147
@@ -191,35 +188,37 @@ static int cs2000_clk_out_enable(struct cs2000_priv *priv, bool enable)
191
188
(AUXOUTDIS | CLKOUTDIS ));
192
189
}
193
190
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 )
195
192
{
196
193
u64 ratio ;
194
+ u32 multiplier = lf_ratio ? 12 : 20 ;
197
195
198
196
/*
199
- * ratio = rate_out / rate_in * 2^20
197
+ * ratio = rate_out / rate_in * 2^multiplier
200
198
*
201
199
* To avoid over flow, rate_out is u64.
202
200
* The result should be u32.
203
201
*/
204
- ratio = (u64 )rate_out << 20 ;
202
+ ratio = (u64 )rate_out << multiplier ;
205
203
do_div (ratio , rate_in );
206
204
207
205
return ratio ;
208
206
}
209
207
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 )
211
209
{
212
210
u64 rate_out ;
211
+ u32 multiplier = lf_ratio ? 12 : 20 ;
213
212
214
213
/*
215
- * ratio = rate_out / rate_in * 2^20
214
+ * ratio = rate_out / rate_in * 2^multiplier
216
215
*
217
216
* To avoid over flow, rate_out is u64.
218
217
* The result should be u32 or unsigned long.
219
218
*/
220
219
221
220
rate_out = (u64 )ratio * rate_in ;
222
- return rate_out >> 20 ;
221
+ return rate_out >> multiplier ;
223
222
}
224
223
225
224
static int cs2000_ratio_set (struct cs2000_priv * priv ,
@@ -232,7 +231,7 @@ static int cs2000_ratio_set(struct cs2000_priv *priv,
232
231
if (CH_SIZE_ERR (ch ))
233
232
return - EINVAL ;
234
233
235
- val = cs2000_rate_to_ratio (rate_in , rate_out );
234
+ val = cs2000_rate_to_ratio (rate_in , rate_out , priv -> lf_ratio );
236
235
for (i = 0 ; i < RATIO_REG_SIZE ; i ++ ) {
237
236
ret = cs2000_write (priv ,
238
237
Ratio_Add (ch , i ),
@@ -265,22 +264,20 @@ static u32 cs2000_ratio_get(struct cs2000_priv *priv, int ch)
265
264
static int cs2000_ratio_select (struct cs2000_priv * priv , int ch )
266
265
{
267
266
int ret ;
267
+ u8 fracnsrc ;
268
268
269
269
if (CH_SIZE_ERR (ch ))
270
270
return - EINVAL ;
271
271
272
- /*
273
- * FIXME
274
- *
275
- * this driver supports static ratio mode only at this point.
276
- */
277
272
ret = cs2000_bset (priv , DEVICE_CFG1 , RSEL_MASK , RSEL (ch ));
278
273
if (ret < 0 )
279
274
return ret ;
280
275
276
+ fracnsrc = priv -> dynamic_mode ? FRACNSRC_DYNAMIC : FRACNSRC_STATIC ;
277
+
281
278
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 );
284
281
if (ret < 0 )
285
282
return ret ;
286
283
@@ -296,17 +293,39 @@ static unsigned long cs2000_recalc_rate(struct clk_hw *hw,
296
293
297
294
ratio = cs2000_ratio_get (priv , ch );
298
295
299
- return cs2000_ratio_to_rate (ratio , parent_rate );
296
+ return cs2000_ratio_to_rate (ratio , parent_rate , priv -> lf_ratio );
300
297
}
301
298
302
299
static long cs2000_round_rate (struct clk_hw * hw , unsigned long rate ,
303
300
unsigned long * parent_rate )
304
301
{
302
+ struct cs2000_priv * priv = hw_to_priv (hw );
305
303
u32 ratio ;
306
304
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 );
308
326
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 );
310
329
}
311
330
312
331
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,
315
334
{
316
335
int ret ;
317
336
318
- ret = cs2000_clk_in_bound_rate (priv , parent_rate );
337
+ ret = cs2000_select_ratio_mode (priv , rate , parent_rate );
319
338
if (ret < 0 )
320
339
return ret ;
321
340
@@ -382,8 +401,13 @@ static void cs2000_disable(struct clk_hw *hw)
382
401
383
402
static u8 cs2000_get_parent (struct clk_hw * hw )
384
403
{
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 ;
387
411
}
388
412
389
413
static const struct clk_ops cs2000_ops = {
@@ -424,28 +448,41 @@ static int cs2000_clk_register(struct cs2000_priv *priv)
424
448
const char * name = np -> name ;
425
449
static const char * parent_names [CLK_MAX ];
426
450
u32 aux_out = 0 ;
451
+ int ref_clk_rate ;
427
452
int ch = 0 ; /* it uses ch0 only at this point */
428
- int rate ;
429
453
int ret ;
430
454
431
455
of_property_read_string (np , "clock-output-names" , & name );
432
456
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
+
433
461
of_property_read_u32 (np , "cirrus,aux-output-source" , & aux_out );
434
462
ret = cs2000_bset (priv , DEVICE_CFG1 ,
435
463
AUXOUTSRC_MASK , AUXOUTSRC (aux_out ));
436
464
if (ret < 0 )
437
465
return ret ;
438
466
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 );
446
469
if (ret < 0 )
447
470
return ret ;
448
471
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
+
449
486
parent_names [CLK_IN ] = __clk_get_name (priv -> clk_in );
450
487
parent_names [REF_CLK ] = __clk_get_name (priv -> ref_clk );
451
488
0 commit comments