diff options
Diffstat (limited to 'arch/arm/mach-omap2/dpll3xxx.c')
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 183 |
1 files changed, 90 insertions, 93 deletions
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index eacf51f2bc27..fafb28c0dcbc 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,7 @@ /* Private functions */ /* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */ -static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) +static void _omap3_dpll_write_clken(struct clk_hw_omap *clk, u8 clken_bits) { const struct dpll_data *dd; u32 v; @@ -56,7 +57,7 @@ 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 */ -static int _omap3_wait_dpll_status(struct clk *clk, u8 state) +static int _omap3_wait_dpll_status(struct clk_hw_omap *clk, u8 state) { const struct dpll_data *dd; int i = 0; @@ -64,7 +65,7 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) const char *clk_name; dd = clk->dpll_data; - clk_name = __clk_get_name(clk); + clk_name = __clk_get_name(clk->hw.clk); state <<= __ffs(dd->idlest_mask); @@ -88,7 +89,7 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) } /* From 3430 TRM ES2 4.7.6.2 */ -static u16 _omap3_dpll_compute_freqsel(struct clk *clk, u8 n) +static u16 _omap3_dpll_compute_freqsel(struct clk_hw_omap *clk, u8 n) { unsigned long fint; u16 f = 0; @@ -133,14 +134,14 @@ 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. */ -static int _omap3_noncore_dpll_lock(struct clk *clk) +static int _omap3_noncore_dpll_lock(struct clk_hw_omap *clk) { const struct dpll_data *dd; u8 ai; u8 state = 1; int r = 0; - pr_debug("clock: locking DPLL %s\n", __clk_get_name(clk)); + pr_debug("clock: locking DPLL %s\n", __clk_get_name(clk->hw.clk)); dd = clk->dpll_data; state <<= __ffs(dd->idlest_mask); @@ -178,7 +179,7 @@ done: * DPLL3 was passed in, or the DPLL does not support low-power bypass, * return -EINVAL. */ -static int _omap3_noncore_dpll_bypass(struct clk *clk) +static int _omap3_noncore_dpll_bypass(struct clk_hw_omap *clk) { int r; u8 ai; @@ -187,7 +188,7 @@ static int _omap3_noncore_dpll_bypass(struct clk *clk) return -EINVAL; pr_debug("clock: configuring DPLL %s for low-power bypass\n", - __clk_get_name(clk)); + __clk_get_name(clk->hw.clk)); ai = omap3_dpll_autoidle_read(clk); @@ -210,14 +211,14 @@ 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. */ -static int _omap3_noncore_dpll_stop(struct clk *clk) +static int _omap3_noncore_dpll_stop(struct clk_hw_omap *clk) { u8 ai; if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) return -EINVAL; - pr_debug("clock: stopping DPLL %s\n", __clk_get_name(clk)); + pr_debug("clock: stopping DPLL %s\n", __clk_get_name(clk->hw.clk)); ai = omap3_dpll_autoidle_read(clk); @@ -241,11 +242,11 @@ 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? */ -static void _lookup_dco(struct clk *clk, u8 *dco, u16 m, u8 n) +static void _lookup_dco(struct clk_hw_omap *clk, u8 *dco, u16 m, u8 n) { unsigned long fint, clkinp; /* watch out for overflow */ - clkinp = __clk_get_rate(__clk_get_parent(clk)); + clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk)); fint = (clkinp / n) * m; if (fint < 1000000000) @@ -266,12 +267,12 @@ 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? */ -static void _lookup_sddiv(struct clk *clk, u8 *sd_div, u16 m, u8 n) +static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) { unsigned long clkinp, sd; /* watch out for overflow */ int mod1, mod2; - clkinp = __clk_get_rate(__clk_get_parent(clk)); + clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk)); /* * target sigma-delta to near 250MHz @@ -298,7 +299,8 @@ 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. */ -static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) +static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, + u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u8 dco, sd_div; @@ -355,8 +357,10 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel) * * Recalculate and propagate the DPLL rate. */ -unsigned long omap3_dpll_recalc(struct clk *clk) +unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate) { + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + return omap2_get_dpll_rate(clk); } @@ -376,8 +380,9 @@ 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. */ -int omap3_noncore_dpll_enable(struct clk *clk) +int omap3_noncore_dpll_enable(struct clk_hw *hw) { + struct clk_hw_omap *clk = to_clk_hw_omap(hw); int r; struct dpll_data *dd; struct clk *parent; @@ -386,22 +391,26 @@ int omap3_noncore_dpll_enable(struct clk *clk) if (!dd) return -EINVAL; - parent = __clk_get_parent(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(clk) == __clk_get_rate(dd->clk_bypass)) { + if (__clk_get_rate(hw->clk) == __clk_get_rate(dd->clk_bypass)) { 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); } - /* - *FIXME: this is dubious - if clk->rate has changed, what about - * propagating? - */ - if (!r) - clk->rate = (clk->recalc) ? clk->recalc(clk) : - omap2_get_dpll_rate(clk); return r; } @@ -413,9 +422,13 @@ 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. */ -void omap3_noncore_dpll_disable(struct clk *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); } @@ -432,80 +445,72 @@ 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. */ -int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) +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; - unsigned long hw_rate, bypass_rate; u16 freqsel = 0; struct dpll_data *dd; int ret; - if (!clk || !rate) + if (!hw || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; - hw_rate = (clk->recalc) ? clk->recalc(clk) : omap2_get_dpll_rate(clk); - if (rate == hw_rate) - return 0; - - /* - * Ensure both the bypass and ref clocks are enabled prior to - * doing anything; we need the bypass clock running to reprogram - * the DPLL. - */ - omap2_clk_enable(dd->clk_bypass); - omap2_clk_enable(dd->clk_ref); + __clk_prepare(dd->clk_bypass); + clk_enable(dd->clk_bypass); + __clk_prepare(dd->clk_ref); + clk_enable(dd->clk_ref); - bypass_rate = __clk_get_rate(dd->clk_bypass); - if (bypass_rate == rate && - (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { - pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); + 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(clk, rate); + rate = __clk_round_rate(hw->clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; /* No freqsel on OMAP4 and OMAP3630 */ - if (!soc_is_am33xx() && !cpu_is_omap44xx() && !cpu_is_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("clock: %s: set rate: locking rate to %lu.\n", - __clk_get_name(clk), rate); + 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); + dd->last_rounded_n, freqsel); if (!ret) new_parent = dd->clk_ref; } - if (!ret) { - /* - * Switch the parent clock in the hierarchy, and make sure - * that the new parent's usecount is correct. Note: we - * enable the new parent before disabling the old to avoid - * any unnecessary hardware disable->enable transitions. - */ - if (clk->usecount) { - omap2_clk_enable(new_parent); - omap2_clk_disable(clk->parent); - } - clk_reparent(clk, new_parent); - clk->rate = rate; - } - omap2_clk_disable(dd->clk_ref); - omap2_clk_disable(dd->clk_bypass); + /* + * 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; } @@ -520,7 +525,7 @@ 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. */ -u32 omap3_dpll_autoidle_read(struct clk *clk) +u32 omap3_dpll_autoidle_read(struct clk_hw_omap *clk) { const struct dpll_data *dd; u32 v; @@ -549,7 +554,7 @@ 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. */ -void omap3_dpll_allow_idle(struct clk *clk) +void omap3_dpll_allow_idle(struct clk_hw_omap *clk) { const struct dpll_data *dd; u32 v; @@ -559,11 +564,8 @@ void omap3_dpll_allow_idle(struct clk *clk) dd = clk->dpll_data; - if (!dd->autoidle_reg) { - pr_debug("clock: DPLL %s: autoidle not supported\n", - __clk_get_name(clk)); + if (!dd->autoidle_reg) return; - } /* * REVISIT: CORE DPLL can optionally enter low-power bypass @@ -583,7 +585,7 @@ void omap3_dpll_allow_idle(struct clk *clk) * * Disable DPLL automatic idle control. No return value. */ -void omap3_dpll_deny_idle(struct clk *clk) +void omap3_dpll_deny_idle(struct clk_hw_omap *clk) { const struct dpll_data *dd; u32 v; @@ -593,11 +595,8 @@ void omap3_dpll_deny_idle(struct clk *clk) dd = clk->dpll_data; - if (!dd->autoidle_reg) { - pr_debug("clock: DPLL %s: autoidle not supported\n", - __clk_get_name(clk)); + if (!dd->autoidle_reg) return; - } v = __raw_readl(dd->autoidle_reg); v &= ~dd->autoidle_mask; @@ -615,18 +614,25 @@ 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. */ -unsigned long omap3_clkoutx2_recalc(struct clk *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 *pclk; - unsigned long parent_rate; + struct clk_hw_omap *pclk = NULL; + struct clk *parent; /* Walk up the parents of clk, looking for a DPLL */ - pclk = __clk_get_parent(clk); - while (pclk && !pclk->dpll_data) - pclk = __clk_get_parent(pclk); + 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); /* clk does not have a DPLL as a parent? error in the clock data */ if (!pclk) { @@ -638,7 +644,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,15 +654,7 @@ unsigned long omap3_clkoutx2_recalc(struct clk *clk) } /* OMAP3/4 non-CORE DPLL clkops */ - -const struct clkops clkops_omap3_noncore_dpll_ops = { - .enable = omap3_noncore_dpll_enable, - .disable = omap3_noncore_dpll_disable, - .allow_idle = omap3_dpll_allow_idle, - .deny_idle = omap3_dpll_deny_idle, -}; - -const struct clkops clkops_omap3_core_dpll_ops = { +const struct clk_hw_omap_ops clkhwops_omap3_dpll = { .allow_idle = omap3_dpll_allow_idle, .deny_idle = omap3_dpll_deny_idle, }; |