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
225224static 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)
265264static 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
302299static 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
312331static 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
383402static 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
389413static 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