5
5
6
6
#include <linux/clk-provider.h>
7
7
#include <linux/clkdev.h>
8
+ #include <linux/clk.h>
8
9
#include <linux/clk/at91_pmc.h>
9
10
#include <linux/of.h>
10
11
#include <linux/mfd/syscon.h>
@@ -36,8 +37,12 @@ struct clk_master {
36
37
u8 id ;
37
38
u8 parent ;
38
39
u8 div ;
40
+ u32 safe_div ;
39
41
};
40
42
43
+ /* MCK div reference to be used by notifier. */
44
+ static struct clk_master * master_div ;
45
+
41
46
static inline bool clk_master_ready (struct clk_master * master )
42
47
{
43
48
unsigned int bit = master -> id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY ;
@@ -153,121 +158,198 @@ static const struct clk_ops master_div_ops = {
153
158
.restore_context = clk_master_div_restore_context ,
154
159
};
155
160
156
- static int clk_master_div_set_rate (struct clk_hw * hw , unsigned long rate ,
157
- unsigned long parent_rate )
161
+ /* This function must be called with lock acquired. */
162
+ static int clk_master_div_set (struct clk_master * master ,
163
+ unsigned long parent_rate , int div )
158
164
{
159
- struct clk_master * master = to_clk_master (hw );
160
165
const struct clk_master_characteristics * characteristics =
161
166
master -> characteristics ;
162
- unsigned long flags ;
163
- unsigned int mckr , tmp ;
164
- int div , i ;
167
+ unsigned long rate = parent_rate ;
168
+ unsigned int max_div = 0 , div_index = 0 , max_div_index = 0 ;
169
+ unsigned int i , mckr , tmp ;
165
170
int ret ;
166
171
167
- div = DIV_ROUND_CLOSEST (parent_rate , rate );
168
- if (div > ARRAY_SIZE (characteristics -> divisors ))
169
- return - EINVAL ;
170
-
171
172
for (i = 0 ; i < ARRAY_SIZE (characteristics -> divisors ); i ++ ) {
172
173
if (!characteristics -> divisors [i ])
173
174
break ;
174
175
175
- if (div == characteristics -> divisors [i ]) {
176
- div = i ;
177
- break ;
176
+ if (div == characteristics -> divisors [i ])
177
+ div_index = i ;
178
+
179
+ if (max_div < characteristics -> divisors [i ]) {
180
+ max_div = characteristics -> divisors [i ];
181
+ max_div_index = i ;
178
182
}
179
183
}
180
184
181
- if (i == ARRAY_SIZE ( characteristics -> divisors ) )
182
- return - EINVAL ;
185
+ if (div > max_div )
186
+ div_index = max_div_index ;
183
187
184
- spin_lock_irqsave (master -> lock , flags );
185
188
ret = regmap_read (master -> regmap , master -> layout -> offset , & mckr );
186
189
if (ret )
187
- goto unlock ;
190
+ return ret ;
188
191
189
192
mckr &= master -> layout -> mask ;
190
193
tmp = (mckr >> MASTER_DIV_SHIFT ) & MASTER_DIV_MASK ;
191
- if (tmp == div )
192
- goto unlock ;
194
+ if (tmp == div_index )
195
+ return 0 ;
196
+
197
+ rate /= characteristics -> divisors [div_index ];
198
+ if (rate < characteristics -> output .min )
199
+ pr_warn ("master clk div is underclocked" );
200
+ else if (rate > characteristics -> output .max )
201
+ pr_warn ("master clk div is overclocked" );
193
202
194
203
mckr &= ~(MASTER_DIV_MASK << MASTER_DIV_SHIFT );
195
- mckr |= (div << MASTER_DIV_SHIFT );
204
+ mckr |= (div_index << MASTER_DIV_SHIFT );
196
205
ret = regmap_write (master -> regmap , master -> layout -> offset , mckr );
197
206
if (ret )
198
- goto unlock ;
207
+ return ret ;
199
208
200
209
while (!clk_master_ready (master ))
201
210
cpu_relax ();
202
- unlock :
203
- spin_unlock_irqrestore ( master -> lock , flags ) ;
211
+
212
+ master -> div = characteristics -> divisors [ div_index ] ;
204
213
205
214
return 0 ;
206
215
}
207
216
208
- static int clk_master_div_determine_rate (struct clk_hw * hw ,
209
- struct clk_rate_request * req )
217
+ static unsigned long clk_master_div_recalc_rate_chg (struct clk_hw * hw ,
218
+ unsigned long parent_rate )
210
219
{
211
220
struct clk_master * master = to_clk_master (hw );
212
- const struct clk_master_characteristics * characteristics =
213
- master -> characteristics ;
214
- struct clk_hw * parent ;
215
- unsigned long parent_rate , tmp_rate , best_rate = 0 ;
216
- int i , best_diff = INT_MIN , tmp_diff ;
217
-
218
- parent = clk_hw_get_parent (hw );
219
- if (!parent )
220
- return - EINVAL ;
221
-
222
- parent_rate = clk_hw_get_rate (parent );
223
- if (!parent_rate )
224
- return - EINVAL ;
225
-
226
- for (i = 0 ; i < ARRAY_SIZE (characteristics -> divisors ); i ++ ) {
227
- if (!characteristics -> divisors [i ])
228
- break ;
229
221
230
- tmp_rate = DIV_ROUND_CLOSEST_ULL (parent_rate ,
231
- characteristics -> divisors [i ]);
232
- tmp_diff = abs (tmp_rate - req -> rate );
233
-
234
- if (!best_rate || best_diff > tmp_diff ) {
235
- best_diff = tmp_diff ;
236
- best_rate = tmp_rate ;
237
- }
238
-
239
- if (!best_diff )
240
- break ;
241
- }
242
-
243
- req -> best_parent_rate = best_rate ;
244
- req -> best_parent_hw = parent ;
245
- req -> rate = best_rate ;
246
-
247
- return 0 ;
222
+ return DIV_ROUND_CLOSEST_ULL (parent_rate , master -> div );
248
223
}
249
224
250
225
static void clk_master_div_restore_context_chg (struct clk_hw * hw )
251
226
{
252
227
struct clk_master * master = to_clk_master (hw );
228
+ unsigned long flags ;
253
229
int ret ;
254
230
255
- ret = clk_master_div_set_rate (hw , master -> pms .rate ,
256
- master -> pms .parent_rate );
231
+ spin_lock_irqsave (master -> lock , flags );
232
+ ret = clk_master_div_set (master , master -> pms .parent_rate ,
233
+ DIV_ROUND_CLOSEST (master -> pms .parent_rate ,
234
+ master -> pms .rate ));
235
+ spin_unlock_irqrestore (master -> lock , flags );
257
236
if (ret )
258
237
pr_warn ("Failed to restore MCK DIV clock\n" );
259
238
}
260
239
261
240
static const struct clk_ops master_div_ops_chg = {
262
241
.prepare = clk_master_prepare ,
263
242
.is_prepared = clk_master_is_prepared ,
264
- .recalc_rate = clk_master_div_recalc_rate ,
265
- .determine_rate = clk_master_div_determine_rate ,
266
- .set_rate = clk_master_div_set_rate ,
243
+ .recalc_rate = clk_master_div_recalc_rate_chg ,
267
244
.save_context = clk_master_div_save_context ,
268
245
.restore_context = clk_master_div_restore_context_chg ,
269
246
};
270
247
248
+ static int clk_master_div_notifier_fn (struct notifier_block * notifier ,
249
+ unsigned long code , void * data )
250
+ {
251
+ const struct clk_master_characteristics * characteristics =
252
+ master_div -> characteristics ;
253
+ struct clk_notifier_data * cnd = data ;
254
+ unsigned long flags , new_parent_rate , new_rate ;
255
+ unsigned int mckr , div , new_div = 0 ;
256
+ int ret , i ;
257
+ long tmp_diff ;
258
+ long best_diff = -1 ;
259
+
260
+ spin_lock_irqsave (master_div -> lock , flags );
261
+ switch (code ) {
262
+ case PRE_RATE_CHANGE :
263
+ /*
264
+ * We want to avoid any overclocking of MCK DIV domain. To do
265
+ * this we set a safe divider (the underclocking is not of
266
+ * interest as we can go as low as 32KHz). The relation
267
+ * b/w this clock and its parents are as follows:
268
+ *
269
+ * FRAC PLL -> DIV PLL -> MCK DIV
270
+ *
271
+ * With the proper safe divider we should be good even with FRAC
272
+ * PLL at its maximum value.
273
+ */
274
+ ret = regmap_read (master_div -> regmap , master_div -> layout -> offset ,
275
+ & mckr );
276
+ if (ret ) {
277
+ ret = NOTIFY_STOP_MASK ;
278
+ goto unlock ;
279
+ }
280
+
281
+ mckr &= master_div -> layout -> mask ;
282
+ div = (mckr >> MASTER_DIV_SHIFT ) & MASTER_DIV_MASK ;
283
+
284
+ /* Switch to safe divider. */
285
+ clk_master_div_set (master_div ,
286
+ cnd -> old_rate * characteristics -> divisors [div ],
287
+ master_div -> safe_div );
288
+ break ;
289
+
290
+ case POST_RATE_CHANGE :
291
+ /*
292
+ * At this point we want to restore MCK DIV domain to its maximum
293
+ * allowed rate.
294
+ */
295
+ ret = regmap_read (master_div -> regmap , master_div -> layout -> offset ,
296
+ & mckr );
297
+ if (ret ) {
298
+ ret = NOTIFY_STOP_MASK ;
299
+ goto unlock ;
300
+ }
301
+
302
+ mckr &= master_div -> layout -> mask ;
303
+ div = (mckr >> MASTER_DIV_SHIFT ) & MASTER_DIV_MASK ;
304
+ new_parent_rate = cnd -> new_rate * characteristics -> divisors [div ];
305
+
306
+ for (i = 0 ; i < ARRAY_SIZE (characteristics -> divisors ); i ++ ) {
307
+ if (!characteristics -> divisors [i ])
308
+ break ;
309
+
310
+ new_rate = DIV_ROUND_CLOSEST_ULL (new_parent_rate ,
311
+ characteristics -> divisors [i ]);
312
+
313
+ tmp_diff = characteristics -> output .max - new_rate ;
314
+ if (tmp_diff < 0 )
315
+ continue ;
316
+
317
+ if (best_diff < 0 || best_diff > tmp_diff ) {
318
+ new_div = characteristics -> divisors [i ];
319
+ best_diff = tmp_diff ;
320
+ }
321
+
322
+ if (!tmp_diff )
323
+ break ;
324
+ }
325
+
326
+ if (!new_div ) {
327
+ ret = NOTIFY_STOP_MASK ;
328
+ goto unlock ;
329
+ }
330
+
331
+ /* Update the div to preserve MCK DIV clock rate. */
332
+ clk_master_div_set (master_div , new_parent_rate ,
333
+ new_div );
334
+
335
+ ret = NOTIFY_OK ;
336
+ break ;
337
+
338
+ default :
339
+ ret = NOTIFY_DONE ;
340
+ break ;
341
+ }
342
+
343
+ unlock :
344
+ spin_unlock_irqrestore (master_div -> lock , flags );
345
+
346
+ return ret ;
347
+ }
348
+
349
+ static struct notifier_block clk_master_div_notifier = {
350
+ .notifier_call = clk_master_div_notifier_fn ,
351
+ };
352
+
271
353
static void clk_sama7g5_master_best_diff (struct clk_rate_request * req ,
272
354
struct clk_hw * parent ,
273
355
unsigned long parent_rate ,
@@ -496,6 +578,8 @@ at91_clk_register_master_internal(struct regmap *regmap,
496
578
struct clk_master * master ;
497
579
struct clk_init_data init ;
498
580
struct clk_hw * hw ;
581
+ unsigned int mckr ;
582
+ unsigned long irqflags ;
499
583
int ret ;
500
584
501
585
if (!name || !num_parents || !parent_names || !lock )
@@ -518,6 +602,16 @@ at91_clk_register_master_internal(struct regmap *regmap,
518
602
master -> chg_pid = chg_pid ;
519
603
master -> lock = lock ;
520
604
605
+ if (ops == & master_div_ops_chg ) {
606
+ spin_lock_irqsave (master -> lock , irqflags );
607
+ regmap_read (master -> regmap , master -> layout -> offset , & mckr );
608
+ spin_unlock_irqrestore (master -> lock , irqflags );
609
+
610
+ mckr &= layout -> mask ;
611
+ mckr = (mckr >> MASTER_DIV_SHIFT ) & MASTER_DIV_MASK ;
612
+ master -> div = characteristics -> divisors [mckr ];
613
+ }
614
+
521
615
hw = & master -> hw ;
522
616
ret = clk_hw_register (NULL , & master -> hw );
523
617
if (ret ) {
@@ -554,19 +648,29 @@ at91_clk_register_master_div(struct regmap *regmap,
554
648
const char * name , const char * parent_name ,
555
649
const struct clk_master_layout * layout ,
556
650
const struct clk_master_characteristics * characteristics ,
557
- spinlock_t * lock , u32 flags )
651
+ spinlock_t * lock , u32 flags , u32 safe_div )
558
652
{
559
653
const struct clk_ops * ops ;
654
+ struct clk_hw * hw ;
560
655
561
656
if (flags & CLK_SET_RATE_GATE )
562
657
ops = & master_div_ops ;
563
658
else
564
659
ops = & master_div_ops_chg ;
565
660
566
- return at91_clk_register_master_internal (regmap , name , 1 ,
567
- & parent_name , layout ,
568
- characteristics , ops ,
569
- lock , flags , - EINVAL );
661
+ hw = at91_clk_register_master_internal (regmap , name , 1 ,
662
+ & parent_name , layout ,
663
+ characteristics , ops ,
664
+ lock , flags , - EINVAL );
665
+
666
+ if (!IS_ERR (hw ) && safe_div ) {
667
+ master_div = to_clk_master (hw );
668
+ master_div -> safe_div = safe_div ;
669
+ clk_notifier_register (hw -> clk ,
670
+ & clk_master_div_notifier );
671
+ }
672
+
673
+ return hw ;
570
674
}
571
675
572
676
static unsigned long
0 commit comments