diff options
Diffstat (limited to 'arch/arm/mach-omap2/dpll3xxx.c')
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 226 |
1 files changed, 224 insertions, 2 deletions
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index eacf51f2bc27..f72dedb4eee8 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c @@ -29,6 +29,7 @@ #include <linux/clkdev.h> #include "soc.h" +#include "clockdomain.h" #include "clock.h" #include "cm2xxx_3xxx.h" #include "cm-regbits-34xx.h" @@ -42,7 +43,11 @@ /* Private functions */ /* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */ +#ifdef CONFIG_COMMON_CLK +static void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits) +#else static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) +#endif { const struct dpll_data *dd; u32 v; @@ -56,7 +61,11 @@ static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) } /* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ +#ifdef CONFIG_COMMON_CLK +static int _omap3_wait_dpll_status(struct clk_hw_omap *clk, u8 state) +#else static int _omap3_wait_dpll_status(struct clk *clk, u8 state) +#endif { const struct dpll_data *dd; int i = 0; @@ -64,7 +73,11 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) const char *clk_name; dd = clk->dpll_data; +#ifdef CONFIG_COMMON_CLK + clk_name = __clk_get_name(clk->hw.clk); +#else clk_name = __clk_get_name(clk); +#endif state <<= __ffs(dd->idlest_mask); @@ -88,7 +101,11 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) } /* From 3430 TRM ES2 4.7.6.2 */ +#ifdef CONFIG_COMMON_CLK +static u16 _omap3_dpll_compute_freqsel(struct clk_hw_omap *clk, u8 n) +#else static u16 _omap3_dpll_compute_freqsel(struct clk *clk, u8 n) +#endif { unsigned long fint; u16 f = 0; @@ -133,14 +150,22 @@ static u16 _omap3_dpll_compute_freqsel(struct clk *clk, u8 n) * locked successfully, return 0; if the DPLL did not lock in the time * allotted, or DPLL3 was passed in, return -EINVAL. */ +#ifdef CONFIG_COMMON_CLK +static int _omap3_noncore_dpll_lock(struct clk_hw_omap *clk) +#else static int _omap3_noncore_dpll_lock(struct clk *clk) +#endif { const struct dpll_data *dd; u8 ai; u8 state = 1; int r = 0; +#ifdef CONFIG_COMMON_CLK + pr_debug("clock: locking DPLL %s\n", __clk_get_name(clk->hw.clk)); +#else pr_debug("clock: locking DPLL %s\n", __clk_get_name(clk)); +#endif dd = clk->dpll_data; state <<= __ffs(dd->idlest_mask); @@ -178,7 +203,11 @@ done: * DPLL3 was passed in, or the DPLL does not support low-power bypass, * return -EINVAL. */ +#ifdef CONFIG_COMMON_CLK +static int _omap3_noncore_dpll_bypass(struct clk_hw_omap *clk) +#else static int _omap3_noncore_dpll_bypass(struct clk *clk) +#endif { int r; u8 ai; @@ -187,7 +216,11 @@ static int _omap3_noncore_dpll_bypass(struct clk *clk) return -EINVAL; pr_debug("clock: configuring DPLL %s for low-power bypass\n", +#ifdef CONFIG_COMMON_CLK + __clk_get_name(clk->hw.clk)); +#else __clk_get_name(clk)); +#endif ai = omap3_dpll_autoidle_read(clk); @@ -210,14 +243,22 @@ static int _omap3_noncore_dpll_bypass(struct clk *clk) * code. If DPLL3 was passed in, or the DPLL does not support * low-power stop, return -EINVAL; otherwise, return 0. */ +#ifdef CONFIG_COMMON_CLK +static int _omap3_noncore_dpll_stop(struct clk_hw_omap *clk) +#else static int _omap3_noncore_dpll_stop(struct clk *clk) +#endif { u8 ai; if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) return -EINVAL; +#ifdef CONFIG_COMMON_CLK + pr_debug("clock: stopping DPLL %s\n", __clk_get_name(clk->hw.clk)); +#else pr_debug("clock: stopping DPLL %s\n", __clk_get_name(clk)); +#endif ai = omap3_dpll_autoidle_read(clk); @@ -241,11 +282,19 @@ static int _omap3_noncore_dpll_stop(struct clk *clk) * XXX This code is not needed for 3430/AM35xx; can it be optimized * out in non-multi-OMAP builds for those chips? */ +#ifdef CONFIG_COMMON_CLK +static void _lookup_dco(struct clk_hw_omap *clk, u8 *dco, u16 m, u8 n) +#else static void _lookup_dco(struct clk *clk, u8 *dco, u16 m, u8 n) +#endif { unsigned long fint, clkinp; /* watch out for overflow */ +#ifdef CONFIG_COMMON_CLK + clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk)); +#else clkinp = __clk_get_rate(__clk_get_parent(clk)); +#endif fint = (clkinp / n) * m; if (fint < 1000000000) @@ -266,12 +315,20 @@ static void _lookup_dco(struct clk *clk, u8 *dco, u16 m, u8 n) * XXX This code is not needed for 3430/AM35xx; can it be optimized * out in non-multi-OMAP builds for those chips? */ +#ifdef CONFIG_COMMON_CLK +static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) +#else static void _lookup_sddiv(struct clk *clk, u8 *sd_div, u16 m, u8 n) +#endif { unsigned long clkinp, sd; /* watch out for overflow */ int mod1, mod2; +#ifdef CONFIG_COMMON_CLK + clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk)); +#else clkinp = __clk_get_rate(__clk_get_parent(clk)); +#endif /* * target sigma-delta to near 250MHz @@ -298,7 +355,12 @@ static void _lookup_sddiv(struct clk *clk, u8 *sd_div, u16 m, u8 n) * Program the DPLL with the supplied M, N values, and wait for the DPLL to * lock.. Returns -EINVAL upon error, or 0 upon success. */ +#ifdef CONFIG_COMMON_CLK +static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, + u16 freqsel) +#else static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) +#endif { struct dpll_data *dd = clk->dpll_data; u8 dco, sd_div; @@ -355,8 +417,14 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) * * Recalculate and propagate the DPLL rate. */ +#ifdef CONFIG_COMMON_CLK +unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); +#else unsigned long omap3_dpll_recalc(struct clk *clk) { +#endif return omap2_get_dpll_rate(clk); } @@ -376,8 +444,14 @@ unsigned long omap3_dpll_recalc(struct clk *clk) * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ +#ifdef CONFIG_COMMON_CLK +int omap3_noncore_dpll_enable(struct clk_hw *hw) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); +#else int omap3_noncore_dpll_enable(struct clk *clk) { +#endif int r; struct dpll_data *dd; struct clk *parent; @@ -386,15 +460,34 @@ int omap3_noncore_dpll_enable(struct clk *clk) if (!dd) return -EINVAL; +#ifdef CONFIG_COMMON_CLK + if (clk->clkdm) { + r = clkdm_clk_enable(clk->clkdm, hw->clk); + if (r) { + WARN(1, + "%s: could not enable %s's clockdomain %s: %d\n", + __func__, __clk_get_name(hw->clk), + clk->clkdm->name, r); + return r; + } + } + + parent = __clk_get_parent(hw->clk); + + if (__clk_get_rate(hw->clk) == __clk_get_rate(dd->clk_bypass)) { +#else parent = __clk_get_parent(clk); if (__clk_get_rate(clk) == __clk_get_rate(dd->clk_bypass)) { +#endif WARN_ON(parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } + +#ifndef CONFIG_COMMON_CLK /* *FIXME: this is dubious - if clk->rate has changed, what about * propagating? @@ -402,6 +495,7 @@ int omap3_noncore_dpll_enable(struct clk *clk) if (!r) clk->rate = (clk->recalc) ? clk->recalc(clk) : omap2_get_dpll_rate(clk); +#endif return r; } @@ -413,9 +507,21 @@ int omap3_noncore_dpll_enable(struct clk *clk) * Instructs a non-CORE DPLL to enter low-power stop. This function is * intended for use in struct clkops. No return value. */ +#ifdef CONFIG_COMMON_CLK +void omap3_noncore_dpll_disable(struct clk_hw *hw) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + + _omap3_noncore_dpll_stop(clk); + if (clk->clkdm) + clkdm_clk_disable(clk->clkdm, hw->clk); +#else void omap3_noncore_dpll_disable(struct clk *clk) { _omap3_noncore_dpll_stop(clk); + if (clk->clkdm) + clkdm_clk_disable(clk->clkdm, clk); +#endif } @@ -432,6 +538,77 @@ void omap3_noncore_dpll_disable(struct clk *clk) * target rate if it hasn't been done already, then program and lock * the DPLL. Returns -EINVAL upon error, or 0 upon success. */ +#ifdef CONFIG_COMMON_CLK +int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct clk *new_parent = NULL; + u16 freqsel = 0; + struct dpll_data *dd; + int ret; + + if (!hw || !rate) + return -EINVAL; + + dd = clk->dpll_data; + if (!dd) + return -EINVAL; + + __clk_prepare(dd->clk_bypass); + clk_enable(dd->clk_bypass); + __clk_prepare(dd->clk_ref); + clk_enable(dd->clk_ref); + + if (__clk_get_rate(dd->clk_bypass) == rate && + (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { + pr_debug("%s: %s: set rate: entering bypass.\n", + __func__, __clk_get_name(hw->clk)); + + ret = _omap3_noncore_dpll_bypass(clk); + if (!ret) + new_parent = dd->clk_bypass; + } else { + if (dd->last_rounded_rate != rate) + rate = __clk_round_rate(hw->clk, rate); + + if (dd->last_rounded_rate == 0) + return -EINVAL; + + /* No freqsel on OMAP4 and OMAP3630 */ + if (!cpu_is_omap44xx() && !cpu_is_omap3630()) { + freqsel = _omap3_dpll_compute_freqsel(clk, + dd->last_rounded_n); + if (!freqsel) + WARN_ON(1); + } + + pr_debug("%s: %s: set rate: locking rate to %lu.\n", + __func__, __clk_get_name(hw->clk), rate); + + ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, + dd->last_rounded_n, freqsel); + if (!ret) + new_parent = dd->clk_ref; + } + /* + * FIXME - this is all wrong. common code handles reparenting and + * migrating prepare/enable counts. dplls should be a multiplexer + * clock and this should be a set_parent operation so that all of that + * stuff is inherited for free + */ + + if (!ret) + __clk_reparent(hw->clk, new_parent); + + clk_disable(dd->clk_ref); + __clk_unprepare(dd->clk_ref); + clk_disable(dd->clk_bypass); + __clk_unprepare(dd->clk_bypass); + + return 0; +} +#else int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { struct clk *new_parent = NULL; @@ -509,6 +686,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) return 0; } +#endif /* DPLL autoidle read/set code */ @@ -520,7 +698,11 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) * -EINVAL if passed a null pointer or if the struct clk does not * appear to refer to a DPLL. */ +#ifdef CONFIG_COMMON_CLK +u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk) +#else u32 omap3_dpll_autoidle_read(struct clk *clk) +#endif { const struct dpll_data *dd; u32 v; @@ -549,7 +731,11 @@ u32 omap3_dpll_autoidle_read(struct clk *clk) * OMAP3430. The DPLL will enter low-power stop when its downstream * clocks are gated. No return value. */ +#ifdef CONFIG_COMMON_CLK +void omap3_dpll_allow_idle(struct clk_hw_omap *clk) +#else void omap3_dpll_allow_idle(struct clk *clk) +#endif { const struct dpll_data *dd; u32 v; @@ -560,8 +746,10 @@ void omap3_dpll_allow_idle(struct clk *clk) dd = clk->dpll_data; if (!dd->autoidle_reg) { +#ifndef CONFIG_COMMON_CLK pr_debug("clock: DPLL %s: autoidle not supported\n", __clk_get_name(clk)); +#endif return; } @@ -583,7 +771,11 @@ void omap3_dpll_allow_idle(struct clk *clk) * * Disable DPLL automatic idle control. No return value. */ +#ifdef CONFIG_COMMON_CLK +void omap3_dpll_deny_idle(struct clk_hw_omap *clk) +#else void omap3_dpll_deny_idle(struct clk *clk) +#endif { const struct dpll_data *dd; u32 v; @@ -594,8 +786,10 @@ void omap3_dpll_deny_idle(struct clk *clk) dd = clk->dpll_data; if (!dd->autoidle_reg) { +#ifndef CONFIG_COMMON_CLK pr_debug("clock: DPLL %s: autoidle not supported\n", __clk_get_name(clk)); +#endif return; } @@ -615,6 +809,27 @@ void omap3_dpll_deny_idle(struct clk *clk) * Using parent clock DPLL data, look up DPLL state. If locked, set our * rate to the dpll_clk * 2; otherwise, just use dpll_clk. */ +#ifdef CONFIG_COMMON_CLK +unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + const struct dpll_data *dd; + unsigned long rate; + u32 v; + struct clk_hw_omap *pclk = NULL; + struct clk *parent; + + /* Walk up the parents of clk, looking for a DPLL */ + do { + do { + parent = __clk_get_parent(hw->clk); + hw = __clk_get_hw(parent); + } while (hw && (__clk_get_flags(hw->clk) & CLK_IS_BASIC)); + if (!hw) + break; + pclk = to_clk_hw_omap(hw); + } while (pclk && !pclk->dpll_data); +#else unsigned long omap3_clkoutx2_recalc(struct clk *clk) { const struct dpll_data *dd; @@ -628,6 +843,8 @@ unsigned long omap3_clkoutx2_recalc(struct clk *clk) while (pclk && !pclk->dpll_data) pclk = __clk_get_parent(pclk); + parent_rate = __clk_get_rate(__clk_get_parent(clk)); +#endif /* clk does not have a DPLL as a parent? error in the clock data */ if (!pclk) { WARN_ON(1); @@ -638,7 +855,6 @@ unsigned long omap3_clkoutx2_recalc(struct clk *clk) WARN_ON(!dd->enable_mask); - parent_rate = __clk_get_rate(__clk_get_parent(clk)); v = __raw_readl(dd->control_reg) & dd->enable_mask; v >>= __ffs(dd->enable_mask); if ((v != OMAP3XXX_EN_DPLL_LOCKED) || (dd->flags & DPLL_J_TYPE)) @@ -649,7 +865,12 @@ unsigned long omap3_clkoutx2_recalc(struct clk *clk) } /* OMAP3/4 non-CORE DPLL clkops */ - +#ifdef CONFIG_COMMON_CLK +const struct clk_hw_omap_ops clkhwops_omap3_dpll = { + .allow_idle = omap3_dpll_allow_idle, + .deny_idle = omap3_dpll_deny_idle, +}; +#else const struct clkops clkops_omap3_noncore_dpll_ops = { .enable = omap3_noncore_dpll_enable, .disable = omap3_noncore_dpll_disable, @@ -661,3 +882,4 @@ const struct clkops clkops_omap3_core_dpll_ops = { .allow_idle = omap3_dpll_allow_idle, .deny_idle = omap3_dpll_deny_idle, }; +#endif |