diff options
author | Mike Turquette <mturquette@ti.com> | 2010-02-24 14:06:00 -0500 |
---|---|---|
committer | Paul Walmsley <paul@pwsan.com> | 2010-02-24 14:06:00 -0500 |
commit | a7e069fc5a560c096a2597d7be27f45fb4a01df7 (patch) | |
tree | 5171d2350215339101f9966ea17decbb1819d7f3 /arch/arm/mach-omap2 | |
parent | c23a97d377077c67e01f7526de3a411b316ee4f6 (diff) |
OMAP3630: Clock: Workaround for DPLL HS divider limitation
This patch implements a workaround for the DPLL HS divider limitation
in OMAP3630 as given by Errata ID: i556.
Errata:
When PWRDN bit is set, it resets the internal HSDIVIDER divide-by value (Mx).
The reset value gets loaded instead of the previous value.
The following HSDIVIDERs exhibit above behavior:
. DPLL4 : M6 / M5 / M4 / M3 / M2 (CM_CLKEN_PLL[31:26] register bits)
. DPLL3 : M3 (CM_CLKEN_PLL[12] register bit).
Work Around:
It is mandatory to apply the following sequence to ensure the write
value will
be loaded in DPLL HSDIVIDER FSM:
The global sequence when using PWRDN bit is the following:
. Disable Mx HSDIVIDER clock output related functional clock enable bits
(in CM_FCLKEN_xxx / CM_ICLKEN_xxx)
. Enable PWRDN bit of HSDIVIDER
. Disable PWRDN bit of HSDIVIDER
. Read current HSDIVIDER register value
. Write different value in HSDIVIDER register
. Write expected value in HSDIVIDER register
. Enable Mx HSDIVIDER clock output related functional clocks
(CM_FCLKEN_xxx / CM_ICLKEN_xxx)
Signed-off-by: Mike Turquette <mturquette@ti.com>
Signed-off-by: Vishwanath BS <vishwanath.bs@ti.com>
Signed-off-by: Vijaykumar GN <vijaykumar.gn@ti.com>
[paul@pwsan.com: updated patch to apply; made workaround function static;
marked as being 36xx-specific]
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r-- | arch/arm/mach-omap2/clock34xx.c | 43 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock34xx.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock34xx_data.c | 19 |
3 files changed, 65 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c index de7391b6bcfd..9039e8cbe487 100644 --- a/arch/arm/mach-omap2/clock34xx.c +++ b/arch/arm/mach-omap2/clock34xx.c | |||
@@ -150,6 +150,49 @@ const struct clkops clkops_omap3430es2_hsotgusb_wait = { | |||
150 | .find_companion = omap2_clk_dflt_find_companion, | 150 | .find_companion = omap2_clk_dflt_find_companion, |
151 | }; | 151 | }; |
152 | 152 | ||
153 | /** | ||
154 | * omap36xx_pwrdn_clk_enable_with_hsdiv_restore - enable clocks suffering | ||
155 | * from HSDivider PWRDN problem Implements Errata ID: i556. | ||
156 | * @clk: DPLL output struct clk | ||
157 | * | ||
158 | * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, | ||
159 | * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset | ||
160 | * valueafter their respective PWRDN bits are set. Any dummy write | ||
161 | * (Any other value different from the Read value) to the | ||
162 | * corresponding CM_CLKSEL register will refresh the dividers. | ||
163 | */ | ||
164 | static int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk *clk) | ||
165 | { | ||
166 | u32 dummy_v, orig_v, clksel_shift; | ||
167 | int ret; | ||
168 | |||
169 | /* Clear PWRDN bit of HSDIVIDER */ | ||
170 | ret = omap2_dflt_clk_enable(clk); | ||
171 | |||
172 | /* Restore the dividers */ | ||
173 | if (!ret) { | ||
174 | clksel_shift = __ffs(clk->parent->clksel_mask); | ||
175 | orig_v = __raw_readl(clk->parent->clksel_reg); | ||
176 | dummy_v = orig_v; | ||
177 | |||
178 | /* Write any other value different from the Read value */ | ||
179 | dummy_v ^= (1 << clksel_shift); | ||
180 | __raw_writel(dummy_v, clk->parent->clksel_reg); | ||
181 | |||
182 | /* Write the original divider */ | ||
183 | __raw_writel(orig_v, clk->parent->clksel_reg); | ||
184 | } | ||
185 | |||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | const struct clkops clkops_omap36xx_pwrdn_with_hsdiv_wait_restore = { | ||
190 | .enable = omap36xx_pwrdn_clk_enable_with_hsdiv_restore, | ||
191 | .disable = omap2_dflt_clk_disable, | ||
192 | .find_companion = omap2_clk_dflt_find_companion, | ||
193 | .find_idlest = omap2_clk_dflt_find_idlest, | ||
194 | }; | ||
195 | |||
153 | const struct clkops omap3_clkops_noncore_dpll_ops = { | 196 | const struct clkops omap3_clkops_noncore_dpll_ops = { |
154 | .enable = omap3_noncore_dpll_enable, | 197 | .enable = omap3_noncore_dpll_enable, |
155 | .disable = omap3_noncore_dpll_disable, | 198 | .disable = omap3_noncore_dpll_disable, |
diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h index bc1051578e81..720091ddced1 100644 --- a/arch/arm/mach-omap2/clock34xx.h +++ b/arch/arm/mach-omap2/clock34xx.h | |||
@@ -26,4 +26,7 @@ extern const struct clkops omap3_clkops_noncore_dpll_ops; | |||
26 | extern const struct clkops clkops_am35xx_ipss_module_wait; | 26 | extern const struct clkops clkops_am35xx_ipss_module_wait; |
27 | extern const struct clkops clkops_am35xx_ipss_wait; | 27 | extern const struct clkops clkops_am35xx_ipss_wait; |
28 | 28 | ||
29 | /* OMAP36xx-specific clkops */ | ||
30 | extern const struct clkops clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
31 | |||
29 | #endif | 32 | #endif |
diff --git a/arch/arm/mach-omap2/clock34xx_data.c b/arch/arm/mach-omap2/clock34xx_data.c index 221e831a7151..8bb8134872ce 100644 --- a/arch/arm/mach-omap2/clock34xx_data.c +++ b/arch/arm/mach-omap2/clock34xx_data.c | |||
@@ -3358,6 +3358,25 @@ int __init omap3xxx_clk_init(void) | |||
3358 | } | 3358 | } |
3359 | } | 3359 | } |
3360 | 3360 | ||
3361 | if (cpu_is_omap3630()) { | ||
3362 | /* | ||
3363 | * For 3630: override clkops_omap2_dflt_wait for the | ||
3364 | * clocks affected from PWRDN reset Limitation | ||
3365 | */ | ||
3366 | dpll3_m3x2_ck.ops = | ||
3367 | &clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
3368 | dpll4_m2x2_ck.ops = | ||
3369 | &clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
3370 | dpll4_m3x2_ck.ops = | ||
3371 | &clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
3372 | dpll4_m4x2_ck.ops = | ||
3373 | &clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
3374 | dpll4_m5x2_ck.ops = | ||
3375 | &clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
3376 | dpll4_m6x2_ck.ops = | ||
3377 | &clkops_omap36xx_pwrdn_with_hsdiv_wait_restore; | ||
3378 | } | ||
3379 | |||
3361 | clk_init(&omap2_clk_functions); | 3380 | clk_init(&omap2_clk_functions); |
3362 | 3381 | ||
3363 | for (c = omap3xxx_clks; c < omap3xxx_clks + ARRAY_SIZE(omap3xxx_clks); c++) | 3382 | for (c = omap3xxx_clks; c < omap3xxx_clks + ARRAY_SIZE(omap3xxx_clks); c++) |