~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-omap1/clock.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  *  linux/arch/arm/mach-omap1/clock.c
  4  *
  5  *  Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation
  6  *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
  7  *
  8  *  Modified to use omap shared clock framework by
  9  *  Tony Lindgren <tony@atomide.com>
 10  */
 11 #include <linux/kernel.h>
 12 #include <linux/export.h>
 13 #include <linux/list.h>
 14 #include <linux/errno.h>
 15 #include <linux/err.h>
 16 #include <linux/io.h>
 17 #include <linux/clk.h>
 18 #include <linux/clkdev.h>
 19 #include <linux/clk-provider.h>
 20 #include <linux/soc/ti/omap1-io.h>
 21 #include <linux/spinlock.h>
 22 
 23 #include <asm/mach-types.h>
 24 
 25 #include "hardware.h"
 26 #include "soc.h"
 27 #include "iomap.h"
 28 #include "clock.h"
 29 #include "opp.h"
 30 #include "sram.h"
 31 
 32 __u32 arm_idlect1_mask;
 33 /* provide direct internal access (not via clk API) to some clocks */
 34 struct omap1_clk *api_ck_p, *ck_dpll1_p, *ck_ref_p;
 35 
 36 /* protect registeres shared among clk_enable/disable() and clk_set_rate() operations */
 37 static DEFINE_SPINLOCK(arm_ckctl_lock);
 38 static DEFINE_SPINLOCK(arm_idlect2_lock);
 39 static DEFINE_SPINLOCK(mod_conf_ctrl_0_lock);
 40 static DEFINE_SPINLOCK(mod_conf_ctrl_1_lock);
 41 static DEFINE_SPINLOCK(swd_clk_div_ctrl_sel_lock);
 42 
 43 /*
 44  * Omap1 specific clock functions
 45  */
 46 
 47 unsigned long omap1_uart_recalc(struct omap1_clk *clk, unsigned long p_rate)
 48 {
 49         unsigned int val = __raw_readl(clk->enable_reg);
 50         return val & 1 << clk->enable_bit ? 48000000 : 12000000;
 51 }
 52 
 53 unsigned long omap1_sossi_recalc(struct omap1_clk *clk, unsigned long p_rate)
 54 {
 55         u32 div = omap_readl(MOD_CONF_CTRL_1);
 56 
 57         div = (div >> 17) & 0x7;
 58         div++;
 59 
 60         return p_rate / div;
 61 }
 62 
 63 static void omap1_clk_allow_idle(struct omap1_clk *clk)
 64 {
 65         struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk;
 66 
 67         if (!(clk->flags & CLOCK_IDLE_CONTROL))
 68                 return;
 69 
 70         if (iclk->no_idle_count > 0 && !(--iclk->no_idle_count))
 71                 arm_idlect1_mask |= 1 << iclk->idlect_shift;
 72 }
 73 
 74 static void omap1_clk_deny_idle(struct omap1_clk *clk)
 75 {
 76         struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk;
 77 
 78         if (!(clk->flags & CLOCK_IDLE_CONTROL))
 79                 return;
 80 
 81         if (iclk->no_idle_count++ == 0)
 82                 arm_idlect1_mask &= ~(1 << iclk->idlect_shift);
 83 }
 84 
 85 static __u16 verify_ckctl_value(__u16 newval)
 86 {
 87         /* This function checks for following limitations set
 88          * by the hardware (all conditions must be true):
 89          * DSPMMU_CK == DSP_CK  or  DSPMMU_CK == DSP_CK/2
 90          * ARM_CK >= TC_CK
 91          * DSP_CK >= TC_CK
 92          * DSPMMU_CK >= TC_CK
 93          *
 94          * In addition following rules are enforced:
 95          * LCD_CK <= TC_CK
 96          * ARMPER_CK <= TC_CK
 97          *
 98          * However, maximum frequencies are not checked for!
 99          */
100         __u8 per_exp;
101         __u8 lcd_exp;
102         __u8 arm_exp;
103         __u8 dsp_exp;
104         __u8 tc_exp;
105         __u8 dspmmu_exp;
106 
107         per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3;
108         lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3;
109         arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3;
110         dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3;
111         tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3;
112         dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3;
113 
114         if (dspmmu_exp < dsp_exp)
115                 dspmmu_exp = dsp_exp;
116         if (dspmmu_exp > dsp_exp+1)
117                 dspmmu_exp = dsp_exp+1;
118         if (tc_exp < arm_exp)
119                 tc_exp = arm_exp;
120         if (tc_exp < dspmmu_exp)
121                 tc_exp = dspmmu_exp;
122         if (tc_exp > lcd_exp)
123                 lcd_exp = tc_exp;
124         if (tc_exp > per_exp)
125                 per_exp = tc_exp;
126 
127         newval &= 0xf000;
128         newval |= per_exp << CKCTL_PERDIV_OFFSET;
129         newval |= lcd_exp << CKCTL_LCDDIV_OFFSET;
130         newval |= arm_exp << CKCTL_ARMDIV_OFFSET;
131         newval |= dsp_exp << CKCTL_DSPDIV_OFFSET;
132         newval |= tc_exp << CKCTL_TCDIV_OFFSET;
133         newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET;
134 
135         return newval;
136 }
137 
138 static int calc_dsor_exp(unsigned long rate, unsigned long realrate)
139 {
140         /* Note: If target frequency is too low, this function will return 4,
141          * which is invalid value. Caller must check for this value and act
142          * accordingly.
143          *
144          * Note: This function does not check for following limitations set
145          * by the hardware (all conditions must be true):
146          * DSPMMU_CK == DSP_CK  or  DSPMMU_CK == DSP_CK/2
147          * ARM_CK >= TC_CK
148          * DSP_CK >= TC_CK
149          * DSPMMU_CK >= TC_CK
150          */
151         unsigned  dsor_exp;
152 
153         if (unlikely(realrate == 0))
154                 return -EIO;
155 
156         for (dsor_exp=0; dsor_exp<4; dsor_exp++) {
157                 if (realrate <= rate)
158                         break;
159 
160                 realrate /= 2;
161         }
162 
163         return dsor_exp;
164 }
165 
166 unsigned long omap1_ckctl_recalc(struct omap1_clk *clk, unsigned long p_rate)
167 {
168         /* Calculate divisor encoded as 2-bit exponent */
169         int dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));
170 
171         /* update locally maintained rate, required by arm_ck for omap1_show_rates() */
172         clk->rate = p_rate / dsor;
173         return clk->rate;
174 }
175 
176 static int omap1_clk_is_enabled(struct clk_hw *hw)
177 {
178         struct omap1_clk *clk = to_omap1_clk(hw);
179         bool api_ck_was_enabled = true;
180         __u32 regval32;
181         int ret;
182 
183         if (!clk->ops)  /* no gate -- always enabled */
184                 return 1;
185 
186         if (clk->ops == &clkops_dspck) {
187                 api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
188                 if (!api_ck_was_enabled)
189                         if (api_ck_p->ops->enable(api_ck_p) < 0)
190                                 return 0;
191         }
192 
193         if (clk->flags & ENABLE_REG_32BIT)
194                 regval32 = __raw_readl(clk->enable_reg);
195         else
196                 regval32 = __raw_readw(clk->enable_reg);
197 
198         ret = regval32 & (1 << clk->enable_bit);
199 
200         if (!api_ck_was_enabled)
201                 api_ck_p->ops->disable(api_ck_p);
202 
203         return ret;
204 }
205 
206 
207 unsigned long omap1_ckctl_recalc_dsp_domain(struct omap1_clk *clk, unsigned long p_rate)
208 {
209         bool api_ck_was_enabled;
210         int dsor;
211 
212         /* Calculate divisor encoded as 2-bit exponent
213          *
214          * The clock control bits are in DSP domain,
215          * so api_ck is needed for access.
216          * Note that DSP_CKCTL virt addr = phys addr, so
217          * we must use __raw_readw() instead of omap_readw().
218          */
219         api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
220         if (!api_ck_was_enabled)
221                 api_ck_p->ops->enable(api_ck_p);
222         dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset));
223         if (!api_ck_was_enabled)
224                 api_ck_p->ops->disable(api_ck_p);
225 
226         return p_rate / dsor;
227 }
228 
229 /* MPU virtual clock functions */
230 int omap1_select_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
231 {
232         /* Find the highest supported frequency <= rate and switch to it */
233         struct mpu_rate * ptr;
234         unsigned long ref_rate;
235 
236         ref_rate = ck_ref_p->rate;
237 
238         for (ptr = omap1_rate_table; ptr->rate; ptr++) {
239                 if (!(ptr->flags & cpu_mask))
240                         continue;
241 
242                 if (ptr->xtal != ref_rate)
243                         continue;
244 
245                 /* Can check only after xtal frequency check */
246                 if (ptr->rate <= rate)
247                         break;
248         }
249 
250         if (!ptr->rate)
251                 return -EINVAL;
252 
253         /*
254          * In most cases we should not need to reprogram DPLL.
255          * Reprogramming the DPLL is tricky, it must be done from SRAM.
256          */
257         omap_sram_reprogram_clock(ptr->dpllctl_val, ptr->ckctl_val);
258 
259         /* XXX Do we need to recalculate the tree below DPLL1 at this point? */
260         ck_dpll1_p->rate = ptr->pll_rate;
261 
262         return 0;
263 }
264 
265 int omap1_clk_set_rate_dsp_domain(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
266 {
267         int dsor_exp;
268         u16 regval;
269 
270         dsor_exp = calc_dsor_exp(rate, p_rate);
271         if (dsor_exp > 3)
272                 dsor_exp = -EINVAL;
273         if (dsor_exp < 0)
274                 return dsor_exp;
275 
276         regval = __raw_readw(DSP_CKCTL);
277         regval &= ~(3 << clk->rate_offset);
278         regval |= dsor_exp << clk->rate_offset;
279         __raw_writew(regval, DSP_CKCTL);
280         clk->rate = p_rate / (1 << dsor_exp);
281 
282         return 0;
283 }
284 
285 long omap1_clk_round_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate,
286                                     unsigned long *p_rate)
287 {
288         int dsor_exp = calc_dsor_exp(rate, *p_rate);
289 
290         if (dsor_exp < 0)
291                 return dsor_exp;
292         if (dsor_exp > 3)
293                 dsor_exp = 3;
294         return *p_rate / (1 << dsor_exp);
295 }
296 
297 int omap1_clk_set_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
298 {
299         unsigned long flags;
300         int dsor_exp;
301         u16 regval;
302 
303         dsor_exp = calc_dsor_exp(rate, p_rate);
304         if (dsor_exp > 3)
305                 dsor_exp = -EINVAL;
306         if (dsor_exp < 0)
307                 return dsor_exp;
308 
309         /* protect ARM_CKCTL register from concurrent access via clk_enable/disable() */
310         spin_lock_irqsave(&arm_ckctl_lock, flags);
311 
312         regval = omap_readw(ARM_CKCTL);
313         regval &= ~(3 << clk->rate_offset);
314         regval |= dsor_exp << clk->rate_offset;
315         regval = verify_ckctl_value(regval);
316         omap_writew(regval, ARM_CKCTL);
317         clk->rate = p_rate / (1 << dsor_exp);
318 
319         spin_unlock_irqrestore(&arm_ckctl_lock, flags);
320 
321         return 0;
322 }
323 
324 long omap1_round_to_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
325 {
326         /* Find the highest supported frequency <= rate */
327         struct mpu_rate * ptr;
328         long highest_rate;
329         unsigned long ref_rate;
330 
331         ref_rate = ck_ref_p->rate;
332 
333         highest_rate = -EINVAL;
334 
335         for (ptr = omap1_rate_table; ptr->rate; ptr++) {
336                 if (!(ptr->flags & cpu_mask))
337                         continue;
338 
339                 if (ptr->xtal != ref_rate)
340                         continue;
341 
342                 highest_rate = ptr->rate;
343 
344                 /* Can check only after xtal frequency check */
345                 if (ptr->rate <= rate)
346                         break;
347         }
348 
349         return highest_rate;
350 }
351 
352 static unsigned calc_ext_dsor(unsigned long rate)
353 {
354         unsigned dsor;
355 
356         /* MCLK and BCLK divisor selection is not linear:
357          * freq = 96MHz / dsor
358          *
359          * RATIO_SEL range: dsor <-> RATIO_SEL
360          * 0..6: (RATIO_SEL+2) <-> (dsor-2)
361          * 6..48:  (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6)
362          * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9
363          * can not be used.
364          */
365         for (dsor = 2; dsor < 96; ++dsor) {
366                 if ((dsor & 1) && dsor > 8)
367                         continue;
368                 if (rate >= 96000000 / dsor)
369                         break;
370         }
371         return dsor;
372 }
373 
374 /* XXX Only needed on 1510 */
375 long omap1_round_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
376 {
377         return rate > 24000000 ? 48000000 : 12000000;
378 }
379 
380 int omap1_set_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
381 {
382         unsigned long flags;
383         unsigned int val;
384 
385         if (rate == 12000000)
386                 val = 0;
387         else if (rate == 48000000)
388                 val = 1 << clk->enable_bit;
389         else
390                 return -EINVAL;
391 
392         /* protect MOD_CONF_CTRL_0 register from concurrent access via clk_enable/disable() */
393         spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags);
394 
395         val |= __raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit);
396         __raw_writel(val, clk->enable_reg);
397 
398         spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags);
399 
400         clk->rate = rate;
401 
402         return 0;
403 }
404 
405 /* External clock (MCLK & BCLK) functions */
406 int omap1_set_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
407 {
408         unsigned long flags;
409         unsigned dsor;
410         __u16 ratio_bits;
411 
412         dsor = calc_ext_dsor(rate);
413         clk->rate = 96000000 / dsor;
414         if (dsor > 8)
415                 ratio_bits = ((dsor - 8) / 2 + 6) << 2;
416         else
417                 ratio_bits = (dsor - 2) << 2;
418 
419         /* protect SWD_CLK_DIV_CTRL_SEL register from concurrent access via clk_enable/disable() */
420         spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags);
421 
422         ratio_bits |= __raw_readw(clk->enable_reg) & ~0xfd;
423         __raw_writew(ratio_bits, clk->enable_reg);
424 
425         spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags);
426 
427         return 0;
428 }
429 
430 static int calc_div_sossi(unsigned long rate, unsigned long p_rate)
431 {
432         int div;
433 
434         /* Round towards slower frequency */
435         div = (p_rate + rate - 1) / rate;
436 
437         return --div;
438 }
439 
440 long omap1_round_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
441 {
442         int div;
443 
444         div = calc_div_sossi(rate, *p_rate);
445         if (div < 0)
446                 div = 0;
447         else if (div > 7)
448                 div = 7;
449 
450         return *p_rate / (div + 1);
451 }
452 
453 int omap1_set_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
454 {
455         unsigned long flags;
456         u32 l;
457         int div;
458 
459         div = calc_div_sossi(rate, p_rate);
460         if (div < 0 || div > 7)
461                 return -EINVAL;
462 
463         /* protect MOD_CONF_CTRL_1 register from concurrent access via clk_enable/disable() */
464         spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags);
465 
466         l = omap_readl(MOD_CONF_CTRL_1);
467         l &= ~(7 << 17);
468         l |= div << 17;
469         omap_writel(l, MOD_CONF_CTRL_1);
470 
471         clk->rate = p_rate / (div + 1);
472 
473         spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags);
474 
475         return 0;
476 }
477 
478 long omap1_round_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
479 {
480         return 96000000 / calc_ext_dsor(rate);
481 }
482 
483 int omap1_init_ext_clk(struct omap1_clk *clk)
484 {
485         unsigned dsor;
486         __u16 ratio_bits;
487 
488         /* Determine current rate and ensure clock is based on 96MHz APLL */
489         ratio_bits = __raw_readw(clk->enable_reg) & ~1;
490         __raw_writew(ratio_bits, clk->enable_reg);
491 
492         ratio_bits = (ratio_bits & 0xfc) >> 2;
493         if (ratio_bits > 6)
494                 dsor = (ratio_bits - 6) * 2 + 8;
495         else
496                 dsor = ratio_bits + 2;
497 
498         clk-> rate = 96000000 / dsor;
499 
500         return 0;
501 }
502 
503 static int omap1_clk_enable(struct clk_hw *hw)
504 {
505         struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw));
506         int ret = 0;
507 
508         if (parent && clk->flags & CLOCK_NO_IDLE_PARENT)
509                 omap1_clk_deny_idle(parent);
510 
511         if (clk->ops && !(WARN_ON(!clk->ops->enable)))
512                 ret = clk->ops->enable(clk);
513 
514         return ret;
515 }
516 
517 static void omap1_clk_disable(struct clk_hw *hw)
518 {
519         struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw));
520 
521         if (clk->ops && !(WARN_ON(!clk->ops->disable)))
522                 clk->ops->disable(clk);
523 
524         if (likely(parent) && clk->flags & CLOCK_NO_IDLE_PARENT)
525                 omap1_clk_allow_idle(parent);
526 }
527 
528 static int omap1_clk_enable_generic(struct omap1_clk *clk)
529 {
530         unsigned long flags;
531         __u16 regval16;
532         __u32 regval32;
533 
534         if (unlikely(clk->enable_reg == NULL)) {
535                 printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
536                        clk_hw_get_name(&clk->hw));
537                 return -EINVAL;
538         }
539 
540         /* protect clk->enable_reg from concurrent access via clk_set_rate() */
541         if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
542                 spin_lock_irqsave(&arm_ckctl_lock, flags);
543         else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
544                 spin_lock_irqsave(&arm_idlect2_lock, flags);
545         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
546                 spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags);
547         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
548                 spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags);
549         else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
550                 spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags);
551 
552         if (clk->flags & ENABLE_REG_32BIT) {
553                 regval32 = __raw_readl(clk->enable_reg);
554                 regval32 |= (1 << clk->enable_bit);
555                 __raw_writel(regval32, clk->enable_reg);
556         } else {
557                 regval16 = __raw_readw(clk->enable_reg);
558                 regval16 |= (1 << clk->enable_bit);
559                 __raw_writew(regval16, clk->enable_reg);
560         }
561 
562         if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
563                 spin_unlock_irqrestore(&arm_ckctl_lock, flags);
564         else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
565                 spin_unlock_irqrestore(&arm_idlect2_lock, flags);
566         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
567                 spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags);
568         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
569                 spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags);
570         else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
571                 spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags);
572 
573         return 0;
574 }
575 
576 static void omap1_clk_disable_generic(struct omap1_clk *clk)
577 {
578         unsigned long flags;
579         __u16 regval16;
580         __u32 regval32;
581 
582         if (clk->enable_reg == NULL)
583                 return;
584 
585         /* protect clk->enable_reg from concurrent access via clk_set_rate() */
586         if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
587                 spin_lock_irqsave(&arm_ckctl_lock, flags);
588         else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
589                 spin_lock_irqsave(&arm_idlect2_lock, flags);
590         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
591                 spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags);
592         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
593                 spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags);
594         else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
595                 spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags);
596 
597         if (clk->flags & ENABLE_REG_32BIT) {
598                 regval32 = __raw_readl(clk->enable_reg);
599                 regval32 &= ~(1 << clk->enable_bit);
600                 __raw_writel(regval32, clk->enable_reg);
601         } else {
602                 regval16 = __raw_readw(clk->enable_reg);
603                 regval16 &= ~(1 << clk->enable_bit);
604                 __raw_writew(regval16, clk->enable_reg);
605         }
606 
607         if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
608                 spin_unlock_irqrestore(&arm_ckctl_lock, flags);
609         else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
610                 spin_unlock_irqrestore(&arm_idlect2_lock, flags);
611         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
612                 spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags);
613         else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
614                 spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags);
615         else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
616                 spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags);
617 }
618 
619 const struct clkops clkops_generic = {
620         .enable         = omap1_clk_enable_generic,
621         .disable        = omap1_clk_disable_generic,
622 };
623 
624 static int omap1_clk_enable_dsp_domain(struct omap1_clk *clk)
625 {
626         bool api_ck_was_enabled;
627         int retval = 0;
628 
629         api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
630         if (!api_ck_was_enabled)
631                 retval = api_ck_p->ops->enable(api_ck_p);
632 
633         if (!retval) {
634                 retval = omap1_clk_enable_generic(clk);
635 
636                 if (!api_ck_was_enabled)
637                         api_ck_p->ops->disable(api_ck_p);
638         }
639 
640         return retval;
641 }
642 
643 static void omap1_clk_disable_dsp_domain(struct omap1_clk *clk)
644 {
645         bool api_ck_was_enabled;
646 
647         api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
648         if (!api_ck_was_enabled)
649                 if (api_ck_p->ops->enable(api_ck_p) < 0)
650                         return;
651 
652         omap1_clk_disable_generic(clk);
653 
654         if (!api_ck_was_enabled)
655                 api_ck_p->ops->disable(api_ck_p);
656 }
657 
658 const struct clkops clkops_dspck = {
659         .enable         = omap1_clk_enable_dsp_domain,
660         .disable        = omap1_clk_disable_dsp_domain,
661 };
662 
663 /* XXX SYSC register handling does not belong in the clock framework */
664 static int omap1_clk_enable_uart_functional_16xx(struct omap1_clk *clk)
665 {
666         int ret;
667         struct uart_clk *uclk;
668 
669         ret = omap1_clk_enable_generic(clk);
670         if (ret == 0) {
671                 /* Set smart idle acknowledgement mode */
672                 uclk = (struct uart_clk *)clk;
673                 omap_writeb((omap_readb(uclk->sysc_addr) & ~0x10) | 8,
674                             uclk->sysc_addr);
675         }
676 
677         return ret;
678 }
679 
680 /* XXX SYSC register handling does not belong in the clock framework */
681 static void omap1_clk_disable_uart_functional_16xx(struct omap1_clk *clk)
682 {
683         struct uart_clk *uclk;
684 
685         /* Set force idle acknowledgement mode */
686         uclk = (struct uart_clk *)clk;
687         omap_writeb((omap_readb(uclk->sysc_addr) & ~0x18), uclk->sysc_addr);
688 
689         omap1_clk_disable_generic(clk);
690 }
691 
692 /* XXX SYSC register handling does not belong in the clock framework */
693 const struct clkops clkops_uart_16xx = {
694         .enable         = omap1_clk_enable_uart_functional_16xx,
695         .disable        = omap1_clk_disable_uart_functional_16xx,
696 };
697 
698 static unsigned long omap1_clk_recalc_rate(struct clk_hw *hw, unsigned long p_rate)
699 {
700         struct omap1_clk *clk = to_omap1_clk(hw);
701 
702         if (clk->recalc)
703                 return clk->recalc(clk, p_rate);
704 
705         return clk->rate;
706 }
707 
708 static long omap1_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *p_rate)
709 {
710         struct omap1_clk *clk = to_omap1_clk(hw);
711 
712         if (clk->round_rate != NULL)
713                 return clk->round_rate(clk, rate, p_rate);
714 
715         return omap1_clk_recalc_rate(hw, *p_rate);
716 }
717 
718 static int omap1_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
719 {
720         struct omap1_clk *clk = to_omap1_clk(hw);
721         int  ret = -EINVAL;
722 
723         if (clk->set_rate)
724                 ret = clk->set_rate(clk, rate, p_rate);
725         return ret;
726 }
727 
728 /*
729  * Omap1 clock reset and init functions
730  */
731 
732 static int omap1_clk_init_op(struct clk_hw *hw)
733 {
734         struct omap1_clk *clk = to_omap1_clk(hw);
735 
736         if (clk->init)
737                 return clk->init(clk);
738 
739         return 0;
740 }
741 
742 #ifdef CONFIG_OMAP_RESET_CLOCKS
743 
744 static void omap1_clk_disable_unused(struct clk_hw *hw)
745 {
746         struct omap1_clk *clk = to_omap1_clk(hw);
747         const char *name = clk_hw_get_name(hw);
748 
749         /* Clocks in the DSP domain need api_ck. Just assume bootloader
750          * has not enabled any DSP clocks */
751         if (clk->enable_reg == DSP_IDLECT2) {
752                 pr_info("Skipping reset check for DSP domain clock \"%s\"\n", name);
753                 return;
754         }
755 
756         pr_info("Disabling unused clock \"%s\"... ", name);
757         omap1_clk_disable(hw);
758         printk(" done\n");
759 }
760 
761 #endif
762 
763 const struct clk_ops omap1_clk_gate_ops = {
764         .enable         = omap1_clk_enable,
765         .disable        = omap1_clk_disable,
766         .is_enabled     = omap1_clk_is_enabled,
767 #ifdef CONFIG_OMAP_RESET_CLOCKS
768         .disable_unused = omap1_clk_disable_unused,
769 #endif
770 };
771 
772 const struct clk_ops omap1_clk_rate_ops = {
773         .recalc_rate    = omap1_clk_recalc_rate,
774         .round_rate     = omap1_clk_round_rate,
775         .set_rate       = omap1_clk_set_rate,
776         .init           = omap1_clk_init_op,
777 };
778 
779 const struct clk_ops omap1_clk_full_ops = {
780         .enable         = omap1_clk_enable,
781         .disable        = omap1_clk_disable,
782         .is_enabled     = omap1_clk_is_enabled,
783 #ifdef CONFIG_OMAP_RESET_CLOCKS
784         .disable_unused = omap1_clk_disable_unused,
785 #endif
786         .recalc_rate    = omap1_clk_recalc_rate,
787         .round_rate     = omap1_clk_round_rate,
788         .set_rate       = omap1_clk_set_rate,
789         .init           = omap1_clk_init_op,
790 };
791 
792 /*
793  * OMAP specific clock functions shared between omap1 and omap2
794  */
795 
796 /* Used for clocks that always have same value as the parent clock */
797 unsigned long followparent_recalc(struct omap1_clk *clk, unsigned long p_rate)
798 {
799         return p_rate;
800 }
801 
802 /*
803  * Used for clocks that have the same value as the parent clock,
804  * divided by some factor
805  */
806 unsigned long omap_fixed_divisor_recalc(struct omap1_clk *clk, unsigned long p_rate)
807 {
808         WARN_ON(!clk->fixed_div);
809 
810         return p_rate / clk->fixed_div;
811 }
812 
813 /* Propagate rate to children */
814 void propagate_rate(struct omap1_clk *tclk)
815 {
816         struct clk *clkp;
817 
818         /* depend on CCF ability to recalculate new rates across whole clock subtree */
819         if (WARN_ON(!(clk_hw_get_flags(&tclk->hw) & CLK_GET_RATE_NOCACHE)))
820                 return;
821 
822         clkp = clk_get_sys(NULL, clk_hw_get_name(&tclk->hw));
823         if (WARN_ON(!clkp))
824                 return;
825 
826         clk_get_rate(clkp);
827         clk_put(clkp);
828 }
829 
830 const struct clk_ops omap1_clk_null_ops = {
831 };
832 
833 /*
834  * Dummy clock
835  *
836  * Used for clock aliases that are needed on some OMAPs, but not others
837  */
838 struct omap1_clk dummy_ck __refdata = {
839         .hw.init        = CLK_HW_INIT_NO_PARENT("dummy", &omap1_clk_null_ops, 0),
840 };
841 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php