2009-01-28 20:46:54

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 00/13] OMAP clock, C of F: DPLL updates

This series is the third of six to bring the mainline kernel OMAP
clock code up-to-date with the linux-omap tree.

Major changes in this series:

- Fix DPLL jitter compensation (FREQSEL).
- Calculate correct rates when DPLL is in bypass.
- Remove the virtual "bypass clocks."
- Optimize DPLL rate rounding algorithm.

Some patches have been "compressed" together, as requested by rmk -
original commit IDs are in the patch descriptions.

Compile-tested on OSK5912 (OMAP1), H4 and 2430SDP (OMAP2), and
BeagleBoard (OMAP3). Boot-tested on 2430SDP and BeagleBoard.

Applies on top of series B, posted earlier.


- Paul

---

Paul Walmsley (12):
OMAP3 clock: disable DPLL autoidle while waiting for DPLL to lock
OMAP3 clock: avoid invalid FREQSEL values during DPLL rate rounding
OMAP3 clock: optimize DPLL rate rounding algorithm
OMAP3 clock: remove unnecessary dpll_data dereferences
OMAP3 clock: fix non-CORE DPLL rate assignment bugs
OMAP3 clock: put DPLL into bypass if bypass rate = clk->rate, not hardware rate
OMAP3 clock: recalculate DPLL subtree after bypass entry/exit
OMAP3 clock: DPLLs should enter bypass if new rate is sys_ck
OMAP3 clock: note the bypass source clock for DPLLs
OMAP3 clock: convert dpll_data.idlest_bit to idlest_mask
OMAP3 clock: DPLL{1,2}_FCLK clksel can divide by 4
OMAP3 clock: fix DPLL jitter correction and rate programming

Tero Kristo (1):
OMAP2/3 clock: fix DPLL rate calculation


arch/arm/mach-omap2/clock.c | 162 +++++++++++++++++++-----
arch/arm/mach-omap2/clock.h | 15 ++
arch/arm/mach-omap2/clock24xx.c | 43 ++++--
arch/arm/mach-omap2/clock24xx.h | 3
arch/arm/mach-omap2/clock34xx.c | 95 +++++++++-----
arch/arm/mach-omap2/clock34xx.h | 213 +++++++------------------------
arch/arm/mach-omap2/memory.c | 2
arch/arm/plat-omap/include/mach/clock.h | 15 +-
8 files changed, 296 insertions(+), 252 deletions(-)

text data bss dec hex filename
3241711 163872 100912 3506495 35813f vmlinux.beagle.orig
3241515 163680 100912 3506107 357fbb vmlinux.beagle


2009-01-28 20:47:19

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 01/13] OMAP3 clock: fix DPLL jitter correction and rate programming

Fix DPLL jitter correction programming. Previously,
omap3_noncore_dpll_program() stored the FREQSEL jitter correction
parameter to the wrong register. This caused jitter correction to be set
incorrectly and also caused the DPLL divider to be programmed incorrectly.

Also, fix DPLL divider programming. An off-by-one error existed in
omap3_noncore_dpll_program(), causing DPLLs to be programmed with a higher
divider than intended.

linux-omap source commit is 5c0ec88a2145cdf2f2c9cc5fae49635c4c2476c7.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 13 ++++++++-----
1 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index b6fe2c9..ff43058 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -346,14 +346,17 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
_omap3_noncore_dpll_bypass(clk);

+ /* Set jitter correction */
+ v = __raw_readl(dd->control_reg);
+ v &= ~dd->freqsel_mask;
+ v |= freqsel << __ffs(dd->freqsel_mask);
+ __raw_writel(v, dd->control_reg);
+
+ /* Set DPLL multiplier, divider */
v = __raw_readl(dd->mult_div1_reg);
v &= ~(dd->mult_mask | dd->div1_mask);
-
- /* Set mult (M), div1 (N), freqsel */
v |= m << __ffs(dd->mult_mask);
- v |= n << __ffs(dd->div1_mask);
- v |= freqsel << __ffs(dd->freqsel_mask);
-
+ v |= (n - 1) << __ffs(dd->div1_mask);
__raw_writel(v, dd->mult_div1_reg);

/* We let the clock framework set the other output dividers later */

2009-01-28 20:47:41

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 04/13] OMAP3 clock: note the bypass source clock for DPLLs

Most 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.

linux-omap source commit is 7d06c48dd63545c6d5ea71c80b9049b55c6dab6e.

Signed-off-by: Paul Walmsley <[email protected]>
Cc: Kevin Hilman <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.h | 9 +++++++++
arch/arm/plat-omap/include/mach/clock.h | 1 +
2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index 277b221..5d20075 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -48,6 +48,10 @@ static int omap3_noncore_dpll_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
@@ -304,6 +308,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
@@ -376,6 +381,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
@@ -436,6 +442,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
@@ -626,6 +633,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
@@ -968,6 +976,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
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index 666f52b..0fb4271 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -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;

2009-01-28 20:51:38

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 03/13] OMAP3 clock: convert dpll_data.idlest_bit to idlest_mask

Convert struct dpll_data.idlest_bit field to idlest_mask. Needed since
OMAP2 uses two bits for DPLL IDLEST rather than one.

While here, add the missing idlest_* fields for DPLL3.

linux-omap source commits are 25bab0f176b0a97be18a1b38153f266c3a155784
and b0f7fd17db2aaf8e6e9a2732ae3f4de0874db01c.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 6 ++----
arch/arm/mach-omap2/clock34xx.h | 10 ++++++----
arch/arm/plat-omap/include/mach/clock.h | 4 ++--
3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index ff43058..0f493f5 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -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);
diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index 9d123c6..277b221 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -303,7 +303,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
@@ -375,7 +375,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
@@ -434,6 +434,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
@@ -623,7 +625,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
@@ -965,7 +967,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
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index c6762e9..666f52b 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -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
};


2009-01-28 20:51:57

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 02/13] OMAP3 clock: DPLL{1,2}_FCLK clksel can divide by 4

OMAP34xx ES2 TRM Delta G to H states that the divider for DPLL1_FCLK and
DPLL2_FCLK can divide by 4 in addition to dividing by 1 and 2. Encode this
into the OMAP3 clock framework.

linux-omap source commit is 050684c18f2ea0b08fdd5233a0cd3c7f96e00a0e.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.h | 20 ++++++++++++++++----
1 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index 781da12..9d123c6 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -1102,8 +1102,15 @@ static struct clk corex2_fck = {

/* DPLL power domain clock controls */

-static const struct clksel div2_core_clksel[] = {
- { .parent = &core_ck, .rates = div2_rates },
+static const struct clksel_rate div4_rates[] = {
+ { .div = 1, .val = 1, .flags = RATE_IN_343X | DEFAULT_RATE },
+ { .div = 2, .val = 2, .flags = RATE_IN_343X },
+ { .div = 4, .val = 4, .flags = RATE_IN_343X },
+ { .div = 0 }
+};
+
+static const struct clksel div4_core_clksel[] = {
+ { .parent = &core_ck, .rates = div4_rates },
{ .parent = NULL }
};

@@ -1113,7 +1120,7 @@ static struct clk dpll1_fck = {
.init = &omap2_init_clksel_parent,
.clksel_reg = _OMAP34XX_CM_REGADDR(MPU_MOD, OMAP3430_CM_CLKSEL1_PLL),
.clksel_mask = OMAP3430_MPU_CLK_SRC_MASK,
- .clksel = div2_core_clksel,
+ .clksel = div4_core_clksel,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "cm_clkdm" },
@@ -1191,7 +1198,7 @@ static struct clk dpll2_fck = {
.init = &omap2_init_clksel_parent,
.clksel_reg = _OMAP34XX_CM_REGADDR(OMAP3430_IVA2_MOD, OMAP3430_CM_CLKSEL1_PLL),
.clksel_mask = OMAP3430_IVA2_CLK_SRC_MASK,
- .clksel = div2_core_clksel,
+ .clksel = div4_core_clksel,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "cm_clkdm" },
@@ -1228,6 +1235,11 @@ static struct clk iva2_ck = {

/* Common interface clocks */

+static const struct clksel div2_core_clksel[] = {
+ { .parent = &core_ck, .rates = div2_rates },
+ { .parent = NULL }
+};
+
static struct clk l3_ick = {
.name = "l3_ick",
.parent = &core_ck,

2009-01-28 20:52:57

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 06/13] OMAP3 clock: DPLLs should enter bypass if new rate is sys_ck

This 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.

linux-omap source commit is 9c909ac90902a9c0ecca2bc9ac36289cb84990f5.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 40 ++++++++++++++++++++++++++++-----------
1 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index c5efd7e..0f058c3 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -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);


2009-01-28 20:53:30

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 05/13] OMAP2/3 clock: fix DPLL rate calculation

From: Tero Kristo <[email protected]>

Removes 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.

Also, previously, the OMAP2xxx code returned the wrong value for the
DPLL rate under some conditions. Move the CORE_CLK rate recalculation
to clock24xx.c:omap2xxx_clk_get_core_rate().

This patch is a collaboration between Tero Kristo <[email protected]>
and Paul Walmsley <[email protected]>. Thanks to Peter de Schrijver
<[email protected]> for help debugging and Kevin Hilman
<[email protected]> for reporting the OMAP2 build problems with
an earlier version of this patch.

linux-omap source commits are 3241b19e23fb95cf03d0e9747273458cfb82d7aa,
88b5d9b6872be2d944895ca0f6c236f262e4e8d3, and
18a550088456d3c39e4eccf901ddd8669dbe6ee3.

Signed-off-by: Tero Kristo <[email protected]>
Signed-off-by: Paul Walmsley <[email protected]>
Cc: Kevin Hilman <[email protected]>
Cc: Peter de Schrijver <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock.c | 43 +++++++-
arch/arm/mach-omap2/clock.h | 15 +++
arch/arm/mach-omap2/clock24xx.c | 43 +++++---
arch/arm/mach-omap2/clock24xx.h | 2
arch/arm/mach-omap2/clock34xx.c | 4 -
arch/arm/mach-omap2/clock34xx.h | 169 +++----------------------------
arch/arm/mach-omap2/memory.c | 2
arch/arm/plat-omap/include/mach/clock.h | 13 +-
8 files changed, 105 insertions(+), 186 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index d902309..b517469 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -136,22 +136,53 @@ 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->control_reg);
+ v &= dd->enable_mask;
+ v >>= __ffs(dd->enable_mask);
+
+ if (cpu_is_omap24xx()) {
+
+ if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
+ v == OMAP2XXX_EN_DPLL_FRBYPASS)
+ return clk->parent->rate;
+
+ } else if (cpu_is_omap34xx()) {
+
+ if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
+ v == OMAP3XXX_EN_DPLL_FRBYPASS)
+ 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;
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 3fa2e26..72bb320 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -21,6 +21,21 @@
/* The maximum error between a target DPLL rate and the rounded rate in Hz */
#define DEFAULT_DPLL_RATE_TOLERANCE 50000

+/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */
+#define CORE_CLK_SRC_32K 0x0
+#define CORE_CLK_SRC_DPLL 0x1
+#define CORE_CLK_SRC_DPLL_X2 0x2
+
+/* OMAP2xxx CM_CLKEN_PLL.EN_DPLL bits - for omap2_get_dpll_rate() */
+#define OMAP2XXX_EN_DPLL_LPBYPASS 0x1
+#define OMAP2XXX_EN_DPLL_FRBYPASS 0x2
+#define OMAP2XXX_EN_DPLL_LOCKED 0x3
+
+/* OMAP3xxx CM_CLKEN_PLL*.EN_*_DPLL bits - for omap2_get_dpll_rate() */
+#define OMAP3XXX_EN_DPLL_LPBYPASS 0x5
+#define OMAP3XXX_EN_DPLL_FRBYPASS 0x6
+#define OMAP3XXX_EN_DPLL_LOCKED 0x7
+
int omap2_clk_init(void);
int omap2_clk_enable(struct clk *clk);
void omap2_clk_disable(struct clk *clk);
diff --git a/arch/arm/mach-omap2/clock24xx.c b/arch/arm/mach-omap2/clock24xx.c
index 8533c3a..a2c13ef 100644
--- a/arch/arm/mach-omap2/clock24xx.c
+++ b/arch/arm/mach-omap2/clock24xx.c
@@ -61,19 +61,32 @@ static struct clk *sclk;
* Omap24xx specific clock functions
*-------------------------------------------------------------------------*/

-/* This actually returns the rate of core_ck, not dpll_ck. */
-static u32 omap2_get_dpll_rate_24xx(struct clk *tclk)
+/**
+ * omap2xxx_clk_get_core_rate - return the CORE_CLK rate
+ * @clk: pointer to the combined dpll_ck + core_ck (currently "dpll_ck")
+ *
+ * Returns the CORE_CLK rate. CORE_CLK can have one of three rate
+ * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz
+ * (the latter is unusual). This currently should be called with
+ * struct clk *dpll_ck, which is a composite clock of dpll_ck and
+ * core_ck.
+ */
+static u32 omap2xxx_clk_get_core_rate(struct clk *clk)
{
- long long dpll_clk;
- u8 amult;
+ long long core_clk;
+ u32 v;

- dpll_clk = omap2_get_dpll_rate(tclk);
+ core_clk = omap2_get_dpll_rate(clk);

- amult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
- amult &= OMAP24XX_CORE_CLK_SRC_MASK;
- dpll_clk *= amult;
+ v = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
+ v &= OMAP24XX_CORE_CLK_SRC_MASK;

- return dpll_clk;
+ if (v == CORE_CLK_SRC_32K)
+ core_clk = 32768;
+ else
+ core_clk *= v;
+
+ return core_clk;
}

static int omap2_enable_osc_ck(struct clk *clk)
@@ -173,7 +186,7 @@ static long omap2_dpllcore_round_rate(unsigned long target_rate)

static void omap2_dpllcore_recalc(struct clk *clk)
{
- clk->rate = omap2_get_dpll_rate_24xx(clk);
+ clk->rate = omap2xxx_clk_get_core_rate(clk);

propagate_rate(clk);
}
@@ -188,7 +201,7 @@ static int omap2_reprogram_dpllcore(struct clk *clk, unsigned long rate)
int ret = -EINVAL;

local_irq_save(flags);
- cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck);
+ cur_rate = omap2xxx_clk_get_core_rate(&dpll_ck);
mult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
mult &= OMAP24XX_CORE_CLK_SRC_MASK;

@@ -326,7 +339,7 @@ static int omap2_select_table_rate(struct clk *clk, unsigned long rate)
}

curr_prcm_set = prcm;
- cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck);
+ cur_rate = omap2xxx_clk_get_core_rate(&dpll_ck);

if (prcm->dpll_speed == cur_rate / 2) {
omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL, 1);
@@ -535,8 +548,10 @@ 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->control_reg);
+ }
}

int __init omap2_clk_init(void)
@@ -579,7 +594,7 @@ int __init omap2_clk_init(void)
}

/* Check the MPU rate set by bootloader */
- clkrate = omap2_get_dpll_rate_24xx(&dpll_ck);
+ clkrate = omap2xxx_clk_get_core_rate(&dpll_ck);
for (prcm = rate_table; prcm->mpu_speed; prcm++) {
if (!(prcm->flags & cpu_mask))
continue;
diff --git a/arch/arm/mach-omap2/clock24xx.h b/arch/arm/mach-omap2/clock24xx.h
index 2587c3d..f6177e3 100644
--- a/arch/arm/mach-omap2/clock24xx.h
+++ b/arch/arm/mach-omap2/clock24xx.h
@@ -680,6 +680,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,
+ .control_reg = _CM_REG_OFFSET(PLL_MOD, CM_CLKEN),
+ .enable_mask = OMAP24XX_EN_DPLL_MASK,
.max_multiplier = 1024,
.max_divider = 16,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index 0f493f5..c5efd7e 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -512,11 +512,11 @@ static void omap3_clkoutx2_recalc(struct clk *clk)

dd = pclk->dpll_data;

- WARN_ON(!dd->control_reg || !dd->enable_mask);
+ WARN_ON(!dd->enable_mask);

v = __raw_readl(dd->control_reg) & dd->enable_mask;
v >>= __ffs(dd->enable_mask);
- if (v != DPLL_LOCKED)
+ if (v != OMAP3XXX_EN_DPLL_LOCKED)
clk->rate = clk->parent->rate;
else
clk->rate = clk->parent->rate * 2;
diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index 5d20075..0c50a2d 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -257,16 +257,6 @@ static struct clk sys_clkout1 = {

/* CM CLOCKS */

-static const struct clksel_rate dpll_bypass_rates[] = {
- { .div = 1, .val = 0, .flags = RATE_IN_343X | DEFAULT_RATE },
- { .div = 0 }
-};
-
-static const struct clksel_rate dpll_locked_rates[] = {
- { .div = 1, .val = 1, .flags = RATE_IN_343X | DEFAULT_RATE },
- { .div = 0 }
-};
-
static const struct clksel_rate div16_dpll_rates[] = {
{ .div = 1, .val = 1, .flags = RATE_IN_343X | DEFAULT_RATE },
{ .div = 2, .val = 2, .flags = RATE_IN_343X },
@@ -529,40 +519,22 @@ static struct clk dpll3_m2_ck = {
.recalc = &omap2_clksel_recalc,
};

-static const struct clksel core_ck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll3_m2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
static struct clk core_ck = {
.name = "core_ck",
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_CORE_CLK_MASK,
- .clksel = core_ck_clksel,
+ .parent = &dpll3_m2_ck,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "cm_clkdm" },
- .recalc = &omap2_clksel_recalc,
-};
-
-static const struct clksel dpll3_m2x2_ck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll3_x2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
+ .recalc = &followparent_recalc,
};

static struct clk dpll3_m2x2_ck = {
.name = "dpll3_m2x2_ck",
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_CORE_CLK_MASK,
- .clksel = dpll3_m2x2_ck_clksel,
+ .parent = &dpll3_x2_ck,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "dpll3_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

/* The PWRDN bit is apparently only available on 3430ES2 and above */
@@ -596,23 +568,13 @@ static struct clk dpll3_m3x2_ck = {
.recalc = &omap3_clkoutx2_recalc,
};

-static const struct clksel emu_core_alwon_ck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll3_m3x2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
static struct clk emu_core_alwon_ck = {
.name = "emu_core_alwon_ck",
.parent = &dpll3_m3x2_ck,
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_CORE_CLK_MASK,
- .clksel = emu_core_alwon_ck_clksel,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "dpll3_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

/* DPLL4 */
@@ -696,12 +658,6 @@ static struct clk dpll4_m2x2_ck = {
.recalc = &omap3_clkoutx2_recalc,
};

-static const struct clksel omap_96m_alwon_fck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll4_m2x2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
/*
* DPLL4 generates DPLL4_M2X2_CLK which is then routed into the PRM as
* PRM_96M_ALWON_(F)CLK. Two clocks then emerge from the PRM:
@@ -711,14 +667,10 @@ static const struct clksel omap_96m_alwon_fck_clksel[] = {
static struct clk omap_96m_alwon_fck = {
.name = "omap_96m_alwon_fck",
.parent = &dpll4_m2x2_ck,
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_PERIPH_CLK_MASK,
- .clksel = omap_96m_alwon_fck_clksel,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "prm_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

static struct clk cm_96m_fck = {
@@ -785,25 +737,6 @@ static struct clk dpll4_m3x2_ck = {
.recalc = &omap3_clkoutx2_recalc,
};

-static const struct clksel virt_omap_54m_fck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll4_m3x2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
-static struct clk virt_omap_54m_fck = {
- .name = "virt_omap_54m_fck",
- .parent = &dpll4_m3x2_ck,
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_PERIPH_CLK_MASK,
- .clksel = virt_omap_54m_fck_clksel,
- .flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
- PARENT_CONTROLS_CLOCK,
- .clkdm = { .name = "dpll4_clkdm" },
- .recalc = &omap2_clksel_recalc,
-};
-
static const struct clksel_rate omap_54m_d4m3x2_rates[] = {
{ .div = 1, .val = 0, .flags = RATE_IN_343X | DEFAULT_RATE },
{ .div = 0 }
@@ -815,7 +748,7 @@ static const struct clksel_rate omap_54m_alt_rates[] = {
};

static const struct clksel omap_54m_clksel[] = {
- { .parent = &virt_omap_54m_fck, .rates = omap_54m_d4m3x2_rates },
+ { .parent = &dpll4_m3x2_ck, .rates = omap_54m_d4m3x2_rates },
{ .parent = &sys_altclk, .rates = omap_54m_alt_rates },
{ .parent = NULL }
};
@@ -1013,25 +946,6 @@ static struct clk dpll5_m2_ck = {
.recalc = &omap2_clksel_recalc,
};

-static const struct clksel omap_120m_fck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll5_m2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
-static struct clk omap_120m_fck = {
- .name = "omap_120m_fck",
- .parent = &dpll5_m2_ck,
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST2),
- .clksel_mask = OMAP3430ES2_ST_PERIPH2_CLK_MASK,
- .clksel = omap_120m_fck_clksel,
- .flags = CLOCK_IN_OMAP3430ES2 | RATE_PROPAGATES |
- PARENT_CONTROLS_CLOCK,
- .clkdm = { .name = "dpll5_clkdm" },
- .recalc = &omap2_clksel_recalc,
-};
-
/* CM EXTERNAL CLOCK OUTPUTS */

static const struct clksel_rate clkout2_src_core_rates[] = {
@@ -1138,29 +1052,13 @@ static struct clk dpll1_fck = {
.recalc = &omap2_clksel_recalc,
};

-/*
- * MPU clksel:
- * If DPLL1 is locked, mpu_ck derives from DPLL1; otherwise, mpu_ck
- * derives from the high-frequency bypass clock originating from DPLL3,
- * called 'dpll1_fck'
- */
-static const struct clksel mpu_clksel[] = {
- { .parent = &dpll1_fck, .rates = dpll_bypass_rates },
- { .parent = &dpll1_x2m2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
static struct clk mpu_ck = {
.name = "mpu_ck",
.parent = &dpll1_x2m2_ck,
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(MPU_MOD, OMAP3430_CM_IDLEST_PLL),
- .clksel_mask = OMAP3430_ST_MPU_CLK_MASK,
- .clksel = mpu_clksel,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES |
PARENT_CONTROLS_CLOCK,
.clkdm = { .name = "mpu_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

/* arm_fck is divided by two when DPLL1 locked; otherwise, passthrough mpu_ck */
@@ -1216,32 +1114,15 @@ static struct clk dpll2_fck = {
.recalc = &omap2_clksel_recalc,
};

-/*
- * IVA2 clksel:
- * If DPLL2 is locked, iva2_ck derives from DPLL2; otherwise, iva2_ck
- * derives from the high-frequency bypass clock originating from DPLL3,
- * called 'dpll2_fck'
- */
-
-static const struct clksel iva2_clksel[] = {
- { .parent = &dpll2_fck, .rates = dpll_bypass_rates },
- { .parent = &dpll2_m2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
static struct clk iva2_ck = {
.name = "iva2_ck",
.parent = &dpll2_m2_ck,
.init = &omap2_init_clksel_parent,
.enable_reg = _OMAP34XX_CM_REGADDR(OMAP3430_IVA2_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_SHIFT,
- .clksel_reg = _OMAP34XX_CM_REGADDR(OMAP3430_IVA2_MOD,
- OMAP3430_CM_IDLEST_PLL),
- .clksel_mask = OMAP3430_ST_IVA2_CLK_MASK,
- .clksel = iva2_clksel,
.flags = CLOCK_IN_OMAP343X | RATE_PROPAGATES,
.clkdm = { .name = "iva2_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

/* Common interface clocks */
@@ -1473,7 +1354,7 @@ static struct clk ts_fck = {

static struct clk usbtll_fck = {
.name = "usbtll_fck",
- .parent = &omap_120m_fck,
+ .parent = &dpll5_m2_ck,
.enable_reg = _OMAP34XX_CM_REGADDR(CORE_MOD, OMAP3430ES2_CM_FCLKEN3),
.enable_bit = OMAP3430ES2_EN_USBTLL_SHIFT,
.flags = CLOCK_IN_OMAP3430ES2,
@@ -2207,24 +2088,14 @@ static struct clk des1_ick = {
};

/* DSS */
-static const struct clksel dss1_alwon_fck_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll4_m4x2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
static struct clk dss1_alwon_fck = {
.name = "dss1_alwon_fck",
.parent = &dpll4_m4x2_ck,
- .init = &omap2_init_clksel_parent,
.enable_reg = _OMAP34XX_CM_REGADDR(OMAP3430_DSS_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_DSS1_SHIFT,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_PERIPH_CLK_MASK,
- .clksel = dss1_alwon_fck_clksel,
.flags = CLOCK_IN_OMAP343X,
.clkdm = { .name = "dss_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

static struct clk dss_tv_fck = {
@@ -2270,24 +2141,14 @@ static struct clk dss_ick = {

/* CAM */

-static const struct clksel cam_mclk_clksel[] = {
- { .parent = &sys_ck, .rates = dpll_bypass_rates },
- { .parent = &dpll4_m5x2_ck, .rates = dpll_locked_rates },
- { .parent = NULL }
-};
-
static struct clk cam_mclk = {
.name = "cam_mclk",
.parent = &dpll4_m5x2_ck,
- .init = &omap2_init_clksel_parent,
- .clksel_reg = _OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST),
- .clksel_mask = OMAP3430_ST_PERIPH_CLK_MASK,
- .clksel = cam_mclk_clksel,
.enable_reg = _OMAP34XX_CM_REGADDR(OMAP3430_CAM_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_CAM_SHIFT,
.flags = CLOCK_IN_OMAP343X,
.clkdm = { .name = "cam_clkdm" },
- .recalc = &omap2_clksel_recalc,
+ .recalc = &followparent_recalc,
};

static struct clk cam_ick = {
@@ -2315,7 +2176,7 @@ static struct clk csi2_96m_fck = {

static struct clk usbhost_120m_fck = {
.name = "usbhost_120m_fck",
- .parent = &omap_120m_fck,
+ .parent = &dpll5_m2_ck,
.enable_reg = _OMAP34XX_CM_REGADDR(OMAP3430ES2_USBHOST_MOD, CM_FCLKEN),
.enable_bit = OMAP3430ES2_EN_USBHOST2_SHIFT,
.flags = CLOCK_IN_OMAP3430ES2,
@@ -2364,7 +2225,7 @@ static const struct clksel_rate usim_120m_rates[] = {

static const struct clksel usim_clksel[] = {
{ .parent = &omap_96m_fck, .rates = usim_96m_rates },
- { .parent = &omap_120m_fck, .rates = usim_120m_rates },
+ { .parent = &dpll5_m2_ck, .rates = usim_120m_rates },
{ .parent = &sys_ck, .rates = div2_rates },
{ .parent = NULL },
};
@@ -3176,7 +3037,6 @@ static struct clk *onchip_34xx_clks[] __initdata = {
&omap_96m_alwon_fck,
&omap_96m_fck,
&cm_96m_fck,
- &virt_omap_54m_fck,
&omap_54m_fck,
&omap_48m_fck,
&omap_12m_fck,
@@ -3193,7 +3053,6 @@ static struct clk *onchip_34xx_clks[] __initdata = {
&emu_per_alwon_ck,
&dpll5_ck,
&dpll5_m2_ck,
- &omap_120m_fck,
&clkout2_src_ck,
&sys_clkout2,
&corex2_fck,
diff --git a/arch/arm/mach-omap2/memory.c b/arch/arm/mach-omap2/memory.c
index b7669c8..db024fd 100644
--- a/arch/arm/mach-omap2/memory.c
+++ b/arch/arm/mach-omap2/memory.c
@@ -29,6 +29,8 @@

#include "prm.h"

+#include "clock.h"
+
#include "memory.h"
#include "sdrc.h"

diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index 0fb4271..73676ee 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -41,14 +41,14 @@ struct dpll_data {
u16 max_multiplier;
u8 max_divider;
u32 max_tolerance;
- void __iomem *idlest_reg;
- u32 idlest_mask;
struct clk *bypass_clk;
+ void __iomem *control_reg;
+ u32 enable_mask;
# if defined(CONFIG_ARCH_OMAP3)
+ void __iomem *idlest_reg;
+ u32 idlest_mask;
u32 freqsel_mask;
u8 modes;
- void __iomem *control_reg;
- u32 enable_mask;
u8 auto_recal_bit;
u8 recal_en_bit;
u8 recal_st_bit;
@@ -165,9 +165,4 @@ extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table);
#define RATE_IN_24XX (RATE_IN_242X | RATE_IN_243X)


-/* CM_CLKSEL2_PLL.CORE_CLK_SRC options (24XX) */
-#define CORE_CLK_SRC_32K 0
-#define CORE_CLK_SRC_DPLL 1
-#define CORE_CLK_SRC_DPLL_X2 2
-
#endif

2009-01-28 20:54:55

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 07/13] OMAP3 clock: recalculate DPLL subtree after bypass entry/exit

The 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).

linux-omap source commit is 5c6497bc6f4d2d629efa2bcfa6486539e4b5a797.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 17 +++++++++++++++--
1 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index 0f058c3..844fe82 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -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 {


2009-01-28 20:55:19

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 08/13] OMAP3 clock: put DPLL into bypass if bypass rate = clk->rate, not hardware rate

When a non-CORE DPLL is enabled via omap3_noncore_dpll_enable(), use
the user's desired rate in clk->rate to determine whether to put the
DPLL into bypass or lock mode, rather than reading the DPLL's current
idle state from its hardware registers.

This fixes a bug observed when leaving retention. Non-CORE DPLLs were
not being relocked when downstream clocks re-enabled; rather, the DPLL
entered bypass mode.

Problem reported by Tero Kristo <[email protected]>.

linux-omap source commit is 8b1f0bd44fe490ec631230c8c040753a2bda8caa.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
Cc: Tero Kristo <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index 844fe82..424eed6 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -280,9 +280,7 @@ static int omap3_noncore_dpll_enable(struct clk *clk)
if (!dd)
return -EINVAL;

- rate = omap2_get_dpll_rate(clk);
-
- if (dd->bypass_clk->rate == rate)
+ if (clk->rate == dd->bypass_clk->rate)
r = _omap3_noncore_dpll_bypass(clk);
else
r = _omap3_noncore_dpll_lock(clk);

2009-01-28 20:55:45

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 10/13] OMAP3 clock: remove unnecessary dpll_data dereferences

Remove some clutter from omap2_dpll_round_rate().

linux-omap source commit is 4625dceb8583c02a6d67ededc9f6a8347b6b8cb7.

Signed-off-by: Paul Walmsley <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock.c | 23 +++++++++++++----------
1 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index b517469..4c3d962 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -925,19 +925,22 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
int m, n, r, e, scaled_max_m;
unsigned long scaled_rt_rp, new_rate;
int min_e = -1, min_e_m = -1, min_e_n = -1;
+ struct dpll_data *dd;

if (!clk || !clk->dpll_data)
return ~0;

+ dd = clk->dpll_data;
+
pr_debug("clock: starting DPLL round_rate for clock %s, target rate "
"%ld\n", clk->name, target_rate);

scaled_rt_rp = target_rate / (clk->parent->rate / DPLL_SCALE_FACTOR);
- scaled_max_m = clk->dpll_data->max_multiplier * DPLL_SCALE_FACTOR;
+ scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR;

- clk->dpll_data->last_rounded_rate = 0;
+ dd->last_rounded_rate = 0;

- for (n = clk->dpll_data->max_divider; n >= DPLL_MIN_DIVIDER; n--) {
+ for (n = dd->max_divider; n >= DPLL_MIN_DIVIDER; n--) {

/* Compute the scaled DPLL multiplier, based on the divider */
m = scaled_rt_rp * n;
@@ -957,7 +960,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
"(new_rate = %ld)\n", n, m, e, new_rate);

if (min_e == -1 ||
- min_e >= (int)(abs(e) - clk->dpll_data->rate_tolerance)) {
+ min_e >= (int)(abs(e) - dd->rate_tolerance)) {
min_e = e;
min_e_m = m;
min_e_n = n;
@@ -980,17 +983,17 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
return ~0;
}

- clk->dpll_data->last_rounded_m = min_e_m;
- clk->dpll_data->last_rounded_n = min_e_n;
- clk->dpll_data->last_rounded_rate =
- _dpll_compute_new_rate(clk->parent->rate, min_e_m, min_e_n);
+ dd->last_rounded_m = min_e_m;
+ dd->last_rounded_n = min_e_n;
+ dd->last_rounded_rate = _dpll_compute_new_rate(clk->parent->rate,
+ min_e_m, min_e_n);

pr_debug("clock: final least error: e = %d, m = %d, n = %d\n",
min_e, min_e_m, min_e_n);
pr_debug("clock: final rate: %ld (target rate: %ld)\n",
- clk->dpll_data->last_rounded_rate, target_rate);
+ dd->last_rounded_rate, target_rate);

- return clk->dpll_data->last_rounded_rate;
+ return dd->last_rounded_rate;
}

/*-------------------------------------------------------------------------

2009-01-28 20:56:07

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 12/13] OMAP3 clock: avoid invalid FREQSEL values during DPLL rate rounding

The DPLL FREQSEL jitter correction bits are set based on a table in
the 34xx TRM, Table 4-38, according to the DPLL's internal clock
frequency "Fint." Several Fint frequency ranges are missing from this
table. Previously, we allowed these Fint frequency ranges to be
selected in the rate rounding code, but did not change the FREQSEL bits.
Correspondence with the OMAP hardware team indicates that Fint values
not in the table should not be used. So, prevent them from being
selected during DPLL rate rounding. This removes warnings and also
can prevent the chip from locking up.

The first pass through the rate rounding code will update the DPLL max
and min dividers appropriately, so later rate rounding passes will run
faster than the first.

Peter de Schrijver <[email protected]> put up with several
test cycles of this patch - thanks Peter.

linux-omap source commit is f9c1b82f55b60fc39eaa6e7aa1fbe380c0ffe2e9.

Signed-off-by: Paul Walmsley <[email protected]>
Cc: Peter de Schrijver <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock.c | 67 ++++++++++++++++++++++++++++++-
arch/arm/mach-omap2/clock24xx.h | 1
arch/arm/mach-omap2/clock34xx.h | 5 ++
arch/arm/plat-omap/include/mach/clock.h | 1
4 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index cd13972..e57694f 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -59,12 +59,68 @@
#define DPLL_ROUNDING_VAL ((DPLL_SCALE_BASE / 2) * \
(DPLL_SCALE_FACTOR / DPLL_SCALE_BASE))

+/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */
+#define DPLL_FINT_BAND1_MIN 750000
+#define DPLL_FINT_BAND1_MAX 2100000
+#define DPLL_FINT_BAND2_MIN 7500000
+#define DPLL_FINT_BAND2_MAX 21000000
+
+/* _dpll_test_fint() return codes */
+#define DPLL_FINT_UNDERFLOW -1
+#define DPLL_FINT_INVALID -2
+
u8 cpu_mask;

/*-------------------------------------------------------------------------
* OMAP2/3 specific clock functions
*-------------------------------------------------------------------------*/

+/*
+ * _dpll_test_fint - test whether an Fint value is valid for the DPLL
+ * @clk: DPLL struct clk to test
+ * @n: divider value (N) to test
+ *
+ * Tests whether a particular divider @n will result in a valid DPLL
+ * internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter
+ * Correction". Returns 0 if OK, -1 if the enclosing loop can terminate
+ * (assuming that it is counting N upwards), or -2 if the enclosing loop
+ * should skip to the next iteration (again assuming N is increasing).
+ */
+static int _dpll_test_fint(struct clk *clk, u8 n)
+{
+ struct dpll_data *dd;
+ long fint;
+ int ret = 0;
+
+ dd = clk->dpll_data;
+
+ /* DPLL divider must result in a valid jitter correction val */
+ fint = clk->parent->rate / (n + 1);
+ if (fint < DPLL_FINT_BAND1_MIN) {
+
+ pr_debug("rejecting n=%d due to Fint failure, "
+ "lowering max_divider\n", n);
+ dd->max_divider = n;
+ ret = DPLL_FINT_UNDERFLOW;
+
+ } else if (fint > DPLL_FINT_BAND1_MAX &&
+ fint < DPLL_FINT_BAND2_MIN) {
+
+ pr_debug("rejecting n=%d due to Fint failure\n", n);
+ ret = DPLL_FINT_INVALID;
+
+ } else if (fint > DPLL_FINT_BAND2_MAX) {
+
+ pr_debug("rejecting n=%d due to Fint failure, "
+ "boosting min_divider\n", n);
+ dd->min_divider = n;
+ ret = DPLL_FINT_INVALID;
+
+ }
+
+ return ret;
+}
+
/**
* omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
* @clk: OMAP clock struct ptr to use
@@ -940,7 +996,14 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)

dd->last_rounded_rate = 0;

- for (n = DPLL_MIN_DIVIDER; n <= dd->max_divider; n++) {
+ for (n = dd->min_divider; n <= dd->max_divider; n++) {
+
+ /* Is the (input clk, divider) pair valid for the DPLL? */
+ r = _dpll_test_fint(clk, n);
+ if (r == DPLL_FINT_UNDERFLOW)
+ break;
+ else if (r == DPLL_FINT_INVALID)
+ continue;

/* Compute the scaled DPLL multiplier, based on the divider */
m = scaled_rt_rp * n;
@@ -974,7 +1037,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
pr_debug("clock: found new least error %d\n", min_e);

/* We found good settings -- bail out now */
- if (min_e <= clk->dpll_data->rate_tolerance)
+ if (min_e <= dd->rate_tolerance)
break;
}
}
diff --git a/arch/arm/mach-omap2/clock24xx.h b/arch/arm/mach-omap2/clock24xx.h
index f6177e3..8de312a 100644
--- a/arch/arm/mach-omap2/clock24xx.h
+++ b/arch/arm/mach-omap2/clock24xx.h
@@ -683,6 +683,7 @@ static struct dpll_data dpll_dd = {
.control_reg = _CM_REG_OFFSET(PLL_MOD, CM_CLKEN),
.enable_mask = OMAP24XX_EN_DPLL_MASK,
.max_multiplier = 1024,
+ .min_divider = 1,
.max_divider = 16,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index 0c50a2d..00ac92f 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -300,6 +300,7 @@ static struct dpll_data dpll1_dd = {
.idlest_mask = OMAP3430_ST_MPU_CLK_MASK,
.bypass_clk = &dpll1_fck,
.max_multiplier = OMAP3_MAX_DPLL_MULT,
+ .min_divider = 1,
.max_divider = OMAP3_MAX_DPLL_DIV,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
@@ -373,6 +374,7 @@ static struct dpll_data dpll2_dd = {
.idlest_mask = OMAP3430_ST_IVA2_CLK_MASK,
.bypass_clk = &dpll2_fck,
.max_multiplier = OMAP3_MAX_DPLL_MULT,
+ .min_divider = 1,
.max_divider = OMAP3_MAX_DPLL_DIV,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
@@ -434,6 +436,7 @@ static struct dpll_data dpll3_dd = {
.idlest_mask = OMAP3430_ST_CORE_CLK_MASK,
.bypass_clk = &sys_ck,
.max_multiplier = OMAP3_MAX_DPLL_MULT,
+ .min_divider = 1,
.max_divider = OMAP3_MAX_DPLL_DIV,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
@@ -597,6 +600,7 @@ static struct dpll_data dpll4_dd = {
.idlest_mask = OMAP3430_ST_PERIPH_CLK_MASK,
.bypass_clk = &sys_ck,
.max_multiplier = OMAP3_MAX_DPLL_MULT,
+ .min_divider = 1,
.max_divider = OMAP3_MAX_DPLL_DIV,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
@@ -911,6 +915,7 @@ static struct dpll_data dpll5_dd = {
.idlest_mask = OMAP3430ES2_ST_PERIPH2_CLK_MASK,
.bypass_clk = &sys_ck,
.max_multiplier = OMAP3_MAX_DPLL_MULT,
+ .min_divider = 1,
.max_divider = OMAP3_MAX_DPLL_DIV,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index 73676ee..f6adf39 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -39,6 +39,7 @@ struct dpll_data {
unsigned long last_rounded_rate;
unsigned int rate_tolerance;
u16 max_multiplier;
+ u8 min_divider;
u8 max_divider;
u32 max_tolerance;
struct clk *bypass_clk;

2009-01-28 20:56:37

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 09/13] OMAP3 clock: fix non-CORE DPLL rate assignment bugs

Commit 8b1f0bd44fe490ec631230c8c040753a2bda8caa introduced a bug that
caused non-CORE DPLL rates to be incorrectly set on boot in
omap3_noncore_dpll_enable(). Debugged by Tomi Valkeinen
<[email protected]> - thanks Tomi.

Also fix omap3_noncore_dpll_set_rate() to assign clk->rate after a
DPLL reprogram.

Tested on 3430SDP.

linux-omap source commit is 2ac1da8c787f73f067e717408e631501ba60aabc.

Signed-off-by: Paul Walmsley <[email protected]>
Cc: Tomi Valkeinen <[email protected]>
Cc: Rick Bronson <[email protected]>
Cc: Timo Kokkonen <[email protected]>
Cc: Sakari Poussa <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index 424eed6..c943043 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -270,7 +270,6 @@ 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)
@@ -286,7 +285,7 @@ static int omap3_noncore_dpll_enable(struct clk *clk)
r = _omap3_noncore_dpll_lock(clk);

if (!r)
- clk->rate = rate;
+ clk->rate = omap2_get_dpll_rate(clk);

return r;
}
@@ -429,6 +428,9 @@ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
dd->last_rounded_n, freqsel);

+ if (!ret)
+ clk->rate = rate;
+
}

omap3_dpll_recalc(clk);

2009-01-28 20:56:54

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 13/13] OMAP3 clock: disable DPLL autoidle while waiting for DPLL to lock

During _omap3_noncore_dpll_lock(), if a DPLL has no active downstream
clocks and DPLL autoidle is enabled, the DPLL may never lock, since it
will enter autoidle immediately. To resolve this, disable DPLL
autoidle while locking the DPLL, and unconditionally wait for the DPLL
to lock. This fixes some bugs where the kernel would hang when returning
from retention or return the wrong rate for the DPLL.

This patch is a collaboration with Peter de Schrijver
<[email protected]> and Kevin Hilman
<[email protected]>.

linux-omap source commit is 3b7de4be879f1f4f55ae59882a5cbd80f6dcf0f0.

Signed-off-by: Paul Walmsley <[email protected]>
Cc: Peter de Schrijver <[email protected]>
Cc: Kevin Hilman <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock34xx.c | 15 +++++----------
1 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index c943043..33d5a51 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -162,19 +162,14 @@ static int _omap3_noncore_dpll_lock(struct clk *clk)

ai = omap3_dpll_autoidle_read(clk);

+ omap3_dpll_deny_idle(clk);
+
_omap3_dpll_write_clken(clk, DPLL_LOCKED);

- if (ai) {
- /*
- * If no downstream clocks are enabled, CM_IDLEST bit
- * may never become active, so don't wait for DPLL to lock.
- */
- r = 0;
+ r = _omap3_wait_dpll_status(clk, 1);
+
+ if (ai)
omap3_dpll_allow_idle(clk);
- } else {
- r = _omap3_wait_dpll_status(clk, 1);
- omap3_dpll_deny_idle(clk);
- };

return r;
}

2009-01-28 20:57:25

by Paul Walmsley

[permalink] [raw]
Subject: [PATCH C 11/13] OMAP3 clock: optimize DPLL rate rounding algorithm

The previous DPLL rate rounding algorithm counted the divider (N) down
from the maximum to 1. Since we currently use a broad DPLL rate
tolerance, and lower N values are more power-efficient, we can often
bypass several iterations through the loop by counting N upwards from
1.

Peter de Schrijver <[email protected]> put up with several
test cycles of this patch - thanks Peter.

linux-omap source commit is 6f6d82bb2f80fa20a841ac3e95a6f44a5a156188.

Signed-off-by: Paul Walmsley <[email protected]>
Cc: Peter de Schrijver <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
arch/arm/mach-omap2/clock.c | 35 ++++++++++++++++++-----------------
1 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 4c3d962..cd13972 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -46,7 +46,7 @@
#define DPLL_MIN_DIVIDER 1

/* Possible error results from _dpll_test_mult */
-#define DPLL_MULT_UNDERFLOW (1 << 0)
+#define DPLL_MULT_UNDERFLOW -1

/*
* Scale factor to mitigate roundoff errors in DPLL rate rounding.
@@ -874,7 +874,7 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
unsigned long target_rate,
unsigned long parent_rate)
{
- int flags = 0, carry = 0;
+ int r = 0, carry = 0;

/* Unscale m and round if necessary */
if (*m % DPLL_SCALE_FACTOR >= DPLL_ROUNDING_VAL)
@@ -895,13 +895,13 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
if (*m < DPLL_MIN_MULTIPLIER) {
*m = DPLL_MIN_MULTIPLIER;
*new_rate = 0;
- flags = DPLL_MULT_UNDERFLOW;
+ r = DPLL_MULT_UNDERFLOW;
}

if (*new_rate == 0)
*new_rate = _dpll_compute_new_rate(parent_rate, *m, n);

- return flags;
+ return r;
}

/**
@@ -940,21 +940,27 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)

dd->last_rounded_rate = 0;

- for (n = dd->max_divider; n >= DPLL_MIN_DIVIDER; n--) {
+ for (n = DPLL_MIN_DIVIDER; n <= dd->max_divider; n++) {

/* Compute the scaled DPLL multiplier, based on the divider */
m = scaled_rt_rp * n;

/*
- * Since we're counting n down, a m overflow means we can
- * can immediately skip to the next n
+ * Since we're counting n up, a m overflow means we
+ * can bail out completely (since as n increases in
+ * the next iteration, there's no way that m can
+ * increase beyond the current m)
*/
if (m > scaled_max_m)
- continue;
+ break;

r = _dpll_test_mult(&m, n, &new_rate, target_rate,
clk->parent->rate);

+ /* m can't be set low enough for this n - try with a larger n */
+ if (r == DPLL_MULT_UNDERFLOW)
+ continue;
+
e = target_rate - new_rate;
pr_debug("clock: n = %d: m = %d: rate error is %d "
"(new_rate = %ld)\n", n, m, e, new_rate);
@@ -966,16 +972,11 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
min_e_n = n;

pr_debug("clock: found new least error %d\n", min_e);
- }

- /*
- * Since we're counting n down, a m underflow means we
- * can bail out completely (since as n decreases in
- * the next iteration, there's no way that m can
- * increase beyond the current m)
- */
- if (r & DPLL_MULT_UNDERFLOW)
- break;
+ /* We found good settings -- bail out now */
+ if (min_e <= clk->dpll_data->rate_tolerance)
+ break;
+ }
}

if (min_e < 0) {

2009-01-29 10:27:43

by Russell King

[permalink] [raw]
Subject: Re: [PATCH C 05/13] OMAP2/3 clock: fix DPLL rate calculation

On Wed, Jan 28, 2009 at 12:08:23PM -0700, Paul Walmsley wrote:
> + if (cpu_is_omap24xx()) {
> +
> + if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
> + v == OMAP2XXX_EN_DPLL_FRBYPASS)
> + return clk->parent->rate;
> +
> + } else if (cpu_is_omap34xx()) {
> +
> + if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
> + v == OMAP3XXX_EN_DPLL_FRBYPASS)
> + return dd->bypass_clk->rate;
> +
> + }

You shouldn't introduce two ways of doing the same thing. Make both
OMAP2 and OMAP3 behaviour the same so that you have less to think
about when looking at the code.

Also, when accepting patches, try to make sure that they conform to
the coding style, rather than repeatedly committing noisy coding style
cleanup patches.

So, the above should be:

+ if (cpu_is_omap24xx()) {
+ if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
+ v == OMAP2XXX_EN_DPLL_FRBYPASS)
+ return dd->bypass_clk->rate;
+ } else if (cpu_is_omap34xx()) {
+ if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
+ v == OMAP3XXX_EN_DPLL_FRBYPASS)
+ return dd->bypass_clk->rate;
+ }

And this patch should be combined with the previous one which creates
the whole 'bypass_clk' thing. There's not much point to a patch which
just adds an unused field and initializers to a structure.

2009-01-29 10:30:28

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH C 06/13] OMAP3 clock: DPLLs should enter bypass if new rate is sys_ck

On Wed, Jan 28, 2009 at 12:08:26PM -0700, Paul Walmsley wrote:
> static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
> {
> u16 freqsel;
> struct dpll_data *dd;
> + int ret;

So 'ret' is a new variable...

>
> 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);

which is assigned to...

> +

Additional noise.

> + } else {
> +

More noise.

> + 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);

Assigned to again...

> +

More noise.

> + }
>
> omap3_dpll_recalc(clk);
>

But ret is never actually used.

2009-01-29 10:33:21

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH C 07/13] OMAP3 clock: recalculate DPLL subtree after bypass entry/exit

On Wed, Jan 28, 2009 at 12:08:29PM -0700, Paul Walmsley wrote:
> The 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).

Surely this is a bug fix to the previous patch?

2009-01-29 10:35:28

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH C 08/13] OMAP3 clock: put DPLL into bypass if bypass rate = clk->rate, not hardware rate

On Wed, Jan 28, 2009 at 12:08:32PM -0700, Paul Walmsley wrote:
> When a non-CORE DPLL is enabled via omap3_noncore_dpll_enable(), use
> the user's desired rate in clk->rate to determine whether to put the
> DPLL into bypass or lock mode, rather than reading the DPLL's current
> idle state from its hardware registers.
>
> This fixes a bug observed when leaving retention. Non-CORE DPLLs were
> not being relocked when downstream clocks re-enabled; rather, the DPLL
> entered bypass mode.
>
> Problem reported by Tero Kristo <[email protected]>.
>
> linux-omap source commit is 8b1f0bd44fe490ec631230c8c040753a2bda8caa.
>
> Signed-off-by: Paul Walmsley <[email protected]>
> Signed-off-by: Tony Lindgren <[email protected]>
> Cc: Tero Kristo <[email protected]>

Patch 6 did it this way. Patch 7 changed it to use omap2_get_dpll_rate()
and this patch changes it back. What's the point of submitting all this
detail? It's just pure noise. Collapse these three patches into one
please.

2009-01-29 10:38:30

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH C 09/13] OMAP3 clock: fix non-CORE DPLL rate assignment bugs

On Wed, Jan 28, 2009 at 12:08:35PM -0700, Paul Walmsley wrote:
> diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
> index 424eed6..c943043 100644
> --- a/arch/arm/mach-omap2/clock34xx.c
> +++ b/arch/arm/mach-omap2/clock34xx.c
> @@ -270,7 +270,6 @@ 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)
> @@ -286,7 +285,7 @@ static int omap3_noncore_dpll_enable(struct clk *clk)
> r = _omap3_noncore_dpll_lock(clk);
>
> if (!r)
> - clk->rate = rate;
> + clk->rate = omap2_get_dpll_rate(clk);

This whole patch can be viewed as another bug fix to patch 6.

> @@ -429,6 +428,9 @@ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
> ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
> dd->last_rounded_n, freqsel);
>
> + if (!ret)
> + clk->rate = rate;
> +

So this is creating:

if (...) {
ret = something;

if (!ret)
clk->rate = rate;
} else {
ret = something else;

if (!ret)
clk->rate = rate;
}

So why not make it easier to read and more obvious:

if (...) {
ret = something;
} else {
ret = something else;
}

if (!ret)
clk->rate = rate;

> }
>
> omap3_dpll_recalc(clk);
>
>
>
> -------------------------------------------------------------------
> List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
> FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
> Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php

2009-01-29 12:40:03

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH C 06/13] OMAP3 clock: DPLLs should enter bypass if new rate is sys_ck

On Wed, Jan 28, 2009 at 12:08:26PM -0700, Paul Walmsley wrote:
> This 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.

The more I think about this, especially with reference to patch D1, the
more I'm convinced this is not entirely the right approach.

Patch D1 introduces the necessary mechanics to make clk_get_parent()
work. If you use this on a PLL clock, and the PLL is in bypass mode,
it returns the non-bypass clock.

Now, I suspect that your first thought to resolving that would be to
add some complexity to clk_get_parent(). I advise against that.
Instead, arrange for clk->parent to _always_ point at the correct
parent clock, whether the PLL is in bypass mode or not.

In other words, encapsulate all the PLL mechanics localized inside
the PLL handling code and don't let them leak outside that code.

2009-01-30 05:47:52

by Paul Walmsley

[permalink] [raw]
Subject: Re: [PATCH C 06/13] OMAP3 clock: DPLLs should enter bypass if new rate is sys_ck

Hello Russell,

this E-mail responds to your comments on patches C 04 through C 08.
My understanding is that you'd like me to:

1. Compress patches C 04 through C 09;

2. Modify omap3_noncore_dpll_enable() to move the "if (!ret)
clk->rate = rate" outside and below the conditional;

3. Remove some of the blank lines before and after braces in C 06;

4. Modify the DPLL bypass clock handling to change the DPLL's clk->parent,
rather than return the bypass clock rate.

The above all make sense to me, and #4, if it works in practice, should be
a significant improvement over the existing code.

If this is what you're looking for, I'll send a compressed, modified
patch. Would you like it to be based on the codebase post C 03, or
against another commit?

regards,

- Paul