OMAP2/3 clock: revise DPLL bypass handling in clock tree
Paul Walmsley
maintainerv2 cover OMAP2/3 clock: revise DPLL bypass handling in clock tree
This second version fixes bypass handling for DPLL1 and DPLL2 to use the high-frequency bypass clocks. This came to light courtesy of Kevin Hilman <[email protected]>.
---
Previously, the OMAP3 clock tree handled DPLL bypass rate recalculation by dynamically switching clock parents depending on the DPLL bypass state. It used the clksel mechanism for this. Unfortunately, this does not actually work. The clock code expects the clock's parent to be the one set in the struct clk, causing kernel messages similar to:
clock: Could not find fieldval 0 for clock iva2_ck parent dpll2_m2_ck
This patch series moves DPLL bypass rate computation into omap2_get_dpll_rate(), and removes most of the bypass clocks in the clock tree. It also converts the bypass tests in the code to use the DPLL IDLEST bits rather than the DPLL CLKEN bits.
Problem reported by Ramesh Gupta Guntha <[email protected]>.
Bypass rate recalculation verified with DPLL1 on 3430SDP, which has bypass support built into its clock code. Boot-tested on 2430SDP.
- Paul
---
text data bss dec hex filename
3443780 158320 105504 3707604 3892d4 vmlinux.3430sdp.orig 3443344 158160 105504 3707008 389080 vmlinux.3430sdp
arch/arm/mach-omap2/clock.c | 45 ++++++- arch/arm/mach-omap2/clock24xx.c | 4 - arch/arm/mach-omap2/clock24xx.h | 2 arch/arm/mach-omap2/clock34xx.c | 70 ++++++++---- arch/arm/mach-omap2/clock34xx.h | 188 +++++-------------------------- arch/arm/plat-omap/include/mach/clock.h | 5 - 6 files changed, 125 insertions(+), 189 deletions(-)
Paul Walmsley
maintainerPATCH v2 1/7 OMAP3 clock: convert dpll_data.idlest_bit to idlest_mask
Convert struct dpll_data.idlest_bit field to idlest_mask. Needed since later patches are converting the DPLL bypass state test to use the IDLEST registers, and OMAP2 uses two bits for DPLL IDLEST rather than one.
--- arch/arm/mach-omap2/clock34xx.c | 6 ++---- arch/arm/mach-omap2/clock34xx.h | 8 ++++---- arch/arm/plat-omap/include/mach/clock.h | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-)
arch/arm/mach-omap2/clock34xx.c +2 -4
@@ -78,14 +78,12 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state)const struct dpll_data *dd;int i =0 ;int ret = -EINVAL ; -u32 idlest_mask; dd = clk->dpll_data; - state <<= dd->idlest_bit; - idlest_mask =1 << dd->idlest_bit; + state <<=__ffs (dd->idlest_mask); -while (((__raw_readl (dd->idlest_reg) & idlest_mask) != state) && +while (((__raw_readl (dd->idlest_reg) & dd->idlest_mask) != state) && i <MAX_DPLL_WAIT_TRIES ) { i++;udelay (1 );
arch/arm/mach-omap2/clock34xx.h +4 -4
@@ -310,7 +310,7 @@ static struct dpll_data dpll1_dd = { .autoidle_reg = _OMAP34XX_CM_REGADDR(MPU_MOD, OMAP3430_CM_AUTOIDLE_PLL), .autoidle_mask = OMAP3430_AUTO_MPU_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(MPU_MOD, OMAP3430_CM_IDLEST_PLL), - .idlest_bit = OMAP3430_ST_MPU_CLK_SHIFT, + .idlest_mask = OMAP3430_ST_MPU_CLK_MASK, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -382,7 +382,7 @@ static struct dpll_data dpll2_dd = { .autoidle_reg = _OMAP34XX_CM_REGADDR(OMAP3430_IVA2_MOD, OMAP3430_CM_AUTOIDLE_PLL), .autoidle_mask = OMAP3430_AUTO_IVA2_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(OMAP3430_IVA2_MOD, OMAP3430_CM_IDLEST_PLL), - .idlest_bit = OMAP3430_ST_IVA2_CLK_SHIFT, + .idlest_mask = OMAP3430_ST_IVA2_CLK_MASK, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -628,7 +628,7 @@ static struct dpll_data dpll4_dd = { .autoidle_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_AUTOIDLE), .autoidle_mask = OMAP3430_AUTO_PERIPH_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST), - .idlest_bit = OMAP3430_ST_PERIPH_CLK_SHIFT, + .idlest_mask = OMAP3430_ST_PERIPH_CLK_MASK, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -968,7 +968,7 @@ static struct dpll_data dpll5_dd = { .autoidle_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, OMAP3430ES2_CM_AUTOIDLE2_PLL), .autoidle_mask = OMAP3430ES2_AUTO_PERIPH2_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST2), - .idlest_bit = OMAP3430ES2_ST_PERIPH2_CLK_SHIFT, + .idlest_mask = OMAP3430ES2_ST_PERIPH2_CLK_MASK, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
arch/arm/plat-omap/include/mach/clock.h +2 -2
@@ -41,6 +41,8 @@ struct dpll_data {u16 max_multiplier;u8 max_divider;u32 max_tolerance; +void __iomem *idlest_reg; +u32 idlest_mask;# if defined(CONFIG_ARCH_OMAP3 )u32 freqsel_mask;u8 modes;
@@ -51,8 +53,6 @@ struct dpll_data {u8 recal_st_bit;void __iomem *autoidle_reg;u32 autoidle_mask; -void __iomem *idlest_reg; -u8 idlest_bit;# endif };
Paul Walmsley
maintainerPATCH v2 2/7 OMAP3 clock: add idlest_reg, idlest_mask for DPLL3
In reply to Paul WalmsleyDPLL3 dpll_data was missing idlest_* fields - add them.
--- arch/arm/mach-omap2/clock34xx.h | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clock34xx.h +2 -0
@@ -441,6 +441,8 @@ static struct dpll_data dpll3_dd = { .recal_st_bit = OMAP3430_CORE_DPLL_ST_SHIFT, .autoidle_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_AUTOIDLE), .autoidle_mask = OMAP3430_AUTO_CORE_DPLL_MASK, + .idlest_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST), + .idlest_mask = OMAP3430_ST_CORE_CLK_MASK, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
Paul Walmsley
maintainerPATCH v2 3/7 OMAP3 clock: note the bypass source clock for DPLLs
In reply to Paul WalmsleyMost DPLLs use sys_clk as their bypass rate source. But DPLL1 and DPLL2 use high-frequency bypass clocks dpll1_fclk and dpll2_fclk as their parents during bypass. Add a new struct dpll_data field to track the DPLL's bypass source clock.
Kevin Hilman <[email protected]> helped catch this - thanks Kevin.
--- arch/arm/mach-omap2/clock34xx.h | 9 +++++++++ arch/arm/plat-omap/include/mach/clock.h | 1 + 2 files changed, 10 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clock34xx.h +9 -0
@@ -49,6 +49,10 @@ static int omap3_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate);* DPLL5 supplies other peripheral clocks (USBHOST, USIM). */ +/* Forward declarations for DPLL bypass clocks */ +static struct clk dpll1_fck; +static struct clk dpll2_fck; +/* CM_CLKEN_PLL*.EN* bit values - not all are available for every DPLL */ #define DPLL_LOW_POWER_STOP 0x1#define DPLL_LOW_POWER_BYPASS 0x5
@@ -311,6 +315,7 @@ static struct dpll_data dpll1_dd = { .autoidle_mask = OMAP3430_AUTO_MPU_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(MPU_MOD, OMAP3430_CM_IDLEST_PLL), .idlest_mask = OMAP3430_ST_MPU_CLK_MASK, + .bypass_clk = &dpll1_fck, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -383,6 +388,7 @@ static struct dpll_data dpll2_dd = { .autoidle_mask = OMAP3430_AUTO_IVA2_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(OMAP3430_IVA2_MOD, OMAP3430_CM_IDLEST_PLL), .idlest_mask = OMAP3430_ST_IVA2_CLK_MASK, + .bypass_clk = &dpll2_fck, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -443,6 +449,7 @@ static struct dpll_data dpll3_dd = { .autoidle_mask = OMAP3430_AUTO_CORE_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST), .idlest_mask = OMAP3430_ST_CORE_CLK_MASK, + .bypass_clk = &sys_ck, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -631,6 +638,7 @@ static struct dpll_data dpll4_dd = { .autoidle_mask = OMAP3430_AUTO_PERIPH_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST), .idlest_mask = OMAP3430_ST_PERIPH_CLK_MASK, + .bypass_clk = &sys_ck, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
@@ -971,6 +979,7 @@ static struct dpll_data dpll5_dd = { .autoidle_mask = OMAP3430ES2_AUTO_PERIPH2_DPLL_MASK, .idlest_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST2), .idlest_mask = OMAP3430ES2_ST_PERIPH2_CLK_MASK, + .bypass_clk = &sys_ck, .max_multiplier = OMAP3_MAX_DPLL_MULT, .max_divider =OMAP3_MAX_DPLL_DIV , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
arch/arm/plat-omap/include/mach/clock.h +1 -0
@@ -43,6 +43,7 @@ struct dpll_data {u32 max_tolerance;void __iomem *idlest_reg;u32 idlest_mask; +struct clk *bypass_clk;# if defined(CONFIG_ARCH_OMAP3 )u32 freqsel_mask;u8 modes;
Paul Walmsley
maintainerPATCH v2 4/7 OMAP3 clock: move DPLL bypass rate calculation into omap2_get_dpll_rate()
In reply to Paul WalmsleyRemoves the clksel-based DPLL rate handling from the OMAP3 clock tree. In its place, omap2_get_dpll_rate() now has code to determine whether the DPLL is bypassed. This obsoletes several clocks, which are removed by this patch.
--- arch/arm/mach-omap2/clock.c | 45 +++++++++- arch/arm/mach-omap2/clock24xx.c | 4 + arch/arm/mach-omap2/clock24xx.h | 2 arch/arm/mach-omap2/clock34xx.h | 169 +++------------------------------------ 4 files changed, 58 insertions(+), 162 deletions(-)
arch/arm/mach-omap2/clock.c +39 -6
@@ -60,6 +60,10 @@#define DPLL_ROUNDING_VAL ((DPLL_SCALE_BASE / 2) * \ (DPLL_SCALE_FACTOR / DPLL_SCALE_BASE)) +/* Some OMAP2xxx CM_CLKSEL_PLL.ST_CORE_CLK bits - for omap2_get_dpll_rate() */ +#define ST_CORE_CLK_REF 0x1 +#define ST_CORE_CLK_32K 0x3 +u8 cpu_mask;/*-------------------------------------------------------------------------
@@ -137,22 +141,51 @@ void omap2_init_clksel_parent(struct clk *clk)return ; } -/* Returns the DPLL rate */ +/** +* omap2_get_dpll_rate - returns the current DPLL CLKOUT rate +* @clk: struct clk * of a DPLL +* +* DPLLs can be locked or bypassed - basically, enabled or disabled. +* When locked, the DPLL output depends on the M and N values. When +* bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock +* or sys_clk. Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and +* 2 are bypassed with dpll1_fclk and dpll2_fclk respectively +* (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk. +* Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is +* locked, or the appropriate bypass rate if the DPLL is bypassed, or 0 +* if the clock @clk is not a DPLL. +*/ u32 omap2_get_dpll_rate (struct clk *clk) {long long dpll_clk; -u32 dpll_mult, dpll_div, dpll; +u32 dpll_mult, dpll_div, v;struct dpll_data *dd; dd = clk->dpll_data; -/* REVISIT: What do we return on error? */ if (!dd)return 0 ; - dpll =__raw_readl (dd->mult_div1_reg); - dpll_mult = dpll & dd->mult_mask; +/* Return bypass rate if DPLL is bypassed */ + v =__raw_readl (dd->idlest_reg) & dd->idlest_mask; + v >>=__ffs (dd->idlest_mask); +if (cpu_is_omap24xx ()) { + +if (v ==ST_CORE_CLK_REF ) +return clk->parent->rate;/* sys_clk */ +else if (v ==ST_CORE_CLK_32K ) +return 32768 ; + + }else if (cpu_is_omap34xx ()) { + +if (!v) +return dd->bypass_clk->rate; + + } + + v =__raw_readl (dd->mult_div1_reg); + dpll_mult = v & dd->mult_mask; dpll_mult >>=__ffs (dd->mult_mask); - dpll_div = dpll & dd->div1_mask; + dpll_div = v & dd->div1_mask; dpll_div >>=__ffs (dd->div1_mask); dpll_clk = (long long )clk->parent->rate * dpll_mult;
arch/arm/mach-omap2/clock24xx.c +3 -1
@@ -535,8 +535,10 @@ static void __init omap2_clk_rewrite_base(struct clk *clk) {omap2_clk_check_reg (clk->flags, &clk->clksel_reg);omap2_clk_check_reg (clk->flags, &clk->enable_reg); -if (clk->dpll_data) +if (clk->dpll_data) {omap2_clk_check_reg (0 , &clk->dpll_data->mult_div1_reg); +omap2_clk_check_reg (0 , &clk->dpll_data->idlest_reg); + } }int __init omap2_clk_init (void )
arch/arm/mach-omap2/clock24xx.h +2 -0
@@ -682,6 +682,8 @@ static struct dpll_data dpll_dd = { .mult_div1_reg = _CM_REG_OFFSET(PLL_MOD, CM_CLKSEL1), .mult_mask = OMAP24XX_DPLL_MULT_MASK, .div1_mask = OMAP24XX_DPLL_DIV_MASK, + .idlest_reg = _CM_REG_OFFSET(PLL_MOD, CM_IDLEST), + .idlest_mask = OMAP24XX_ST_CORE_CLK_MASK, .max_multiplier = 1024, .max_divider =16 , .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
arch/arm/mach-omap2/clock34xx.h +14 -155
320 lines not on this page · raw message
Paul Walmsley
maintainerPATCH v2 5/7 OMAP3 clock: omap3_clkoutx2_recalc() should test DPLL IDLEST to determine if DPLL is bypassed
In reply to Paul WalmsleyConvert the DPLL bypass test in omap3_clkoutx2_recalc() to test DPLL IDLEST bits rather than the CM_CLKEN register.
--- arch/arm/mach-omap2/clock34xx.c | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)
arch/arm/mach-omap2/clock34xx.c +3 -4
@@ -577,11 +577,10 @@ static void omap3_clkoutx2_recalc(struct clk *clk) dd = pclk->dpll_data; -WARN_ON (!dd->control_reg || !dd->enable_mask); +WARN_ON (!dd->idlest_reg || !dd->idlest_mask); - v =__raw_readl (dd->control_reg) & dd->enable_mask; - v >>=__ffs (dd->enable_mask); -if (v !=DPLL_LOCKED ) + v =__raw_readl (dd->idlest_reg) & dd->idlest_mask; +if (!v) clk->rate = clk->parent->rate;else clk->rate = clk->parent->rate *2 ;
Paul Walmsley
maintainerPATCH v2 6/7 OMAP3 clock: DPLLs should enter bypass if new rate is sys_ck
In reply to Paul WalmsleyThis patch causes a DPLL to enter bypass when it is instructed to set its rate to that of its bypass clock. Previously this was only possible after setting the DPLL rate, then disabling and re-enabling it.
--- arch/arm/mach-omap2/clock34xx.c | 40 ++++++++++++++++++++++++++++----------- 1 files changed, 29 insertions(+), 11 deletions(-)
arch/arm/mach-omap2/clock34xx.c +29 -11
@@ -371,13 +371,17 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)* @clk: struct clk * of DPLL to set * @rate: rounded target rate * -* Program the DPLL with the rounded target rate. Returns -EINVAL upon -* error, or 0 upon success. +* Set the DPLL CLKOUT to the target rate. If the DPLL can enter +* low-power bypass, and the target rate is the bypass source clock +* rate, then configure the DPLL for bypass. Otherwise, round the +* target rate if it hasn't been done already, then program and lock +* the DPLL. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_set_rate (struct clk *clk,unsigned long rate) {u16 freqsel;struct dpll_data *dd; +int ret;if (!clk || !rate)return -EINVAL ;
@@ -389,18 +393,32 @@ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)if (rate ==omap2_get_dpll_rate (clk))return 0 ; -if (dd->last_rounded_rate != rate) -omap2_dpll_round_rate (clk, rate); +if (dd->bypass_clk->rate == rate && + (clk->dpll_data->modes & (1 <<DPLL_LOW_POWER_BYPASS ))) { -if (dd->last_rounded_rate ==0 ) -return -EINVAL ; +pr_debug ("clock: %s: set rate: entering bypass.\n" , clk->name); + + ret =_omap3_noncore_dpll_bypass (clk); + + }else { + +if (dd->last_rounded_rate != rate) +omap2_dpll_round_rate (clk, rate); - freqsel =_omap3_dpll_compute_freqsel (clk, dd->last_rounded_n); -if (!freqsel) -WARN_ON (1 ); +if (dd->last_rounded_rate ==0 ) +return -EINVAL ; -omap3_noncore_dpll_program (clk, dd->last_rounded_m, dd->last_rounded_n, - freqsel); + 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->name, rate); + + ret =omap3_noncore_dpll_program (clk, dd->last_rounded_m, + dd->last_rounded_n, freqsel); + + }omap3_dpll_recalc (clk);
Paul Walmsley
maintainerPATCH v2 7/7 OMAP3 clock: recalculate DPLL subtree after bypass entry/exit
In reply to Paul WalmsleyThe DPLL's rate changes when it enters or leaves bypass, so the DPLL's rate and the rates of all dependent clocks need to be recalculated when this happens.
Also, fix test for bypass to test against the appropriate bypass clock, rather than the parent clock (which is not the bypass clock for DPLL1 and DPLL2).
--- arch/arm/mach-omap2/clock34xx.c | 17 +++++++++++++++-- 1 files changed, 15 insertions(+), 2 deletions(-)
arch/arm/mach-omap2/clock34xx.c +15 -2
@@ -180,7 +180,7 @@ static int _omap3_noncore_dpll_lock(struct clk *clk) }/* -* omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness +* _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enter low-power bypass mode. In
@@ -270,15 +270,26 @@ static int _omap3_noncore_dpll_stop(struct clk *clk)static int omap3_noncore_dpll_enable (struct clk *clk) {int r; +long rate; +struct dpll_data *dd;if (clk == &dpll3_ck)return -EINVAL ; -if (clk->parent->rate ==omap2_get_dpll_rate (clk)) + dd = clk->dpll_data; +if (!dd) +return -EINVAL ; + + rate =omap2_get_dpll_rate (clk); + +if (dd->bypass_clk->rate == rate) r =_omap3_noncore_dpll_bypass (clk);else r =_omap3_noncore_dpll_lock (clk); +if (!r) + clk->rate = rate; +return r; }
@@ -399,6 +410,8 @@ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)pr_debug ("clock: %s: set rate: entering bypass.\n" , clk->name); ret =_omap3_noncore_dpll_bypass (clk); +if (!ret) + clk->rate = rate; }else {