254254const char * const fc0_ref_clk_name = "clk_slow_sys" ;
255255
256256#define ABS_DIFF (a , b ) ((a) > (b) ? (a) - (b) : (b) - (a))
257+ #define DIV_NEAREST (a , b ) (((a) + ((b) >> 1)) / (b))
257258#define DIV_U64_NEAREST (a , b ) div_u64(((a) + ((b) >> 1)), (b))
258259
259260/*
@@ -393,6 +394,18 @@ struct rp1_clock {
393394 unsigned long cached_rate ;
394395};
395396
397+
398+ struct rp1_clk_change {
399+ struct clk_hw * hw ;
400+ unsigned long new_rate ;
401+ };
402+
403+ struct rp1_clk_change rp1_clk_chg_tree [3 ];
404+
405+ static struct clk_hw * clk_xosc ;
406+ static struct clk_hw * clk_audio ;
407+ static struct clk_hw * clk_i2s ;
408+
396409static void rp1_debugfs_regset (struct rp1_clockman * clockman , u32 base ,
397410 const struct debugfs_reg32 * regs ,
398411 size_t nregs , struct dentry * dentry )
@@ -749,8 +762,12 @@ static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
749762static long rp1_pll_round_rate (struct clk_hw * hw , unsigned long rate ,
750763 unsigned long * parent_rate )
751764{
765+ const struct rp1_clk_change * chg = & rp1_clk_chg_tree [1 ];
752766 u32 div1 , div2 ;
753767
768+ if (chg -> hw == hw && chg -> new_rate == rate )
769+ * parent_rate = chg [1 ].new_rate ;
770+
754771 get_pll_prim_dividers (rate , * parent_rate , & div1 , & div2 );
755772
756773 return DIV_ROUND_CLOSEST (* parent_rate , div1 * div2 );
@@ -1188,6 +1205,59 @@ static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate,
11881205 return rp1_clock_set_rate_and_parent (hw , rate , parent_rate , 0xff );
11891206}
11901207
1208+ static unsigned long calc_core_pll_rate (struct clk_hw * pll_hw ,
1209+ unsigned long target_rate ,
1210+ int * pdiv_prim , int * pdiv_clk )
1211+ {
1212+ static const int prim_divs [] = {
1213+ 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 12 , 14 , 15 , 16 ,
1214+ 18 , 20 , 21 , 24 , 25 , 28 , 30 , 35 , 36 , 42 , 49 ,
1215+ };
1216+ const unsigned long xosc_rate = clk_hw_get_rate (clk_xosc );
1217+ const unsigned long core_max = 2400000000 ;
1218+ const unsigned long core_min = xosc_rate * 16 ;
1219+ unsigned long best_rate = core_max + 1 ;
1220+ int best_div_prim = 1 , best_div_clk = 1 ;
1221+ unsigned long core_rate = 0 ;
1222+ int div_int , div_frac ;
1223+ u64 div ;
1224+ int i ;
1225+
1226+ /* Given the target rate, choose a set of divisors/multipliers */
1227+ for (i = 0 ; i < ARRAY_SIZE (prim_divs ); i ++ ) {
1228+ int div_prim = prim_divs [i ];
1229+ int div_clk ;
1230+
1231+ for (div_clk = 1 ; div_clk <= 256 ; div_clk ++ ) {
1232+ core_rate = target_rate * div_clk * div_prim ;
1233+ if (core_rate >= core_min ) {
1234+ if (core_rate < best_rate ) {
1235+ best_rate = core_rate ;
1236+ best_div_prim = div_prim ;
1237+ best_div_clk = div_clk ;
1238+ }
1239+ break ;
1240+ }
1241+ }
1242+ }
1243+
1244+ if (best_rate < core_max ) {
1245+ div = ((best_rate << 24 ) + xosc_rate / 2 ) / xosc_rate ;
1246+ div_int = div >> 24 ;
1247+ div_frac = div % (1 << 24 );
1248+ core_rate = (xosc_rate * ((div_int << 24 ) + div_frac ) + (1 << 23 )) >> 24 ;
1249+ } else {
1250+ core_rate = 0 ;
1251+ }
1252+
1253+ if (pdiv_prim )
1254+ * pdiv_prim = best_div_prim ;
1255+ if (pdiv_clk )
1256+ * pdiv_clk = best_div_clk ;
1257+
1258+ return core_rate ;
1259+ }
1260+
11911261static void rp1_clock_choose_div_and_prate (struct clk_hw * hw ,
11921262 int parent_idx ,
11931263 unsigned long rate ,
@@ -1199,8 +1269,43 @@ static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
11991269 struct clk_hw * parent ;
12001270 u32 div ;
12011271 u64 tmp ;
1272+ int i ;
12021273
12031274 parent = clk_hw_get_parent_by_index (hw , parent_idx );
1275+
1276+ for (i = 0 ; i < ARRAY_SIZE (rp1_clk_chg_tree ); i ++ ) {
1277+ const struct rp1_clk_change * chg = & rp1_clk_chg_tree [i ];
1278+
1279+ if (chg -> hw == hw && chg -> new_rate == rate ) {
1280+ if (i == 2 )
1281+ * prate = clk_hw_get_rate (clk_xosc );
1282+ else if (parent == rp1_clk_chg_tree [i + 1 ].hw )
1283+ * prate = rp1_clk_chg_tree [i + 1 ].new_rate ;
1284+ else
1285+ continue ;
1286+ * calc_rate = chg -> new_rate ;
1287+ return ;
1288+ }
1289+ }
1290+
1291+ if (hw == clk_i2s && parent == clk_audio ) {
1292+ unsigned long core_rate , audio_rate , i2s_rate ;
1293+ int div_prim , div_clk ;
1294+
1295+ core_rate = calc_core_pll_rate (parent , rate , & div_prim , & div_clk );
1296+ audio_rate = DIV_NEAREST (core_rate , div_prim );
1297+ i2s_rate = DIV_NEAREST (audio_rate , div_clk );
1298+ rp1_clk_chg_tree [2 ].hw = clk_hw_get_parent (parent );
1299+ rp1_clk_chg_tree [2 ].new_rate = core_rate ;
1300+ rp1_clk_chg_tree [1 ].hw = clk_audio ;
1301+ rp1_clk_chg_tree [1 ].new_rate = audio_rate ;
1302+ rp1_clk_chg_tree [0 ].hw = clk_i2s ;
1303+ rp1_clk_chg_tree [0 ].new_rate = i2s_rate ;
1304+ * prate = audio_rate ;
1305+ * calc_rate = i2s_rate ;
1306+ return ;
1307+ }
1308+
12041309 * prate = clk_hw_get_rate (parent );
12051310 div = rp1_clock_choose_div (rate , * prate , data );
12061311
@@ -1608,6 +1713,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
16081713 .source_pll = "pll_audio_core" ,
16091714 .ctrl_reg = PLL_AUDIO_PRIM ,
16101715 .fc0_src = FC_NUM (4 , 2 ),
1716+ .flags = CLK_SET_RATE_PARENT ,
16111717 ),
16121718
16131719 [RP1_PLL_VIDEO ] = REGISTER_PLL (
@@ -1850,6 +1956,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
18501956 .div_int_max = DIV_INT_8BIT_MAX ,
18511957 .max_freq = 50 * MHz ,
18521958 .fc0_src = FC_NUM (4 , 4 ),
1959+ .flags = CLK_SET_RATE_PARENT ,
18531960 ),
18541961
18551962 [RP1_CLK_MIPI0_CFG ] = REGISTER_CLK (
@@ -2272,8 +2379,14 @@ static int rp1_clk_probe(struct platform_device *pdev)
22722379
22732380 for (i = 0 ; i < asize ; i ++ ) {
22742381 desc = & clk_desc_array [i ];
2275- if (desc -> clk_register && desc -> data )
2382+ if (desc -> clk_register && desc -> data ) {
22762383 hws [i ] = desc -> clk_register (clockman , desc -> data );
2384+ if (!strcmp (clk_hw_get_name (hws [i ]), "clk_i2s" )) {
2385+ clk_i2s = hws [i ];
2386+ clk_xosc = clk_hw_get_parent_by_index (clk_i2s , 0 );
2387+ clk_audio = clk_hw_get_parent_by_index (clk_i2s , 1 );
2388+ }
2389+ }
22772390 }
22782391
22792392 ret = of_clk_add_hw_provider (dev -> of_node , of_clk_hw_onecell_get ,
0 commit comments