254
254
const char * const fc0_ref_clk_name = "clk_slow_sys" ;
255
255
256
256
#define ABS_DIFF (a , b ) ((a) > (b) ? (a) - (b) : (b) - (a))
257
+ #define DIV_NEAREST (a , b ) (((a) + ((b) >> 1)) / (b))
257
258
#define DIV_U64_NEAREST (a , b ) div_u64(((a) + ((b) >> 1)), (b))
258
259
259
260
/*
@@ -393,6 +394,18 @@ struct rp1_clock {
393
394
unsigned long cached_rate ;
394
395
};
395
396
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
+
396
409
static void rp1_debugfs_regset (struct rp1_clockman * clockman , u32 base ,
397
410
const struct debugfs_reg32 * regs ,
398
411
size_t nregs , struct dentry * dentry )
@@ -749,8 +762,12 @@ static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
749
762
static long rp1_pll_round_rate (struct clk_hw * hw , unsigned long rate ,
750
763
unsigned long * parent_rate )
751
764
{
765
+ const struct rp1_clk_change * chg = & rp1_clk_chg_tree [1 ];
752
766
u32 div1 , div2 ;
753
767
768
+ if (chg -> hw == hw && chg -> new_rate == rate )
769
+ * parent_rate = chg [1 ].new_rate ;
770
+
754
771
get_pll_prim_dividers (rate , * parent_rate , & div1 , & div2 );
755
772
756
773
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,
1188
1205
return rp1_clock_set_rate_and_parent (hw , rate , parent_rate , 0xff );
1189
1206
}
1190
1207
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
+
1191
1261
static void rp1_clock_choose_div_and_prate (struct clk_hw * hw ,
1192
1262
int parent_idx ,
1193
1263
unsigned long rate ,
@@ -1199,8 +1269,43 @@ static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
1199
1269
struct clk_hw * parent ;
1200
1270
u32 div ;
1201
1271
u64 tmp ;
1272
+ int i ;
1202
1273
1203
1274
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
+
1204
1309
* prate = clk_hw_get_rate (parent );
1205
1310
div = rp1_clock_choose_div (rate , * prate , data );
1206
1311
@@ -1608,6 +1713,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
1608
1713
.source_pll = "pll_audio_core" ,
1609
1714
.ctrl_reg = PLL_AUDIO_PRIM ,
1610
1715
.fc0_src = FC_NUM (4 , 2 ),
1716
+ .flags = CLK_SET_RATE_PARENT ,
1611
1717
),
1612
1718
1613
1719
[RP1_PLL_VIDEO ] = REGISTER_PLL (
@@ -1850,6 +1956,7 @@ static const struct rp1_clk_desc clk_desc_array[] = {
1850
1956
.div_int_max = DIV_INT_8BIT_MAX ,
1851
1957
.max_freq = 50 * MHz ,
1852
1958
.fc0_src = FC_NUM (4 , 4 ),
1959
+ .flags = CLK_SET_RATE_PARENT ,
1853
1960
),
1854
1961
1855
1962
[RP1_CLK_MIPI0_CFG ] = REGISTER_CLK (
@@ -2272,8 +2379,14 @@ static int rp1_clk_probe(struct platform_device *pdev)
2272
2379
2273
2380
for (i = 0 ; i < asize ; i ++ ) {
2274
2381
desc = & clk_desc_array [i ];
2275
- if (desc -> clk_register && desc -> data )
2382
+ if (desc -> clk_register && desc -> data ) {
2276
2383
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
+ }
2277
2390
}
2278
2391
2279
2392
ret = of_clk_add_hw_provider (dev -> of_node , of_clk_hw_onecell_get ,
0 commit comments