* [PATCH 21/21] ARM: tegra: clock: Miscellaneous clock updates
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Correct max rates for pclk and sclk (Originally fixed by
Dima Zavin <dima@android.com>)
Correct max rate for plla (Originally fixed by
Stephen Warren <swarren@nvidia.com>)
Remove unnecessary no-op set_rate on audio clocks
Add clock lookup entries for grhost, bsea, and vde clocks
Update clock clookup entries for vcp, bsea, and vde clocks
Add shared clock entries for sclk and emc
Add a virtual cop clock to provide a reset op (Originally fixed by
Dima Zavin <dima@android.com>)
Pass set_rate on super clocks through to parent
Fix pllx frequency table entry for 608 MHz
Remove incorrect plla frequency table entries
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/tegra2_clocks.c | 98 ++++++++++++++++++++++++-----------
1 files changed, 67 insertions(+), 31 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 00e348a..7799cba 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -362,11 +362,24 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
return -EINVAL;
}
+/*
+ * Super clocks have "clock skippers" instead of dividers. Dividing using
+ * a clock skipper does not allow the voltage to be scaled down, so instead
+ * adjust the rate of the parent clock. This requires that the parent of a
+ * super clock have no other children, otherwise the rate will change
+ * underneath the other children.
+ */
+static int tegra2_super_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ return clk_set_rate(c->parent, rate);
+}
+
static struct clk_ops tegra_super_ops = {
.init = tegra2_super_clk_init,
.enable = tegra2_super_clk_enable,
.disable = tegra2_super_clk_disable,
.set_parent = tegra2_super_clk_set_parent,
+ .set_rate = tegra2_super_clk_set_rate,
};
/* virtual cpu clock functions */
@@ -435,6 +448,20 @@ static struct clk_ops tegra_cpu_ops = {
.set_rate = tegra2_cpu_clk_set_rate,
};
+/* virtual cop clock functions. Used to acquire the fake 'cop' clock to
+ * reset the COP block (i.e. AVP) */
+static void tegra2_cop_clk_reset(struct clk *c, bool assert)
+{
+ unsigned long reg = assert ? RST_DEVICES_SET : RST_DEVICES_CLR;
+
+ pr_debug("%s %s\n", __func__, assert ? "assert" : "deassert");
+ clk_writel(1 << 1, reg);
+}
+
+static struct clk_ops tegra_cop_ops = {
+ .reset = tegra2_cop_clk_reset,
+};
+
/* bus clock functions */
static void tegra2_bus_clk_init(struct clk *c)
{
@@ -1224,30 +1251,10 @@ static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
return -EINVAL;
}
-static int tegra2_audio_sync_clk_set_rate(struct clk *c, unsigned long rate)
-{
- unsigned long parent_rate;
- if (!c->parent) {
- pr_err("%s: clock has no parent\n", __func__);
- return -EINVAL;
- }
- parent_rate = c->parent->rate;
- if (rate != parent_rate) {
- pr_err("%s: %s/%ld differs from parent %s/%ld\n",
- __func__,
- c->name, rate,
- c->parent->name, parent_rate);
- return -EINVAL;
- }
- c->rate = parent_rate;
- return 0;
-}
-
static struct clk_ops tegra_audio_sync_clk_ops = {
.init = tegra2_audio_sync_clk_init,
.enable = tegra2_audio_sync_clk_enable,
.disable = tegra2_audio_sync_clk_disable,
- .set_rate = tegra2_audio_sync_clk_set_rate,
.set_parent = tegra2_audio_sync_clk_set_parent,
};
@@ -1547,8 +1554,6 @@ static struct clk tegra_pll_p_out4 = {
static struct clk_pll_freq_table tegra_pll_a_freq_table[] = {
{ 28800000, 56448000, 49, 25, 1, 1},
{ 28800000, 73728000, 64, 25, 1, 1},
- { 28800000, 11289600, 49, 25, 1, 1},
- { 28800000, 12288000, 64, 25, 1, 1},
{ 28800000, 24000000, 5, 6, 1, 1},
{ 0, 0, 0, 0, 0, 0 },
};
@@ -1559,7 +1564,7 @@ static struct clk tegra_pll_a = {
.ops = &tegra_pll_ops,
.reg = 0xb0,
.parent = &tegra_pll_p_out1,
- .max_rate = 56448000,
+ .max_rate = 73728000,
.u.pll = {
.input_min = 2000000,
.input_max = 31000000,
@@ -1579,7 +1584,7 @@ static struct clk tegra_pll_a_out0 = {
.parent = &tegra_pll_a,
.reg = 0xb4,
.reg_shift = 0,
- .max_rate = 56448000,
+ .max_rate = 73728000,
};
static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
@@ -1681,10 +1686,10 @@ static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
{ 26000000, 760000000, 760, 26, 1, 12},
/* 608 MHz */
- { 12000000, 608000000, 760, 12, 1, 12},
- { 13000000, 608000000, 760, 13, 1, 12},
+ { 12000000, 608000000, 608, 12, 1, 12},
+ { 13000000, 608000000, 608, 13, 1, 12},
{ 19200000, 608000000, 380, 12, 1, 8},
- { 26000000, 608000000, 760, 26, 1, 12},
+ { 26000000, 608000000, 608, 26, 1, 12},
/* 456 MHz */
{ 12000000, 456000000, 456, 12, 1, 12},
@@ -1797,7 +1802,7 @@ static struct clk tegra_clk_audio = {
.name = "audio",
.inputs = mux_audio_sync_clk,
.reg = 0x38,
- .max_rate = 24000000,
+ .max_rate = 73728000,
.ops = &tegra_audio_sync_clk_ops
};
@@ -1883,7 +1888,8 @@ static struct clk tegra_clk_sclk = {
.inputs = mux_sclk,
.reg = 0x28,
.ops = &tegra_super_ops,
- .max_rate = 600000000,
+ .max_rate = 240000000,
+ .min_rate = 120000000,
};
static struct clk tegra_clk_virtual_cpu = {
@@ -1897,6 +1903,13 @@ static struct clk tegra_clk_virtual_cpu = {
},
};
+static struct clk tegra_clk_cop = {
+ .name = "cop",
+ .parent = &tegra_clk_sclk,
+ .ops = &tegra_cop_ops,
+ .max_rate = 240000000,
+};
+
static struct clk tegra_clk_hclk = {
.name = "hclk",
.flags = DIV_BUS,
@@ -1914,7 +1927,7 @@ static struct clk tegra_clk_pclk = {
.reg = 0x30,
.reg_shift = 0,
.ops = &tegra_bus_ops,
- .max_rate = 108000000,
+ .max_rate = 120000000,
};
static struct clk tegra_clk_blink = {
@@ -2063,7 +2076,10 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x164, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("vcp", "tegra-avp", "vcp", 29, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsea", "tegra-avp", "bsea", 62, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsev", "tegra-aes", "bsev", 63, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("vde", "tegra-avp", "vde", 61, 0x1c8, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */
/* FIXME: what is la? */
PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
@@ -2106,6 +2122,18 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("pex", NULL, "pex", 70, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
PERIPH_CLK("afi", NULL, "afi", 72, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
PERIPH_CLK("pcie_xclk", NULL, "pcie_xclk", 74, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET),
+
+ SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sclk),
+ SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc),
+ SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc),
+ SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc),
+ SHARED_CLK("disp2.emc", "tegradc.1", "emc", &tegra_clk_emc),
+ SHARED_CLK("hdmi.emc", "hdmi", "emc", &tegra_clk_emc),
+ SHARED_CLK("host.emc", "tegra_grhost", "emc", &tegra_clk_emc),
+ SHARED_CLK("usbd.emc", "fsl-tegra-udc", "emc", &tegra_clk_emc),
+ SHARED_CLK("usb1.emc", "tegra-ehci.0", "emc", &tegra_clk_emc),
+ SHARED_CLK("usb2.emc", "tegra-ehci.1", "emc", &tegra_clk_emc),
+ SHARED_CLK("usb3.emc", "tegra-ehci.2", "emc", &tegra_clk_emc),
};
#define CLK_DUPLICATE(_name, _dev, _con) \
@@ -2136,6 +2164,13 @@ struct clk_duplicate tegra_clk_duplicates[] = {
CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
+ CLK_DUPLICATE("host1x", "tegra_grhost", "host1x"),
+ CLK_DUPLICATE("2d", "tegra_grhost", "gr2d"),
+ CLK_DUPLICATE("3d", "tegra_grhost", "gr3d"),
+ CLK_DUPLICATE("epp", "tegra_grhost", "epp"),
+ CLK_DUPLICATE("mpe", "tegra_grhost", "mpe"),
+ CLK_DUPLICATE("cop", "tegra-avp", "cop"),
+ CLK_DUPLICATE("vde", "tegra-aes", "vde"),
};
#define CLK(dev, con, ck) \
@@ -2174,6 +2209,7 @@ struct clk *tegra_ptr_clks[] = {
&tegra_dev2_clk,
&tegra_clk_virtual_cpu,
&tegra_clk_blink,
+ &tegra_clk_cop,
&tegra_clk_emc,
};
--
1.7.3.1
^ permalink raw reply related
* [PATCH 20/21] ARM: tegra: clock: Fix clock issues in suspend
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
The PLLP registers are now being restored by the low-level resume code,
and the CPU may be running off PLLP, so don't touch them during clock
resume.
Save plld, plls, pllu, and audio clock during suspend (originally
fixed by Mayuresh Kulkarni <mkulkarni@nvidia.com>)
The lock time for plld is 1000 us, so increase the delay after
setting the PLLs.
Add a BUG_ON to ensure the size of the suspend context area is
correct.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/tegra2_clocks.c | 30 ++++++++++++++++++++----------
1 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index bfc9792..00e348a 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2215,7 +2215,7 @@ void __init tegra2_init_clocks(void)
#ifdef CONFIG_PM
static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
- PERIPH_CLK_SOURCE_NUM + 19];
+ PERIPH_CLK_SOURCE_NUM + 22];
void tegra_clk_suspend(void)
{
@@ -2223,16 +2223,18 @@ void tegra_clk_suspend(void)
u32 *ctx = clk_rst_suspend;
*ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
- *ctx++ = clk_readl(tegra_pll_p.reg + PLL_BASE);
- *ctx++ = clk_readl(tegra_pll_p.reg + PLL_MISC(&tegra_pll_p));
*ctx++ = clk_readl(tegra_pll_c.reg + PLL_BASE);
*ctx++ = clk_readl(tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
*ctx++ = clk_readl(tegra_pll_a.reg + PLL_BASE);
*ctx++ = clk_readl(tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+ *ctx++ = clk_readl(tegra_pll_s.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_s.reg + PLL_MISC(&tegra_pll_s));
+ *ctx++ = clk_readl(tegra_pll_d.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_d.reg + PLL_MISC(&tegra_pll_d));
+ *ctx++ = clk_readl(tegra_pll_u.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_u.reg + PLL_MISC(&tegra_pll_u));
*ctx++ = clk_readl(tegra_pll_m_out1.reg);
- *ctx++ = clk_readl(tegra_pll_p_out1.reg);
- *ctx++ = clk_readl(tegra_pll_p_out3.reg);
*ctx++ = clk_readl(tegra_pll_a_out0.reg);
*ctx++ = clk_readl(tegra_pll_c_out1.reg);
@@ -2243,6 +2245,8 @@ void tegra_clk_suspend(void)
*ctx++ = clk_readl(tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
*ctx++ = clk_readl(tegra_clk_pclk.reg);
+ *ctx++ = clk_readl(tegra_clk_audio.reg);
+
for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
off += 4) {
if (off == PERIPH_CLK_SOURCE_EMC)
@@ -2260,6 +2264,8 @@ void tegra_clk_suspend(void)
*ctx++ = clk_readl(MISC_CLK_ENB);
*ctx++ = clk_readl(CLK_MASK_ARM);
+
+ BUG_ON(ctx - clk_rst_suspend != ARRAY_SIZE(clk_rst_suspend));
}
void tegra_clk_resume(void)
@@ -2272,17 +2278,19 @@ void tegra_clk_resume(void)
val |= *ctx++;
clk_writel(val, OSC_CTRL);
- clk_writel(*ctx++, tegra_pll_p.reg + PLL_BASE);
- clk_writel(*ctx++, tegra_pll_p.reg + PLL_MISC(&tegra_pll_p));
clk_writel(*ctx++, tegra_pll_c.reg + PLL_BASE);
clk_writel(*ctx++, tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
clk_writel(*ctx++, tegra_pll_a.reg + PLL_BASE);
clk_writel(*ctx++, tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
- udelay(300);
+ clk_writel(*ctx++, tegra_pll_s.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_s.reg + PLL_MISC(&tegra_pll_s));
+ clk_writel(*ctx++, tegra_pll_d.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_d.reg + PLL_MISC(&tegra_pll_d));
+ clk_writel(*ctx++, tegra_pll_u.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_u.reg + PLL_MISC(&tegra_pll_u));
+ udelay(1000);
clk_writel(*ctx++, tegra_pll_m_out1.reg);
- clk_writel(*ctx++, tegra_pll_p_out1.reg);
- clk_writel(*ctx++, tegra_pll_p_out3.reg);
clk_writel(*ctx++, tegra_pll_a_out0.reg);
clk_writel(*ctx++, tegra_pll_c_out1.reg);
@@ -2293,6 +2301,8 @@ void tegra_clk_resume(void)
clk_writel(*ctx++, tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
clk_writel(*ctx++, tegra_clk_pclk.reg);
+ clk_writel(*ctx++, tegra_clk_audio.reg);
+
/* enable all clocks before configuring clock sources */
clk_writel(0xbffffff9ul, CLK_OUT_ENB);
clk_writel(0xfefffff7ul, CLK_OUT_ENB + 4);
--
1.7.3.1
^ permalink raw reply related
* [PATCH 19/21] ARM: tegra: clock: Add function to set SDMMC tap delay
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
The SDMMC controllers have extra bits in the clock source
register that adjust the delay between the clock and data
to compenstate for delays on the PCB. The values need to
be set from the clock code so the clock can be locked
during the read-modify-write on the clock source register.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.c | 12 ++++++++++++
arch/arm/mach-tegra/clock.h | 1 +
arch/arm/mach-tegra/include/mach/clk.h | 2 ++
arch/arm/mach-tegra/tegra2_clocks.c | 19 +++++++++++++++++++
4 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 4865bb4..2bca01c 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -434,6 +434,18 @@ int __init tegra_disable_boot_clocks(void)
late_initcall(tegra_disable_boot_clocks);
#endif
+/* The SDMMC controllers have extra bits in the clock source register that
+ * adjust the delay between the clock and data to compenstate for delays
+ * on the PCB. */
+void tegra_sdmmc_tap_delay(struct clk *c, int delay)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->spinlock, flags);
+ tegra2_sdmmc_tap_delay(c, delay);
+ spin_unlock_irqrestore(&c->spinlock, flags);
+}
+
#ifdef CONFIG_DEBUG_FS
static int __clk_lock_all_spinlocks(void)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index ba8e64f..2aac9a2 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -154,5 +154,6 @@ unsigned long clk_measure_input_freq(void);
int clk_reparent(struct clk *c, struct clk *parent);
void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
unsigned long clk_get_rate_locked(struct clk *c);
+void tegra2_sdmmc_tap_delay(struct clk *c, int delay);
#endif
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index fa7f9ca..c8baf8f 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -26,4 +26,6 @@ void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
unsigned long clk_get_rate_all_locked(struct clk *c);
+void tegra_sdmmc_tap_delay(struct clk *c, int delay);
+
#endif
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index f4173ec..bfc9792 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -74,6 +74,10 @@
#define PERIPH_CLK_SOURCE_DIVU16_MASK 0xFFFF
#define PERIPH_CLK_SOURCE_DIV_SHIFT 0
+#define SDMMC_CLK_INT_FB_SEL (1 << 23)
+#define SDMMC_CLK_INT_FB_DLY_SHIFT 16
+#define SDMMC_CLK_INT_FB_DLY_MASK (0xF << SDMMC_CLK_INT_FB_DLY_SHIFT)
+
#define PLL_BASE 0x0
#define PLL_BASE_BYPASS (1<<31)
#define PLL_BASE_ENABLE (1<<30)
@@ -1079,6 +1083,21 @@ static struct clk_ops tegra_periph_clk_ops = {
.reset = &tegra2_periph_clk_reset,
};
+/* The SDMMC controllers have extra bits in the clock source register that
+ * adjust the delay between the clock and data to compenstate for delays
+ * on the PCB. */
+void tegra2_sdmmc_tap_delay(struct clk *c, int delay)
+{
+ u32 reg;
+
+ delay = clamp(delay, 0, 15);
+ reg = clk_readl(c->reg);
+ reg &= ~SDMMC_CLK_INT_FB_DLY_MASK;
+ reg |= SDMMC_CLK_INT_FB_SEL;
+ reg |= delay << SDMMC_CLK_INT_FB_DLY_SHIFT;
+ clk_writel(reg, c->reg);
+}
+
/* External memory controller clock ops */
static void tegra2_emc_clk_init(struct clk *c)
{
--
1.7.3.1
^ permalink raw reply related
* [PATCH 18/21] ARM: tegra: cpufreq: Adjust memory frequency with cpu frequency
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/cpu-tegra.c | 21 +++++++++++++++++++++
1 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index f02ba60..0e1016a 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -51,6 +51,7 @@ static struct cpufreq_frequency_table freq_table[] = {
#define NUM_CPUS 2
static struct clk *cpu_clk;
+static struct clk *emc_clk;
static unsigned long target_cpu_speed[NUM_CPUS];
static DEFINE_MUTEX(tegra_cpu_lock);
@@ -83,6 +84,17 @@ static int tegra_update_cpu_speed(unsigned long rate)
if (freqs.old == freqs.new)
return ret;
+ /*
+ * Vote on memory bus frequency based on cpu frequency
+ * This sets the minimum frequency, display or avp may request higher
+ */
+ if (rate >= 816000)
+ clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
+ else if (rate >= 456000)
+ clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
+ else
+ clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
+
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
@@ -173,6 +185,13 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
+ emc_clk = clk_get_sys("cpu", "emc");
+ if (IS_ERR(emc_clk)) {
+ clk_put(cpu_clk);
+ return PTR_ERR(emc_clk);
+ }
+
+ clk_enable(emc_clk);
clk_enable(cpu_clk);
cpufreq_frequency_table_cpuinfo(policy, freq_table);
@@ -195,6 +214,8 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
static int tegra_cpu_exit(struct cpufreq_policy *policy)
{
cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ clk_disable(emc_clk);
+ clk_put(emc_clk);
clk_put(cpu_clk);
return 0;
}
--
1.7.3.1
^ permalink raw reply related
* [PATCH 17/21] ARM: tegra: clocks: Add emc scaling
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/tegra2_clocks.c | 62 ++++++++++++++++++++++++++++++++++-
1 files changed, 61 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 96b2490..f4173ec 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -31,6 +31,7 @@
#include "clock.h"
#include "fuse.h"
+#include "tegra2_emc.h"
#define RST_DEVICES 0x004
#define RST_DEVICES_SET 0x300
@@ -1078,6 +1079,53 @@ static struct clk_ops tegra_periph_clk_ops = {
.reset = &tegra2_periph_clk_reset,
};
+/* External memory controller clock ops */
+static void tegra2_emc_clk_init(struct clk *c)
+{
+ tegra2_periph_clk_init(c);
+ c->max_rate = clk_get_rate_locked(c);
+}
+
+static long tegra2_emc_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ long new_rate = rate;
+
+ new_rate = tegra_emc_round_rate(new_rate);
+ if (new_rate < 0)
+ return c->max_rate;
+
+ BUG_ON(new_rate != tegra2_periph_clk_round_rate(c, new_rate));
+
+ return new_rate;
+}
+
+static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+ /* The Tegra2 memory controller has an interlock with the clock
+ * block that allows memory shadowed registers to be updated,
+ * and then transfer them to the main registers at the same
+ * time as the clock update without glitches. */
+ ret = tegra_emc_set_rate(rate);
+ if (ret < 0)
+ return ret;
+
+ ret = tegra2_periph_clk_set_rate(c, rate);
+ udelay(1);
+
+ return ret;
+}
+
+static struct clk_ops tegra_emc_clk_ops = {
+ .init = &tegra2_emc_clk_init,
+ .enable = &tegra2_periph_clk_enable,
+ .disable = &tegra2_periph_clk_disable,
+ .set_parent = &tegra2_periph_clk_set_parent,
+ .set_rate = &tegra2_emc_clk_set_rate,
+ .round_rate = &tegra2_emc_clk_round_rate,
+ .reset = &tegra2_periph_clk_reset,
+};
+
/* Clock doubler ops */
static void tegra2_clk_double_init(struct clk *c)
{
@@ -1934,6 +1982,18 @@ static struct clk_mux_sel mux_clk_32k[] = {
{ 0, 0},
};
+static struct clk tegra_clk_emc = {
+ .name = "emc",
+ .ops = &tegra_emc_clk_ops,
+ .reg = 0x19c,
+ .max_rate = 800000000,
+ .inputs = mux_pllm_pllc_pllp_clkm,
+ .flags = MUX | DIV_U71 | PERIPH_EMC_ENB,
+ .u.periph = {
+ .clk_num = 57,
+ },
+};
+
#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
{ \
.name = _name, \
@@ -2020,7 +2080,6 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
- PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB),
PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld, 0), /* scales with voltage */
PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 72000000, mux_pllp_out3, 0),
PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
@@ -2096,6 +2155,7 @@ struct clk *tegra_ptr_clks[] = {
&tegra_dev2_clk,
&tegra_clk_virtual_cpu,
&tegra_clk_blink,
+ &tegra_clk_emc,
};
static void tegra2_init_one_clock(struct clk *c)
--
1.7.3.1
^ permalink raw reply related
* [PATCH 16/21] ARM: tegra: Add external memory controller driver
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/Kconfig | 4 +
arch/arm/mach-tegra/Makefile | 1 +
arch/arm/mach-tegra/tegra2_emc.c | 172 ++++++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/tegra2_emc.h | 27 ++++++
4 files changed, 204 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-tegra/tegra2_emc.c
create mode 100644 arch/arm/mach-tegra/tegra2_emc.h
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 9f4fc6b..42d44c7 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -71,3 +71,7 @@ config TEGRA_SYSTEM_DMA
several Tegra device drivers
endif
+
+config TEGRA_EMC_SCALING_ENABLE
+ bool "Enable scaling the memory frequency"
+ default n
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 23de060..3fe357b 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -9,6 +9,7 @@ obj-y += powergate.o
obj-y += fuse.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o
obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
new file mode 100644
index 0000000..bd4fa27
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_emc.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include <mach/iomap.h>
+
+#include "tegra2_emc.h"
+
+#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
+static bool emc_enable = true;
+#else
+static bool emc_enable;
+#endif
+module_param(emc_enable, bool, 0644);
+
+static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
+static const struct tegra_emc_table *tegra_emc_table;
+static int tegra_emc_table_size;
+
+static inline void emc_writel(u32 val, unsigned long addr)
+{
+ writel(val, emc + addr);
+}
+
+static inline u32 emc_readl(unsigned long addr)
+{
+ return readl(emc + addr);
+}
+
+static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = {
+ 0x2c, /* RC */
+ 0x30, /* RFC */
+ 0x34, /* RAS */
+ 0x38, /* RP */
+ 0x3c, /* R2W */
+ 0x40, /* W2R */
+ 0x44, /* R2P */
+ 0x48, /* W2P */
+ 0x4c, /* RD_RCD */
+ 0x50, /* WR_RCD */
+ 0x54, /* RRD */
+ 0x58, /* REXT */
+ 0x5c, /* WDV */
+ 0x60, /* QUSE */
+ 0x64, /* QRST */
+ 0x68, /* QSAFE */
+ 0x6c, /* RDV */
+ 0x70, /* REFRESH */
+ 0x74, /* BURST_REFRESH_NUM */
+ 0x78, /* PDEX2WR */
+ 0x7c, /* PDEX2RD */
+ 0x80, /* PCHG2PDEN */
+ 0x84, /* ACT2PDEN */
+ 0x88, /* AR2PDEN */
+ 0x8c, /* RW2PDEN */
+ 0x90, /* TXSR */
+ 0x94, /* TCKE */
+ 0x98, /* TFAW */
+ 0x9c, /* TRPAB */
+ 0xa0, /* TCLKSTABLE */
+ 0xa4, /* TCLKSTOP */
+ 0xa8, /* TREFBW */
+ 0xac, /* QUSE_EXTRA */
+ 0x114, /* FBIO_CFG6 */
+ 0xb0, /* ODT_WRITE */
+ 0xb4, /* ODT_READ */
+ 0x104, /* FBIO_CFG5 */
+ 0x2bc, /* CFG_DIG_DLL */
+ 0x2c0, /* DLL_XFORM_DQS */
+ 0x2c4, /* DLL_XFORM_QUSE */
+ 0x2e0, /* ZCAL_REF_CNT */
+ 0x2e4, /* ZCAL_WAIT_CNT */
+ 0x2a8, /* AUTO_CAL_INTERVAL */
+ 0x2d0, /* CFG_CLKTRIM_0 */
+ 0x2d4, /* CFG_CLKTRIM_1 */
+ 0x2d8, /* CFG_CLKTRIM_2 */
+};
+
+/* Select the closest EMC rate that is higher than the requested rate */
+long tegra_emc_round_rate(unsigned long rate)
+{
+ int i;
+ int best = -1;
+ unsigned long distance = ULONG_MAX;
+
+ if (!tegra_emc_table)
+ return -EINVAL;
+
+ if (!emc_enable)
+ return -EINVAL;
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* The EMC clock rate is twice the bus rate, and the bus rate is
+ * measured in kHz */
+ rate = rate / 2 / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_table[i].rate >= rate &&
+ (tegra_emc_table[i].rate - rate) < distance) {
+ distance = tegra_emc_table[i].rate - rate;
+ best = i;
+ }
+ }
+
+ if (best < 0)
+ return -EINVAL;
+
+ pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate);
+
+ return tegra_emc_table[best].rate * 2 * 1000;
+}
+
+/* The EMC registers have shadow registers. When the EMC clock is updated
+ * in the clock controller, the shadow registers are copied to the active
+ * registers, allowing glitchless memory bus frequency changes.
+ * This function updates the shadow registers for a new clock frequency,
+ * and relies on the clock lock on the emc clock to avoid races between
+ * multiple frequency changes */
+int tegra_emc_set_rate(unsigned long rate)
+{
+ int i;
+ int j;
+
+ if (!tegra_emc_table)
+ return -EINVAL;
+
+ /* The EMC clock rate is twice the bus rate, and the bus rate is
+ * measured in kHz */
+ rate = rate / 2 / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++)
+ if (tegra_emc_table[i].rate == rate)
+ break;
+
+ if (i >= tegra_emc_table_size)
+ return -EINVAL;
+
+ pr_debug("%s: setting to %lu\n", __func__, rate);
+
+ for (j = 0; j < TEGRA_EMC_NUM_REGS; j++)
+ emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]);
+
+ emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]);
+
+ return 0;
+}
+
+void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
+{
+ tegra_emc_table = table;
+ tegra_emc_table_size = table_size;
+}
diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h
new file mode 100644
index 0000000..3515e57
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_emc.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define TEGRA_EMC_NUM_REGS 46
+
+struct tegra_emc_table {
+ unsigned long rate;
+ u32 regs[TEGRA_EMC_NUM_REGS];
+};
+
+int tegra_emc_set_rate(unsigned long rate);
+long tegra_emc_round_rate(unsigned long rate);
+void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
--
1.7.3.1
^ permalink raw reply related
* [PATCH 15/21] ARM: tegra: clock: Round rate before setting rate
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Call the clock's round_rate op, if it exists, before calling
the set_rate op. This will help later when dvfs is added,
dvfs needs to know what the final rate will be before the
frequency changes.
Also requires fixes to the round rate functions to ensure
calling round rate and then set rate will not cause the
frequency to be rounded down twice. When picking clock
divider values, the clock framework picks the closest
frequency that is lower than the requested frequency. If
the new frequency calculated from the divider value is
rounded down, and then passed to set_rate, it will get
rounded down again, possibly resulting in a frequency two
steps lower than the original requested frequency.
Fix the problem by rounding up when calculating the frequency
coming out of a clock divider, so if that frequency is
requested again, the same divider value will be picked.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.c | 13 +++++++++++++
arch/arm/mach-tegra/tegra2_clocks.c | 8 ++++----
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 2d9ddfd..4865bb4 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -85,6 +85,7 @@ static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
if (c->mul != 0 && c->div != 0) {
rate *= c->mul;
+ rate += c->div / 2; /* round up */
do_div(rate, c->div);
}
@@ -242,6 +243,7 @@ int clk_set_rate(struct clk *c, unsigned long rate)
int ret = 0;
unsigned long flags;
unsigned long old_rate;
+ long new_rate;
spin_lock_irqsave(&c->spinlock, flags);
@@ -255,6 +257,17 @@ int clk_set_rate(struct clk *c, unsigned long rate)
if (rate > c->max_rate)
rate = c->max_rate;
+ if (c->ops && c->ops->round_rate) {
+ new_rate = c->ops->round_rate(c, rate);
+
+ if (new_rate < 0) {
+ ret = new_rate;
+ goto out;
+ }
+
+ rate = new_rate;
+ }
+
ret = c->ops->set_rate(c, rate);
if (ret)
goto out;
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 2f79128..96b2490 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -866,9 +866,9 @@ static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return parent_rate * 2 / (divider + 2);
+ return DIV_ROUND_UP(parent_rate * 2, divider + 2);
} else if (c->flags & DIV_2) {
- return parent_rate / 2;
+ return DIV_ROUND_UP(parent_rate, 2);
}
return -EINVAL;
}
@@ -1058,12 +1058,12 @@ static long tegra2_periph_clk_round_rate(struct clk *c,
if (divider < 0)
return divider;
- return parent_rate * 2 / (divider + 2);
+ return DIV_ROUND_UP(parent_rate * 2, divider + 2);
} else if (c->flags & DIV_U16) {
divider = clk_div16_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return parent_rate / (divider + 1);
+ return DIV_ROUND_UP(parent_rate, divider + 1);
}
return -EINVAL;
}
--
1.7.3.1
^ permalink raw reply related
* [PATCH 14/21] ARM: tegra: clock: Refcount periph clock enables
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Some peripheral clocks share enable bits. Refcount the enables so
that calling clk_disable on one clock will not turn off another
clock.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/tegra2_clocks.c | 35 +++++++++++++++++++++++++++++------
1 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 6905bd0..2f79128 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -154,6 +154,12 @@ static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
*/
static DEFINE_SPINLOCK(clock_register_lock);
+/*
+ * Some peripheral clocks share an enable bit, so refcount the enable bits
+ * in registers CLK_ENABLE_L, CLK_ENABLE_H, and CLK_ENABLE_U
+ */
+static int tegra_periph_clk_enable_refcount[3 * 32];
+
#define clk_writel(value, reg) \
__raw_writel(value, (u32)reg_clk_base + (reg))
#define clk_readl(reg) \
@@ -920,8 +926,19 @@ static void tegra2_periph_clk_init(struct clk *c)
static int tegra2_periph_clk_enable(struct clk *c)
{
u32 val;
+ unsigned long flags;
+ int refcount;
pr_debug("%s on clock %s\n", __func__, c->name);
+ spin_lock_irqsave(&clock_register_lock, flags);
+
+ refcount = tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
+
+ spin_unlock_irqrestore(&clock_register_lock, flags);
+
+ if (refcount > 1)
+ return 0;
+
clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
CLK_OUT_ENB_SET + PERIPH_CLK_TO_ENB_SET_REG(c));
if (!(c->flags & PERIPH_NO_RESET) && !(c->flags & PERIPH_MANUAL_RESET))
@@ -939,10 +956,20 @@ static int tegra2_periph_clk_enable(struct clk *c)
static void tegra2_periph_clk_disable(struct clk *c)
{
+ unsigned long flags;
+
pr_debug("%s on clock %s\n", __func__, c->name);
- clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
- CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
+ spin_lock_irqsave(&clock_register_lock, flags);
+
+ if (c->refcnt)
+ tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
+
+ if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0)
+ clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
+ CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
+
+ spin_unlock_irqrestore(&clock_register_lock, flags);
}
static void tegra2_periph_clk_reset(struct clk *c, bool assert)
@@ -1940,7 +1967,6 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
- /* FIXME: spdif has 2 clocks but 1 enable */
PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71),
PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71),
@@ -1953,7 +1979,6 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
PERIPH_CLK("ide", "ide", NULL, 25, 0x144, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, 164000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
- /* FIXME: vfir shares an enable with uartb */
PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
@@ -1981,13 +2006,11 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- /* FIXME: vi and vi_sensor share an enable */
PERIPH_CLK("vi", "tegra_camera", "vi", 20, 0x148, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
PERIPH_CLK("vi_sensor", "tegra_camera", "vi_sensor", 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 250000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 166000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
- /* FIXME: cve and tvo share an enable */
PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 600000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
--
1.7.3.1
^ permalink raw reply related
* [PATCH 13/21] ARM: tegra: clock: Remove unnecessary uses of #ifdef CONFIG_DEBUG_FS
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.c | 6 ++----
arch/arm/mach-tegra/clock.h | 2 +-
arch/arm/mach-tegra/tegra2_clocks.c | 4 +---
3 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 34a1f8d..2d9ddfd 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -134,7 +134,7 @@ void clk_init(struct clk *c)
if (!c->ops || !c->ops->enable) {
c->refcnt++;
- c->set = 1;
+ c->set = true;
if (c->parent)
c->state = c->parent->state;
else
@@ -168,9 +168,7 @@ int clk_enable(struct clk *c)
goto out;
}
c->state = ON;
-#ifdef CONFIG_DEBUG_FS
- c->set = 1;
-#endif
+ c->set = true;
}
}
c->refcnt++;
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index b460f17..ba8e64f 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -80,8 +80,8 @@ struct clk {
#ifdef CONFIG_DEBUG_FS
struct dentry *dent;
- bool set;
#endif
+ bool set;
struct clk_ops *ops;
unsigned long rate;
unsigned long max_rate;
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 10b9f63..6905bd0 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -1214,9 +1214,7 @@ static void tegra_clk_shared_bus_init(struct clk *c)
c->max_rate = c->parent->max_rate;
c->u.shared_bus_user.rate = c->parent->max_rate;
c->state = OFF;
-#ifdef CONFIG_DEBUG_FS
- c->set = 1;
-#endif
+ c->set = true;
list_add_tail(&c->u.shared_bus_user.node,
&c->parent->shared_bus_list);
--
1.7.3.1
^ permalink raw reply related
* [PATCH 12/21] ARM: tegra: clock: Add shared bus clock type
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Some clocks may have multiple downstream users that need to request a
higher clock rate. Shared bus clocks provide a unique shared_bus_user
clock to each user. The frequency of the bus is set to the highest
enabled shared_bus_user clock, with a minimum value set by the
shared bus. Drivers can use clk_enable and clk_disable to enable
or disable their requirement, and clk_set_rate to set the minimum rate.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.h | 8 ++++
arch/arm/mach-tegra/tegra2_clocks.c | 80 +++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index dd5c029..b460f17 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -85,6 +85,7 @@ struct clk {
struct clk_ops *ops;
unsigned long rate;
unsigned long max_rate;
+ unsigned long min_rate;
u32 flags;
const char *name;
@@ -98,6 +99,8 @@ struct clk {
u32 reg;
u32 reg_shift;
+ struct list_head shared_bus_list;
+
union {
struct {
unsigned int clk_num;
@@ -120,6 +123,11 @@ struct clk {
struct clk *main;
struct clk *backup;
} cpu;
+ struct {
+ struct list_head node;
+ bool enabled;
+ unsigned long rate;
+ } shared_bus_user;
} u;
spinlock_t spinlock;
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index a1c86d8..10b9f63 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -1188,6 +1188,74 @@ static struct clk_ops tegra_cdev_clk_ops = {
.disable = &tegra2_cdev_clk_disable,
};
+/* shared bus ops */
+/*
+ * Some clocks may have multiple downstream users that need to request a
+ * higher clock rate. Shared bus clocks provide a unique shared_bus_user
+ * clock to each user. The frequency of the bus is set to the highest
+ * enabled shared_bus_user clock, with a minimum value set by the
+ * shared bus.
+ */
+static void tegra_clk_shared_bus_update(struct clk *bus)
+{
+ struct clk *c;
+ unsigned long rate = bus->min_rate;
+
+ list_for_each_entry(c, &bus->shared_bus_list, u.shared_bus_user.node)
+ if (c->u.shared_bus_user.enabled)
+ rate = max(c->u.shared_bus_user.rate, rate);
+
+ if (rate != clk_get_rate(bus))
+ clk_set_rate(bus, rate);
+};
+
+static void tegra_clk_shared_bus_init(struct clk *c)
+{
+ c->max_rate = c->parent->max_rate;
+ c->u.shared_bus_user.rate = c->parent->max_rate;
+ c->state = OFF;
+#ifdef CONFIG_DEBUG_FS
+ c->set = 1;
+#endif
+
+ list_add_tail(&c->u.shared_bus_user.node,
+ &c->parent->shared_bus_list);
+}
+
+static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
+{
+ c->u.shared_bus_user.rate = rate;
+ tegra_clk_shared_bus_update(c->parent);
+ return 0;
+}
+
+static long tegra_clk_shared_bus_round_rate(struct clk *c, unsigned long rate)
+{
+ return clk_round_rate(c->parent, rate);
+}
+
+static int tegra_clk_shared_bus_enable(struct clk *c)
+{
+ c->u.shared_bus_user.enabled = true;
+ tegra_clk_shared_bus_update(c->parent);
+ return 0;
+}
+
+static void tegra_clk_shared_bus_disable(struct clk *c)
+{
+ c->u.shared_bus_user.enabled = false;
+ tegra_clk_shared_bus_update(c->parent);
+}
+
+static struct clk_ops tegra_clk_shared_bus_ops = {
+ .init = tegra_clk_shared_bus_init,
+ .enable = tegra_clk_shared_bus_enable,
+ .disable = tegra_clk_shared_bus_disable,
+ .set_rate = tegra_clk_shared_bus_set_rate,
+ .round_rate = tegra_clk_shared_bus_round_rate,
+};
+
+
/* Clock definitions */
static struct clk tegra_clk_32k = {
.name = "clk_32k",
@@ -1858,6 +1926,17 @@ static struct clk_mux_sel mux_clk_32k[] = {
}, \
}
+#define SHARED_CLK(_name, _dev, _con, _parent) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ .ops = &tegra_clk_shared_bus_ops, \
+ .parent = _parent, \
+ }
+
struct clk tegra_list_clks[] = {
PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
@@ -2001,6 +2080,7 @@ struct clk *tegra_ptr_clks[] = {
static void tegra2_init_one_clock(struct clk *c)
{
clk_init(c);
+ INIT_LIST_HEAD(&c->shared_bus_list);
if (!c->lookup.dev_id && !c->lookup.con_id)
c->lookup.con_id = c->name;
c->lookup.clk = c;
--
1.7.3.1
^ permalink raw reply related
* [PATCH 11/21] ARM: tegra: cpufreq: Take an extra reference to pllx
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
During cpu frequency changes, take an extra reference to pllx so
that it doesn't turn off and on while the cpu is temporarily on
pllp. If the cpu is moved to pllp permanently, pllx will be
turned off.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/cpu-tegra.c | 2 ++
arch/arm/mach-tegra/tegra2_clocks.c | 15 +++++++++++----
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index cda03f1..f02ba60 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -173,6 +173,8 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
+ clk_enable(cpu_clk);
+
cpufreq_frequency_table_cpuinfo(policy, freq_table);
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
policy->cur = tegra_getspeed(policy->cpu);
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index ab013b1..a1c86d8 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -385,10 +385,16 @@ static void tegra2_cpu_clk_disable(struct clk *c)
static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
{
int ret;
+ /*
+ * Take an extra reference to the main pll so it doesn't turn
+ * off when we move the cpu off of it
+ */
+ clk_enable(c->u.cpu.main);
+
ret = clk_set_parent(c->parent, c->u.cpu.backup);
if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
- return ret;
+ goto out;
}
if (rate == clk_get_rate(c->u.cpu.backup))
@@ -397,17 +403,18 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
ret = clk_set_rate(c->u.cpu.main, rate);
if (ret) {
pr_err("Failed to change cpu pll to %lu\n", rate);
- return ret;
+ goto out;
}
ret = clk_set_parent(c->parent, c->u.cpu.main);
if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
- return ret;
+ goto out;
}
out:
- return 0;
+ clk_disable(c->u.cpu.main);
+ return ret;
}
static struct clk_ops tegra_cpu_ops = {
--
1.7.3.1
^ permalink raw reply related
* [PATCH 10/21] ARM: tegra: clock: Convert global lock to a lock per clock
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Give each clock its own lock, and remove all lock traversals from
parent to child clocks to prevent AB-BA deadlocks.
This brings the locking in line with the common struct clk
patches and should make conversion simple.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.c | 370 ++++++++++++++++++++------------
arch/arm/mach-tegra/clock.h | 13 +-
arch/arm/mach-tegra/include/mach/clk.h | 1 +
arch/arm/mach-tegra/tegra2_clocks.c | 119 ++++++++---
4 files changed, 327 insertions(+), 176 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index a1193f9..34a1f8d 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -18,83 +18,116 @@
#include <linux/kernel.h>
#include <linux/clk.h>
-#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/list.h>
#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
+#include <linux/sched.h>
#include <linux/seq_file.h>
-#include <linux/clkdev.h>
+#include <linux/slab.h>
+
+#include <mach/clk.h>
#include "board.h"
#include "clock.h"
+/*
+ * Locking:
+ *
+ * Each struct clk has a spinlock.
+ *
+ * To avoid AB-BA locking problems, locks must always be traversed from child
+ * clock to parent clock. For example, when enabling a clock, the clock's lock
+ * is taken, and then clk_enable is called on the parent, which take's the
+ * parent clock's lock. There is one exceptions to this ordering: When dumping
+ * the clock tree through debugfs. In this case, clk_lock_all is called,
+ * which attemps to iterate through the entire list of clocks and take every
+ * clock lock. If any call to spin_trylock fails, all locked clocks are
+ * unlocked, and the process is retried. When all the locks are held,
+ * the only clock operation that can be called is clk_get_rate_all_locked.
+ *
+ * Within a single clock, no clock operation can call another clock operation
+ * on itself, except for clk_get_rate_locked. Any clock operation can call
+ * any other clock operation on any of it's possible parents.
+ *
+ * An additional mutex, clock_list_lock, is used to protect the list of all
+ * clocks.
+ *
+ * The clock operations must lock internally to protect against
+ * read-modify-write on registers that are shared by multiple clocks
+ */
+static DEFINE_MUTEX(clock_list_lock);
static LIST_HEAD(clocks);
-static DEFINE_SPINLOCK(clock_lock);
struct clk *tegra_get_clock_by_name(const char *name)
{
struct clk *c;
struct clk *ret = NULL;
- unsigned long flags;
- spin_lock_irqsave(&clock_lock, flags);
+ mutex_lock(&clock_list_lock);
list_for_each_entry(c, &clocks, node) {
if (strcmp(c->name, name) == 0) {
ret = c;
break;
}
}
- spin_unlock_irqrestore(&clock_lock, flags);
+ mutex_unlock(&clock_list_lock);
return ret;
}
-static void clk_recalculate_rate(struct clk *c)
+/* Must be called with c->spinlock held */
+static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
{
u64 rate;
- if (!c->parent)
- return;
-
- rate = c->parent->rate;
+ rate = clk_get_rate(p);
if (c->mul != 0 && c->div != 0) {
- rate = rate * c->mul;
+ rate *= c->mul;
do_div(rate, c->div);
}
- if (rate > c->max_rate)
- pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n",
- c->name, rate, c->max_rate);
-
- c->rate = rate;
+ return rate;
}
-int clk_reparent(struct clk *c, struct clk *parent)
+/* Must be called with c->spinlock held */
+unsigned long clk_get_rate_locked(struct clk *c)
{
- c->parent = parent;
- list_del(&c->sibling);
- list_add_tail(&c->sibling, &parent->children);
- return 0;
-}
+ unsigned long rate;
-static void propagate_rate(struct clk *c)
-{
- struct clk *clkp;
+ if (c->parent)
+ rate = clk_predict_rate_from_parent(c, c->parent);
+ else
+ rate = c->rate;
- list_for_each_entry(clkp, &c->children, sibling) {
- clk_recalculate_rate(clkp);
- propagate_rate(clkp);
- }
+ return rate;
}
-void clk_init(struct clk *c)
+unsigned long clk_get_rate(struct clk *c)
{
unsigned long flags;
+ unsigned long rate;
- spin_lock_irqsave(&clock_lock, flags);
+ spin_lock_irqsave(&c->spinlock, flags);
+
+ rate = clk_get_rate_locked(c);
+
+ spin_unlock_irqrestore(&c->spinlock, flags);
+
+ return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+int clk_reparent(struct clk *c, struct clk *parent)
+{
+ c->parent = parent;
+ return 0;
+}
- INIT_LIST_HEAD(&c->children);
- INIT_LIST_HEAD(&c->sibling);
+void clk_init(struct clk *c)
+{
+ spin_lock_init(&c->spinlock);
if (c->ops && c->ops->init)
c->ops->init(c);
@@ -108,33 +141,31 @@ void clk_init(struct clk *c)
c->state = ON;
}
- clk_recalculate_rate(c);
-
+ mutex_lock(&clock_list_lock);
list_add(&c->node, &clocks);
-
- if (c->parent)
- list_add_tail(&c->sibling, &c->parent->children);
-
- spin_unlock_irqrestore(&clock_lock, flags);
+ mutex_unlock(&clock_list_lock);
}
-int clk_enable_locked(struct clk *c)
+int clk_enable(struct clk *c)
{
- int ret;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->spinlock, flags);
if (c->refcnt == 0) {
if (c->parent) {
- ret = clk_enable_locked(c->parent);
+ ret = clk_enable(c->parent);
if (ret)
- return ret;
+ goto out;
}
if (c->ops && c->ops->enable) {
ret = c->ops->enable(c);
if (ret) {
if (c->parent)
- clk_disable_locked(c->parent);
- return ret;
+ clk_disable(c->parent);
+ goto out;
}
c->state = ON;
#ifdef CONFIG_DEBUG_FS
@@ -143,27 +174,21 @@ int clk_enable_locked(struct clk *c)
}
}
c->refcnt++;
-
- return 0;
+out:
+ spin_unlock_irqrestore(&c->spinlock, flags);
+ return ret;
}
+EXPORT_SYMBOL(clk_enable);
-int clk_enable(struct clk *c)
+void clk_disable(struct clk *c)
{
- int ret;
unsigned long flags;
- spin_lock_irqsave(&clock_lock, flags);
- ret = clk_enable_locked(c);
- spin_unlock_irqrestore(&clock_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(clk_enable);
+ spin_lock_irqsave(&c->spinlock, flags);
-void clk_disable_locked(struct clk *c)
-{
if (c->refcnt == 0) {
WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
+ spin_unlock_irqrestore(&c->spinlock, flags);
return;
}
if (c->refcnt == 1) {
@@ -171,49 +196,39 @@ void clk_disable_locked(struct clk *c)
c->ops->disable(c);
if (c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
c->state = OFF;
}
c->refcnt--;
-}
-void clk_disable(struct clk *c)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&clock_lock, flags);
- clk_disable_locked(c);
- spin_unlock_irqrestore(&clock_lock, flags);
+ spin_unlock_irqrestore(&c->spinlock, flags);
}
EXPORT_SYMBOL(clk_disable);
-int clk_set_parent_locked(struct clk *c, struct clk *parent)
+int clk_set_parent(struct clk *c, struct clk *parent)
{
int ret;
+ unsigned long flags;
+ unsigned long new_rate;
+ unsigned long old_rate;
- if (!c->ops || !c->ops->set_parent)
- return -ENOSYS;
-
- ret = c->ops->set_parent(c, parent);
-
- if (ret)
- return ret;
+ spin_lock_irqsave(&c->spinlock, flags);
- clk_recalculate_rate(c);
+ if (!c->ops || !c->ops->set_parent) {
+ ret = -ENOSYS;
+ goto out;
+ }
- propagate_rate(c);
+ new_rate = clk_predict_rate_from_parent(c, parent);
+ old_rate = clk_get_rate_locked(c);
- return 0;
-}
+ ret = c->ops->set_parent(c, parent);
+ if (ret)
+ goto out;
-int clk_set_parent(struct clk *c, struct clk *parent)
-{
- int ret;
- unsigned long flags;
- spin_lock_irqsave(&clock_lock, flags);
- ret = clk_set_parent_locked(c, parent);
- spin_unlock_irqrestore(&clock_lock, flags);
+out:
+ spin_unlock_irqrestore(&c->spinlock, flags);
return ret;
}
EXPORT_SYMBOL(clk_set_parent);
@@ -224,64 +239,78 @@ struct clk *clk_get_parent(struct clk *c)
}
EXPORT_SYMBOL(clk_get_parent);
-int clk_set_rate_locked(struct clk *c, unsigned long rate)
+int clk_set_rate(struct clk *c, unsigned long rate)
{
- int ret;
+ int ret = 0;
+ unsigned long flags;
+ unsigned long old_rate;
+
+ spin_lock_irqsave(&c->spinlock, flags);
+
+ if (!c->ops || !c->ops->set_rate) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ old_rate = clk_get_rate_locked(c);
if (rate > c->max_rate)
rate = c->max_rate;
- if (!c->ops || !c->ops->set_rate)
- return -ENOSYS;
-
ret = c->ops->set_rate(c, rate);
-
if (ret)
- return ret;
-
- clk_recalculate_rate(c);
-
- propagate_rate(c);
-
- return 0;
-}
-
-int clk_set_rate(struct clk *c, unsigned long rate)
-{
- int ret = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&clock_lock, flags);
- ret = clk_set_rate_locked(c, rate);
- spin_unlock_irqrestore(&clock_lock, flags);
+ goto out;
+out:
+ spin_unlock_irqrestore(&c->spinlock, flags);
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
-unsigned long clk_get_rate(struct clk *c)
+/* Must be called with clocks lock and all indvidual clock locks held */
+unsigned long clk_get_rate_all_locked(struct clk *c)
{
- unsigned long flags;
- unsigned long ret;
-
- spin_lock_irqsave(&clock_lock, flags);
+ u64 rate;
+ int mul = 1;
+ int div = 1;
+ struct clk *p = c;
+
+ while (p) {
+ c = p;
+ if (c->mul != 0 && c->div != 0) {
+ mul *= c->mul;
+ div *= c->div;
+ }
+ p = c->parent;
+ }
- ret = c->rate;
+ rate = c->rate;
+ rate *= mul;
+ do_div(rate, div);
- spin_unlock_irqrestore(&clock_lock, flags);
- return ret;
+ return rate;
}
-EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *c, unsigned long rate)
{
- if (!c->ops || !c->ops->round_rate)
- return -ENOSYS;
+ unsigned long flags;
+ long ret;
+
+ spin_lock_irqsave(&c->spinlock, flags);
+
+ if (!c->ops || !c->ops->round_rate) {
+ ret = -ENOSYS;
+ goto out;
+ }
if (rate > c->max_rate)
rate = c->max_rate;
- return c->ops->round_rate(c, rate);
+ ret = c->ops->round_rate(c, rate);
+
+out:
+ spin_unlock_irqrestore(&c->spinlock, flags);
+ return ret;
}
EXPORT_SYMBOL(clk_round_rate);
@@ -374,9 +403,10 @@ int __init tegra_disable_boot_clocks(void)
struct clk *c;
- spin_lock_irqsave(&clock_lock, flags);
+ mutex_lock(&clock_list_lock);
list_for_each_entry(c, &clocks, node) {
+ spin_lock_irqsave(&c->spinlock, flags);
if (c->refcnt == 0 && c->state == ON &&
c->ops && c->ops->disable) {
pr_warning("Disabling clock %s left on by bootloader\n",
@@ -384,23 +414,85 @@ int __init tegra_disable_boot_clocks(void)
c->ops->disable(c);
c->state = OFF;
}
+ spin_unlock_irqrestore(&c->spinlock, flags);
}
- spin_unlock_irqrestore(&clock_lock, flags);
-
+ mutex_unlock(&clock_list_lock);
return 0;
}
late_initcall(tegra_disable_boot_clocks);
#endif
#ifdef CONFIG_DEBUG_FS
+
+static int __clk_lock_all_spinlocks(void)
+{
+ struct clk *c;
+
+ list_for_each_entry(c, &clocks, node)
+ if (!spin_trylock(&c->spinlock))
+ goto unlock_spinlocks;
+
+ return 0;
+
+unlock_spinlocks:
+ list_for_each_entry_continue_reverse(c, &clocks, node)
+ spin_unlock(&c->spinlock);
+
+ return -EAGAIN;
+}
+
+static void __clk_unlock_all_spinlocks(void)
+{
+ struct clk *c;
+
+ list_for_each_entry_reverse(c, &clocks, node)
+ spin_unlock(&c->spinlock);
+}
+
+/*
+ * This function retries until it can take all locks, and may take
+ * an arbitrarily long time to complete.
+ * Must be called with irqs enabled, returns with irqs disabled
+ * Must be called with clock_list_lock held
+ */
+static void clk_lock_all(void)
+{
+ int ret;
+retry:
+ local_irq_disable();
+
+ ret = __clk_lock_all_spinlocks();
+ if (ret)
+ goto failed_spinlocks;
+
+ /* All locks taken successfully, return */
+ return;
+
+failed_spinlocks:
+ local_irq_enable();
+ yield();
+ goto retry;
+}
+
+/*
+ * Unlocks all clocks after a clk_lock_all
+ * Must be called with irqs disabled, returns with irqs enabled
+ * Must be called with clock_list_lock held
+ */
+static void clk_unlock_all(void)
+{
+ __clk_unlock_all_spinlocks();
+
+ local_irq_enable();
+}
+
static struct dentry *clk_debugfs_root;
static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
{
struct clk *child;
- struct clk *safe;
const char *state = "uninit";
char div[8] = {0};
@@ -431,8 +523,12 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
c->rate > c->max_rate ? '!' : ' ',
!c->set ? '*' : ' ',
30 - level * 3, c->name,
- state, c->refcnt, div, c->rate);
- list_for_each_entry_safe(child, safe, &c->children, sibling) {
+ state, c->refcnt, div, clk_get_rate_all_locked(c));
+
+ list_for_each_entry(child, &clocks, node) {
+ if (child->parent != c)
+ continue;
+
clock_tree_show_one(s, child, level + 1);
}
}
@@ -440,14 +536,20 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
static int clock_tree_show(struct seq_file *s, void *data)
{
struct clk *c;
- unsigned long flags;
seq_printf(s, " clock state ref div rate\n");
seq_printf(s, "--------------------------------------------------------------\n");
- spin_lock_irqsave(&clock_lock, flags);
+
+ mutex_lock(&clock_list_lock);
+
+ clk_lock_all();
+
list_for_each_entry(c, &clocks, node)
if (c->parent == NULL)
clock_tree_show_one(s, c, 0);
- spin_unlock_irqrestore(&clock_lock, flags);
+
+ clk_unlock_all();
+
+ mutex_unlock(&clock_list_lock);
return 0;
}
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index 20f0ce6..dd5c029 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -20,8 +20,9 @@
#ifndef __MACH_TEGRA_CLOCK_H
#define __MACH_TEGRA_CLOCK_H
-#include <linux/list.h>
#include <linux/clkdev.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#define DIV_BUS (1 << 0)
#define DIV_U71 (1 << 1)
@@ -75,8 +76,6 @@ enum clk_state {
struct clk {
/* node for master clocks list */
struct list_head node; /* node for list of all clocks */
- struct list_head children; /* list of children */
- struct list_head sibling; /* node for children */
struct clk_lookup lookup;
#ifdef CONFIG_DEBUG_FS
@@ -122,8 +121,9 @@ struct clk {
struct clk *backup;
} cpu;
} u;
-};
+ spinlock_t spinlock;
+};
struct clk_duplicate {
const char *name;
@@ -143,11 +143,8 @@ void tegra2_periph_reset_assert(struct clk *c);
void clk_init(struct clk *clk);
struct clk *tegra_get_clock_by_name(const char *name);
unsigned long clk_measure_input_freq(void);
-void clk_disable_locked(struct clk *c);
-int clk_enable_locked(struct clk *c);
-int clk_set_parent_locked(struct clk *c, struct clk *parent);
-int clk_set_rate_locked(struct clk *c, unsigned long rate);
int clk_reparent(struct clk *c, struct clk *parent);
void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
+unsigned long clk_get_rate_locked(struct clk *c);
#endif
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index 6338652..fa7f9ca 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -25,4 +25,5 @@ struct clk;
void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
+unsigned long clk_get_rate_all_locked(struct clk *c);
#endif
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index f03c712..ab013b1 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -23,8 +23,8 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/hrtimer.h>
#include <linux/clkdev.h>
+#include <linux/clk.h>
#include <mach/iomap.h>
#include <mach/suspend.h>
@@ -147,6 +147,13 @@
static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+/*
+ * Some clocks share a register with other clocks. Any clock op that
+ * non-atomically modifies a register used by another clock must lock
+ * clock_register_lock first.
+ */
+static DEFINE_SPINLOCK(clock_register_lock);
+
#define clk_writel(value, reg) \
__raw_writel(value, (u32)reg_clk_base + (reg))
#define clk_readl(reg) \
@@ -330,12 +337,12 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
val |= sel->value << shift;
if (c->refcnt)
- clk_enable_locked(p);
+ clk_enable(p);
clk_writel(val, c->reg);
if (c->refcnt && c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
clk_reparent(c, p);
return 0;
@@ -378,22 +385,22 @@ static void tegra2_cpu_clk_disable(struct clk *c)
static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
{
int ret;
- ret = clk_set_parent_locked(c->parent, c->u.cpu.backup);
+ ret = clk_set_parent(c->parent, c->u.cpu.backup);
if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
return ret;
}
- if (rate == c->u.cpu.backup->rate)
+ if (rate == clk_get_rate(c->u.cpu.backup))
goto out;
- ret = clk_set_rate_locked(c->u.cpu.main, rate);
+ ret = clk_set_rate(c->u.cpu.main, rate);
if (ret) {
pr_err("Failed to change cpu pll to %lu\n", rate);
return ret;
}
- ret = clk_set_parent_locked(c->parent, c->u.cpu.main);
+ ret = clk_set_parent(c->parent, c->u.cpu.main);
if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
return ret;
@@ -421,24 +428,45 @@ static void tegra2_bus_clk_init(struct clk *c)
static int tegra2_bus_clk_enable(struct clk *c)
{
- u32 val = clk_readl(c->reg);
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_register_lock, flags);
+
+ val = clk_readl(c->reg);
val &= ~(BUS_CLK_DISABLE << c->reg_shift);
clk_writel(val, c->reg);
+
+ spin_unlock_irqrestore(&clock_register_lock, flags);
+
return 0;
}
static void tegra2_bus_clk_disable(struct clk *c)
{
- u32 val = clk_readl(c->reg);
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_register_lock, flags);
+
+ val = clk_readl(c->reg);
val |= BUS_CLK_DISABLE << c->reg_shift;
clk_writel(val, c->reg);
+
+ spin_unlock_irqrestore(&clock_register_lock, flags);
}
static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
{
- u32 val = clk_readl(c->reg);
- unsigned long parent_rate = c->parent->rate;
+ u32 val;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ unsigned long flags;
+ int ret = -EINVAL;
int i;
+
+ spin_lock_irqsave(&clock_register_lock, flags);
+
+ val = clk_readl(c->reg);
for (i = 1; i <= 4; i++) {
if (rate == parent_rate / i) {
val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
@@ -446,10 +474,14 @@ static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
clk_writel(val, c->reg);
c->div = i;
c->mul = 1;
- return 0;
+ ret = 0;
+ break;
}
}
- return -EINVAL;
+
+ spin_unlock_irqrestore(&clock_register_lock, flags);
+
+ return ret;
}
static struct clk_ops tegra_bus_ops = {
@@ -511,14 +543,15 @@ static void tegra2_blink_clk_disable(struct clk *c)
static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate)
{
- if (rate >= c->parent->rate) {
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ if (rate >= parent_rate) {
c->div = 1;
pmc_writel(0, c->reg);
} else {
unsigned int on_off;
u32 val;
- on_off = DIV_ROUND_UP(c->parent->rate / 8, rate);
+ on_off = DIV_ROUND_UP(parent_rate / 8, rate);
c->div = on_off * 8;
val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
@@ -604,7 +637,7 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
- input_rate = c->parent->rate;
+ input_rate = clk_get_rate(c->parent);
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
if (sel->input_rate == input_rate && sel->output_rate == rate) {
c->mul = sel->n;
@@ -717,9 +750,11 @@ static int tegra2_pll_div_clk_enable(struct clk *c)
{
u32 val;
u32 new_val;
+ unsigned long flags;
pr_debug("%s: %s\n", __func__, c->name);
if (c->flags & DIV_U71) {
+ spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
new_val &= 0xFFFF;
@@ -729,12 +764,15 @@ static int tegra2_pll_div_clk_enable(struct clk *c)
val &= ~(0xFFFF << c->reg_shift);
val |= new_val << c->reg_shift;
clk_writel(val, c->reg);
+ spin_unlock_irqrestore(&clock_register_lock, flags);
return 0;
} else if (c->flags & DIV_2) {
BUG_ON(!(c->flags & PLLD));
+ spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
val &= ~PLLD_MISC_DIV_RST;
clk_writel(val, c->reg);
+ spin_unlock_irqrestore(&clock_register_lock, flags);
return 0;
}
return -EINVAL;
@@ -744,9 +782,11 @@ static void tegra2_pll_div_clk_disable(struct clk *c)
{
u32 val;
u32 new_val;
+ unsigned long flags;
pr_debug("%s: %s\n", __func__, c->name);
if (c->flags & DIV_U71) {
+ spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
new_val &= 0xFFFF;
@@ -756,11 +796,14 @@ static void tegra2_pll_div_clk_disable(struct clk *c)
val &= ~(0xFFFF << c->reg_shift);
val |= new_val << c->reg_shift;
clk_writel(val, c->reg);
+ spin_unlock_irqrestore(&clock_register_lock, flags);
} else if (c->flags & DIV_2) {
BUG_ON(!(c->flags & PLLD));
+ spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
val |= PLLD_MISC_DIV_RST;
clk_writel(val, c->reg);
+ spin_unlock_irqrestore(&clock_register_lock, flags);
}
}
@@ -769,10 +812,14 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
u32 val;
u32 new_val;
int divider_u71;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ unsigned long flags;
+
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
- divider_u71 = clk_div71_get_divider(c->parent->rate, rate);
+ divider_u71 = clk_div71_get_divider(parent_rate, rate);
if (divider_u71 >= 0) {
+ spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
new_val &= 0xFFFF;
@@ -786,10 +833,11 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
clk_writel(val, c->reg);
c->div = divider_u71 + 2;
c->mul = 2;
+ spin_unlock_irqrestore(&clock_register_lock, flags);
return 0;
}
} else if (c->flags & DIV_2) {
- if (c->parent->rate == rate * 2)
+ if (parent_rate == rate * 2)
return 0;
}
return -EINVAL;
@@ -798,15 +846,16 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
{
int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
- divider = clk_div71_get_divider(c->parent->rate, rate);
+ divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return c->parent->rate * 2 / (divider + 2);
+ return parent_rate * 2 / (divider + 2);
} else if (c->flags & DIV_2) {
- return c->parent->rate / 2;
+ return parent_rate / 2;
}
return -EINVAL;
}
@@ -912,12 +961,12 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
if (c->refcnt)
- clk_enable_locked(p);
+ clk_enable(p);
clk_writel(val, c->reg);
if (c->refcnt && c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
clk_reparent(c, p);
return 0;
@@ -931,9 +980,10 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
{
u32 val;
int divider;
- pr_debug("%s: %lu\n", __func__, rate);
+ unsigned long parent_rate = clk_get_rate(c->parent);
+
if (c->flags & DIV_U71) {
- divider = clk_div71_get_divider(c->parent->rate, rate);
+ divider = clk_div71_get_divider(parent_rate, rate);
if (divider >= 0) {
val = clk_readl(c->reg);
val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
@@ -944,7 +994,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
return 0;
}
} else if (c->flags & DIV_U16) {
- divider = clk_div16_get_divider(c->parent->rate, rate);
+ divider = clk_div16_get_divider(parent_rate, rate);
if (divider >= 0) {
val = clk_readl(c->reg);
val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
@@ -954,7 +1004,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
c->mul = 1;
return 0;
}
- } else if (c->parent->rate <= rate) {
+ } else if (parent_rate <= rate) {
c->div = 1;
c->mul = 1;
return 0;
@@ -966,19 +1016,20 @@ static long tegra2_periph_clk_round_rate(struct clk *c,
unsigned long rate)
{
int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
- divider = clk_div71_get_divider(c->parent->rate, rate);
+ divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return c->parent->rate * 2 / (divider + 2);
+ return parent_rate * 2 / (divider + 2);
} else if (c->flags & DIV_U16) {
- divider = clk_div16_get_divider(c->parent->rate, rate);
+ divider = clk_div16_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return c->parent->rate / (divider + 1);
+ return parent_rate / (divider + 1);
}
return -EINVAL;
}
@@ -1006,7 +1057,7 @@ static void tegra2_clk_double_init(struct clk *c)
static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate)
{
- if (rate != 2 * c->parent->rate)
+ if (rate != 2 * clk_get_rate(c->parent))
return -EINVAL;
c->mul = 2;
c->div = 1;
@@ -1057,12 +1108,12 @@ static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
val |= sel->value;
if (c->refcnt)
- clk_enable_locked(p);
+ clk_enable(p);
clk_writel(val, c->reg);
if (c->refcnt && c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
clk_reparent(c, p);
return 0;
--
1.7.3.1
^ permalink raw reply related
* [PATCH 09/21] ARM: tegra: clock: Move unshared clk struct members into union
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.h | 85 +++++++------
arch/arm/mach-tegra/tegra2_clocks.c | 234 ++++++++++++++++++++---------------
2 files changed, 176 insertions(+), 143 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index 198f234..20f0ce6 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -47,7 +47,7 @@ struct clk_mux_sel {
u32 value;
};
-struct clk_pll_table {
+struct clk_pll_freq_table {
unsigned long input_rate;
unsigned long output_rate;
u16 n;
@@ -74,51 +74,54 @@ enum clk_state {
struct clk {
/* node for master clocks list */
- struct list_head node;
- struct list_head children; /* list of children */
- struct list_head sibling; /* node for children */
-#ifdef CONFIG_DEBUG_FS
- struct dentry *dent;
- struct dentry *parent_dent;
-#endif
- struct clk_ops *ops;
- struct clk *parent;
- struct clk_lookup lookup;
- unsigned long rate;
- unsigned long max_rate;
- u32 flags;
- u32 refcnt;
- const char *name;
- u32 reg;
- u32 reg_shift;
- unsigned int clk_num;
- enum clk_state state;
+ struct list_head node; /* node for list of all clocks */
+ struct list_head children; /* list of children */
+ struct list_head sibling; /* node for children */
+ struct clk_lookup lookup;
+
#ifdef CONFIG_DEBUG_FS
- bool set;
+ struct dentry *dent;
+ bool set;
#endif
+ struct clk_ops *ops;
+ unsigned long rate;
+ unsigned long max_rate;
+ u32 flags;
+ const char *name;
+
+ u32 refcnt;
+ enum clk_state state;
+ struct clk *parent;
+ u32 div;
+ u32 mul;
- /* PLL */
- unsigned long input_min;
- unsigned long input_max;
- unsigned long cf_min;
- unsigned long cf_max;
- unsigned long vco_min;
- unsigned long vco_max;
- const struct clk_pll_table *pll_table;
- int pll_lock_delay;
-
- /* DIV */
- u32 div;
- u32 mul;
-
- /* MUX */
const struct clk_mux_sel *inputs;
- u32 sel;
- u32 reg_mask;
+ u32 reg;
+ u32 reg_shift;
- /* Virtual cpu clock */
- struct clk *main;
- struct clk *backup;
+ union {
+ struct {
+ unsigned int clk_num;
+ } periph;
+ struct {
+ unsigned long input_min;
+ unsigned long input_max;
+ unsigned long cf_min;
+ unsigned long cf_max;
+ unsigned long vco_min;
+ unsigned long vco_max;
+ const struct clk_pll_freq_table *freq_table;
+ int lock_delay;
+ } pll;
+ struct {
+ u32 sel;
+ u32 reg_mask;
+ } mux;
+ struct {
+ struct clk *main;
+ struct clk *backup;
+ } cpu;
+ } u;
};
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 62ec3fb..f03c712 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -109,9 +109,9 @@
#define PLLE_MISC_READY (1 << 15)
-#define PERIPH_CLK_TO_ENB_REG(c) ((c->clk_num / 32) * 4)
-#define PERIPH_CLK_TO_ENB_SET_REG(c) ((c->clk_num / 32) * 8)
-#define PERIPH_CLK_TO_ENB_BIT(c) (1 << (c->clk_num % 32))
+#define PERIPH_CLK_TO_ENB_REG(c) ((c->u.periph.clk_num / 32) * 4)
+#define PERIPH_CLK_TO_ENB_SET_REG(c) ((c->u.periph.clk_num / 32) * 8)
+#define PERIPH_CLK_TO_ENB_BIT(c) (1 << (c->u.periph.clk_num % 32))
#define SUPER_CLK_MUX 0x00
#define SUPER_STATE_SHIFT 28
@@ -378,24 +378,24 @@ static void tegra2_cpu_clk_disable(struct clk *c)
static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
{
int ret;
- ret = clk_set_parent_locked(c->parent, c->backup);
+ ret = clk_set_parent_locked(c->parent, c->u.cpu.backup);
if (ret) {
- pr_err("Failed to switch cpu to clock %s\n", c->backup->name);
+ pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
return ret;
}
- if (rate == c->backup->rate)
+ if (rate == c->u.cpu.backup->rate)
goto out;
- ret = clk_set_rate_locked(c->main, rate);
+ ret = clk_set_rate_locked(c->u.cpu.main, rate);
if (ret) {
pr_err("Failed to change cpu pll to %lu\n", rate);
return ret;
}
- ret = clk_set_parent_locked(c->parent, c->main);
+ ret = clk_set_parent_locked(c->parent, c->u.cpu.main);
if (ret) {
- pr_err("Failed to switch cpu to clock %s\n", c->main->name);
+ pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
return ret;
}
@@ -543,7 +543,7 @@ static struct clk_ops tegra_blink_clk_ops = {
/* PLL Functions */
static int tegra2_pll_clk_wait_for_lock(struct clk *c)
{
- udelay(c->pll_lock_delay);
+ udelay(c->u.pll.lock_delay);
return 0;
}
@@ -600,12 +600,12 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
{
u32 val;
unsigned long input_rate;
- const struct clk_pll_table *sel;
+ const struct clk_pll_freq_table *sel;
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
input_rate = c->parent->rate;
- for (sel = c->pll_table; sel->input_rate != 0; sel++) {
+ for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
if (sel->input_rate == input_rate && sel->output_rate == rate) {
c->mul = sel->n;
c->div = sel->m * sel->p;
@@ -1138,7 +1138,7 @@ static struct clk tegra_clk_32k = {
.max_rate = 32768,
};
-static struct clk_pll_table tegra_pll_s_table[] = {
+static struct clk_pll_freq_table tegra_pll_s_freq_table[] = {
{32768, 12000000, 366, 1, 1, 0},
{32768, 13000000, 397, 1, 1, 0},
{32768, 19200000, 586, 1, 1, 0},
@@ -1150,17 +1150,19 @@ static struct clk tegra_pll_s = {
.name = "pll_s",
.flags = PLL_ALT_MISC_REG,
.ops = &tegra_pll_ops,
- .reg = 0xf0,
- .input_min = 32768,
- .input_max = 32768,
.parent = &tegra_clk_32k,
- .cf_min = 0, /* FIXME */
- .cf_max = 0, /* FIXME */
- .vco_min = 12000000,
- .vco_max = 26000000,
- .pll_table = tegra_pll_s_table,
.max_rate = 26000000,
- .pll_lock_delay = 300,
+ .reg = 0xf0,
+ .u.pll = {
+ .input_min = 32768,
+ .input_max = 32768,
+ .cf_min = 0, /* FIXME */
+ .cf_max = 0, /* FIXME */
+ .vco_min = 12000000,
+ .vco_max = 26000000,
+ .freq_table = tegra_pll_s_freq_table,
+ .lock_delay = 300,
+ },
};
static struct clk_mux_sel tegra_clk_m_sel[] = {
@@ -1168,18 +1170,18 @@ static struct clk_mux_sel tegra_clk_m_sel[] = {
{ .input = &tegra_pll_s, .value = 1},
{ 0, 0},
};
+
static struct clk tegra_clk_m = {
.name = "clk_m",
.flags = ENABLE_ON_INIT,
.ops = &tegra_clk_m_ops,
.inputs = tegra_clk_m_sel,
.reg = 0x1fc,
- .reg_mask = (1<<28),
.reg_shift = 28,
.max_rate = 26000000,
};
-static struct clk_pll_table tegra_pll_c_table[] = {
+static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
{ 0, 0, 0, 0, 0, 0 },
};
@@ -1188,16 +1190,18 @@ static struct clk tegra_pll_c = {
.flags = PLL_HAS_CPCON,
.ops = &tegra_pll_ops,
.reg = 0x80,
- .input_min = 2000000,
- .input_max = 31000000,
.parent = &tegra_clk_m,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 20000000,
- .vco_max = 1400000000,
- .pll_table = tegra_pll_c_table,
.max_rate = 600000000,
- .pll_lock_delay = 300,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_c_freq_table,
+ .lock_delay = 300,
+ },
};
static struct clk tegra_pll_c_out1 = {
@@ -1210,7 +1214,7 @@ static struct clk tegra_pll_c_out1 = {
.max_rate = 600000000,
};
-static struct clk_pll_table tegra_pll_m_table[] = {
+static struct clk_pll_freq_table tegra_pll_m_freq_table[] = {
{ 12000000, 666000000, 666, 12, 1, 8},
{ 13000000, 666000000, 666, 13, 1, 8},
{ 19200000, 666000000, 555, 16, 1, 8},
@@ -1227,16 +1231,18 @@ static struct clk tegra_pll_m = {
.flags = PLL_HAS_CPCON,
.ops = &tegra_pll_ops,
.reg = 0x90,
- .input_min = 2000000,
- .input_max = 31000000,
.parent = &tegra_clk_m,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 20000000,
- .vco_max = 1200000000,
- .pll_table = tegra_pll_m_table,
.max_rate = 800000000,
- .pll_lock_delay = 300,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .freq_table = tegra_pll_m_freq_table,
+ .lock_delay = 300,
+ },
};
static struct clk tegra_pll_m_out1 = {
@@ -1249,7 +1255,7 @@ static struct clk tegra_pll_m_out1 = {
.max_rate = 600000000,
};
-static struct clk_pll_table tegra_pll_p_table[] = {
+static struct clk_pll_freq_table tegra_pll_p_freq_table[] = {
{ 12000000, 216000000, 432, 12, 2, 8},
{ 13000000, 216000000, 432, 13, 2, 8},
{ 19200000, 216000000, 90, 4, 2, 1},
@@ -1266,16 +1272,18 @@ static struct clk tegra_pll_p = {
.flags = ENABLE_ON_INIT | PLL_FIXED | PLL_HAS_CPCON,
.ops = &tegra_pll_ops,
.reg = 0xa0,
- .input_min = 2000000,
- .input_max = 31000000,
.parent = &tegra_clk_m,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 20000000,
- .vco_max = 1400000000,
- .pll_table = tegra_pll_p_table,
.max_rate = 432000000,
- .pll_lock_delay = 300,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_p_freq_table,
+ .lock_delay = 300,
+ },
};
static struct clk tegra_pll_p_out1 = {
@@ -1318,7 +1326,7 @@ static struct clk tegra_pll_p_out4 = {
.max_rate = 432000000,
};
-static struct clk_pll_table tegra_pll_a_table[] = {
+static struct clk_pll_freq_table tegra_pll_a_freq_table[] = {
{ 28800000, 56448000, 49, 25, 1, 1},
{ 28800000, 73728000, 64, 25, 1, 1},
{ 28800000, 11289600, 49, 25, 1, 1},
@@ -1332,16 +1340,18 @@ static struct clk tegra_pll_a = {
.flags = PLL_HAS_CPCON,
.ops = &tegra_pll_ops,
.reg = 0xb0,
- .input_min = 2000000,
- .input_max = 31000000,
.parent = &tegra_pll_p_out1,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 20000000,
- .vco_max = 1400000000,
- .pll_table = tegra_pll_a_table,
.max_rate = 56448000,
- .pll_lock_delay = 300,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_a_freq_table,
+ .lock_delay = 300,
+ },
};
static struct clk tegra_pll_a_out0 = {
@@ -1354,7 +1364,7 @@ static struct clk tegra_pll_a_out0 = {
.max_rate = 56448000,
};
-static struct clk_pll_table tegra_pll_d_table[] = {
+static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
{ 12000000, 216000000, 216, 12, 1, 4},
{ 13000000, 216000000, 216, 13, 1, 4},
{ 19200000, 216000000, 135, 12, 1, 3},
@@ -1378,16 +1388,18 @@ static struct clk tegra_pll_d = {
.flags = PLL_HAS_CPCON | PLLD,
.ops = &tegra_pll_ops,
.reg = 0xd0,
- .input_min = 2000000,
- .input_max = 40000000,
.parent = &tegra_clk_m,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 40000000,
- .vco_max = 1000000000,
- .pll_table = tegra_pll_d_table,
.max_rate = 1000000000,
- .pll_lock_delay = 1000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .freq_table = tegra_pll_d_freq_table,
+ .lock_delay = 1000,
+ },
};
static struct clk tegra_pll_d_out0 = {
@@ -1398,7 +1410,7 @@ static struct clk tegra_pll_d_out0 = {
.max_rate = 500000000,
};
-static struct clk_pll_table tegra_pll_u_table[] = {
+static struct clk_pll_freq_table tegra_pll_u_freq_table[] = {
{ 12000000, 480000000, 960, 12, 2, 0},
{ 13000000, 480000000, 960, 13, 2, 0},
{ 19200000, 480000000, 200, 4, 2, 0},
@@ -1411,19 +1423,21 @@ static struct clk tegra_pll_u = {
.flags = PLLU,
.ops = &tegra_pll_ops,
.reg = 0xc0,
- .input_min = 2000000,
- .input_max = 40000000,
.parent = &tegra_clk_m,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 480000000,
- .vco_max = 960000000,
- .pll_table = tegra_pll_u_table,
.max_rate = 480000000,
- .pll_lock_delay = 1000,
-};
-
-static struct clk_pll_table tegra_pll_x_table[] = {
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 480000000,
+ .vco_max = 960000000,
+ .freq_table = tegra_pll_u_freq_table,
+ .lock_delay = 1000,
+ },
+};
+
+static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
/* 1 GHz */
{ 12000000, 1000000000, 1000, 12, 1, 12},
{ 13000000, 1000000000, 1000, 13, 1, 12},
@@ -1474,19 +1488,21 @@ static struct clk tegra_pll_x = {
.flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG,
.ops = &tegra_pllx_ops,
.reg = 0xe0,
- .input_min = 2000000,
- .input_max = 31000000,
.parent = &tegra_clk_m,
- .cf_min = 1000000,
- .cf_max = 6000000,
- .vco_min = 20000000,
- .vco_max = 1200000000,
- .pll_table = tegra_pll_x_table,
.max_rate = 1000000000,
- .pll_lock_delay = 300,
-};
-
-static struct clk_pll_table tegra_pll_e_table[] = {
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .freq_table = tegra_pll_x_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk_pll_freq_table tegra_pll_e_freq_table[] = {
{ 12000000, 100000000, 200, 24, 1, 0 },
{ 0, 0, 0, 0, 0, 0 },
};
@@ -1495,41 +1511,49 @@ static struct clk tegra_pll_e = {
.name = "pll_e",
.flags = PLL_ALT_MISC_REG,
.ops = &tegra_plle_ops,
- .input_min = 12000000,
- .input_max = 12000000,
- .max_rate = 100000000,
.parent = &tegra_clk_m,
.reg = 0xe8,
- .pll_table = tegra_pll_e_table,
+ .max_rate = 100000000,
+ .u.pll = {
+ .input_min = 12000000,
+ .input_max = 12000000,
+ .freq_table = tegra_pll_e_freq_table,
+ },
};
static struct clk tegra_clk_d = {
.name = "clk_d",
.flags = PERIPH_NO_RESET,
.ops = &tegra_clk_double_ops,
- .clk_num = 90,
.reg = 0x34,
.reg_shift = 12,
.parent = &tegra_clk_m,
.max_rate = 52000000,
+ .u.periph = {
+ .clk_num = 90,
+ },
};
/* dap_mclk1, belongs to the cdev1 pingroup. */
static struct clk tegra_dev1_clk = {
.name = "clk_dev1",
.ops = &tegra_cdev_clk_ops,
- .clk_num = 94,
.rate = 26000000,
.max_rate = 26000000,
+ .u.periph = {
+ .clk_num = 94,
+ },
};
/* dap_mclk2, belongs to the cdev2 pingroup. */
static struct clk tegra_dev2_clk = {
.name = "clk_dev2",
.ops = &tegra_cdev_clk_ops,
- .clk_num = 93,
.rate = 26000000,
.max_rate = 26000000,
+ .u.periph = {
+ .clk_num = 93,
+ },
};
/* initialized before peripheral clocks */
@@ -1564,10 +1588,12 @@ static struct clk tegra_clk_audio_2x = {
.flags = PERIPH_NO_RESET,
.max_rate = 48000000,
.ops = &tegra_clk_double_ops,
- .clk_num = 89,
.reg = 0x34,
.reg_shift = 8,
.parent = &tegra_clk_audio,
+ .u.periph = {
+ .clk_num = 89,
+ },
};
struct clk_lookup tegra_audio_clk_lookups[] = {
@@ -1645,10 +1671,12 @@ static struct clk tegra_clk_sclk = {
static struct clk tegra_clk_virtual_cpu = {
.name = "cpu",
.parent = &tegra_clk_cclk,
- .main = &tegra_pll_x,
- .backup = &tegra_pll_p,
.ops = &tegra_cpu_ops,
.max_rate = 1000000000,
+ .u.cpu = {
+ .main = &tegra_pll_x,
+ .backup = &tegra_pll_p,
+ },
};
static struct clk tegra_clk_hclk = {
@@ -1763,11 +1791,13 @@ static struct clk_mux_sel mux_clk_32k[] = {
.con_id = _con, \
}, \
.ops = &tegra_periph_clk_ops, \
- .clk_num = _clk_num, \
.reg = _reg, \
.inputs = _inputs, \
.flags = _flags, \
.max_rate = _max, \
+ .u.periph = { \
+ .clk_num = _clk_num, \
+ }, \
}
struct clk tegra_list_clks[] = {
--
1.7.3.1
^ permalink raw reply related
* [PATCH 08/21] ARM: tegra: clock: Rearrange static clock tables
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Make the static clocks look more like the array of clocks
so they can all be initalized with the same helper function.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/tegra2_clocks.c | 106 +++++++++++++++++------------------
1 files changed, 51 insertions(+), 55 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index e1e0d81f..62ec3fb 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -1770,7 +1770,7 @@ static struct clk_mux_sel mux_clk_32k[] = {
.max_rate = _max, \
}
-struct clk tegra_periph_clks[] = {
+struct clk tegra_list_clks[] = {
PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71),
@@ -1879,71 +1879,67 @@ struct clk_duplicate tegra_clk_duplicates[] = {
.clk = ck, \
}
-struct clk_lookup tegra_clk_lookups[] = {
- /* external root sources */
- CLK(NULL, "32k_clk", &tegra_clk_32k),
- CLK(NULL, "pll_s", &tegra_pll_s),
- CLK(NULL, "clk_m", &tegra_clk_m),
- CLK(NULL, "pll_m", &tegra_pll_m),
- CLK(NULL, "pll_m_out1", &tegra_pll_m_out1),
- CLK(NULL, "pll_c", &tegra_pll_c),
- CLK(NULL, "pll_c_out1", &tegra_pll_c_out1),
- CLK(NULL, "pll_p", &tegra_pll_p),
- CLK(NULL, "pll_p_out1", &tegra_pll_p_out1),
- CLK(NULL, "pll_p_out2", &tegra_pll_p_out2),
- CLK(NULL, "pll_p_out3", &tegra_pll_p_out3),
- CLK(NULL, "pll_p_out4", &tegra_pll_p_out4),
- CLK(NULL, "pll_a", &tegra_pll_a),
- CLK(NULL, "pll_a_out0", &tegra_pll_a_out0),
- CLK(NULL, "pll_d", &tegra_pll_d),
- CLK(NULL, "pll_d_out0", &tegra_pll_d_out0),
- CLK(NULL, "pll_u", &tegra_pll_u),
- CLK(NULL, "pll_x", &tegra_pll_x),
- CLK(NULL, "pll_e", &tegra_pll_e),
- CLK(NULL, "cclk", &tegra_clk_cclk),
- CLK(NULL, "sclk", &tegra_clk_sclk),
- CLK(NULL, "hclk", &tegra_clk_hclk),
- CLK(NULL, "pclk", &tegra_clk_pclk),
- CLK(NULL, "clk_d", &tegra_clk_d),
- CLK(NULL, "clk_dev1", &tegra_dev1_clk),
- CLK(NULL, "clk_dev2", &tegra_dev2_clk),
- CLK(NULL, "cpu", &tegra_clk_virtual_cpu),
- CLK(NULL, "blink", &tegra_clk_blink),
-};
+struct clk *tegra_ptr_clks[] = {
+ &tegra_clk_32k,
+ &tegra_pll_s,
+ &tegra_clk_m,
+ &tegra_pll_m,
+ &tegra_pll_m_out1,
+ &tegra_pll_c,
+ &tegra_pll_c_out1,
+ &tegra_pll_p,
+ &tegra_pll_p_out1,
+ &tegra_pll_p_out2,
+ &tegra_pll_p_out3,
+ &tegra_pll_p_out4,
+ &tegra_pll_a,
+ &tegra_pll_a_out0,
+ &tegra_pll_d,
+ &tegra_pll_d_out0,
+ &tegra_pll_u,
+ &tegra_pll_x,
+ &tegra_pll_e,
+ &tegra_clk_cclk,
+ &tegra_clk_sclk,
+ &tegra_clk_hclk,
+ &tegra_clk_pclk,
+ &tegra_clk_d,
+ &tegra_dev1_clk,
+ &tegra_dev2_clk,
+ &tegra_clk_virtual_cpu,
+ &tegra_clk_blink,
+};
+
+static void tegra2_init_one_clock(struct clk *c)
+{
+ clk_init(c);
+ if (!c->lookup.dev_id && !c->lookup.con_id)
+ c->lookup.con_id = c->name;
+ c->lookup.clk = c;
+ clkdev_add(&c->lookup);
+}
void __init tegra2_init_clocks(void)
{
int i;
- struct clk_lookup *cl;
struct clk *c;
- struct clk_duplicate *cd;
-
- for (i = 0; i < ARRAY_SIZE(tegra_clk_lookups); i++) {
- cl = &tegra_clk_lookups[i];
- clk_init(cl->clk);
- clkdev_add(cl);
- }
- for (i = 0; i < ARRAY_SIZE(tegra_periph_clks); i++) {
- c = &tegra_periph_clks[i];
- cl = &c->lookup;
- cl->clk = c;
+ for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
+ tegra2_init_one_clock(tegra_ptr_clks[i]);
- clk_init(cl->clk);
- clkdev_add(cl);
- }
+ for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
+ tegra2_init_one_clock(&tegra_list_clks[i]);
for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
- cd = &tegra_clk_duplicates[i];
- c = tegra_get_clock_by_name(cd->name);
- if (c) {
- cl = &cd->lookup;
- cl->clk = c;
- clkdev_add(cl);
- } else {
+ c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
+ if (!c) {
pr_err("%s: Unknown duplicate clock %s\n", __func__,
- cd->name);
+ tegra_clk_duplicates[i].name);
+ continue;
}
+
+ tegra_clk_duplicates[i].lookup.clk = c;
+ clkdev_add(&tegra_clk_duplicates[i].lookup);
}
init_audio_sync_clock_mux();
--
1.7.3.1
^ permalink raw reply related
* [PATCH 07/21] ARM: tegra: clock: Drop CPU dvfs
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
The existing version did not extend well to core dvfs, drop it
for now until the new clk api with clk_prepare and clk_unprepare
is ready and non-atomic clocks are possible.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/Makefile | 1 -
arch/arm/mach-tegra/clock.c | 159 +-------------------------------
arch/arm/mach-tegra/clock.h | 24 -----
arch/arm/mach-tegra/cpu-tegra.c | 2 +-
arch/arm/mach-tegra/include/mach/clk.h | 5 -
arch/arm/mach-tegra/tegra2_clocks.c | 2 -
arch/arm/mach-tegra/tegra2_dvfs.c | 86 -----------------
arch/arm/mach-tegra/tegra2_dvfs.h | 20 ----
8 files changed, 2 insertions(+), 297 deletions(-)
delete mode 100644 arch/arm/mach-tegra/tegra2_dvfs.c
delete mode 100644 arch/arm/mach-tegra/tegra2_dvfs.h
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 6b537de..23de060 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -9,7 +9,6 @@ obj-y += powergate.o
obj-y += fuse.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o
obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 79c0bd8..a1193f9 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -24,81 +24,14 @@
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
-#include <linux/regulator/consumer.h>
#include <linux/clkdev.h>
-#include "clock.h"
#include "board.h"
-#include "fuse.h"
+#include "clock.h"
static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clock_lock);
-static DEFINE_MUTEX(dvfs_lock);
-
-static int clk_is_dvfs(struct clk *c)
-{
- return (c->dvfs != NULL);
-};
-
-static int dvfs_set_rate(struct dvfs *d, unsigned long rate)
-{
- struct dvfs_table *t;
-
- if (d->table == NULL)
- return -ENODEV;
-
- for (t = d->table; t->rate != 0; t++) {
- if (rate <= t->rate) {
- if (!d->reg)
- return 0;
-
- return regulator_set_voltage(d->reg,
- t->millivolts * 1000,
- d->max_millivolts * 1000);
- }
- }
-
- return -EINVAL;
-}
-
-static void dvfs_init(struct clk *c)
-{
- int process_id;
- int i;
- struct dvfs_table *table;
-
- process_id = c->dvfs->cpu ? tegra_core_process_id() :
- tegra_cpu_process_id();
-
- for (i = 0; i < c->dvfs->process_id_table_length; i++)
- if (process_id == c->dvfs->process_id_table[i].process_id)
- c->dvfs->table = c->dvfs->process_id_table[i].table;
-
- if (c->dvfs->table == NULL) {
- pr_err("Failed to find dvfs table for clock %s process %d\n",
- c->name, process_id);
- return;
- }
-
- c->dvfs->max_millivolts = 0;
- for (table = c->dvfs->table; table->rate != 0; table++)
- if (c->dvfs->max_millivolts < table->millivolts)
- c->dvfs->max_millivolts = table->millivolts;
-
- c->dvfs->reg = regulator_get(NULL, c->dvfs->reg_id);
-
- if (IS_ERR(c->dvfs->reg)) {
- pr_err("Failed to get regulator %s for clock %s\n",
- c->dvfs->reg_id, c->name);
- c->dvfs->reg = NULL;
- return;
- }
-
- if (c->refcnt > 0)
- dvfs_set_rate(c->dvfs, c->rate);
-}
-
struct clk *tegra_get_clock_by_name(const char *name)
{
struct clk *c;
@@ -214,34 +147,11 @@ int clk_enable_locked(struct clk *c)
return 0;
}
-int clk_enable_cansleep(struct clk *c)
-{
- int ret;
- unsigned long flags;
-
- mutex_lock(&dvfs_lock);
-
- if (clk_is_dvfs(c) && c->refcnt > 0)
- dvfs_set_rate(c->dvfs, c->rate);
-
- spin_lock_irqsave(&clock_lock, flags);
- ret = clk_enable_locked(c);
- spin_unlock_irqrestore(&clock_lock, flags);
-
- mutex_unlock(&dvfs_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(clk_enable_cansleep);
-
int clk_enable(struct clk *c)
{
int ret;
unsigned long flags;
- if (clk_is_dvfs(c))
- BUG();
-
spin_lock_irqsave(&clock_lock, flags);
ret = clk_enable_locked(c);
spin_unlock_irqrestore(&clock_lock, flags);
@@ -268,30 +178,10 @@ void clk_disable_locked(struct clk *c)
c->refcnt--;
}
-void clk_disable_cansleep(struct clk *c)
-{
- unsigned long flags;
-
- mutex_lock(&dvfs_lock);
-
- spin_lock_irqsave(&clock_lock, flags);
- clk_disable_locked(c);
- spin_unlock_irqrestore(&clock_lock, flags);
-
- if (clk_is_dvfs(c) && c->refcnt == 0)
- dvfs_set_rate(c->dvfs, c->rate);
-
- mutex_unlock(&dvfs_lock);
-}
-EXPORT_SYMBOL(clk_disable_cansleep);
-
void clk_disable(struct clk *c)
{
unsigned long flags;
- if (clk_is_dvfs(c))
- BUG();
-
spin_lock_irqsave(&clock_lock, flags);
clk_disable_locked(c);
spin_unlock_irqrestore(&clock_lock, flags);
@@ -356,41 +246,11 @@ int clk_set_rate_locked(struct clk *c, unsigned long rate)
return 0;
}
-int clk_set_rate_cansleep(struct clk *c, unsigned long rate)
-{
- int ret = 0;
- unsigned long flags;
-
- mutex_lock(&dvfs_lock);
-
- if (rate > c->rate)
- ret = dvfs_set_rate(c->dvfs, rate);
- if (ret)
- goto out;
-
- spin_lock_irqsave(&clock_lock, flags);
- ret = clk_set_rate_locked(c, rate);
- spin_unlock_irqrestore(&clock_lock, flags);
-
- if (ret)
- goto out;
-
- ret = dvfs_set_rate(c->dvfs, rate);
-
-out:
- mutex_unlock(&dvfs_lock);
- return ret;
-}
-EXPORT_SYMBOL(clk_set_rate_cansleep);
-
int clk_set_rate(struct clk *c, unsigned long rate)
{
int ret = 0;
unsigned long flags;
- if (clk_is_dvfs(c))
- BUG();
-
spin_lock_irqsave(&clock_lock, flags);
ret = clk_set_rate_locked(c, rate);
spin_unlock_irqrestore(&clock_lock, flags);
@@ -503,23 +363,6 @@ void __init tegra_init_clock(void)
tegra2_init_clocks();
}
-int __init tegra_init_dvfs(void)
-{
- struct clk *c, *safe;
-
- mutex_lock(&dvfs_lock);
-
- list_for_each_entry_safe(c, safe, &clocks, node)
- if (c->dvfs)
- dvfs_init(c);
-
- mutex_unlock(&dvfs_lock);
-
- return 0;
-}
-
-late_initcall(tegra_init_dvfs);
-
/*
* Iterate through all clocks, disabling any for which the refcount is 0
* but the clock init detected the bootloader left the clock on.
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index b76d33d..198f234 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -41,28 +41,6 @@
#define ENABLE_ON_INIT (1 << 28)
struct clk;
-struct regulator;
-
-struct dvfs_table {
- unsigned long rate;
- int millivolts;
-};
-
-struct dvfs_process_id_table {
- int process_id;
- struct dvfs_table *table;
-};
-
-struct dvfs {
- struct regulator *reg;
- struct dvfs_table *table;
- int max_millivolts;
-
- int process_id_table_length;
- const char *reg_id;
- bool cpu;
- struct dvfs_process_id_table process_id_table[];
-};
struct clk_mux_sel {
struct clk *input;
@@ -141,8 +119,6 @@ struct clk {
/* Virtual cpu clock */
struct clk *main;
struct clk *backup;
-
- struct dvfs *dvfs;
};
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index ad26a9f..cda03f1 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -91,7 +91,7 @@ static int tegra_update_cpu_speed(unsigned long rate)
freqs.old, freqs.new);
#endif
- ret = clk_set_rate_cansleep(cpu_clk, freqs.new * 1000);
+ ret = clk_set_rate(cpu_clk, freqs.new * 1000);
if (ret) {
pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n",
freqs.new);
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index a217f68..6338652 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -25,9 +25,4 @@ struct clk;
void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
-int clk_enable_cansleep(struct clk *clk);
-void clk_disable_cansleep(struct clk *clk);
-int clk_set_rate_cansleep(struct clk *clk, unsigned long rate);
-int clk_set_parent_cansleep(struct clk *clk, struct clk *parent);
-
#endif
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 36da2e8..e1e0d81f 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -31,7 +31,6 @@
#include "clock.h"
#include "fuse.h"
-#include "tegra2_dvfs.h"
#define RST_DEVICES 0x004
#define RST_DEVICES_SET 0x300
@@ -1650,7 +1649,6 @@ static struct clk tegra_clk_virtual_cpu = {
.backup = &tegra_pll_p,
.ops = &tegra_cpu_ops,
.max_rate = 1000000000,
- .dvfs = &tegra_dvfs_virtual_cpu_dvfs,
};
static struct clk tegra_clk_hclk = {
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c
deleted file mode 100644
index 5529c23..0000000
--- a/arch/arm/mach-tegra/tegra2_dvfs.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * arch/arm/mach-tegra/tegra2_dvfs.c
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@google.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-
-#include "clock.h"
-#include "tegra2_dvfs.h"
-
-static struct dvfs_table virtual_cpu_process_0[] = {
- {314000000, 750},
- {456000000, 825},
- {608000000, 900},
- {760000000, 975},
- {817000000, 1000},
- {912000000, 1050},
- {1000000000, 1100},
- {0, 0},
-};
-
-static struct dvfs_table virtual_cpu_process_1[] = {
- {314000000, 750},
- {456000000, 825},
- {618000000, 900},
- {770000000, 975},
- {827000000, 1000},
- {922000000, 1050},
- {1000000000, 1100},
- {0, 0},
-};
-
-static struct dvfs_table virtual_cpu_process_2[] = {
- {494000000, 750},
- {675000000, 825},
- {817000000, 875},
- {922000000, 925},
- {1000000000, 975},
- {0, 0},
-};
-
-static struct dvfs_table virtual_cpu_process_3[] = {
- {730000000, 750},
- {760000000, 775},
- {845000000, 800},
- {1000000000, 875},
- {0, 0},
-};
-
-struct dvfs tegra_dvfs_virtual_cpu_dvfs = {
- .reg_id = "vdd_cpu",
- .process_id_table = {
- {
- .process_id = 0,
- .table = virtual_cpu_process_0,
- },
- {
- .process_id = 1,
- .table = virtual_cpu_process_1,
- },
- {
- .process_id = 2,
- .table = virtual_cpu_process_2,
- },
- {
- .process_id = 3,
- .table = virtual_cpu_process_3,
- },
- },
- .process_id_table_length = 4,
- .cpu = 1,
-};
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.h b/arch/arm/mach-tegra/tegra2_dvfs.h
deleted file mode 100644
index f8c1adb..0000000
--- a/arch/arm/mach-tegra/tegra2_dvfs.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * arch/arm/mach-tegra/tegra2_dvfs.h
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@google.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-extern struct dvfs tegra_dvfs_virtual_cpu_dvfs;
--
1.7.3.1
^ permalink raw reply related
* [PATCH 06/21] ARM: tegra: clock: Initialize clocks that have no enable
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Assume that any clock that has no enable op is always on.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 0ea5d92..79c0bd8 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -166,6 +166,15 @@ void clk_init(struct clk *c)
if (c->ops && c->ops->init)
c->ops->init(c);
+ if (!c->ops || !c->ops->enable) {
+ c->refcnt++;
+ c->set = 1;
+ if (c->parent)
+ c->state = c->parent->state;
+ else
+ c->state = ON;
+ }
+
clk_recalculate_rate(c);
list_add(&c->node, &clocks);
--
1.7.3.1
^ permalink raw reply related
* [PATCH 05/21] ARM: tegra: clock: Disable clocks left on by bootloader
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Adds CONFIG_TEGRA_DISABLE_BOOTLOADER_CLOCKS that iterates
through all clocks, disabling any for which the refcount
is 0 but the clock init detected the bootloader left the
clock on.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/Kconfig | 6 ++++++
arch/arm/mach-tegra/clock.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index f0fda77..9f4fc6b 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -57,6 +57,12 @@ config TEGRA_DEBUG_UARTE
endchoice
+config TEGRA_DISABLE_BOOTLOADER_CLOCKS
+ bool "Disable clocks left on by bootloader"
+ help
+ Disables clocks that are detected to be on but haven't
+ been requested by a kernel driver.
+
config TEGRA_SYSTEM_DMA
bool "Enable system DMA driver for NVIDIA Tegra SoCs"
default y
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index f55bb83..0ea5d92 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -511,6 +511,36 @@ int __init tegra_init_dvfs(void)
late_initcall(tegra_init_dvfs);
+/*
+ * Iterate through all clocks, disabling any for which the refcount is 0
+ * but the clock init detected the bootloader left the clock on.
+ */
+#ifdef CONFIG_TEGRA_DISABLE_BOOTLOADER_CLOCKS
+int __init tegra_disable_boot_clocks(void)
+{
+ unsigned long flags;
+
+ struct clk *c;
+
+ spin_lock_irqsave(&clock_lock, flags);
+
+ list_for_each_entry(c, &clocks, node) {
+ if (c->refcnt == 0 && c->state == ON &&
+ c->ops && c->ops->disable) {
+ pr_warning("Disabling clock %s left on by bootloader\n",
+ c->name);
+ c->ops->disable(c);
+ c->state = OFF;
+ }
+ }
+
+ spin_unlock_irqrestore(&clock_lock, flags);
+
+ return 0;
+}
+late_initcall(tegra_disable_boot_clocks);
+#endif
+
#ifdef CONFIG_DEBUG_FS
static struct dentry *clk_debugfs_root;
--
1.7.3.1
^ permalink raw reply related
* [PATCH 04/21] ARM: tegra: clock: Don't use PLL lock bits
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
The PLL lock bits are not reliable, use per-PLL timeouts instead.
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.h | 2 +-
arch/arm/mach-tegra/tegra2_clocks.c | 31 +++++++++----------------------
2 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index 42f00c0..b76d33d 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -53,7 +53,6 @@ struct dvfs_process_id_table {
struct dvfs_table *table;
};
-
struct dvfs {
struct regulator *reg;
struct dvfs_table *table;
@@ -128,6 +127,7 @@ struct clk {
unsigned long vco_min;
unsigned long vco_max;
const struct clk_pll_table *pll_table;
+ int pll_lock_delay;
/* DIV */
u32 div;
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index db27e59..36da2e8 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -79,7 +79,6 @@
#define PLL_BASE_ENABLE (1<<30)
#define PLL_BASE_REF_ENABLE (1<<29)
#define PLL_BASE_OVERRIDE (1<<28)
-#define PLL_BASE_LOCK (1<<27)
#define PLL_BASE_DIVP_MASK (0x7<<20)
#define PLL_BASE_DIVP_SHIFT 20
#define PLL_BASE_DIVN_MASK (0x3FF<<8)
@@ -94,7 +93,6 @@
#define PLL_OUT_RESET_DISABLE (1<<0)
#define PLL_MISC(c) (((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc)
-#define PLL_MISC_LOCK_ENABLE(c) (((c)->flags & PLLU) ? (1<<22) : (1<<18))
#define PLL_MISC_DCCON_SHIFT 20
#define PLL_MISC_CPCON_SHIFT 8
@@ -546,17 +544,7 @@ static struct clk_ops tegra_blink_clk_ops = {
/* PLL Functions */
static int tegra2_pll_clk_wait_for_lock(struct clk *c)
{
- ktime_t before;
-
- before = ktime_get();
-
- while (!(clk_readl(c->reg + PLL_BASE) & PLL_BASE_LOCK)) {
- if (ktime_us_delta(ktime_get(), before) > 5000) {
- pr_err("Timed out waiting for lock bit on pll %s",
- c->name);
- return -1;
- }
- }
+ udelay(c->pll_lock_delay);
return 0;
}
@@ -594,10 +582,6 @@ static int tegra2_pll_clk_enable(struct clk *c)
val |= PLL_BASE_ENABLE;
clk_writel(val, c->reg + PLL_BASE);
- val = clk_readl(c->reg + PLL_MISC(c));
- val |= PLL_MISC_LOCK_ENABLE(c);
- clk_writel(val, c->reg + PLL_MISC(c));
-
tegra2_pll_clk_wait_for_lock(c);
return 0;
@@ -1177,6 +1161,7 @@ static struct clk tegra_pll_s = {
.vco_max = 26000000,
.pll_table = tegra_pll_s_table,
.max_rate = 26000000,
+ .pll_lock_delay = 300,
};
static struct clk_mux_sel tegra_clk_m_sel[] = {
@@ -1213,6 +1198,7 @@ static struct clk tegra_pll_c = {
.vco_max = 1400000000,
.pll_table = tegra_pll_c_table,
.max_rate = 600000000,
+ .pll_lock_delay = 300,
};
static struct clk tegra_pll_c_out1 = {
@@ -1251,6 +1237,7 @@ static struct clk tegra_pll_m = {
.vco_max = 1200000000,
.pll_table = tegra_pll_m_table,
.max_rate = 800000000,
+ .pll_lock_delay = 300,
};
static struct clk tegra_pll_m_out1 = {
@@ -1289,6 +1276,7 @@ static struct clk tegra_pll_p = {
.vco_max = 1400000000,
.pll_table = tegra_pll_p_table,
.max_rate = 432000000,
+ .pll_lock_delay = 300,
};
static struct clk tegra_pll_p_out1 = {
@@ -1354,6 +1342,7 @@ static struct clk tegra_pll_a = {
.vco_max = 1400000000,
.pll_table = tegra_pll_a_table,
.max_rate = 56448000,
+ .pll_lock_delay = 300,
};
static struct clk tegra_pll_a_out0 = {
@@ -1399,6 +1388,7 @@ static struct clk tegra_pll_d = {
.vco_max = 1000000000,
.pll_table = tegra_pll_d_table,
.max_rate = 1000000000,
+ .pll_lock_delay = 1000,
};
static struct clk tegra_pll_d_out0 = {
@@ -1431,6 +1421,7 @@ static struct clk tegra_pll_u = {
.vco_max = 960000000,
.pll_table = tegra_pll_u_table,
.max_rate = 480000000,
+ .pll_lock_delay = 1000,
};
static struct clk_pll_table tegra_pll_x_table[] = {
@@ -1493,6 +1484,7 @@ static struct clk tegra_pll_x = {
.vco_max = 1200000000,
.pll_table = tegra_pll_x_table,
.max_rate = 1000000000,
+ .pll_lock_delay = 300,
};
static struct clk_pll_table tegra_pll_e_table[] = {
@@ -1966,7 +1958,6 @@ static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
void tegra_clk_suspend(void)
{
unsigned long off, i;
- u32 pllx_misc;
u32 *ctx = clk_rst_suspend;
*ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
@@ -2007,10 +1998,6 @@ void tegra_clk_suspend(void)
*ctx++ = clk_readl(MISC_CLK_ENB);
*ctx++ = clk_readl(CLK_MASK_ARM);
-
- pllx_misc = clk_readl(tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
- pllx_misc &= ~PLL_MISC_LOCK_ENABLE(&tegra_pll_x);
- clk_writel(pllx_misc, tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
}
void tegra_clk_resume(void)
--
1.7.3.1
^ permalink raw reply related
* [PATCH 03/21] ARM: tegra: clock: Drop debugging
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.c | 19 ++-----------------
1 files changed, 2 insertions(+), 17 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 77948e0..f55bb83 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -138,7 +138,6 @@ static void clk_recalculate_rate(struct clk *c)
int clk_reparent(struct clk *c, struct clk *parent)
{
- pr_debug("%s: %s\n", __func__, c->name);
c->parent = parent;
list_del(&c->sibling);
list_add_tail(&c->sibling, &parent->children);
@@ -148,9 +147,8 @@ int clk_reparent(struct clk *c, struct clk *parent)
static void propagate_rate(struct clk *c)
{
struct clk *clkp;
- pr_debug("%s: %s\n", __func__, c->name);
+
list_for_each_entry(clkp, &c->children, sibling) {
- pr_debug(" %s\n", clkp->name);
clk_recalculate_rate(clkp);
propagate_rate(clkp);
}
@@ -160,8 +158,6 @@ void clk_init(struct clk *c)
{
unsigned long flags;
- pr_debug("%s: %s\n", __func__, c->name);
-
spin_lock_irqsave(&clock_lock, flags);
INIT_LIST_HEAD(&c->children);
@@ -183,7 +179,7 @@ void clk_init(struct clk *c)
int clk_enable_locked(struct clk *c)
{
int ret;
- pr_debug("%s: %s\n", __func__, c->name);
+
if (c->refcnt == 0) {
if (c->parent) {
ret = clk_enable_locked(c->parent);
@@ -247,7 +243,6 @@ EXPORT_SYMBOL(clk_enable);
void clk_disable_locked(struct clk *c)
{
- pr_debug("%s: %s\n", __func__, c->name);
if (c->refcnt == 0) {
WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
return;
@@ -298,8 +293,6 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent)
{
int ret;
- pr_debug("%s: %s\n", __func__, c->name);
-
if (!c->ops || !c->ops->set_parent)
return -ENOSYS;
@@ -359,8 +352,6 @@ int clk_set_rate_cansleep(struct clk *c, unsigned long rate)
int ret = 0;
unsigned long flags;
- pr_debug("%s: %s\n", __func__, c->name);
-
mutex_lock(&dvfs_lock);
if (rate > c->rate)
@@ -388,8 +379,6 @@ int clk_set_rate(struct clk *c, unsigned long rate)
int ret = 0;
unsigned long flags;
- pr_debug("%s: %s\n", __func__, c->name);
-
if (clk_is_dvfs(c))
BUG();
@@ -408,8 +397,6 @@ unsigned long clk_get_rate(struct clk *c)
spin_lock_irqsave(&clock_lock, flags);
- pr_debug("%s: %s\n", __func__, c->name);
-
ret = c->rate;
spin_unlock_irqrestore(&clock_lock, flags);
@@ -419,8 +406,6 @@ EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *c, unsigned long rate)
{
- pr_debug("%s: %s\n", __func__, c->name);
-
if (!c->ops || !c->ops->round_rate)
return -ENOSYS;
--
1.7.3.1
^ permalink raw reply related
* [PATCH 02/21] ARM: tegra: clock: Don't BUG on changing an enabled PLL
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/tegra2_clocks.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 240f921..db27e59 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -620,7 +620,6 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
const struct clk_pll_table *sel;
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
- BUG_ON(c->refcnt != 0);
input_rate = c->parent->rate;
for (sel = c->pll_table; sel->input_rate != 0; sel++) {
--
1.7.3.1
^ permalink raw reply related
* [PATCH 01/21] ARM: tegra: clock: enable clk reset for non-peripheral clocks
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297590033-15035-1-git-send-email-ccross@android.com>
From: Dima Zavin <dima@android.com>
Add a new 'reset' clk op. This can be provided for any clock,
not just peripherals.
Signed-off-by: Dima Zavin <dima@android.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/mach-tegra/clock.h | 1 +
arch/arm/mach-tegra/tegra2_clocks.c | 29 ++++++++++++++++++-----------
2 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index 083a4cf..42f00c0 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -86,6 +86,7 @@ struct clk_ops {
int (*set_parent)(struct clk *, struct clk *);
int (*set_rate)(struct clk *, unsigned long);
long (*round_rate)(struct clk *, unsigned long);
+ void (*reset)(struct clk *, bool);
};
enum clk_state {
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 7a2926a..240f921 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -263,6 +263,18 @@ static struct clk_ops tegra_clk_m_ops = {
.disable = tegra2_clk_m_disable,
};
+void tegra2_periph_reset_assert(struct clk *c)
+{
+ BUG_ON(!c->ops->reset);
+ c->ops->reset(c, true);
+}
+
+void tegra2_periph_reset_deassert(struct clk *c)
+{
+ BUG_ON(!c->ops->reset);
+ c->ops->reset(c, false);
+}
+
/* super clock functions */
/* "super clocks" on tegra have two-stage muxes and a clock skipping
* super divider. We will ignore the clock skipping divider, since we
@@ -895,23 +907,17 @@ static void tegra2_periph_clk_disable(struct clk *c)
CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
}
-void tegra2_periph_reset_deassert(struct clk *c)
+static void tegra2_periph_clk_reset(struct clk *c, bool assert)
{
- pr_debug("%s on clock %s\n", __func__, c->name);
- if (!(c->flags & PERIPH_NO_RESET))
- clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
- RST_DEVICES_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
-}
+ unsigned long base = assert ? RST_DEVICES_SET : RST_DEVICES_CLR;
-void tegra2_periph_reset_assert(struct clk *c)
-{
- pr_debug("%s on clock %s\n", __func__, c->name);
+ pr_debug("%s %s on clock %s\n", __func__,
+ assert ? "assert" : "deassert", c->name);
if (!(c->flags & PERIPH_NO_RESET))
clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
- RST_DEVICES_SET + PERIPH_CLK_TO_ENB_SET_REG(c));
+ base + PERIPH_CLK_TO_ENB_SET_REG(c));
}
-
static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
{
u32 val;
@@ -1002,6 +1008,7 @@ static struct clk_ops tegra_periph_clk_ops = {
.set_parent = &tegra2_periph_clk_set_parent,
.set_rate = &tegra2_periph_clk_set_rate,
.round_rate = &tegra2_periph_clk_round_rate,
+ .reset = &tegra2_periph_clk_reset,
};
/* Clock doubler ops */
--
1.7.3.1
^ permalink raw reply related
* [PATCH 00/21] Tegra clock updates for 2.6.39
From: Colin Cross @ 2011-02-13 9:40 UTC (permalink / raw)
To: linux-arm-kernel
This patch series brings the Tegra clock subsystem closer to the
proposed common struct clk design, which should make converting
easier. It also fixes a few bugs and adds new features including
memory bus scaling, disabling clocks left on by the bootloader,
and shared clocks.
Colin Cross (20):
ARM: tegra: clock: Don't BUG on changing an enabled PLL
ARM: tegra: clock: Drop debugging
ARM: tegra: clock: Don't use PLL lock bits
ARM: tegra: clock: Disable clocks left on by bootloader
ARM: tegra: clock: Initialize clocks that have no enable
ARM: tegra: clock: Drop CPU dvfs
ARM: tegra: clock: Rearrange static clock tables
ARM: tegra: clock: Move unshared clk struct members into union
ARM: tegra: clock: Convert global lock to a lock per clock
ARM: tegra: cpufreq: Take an extra reference to pllx
ARM: tegra: clock: Add shared bus clock type
ARM: tegra: clock: Remove unnecessary uses of #ifdef CONFIG_DEBUG_FS
ARM: tegra: clock: Refcount periph clock enables
ARM: tegra: clock: Round rate before setting rate
ARM: tegra: Add external memory controller driver
ARM: tegra: clocks: Add emc scaling
ARM: tegra: cpufreq: Adjust memory frequency with cpu frequency
ARM: tegra: clock: Add function to set SDMMC tap delay
ARM: tegra: clock: Fix clock issues in suspend
ARM: tegra: clock: Miscellaneous clock updates
Dima Zavin (1):
ARM: tegra: clock: enable clk reset for non-peripheral clocks
arch/arm/mach-tegra/Kconfig | 10 +
arch/arm/mach-tegra/Makefile | 2 +-
arch/arm/mach-tegra/clock.c | 572 +++++++-------
arch/arm/mach-tegra/clock.h | 128 ++--
arch/arm/mach-tegra/cpu-tegra.c | 25 +-
arch/arm/mach-tegra/include/mach/clk.h | 6 +-
arch/arm/mach-tegra/tegra2_clocks.c | 829 +++++++++++++-------
arch/arm/mach-tegra/tegra2_dvfs.c | 86 --
arch/arm/mach-tegra/tegra2_emc.c | 172 ++++
.../arm/mach-tegra/{tegra2_dvfs.h => tegra2_emc.h} | 13 +-
10 files changed, 1123 insertions(+), 720 deletions(-)
delete mode 100644 arch/arm/mach-tegra/tegra2_dvfs.c
create mode 100644 arch/arm/mach-tegra/tegra2_emc.c
rename arch/arm/mach-tegra/{tegra2_dvfs.h => tegra2_emc.h} (66%)
^ permalink raw reply
* [RFC PATCH] ARM: pmu: add OF match support
From: Grant Likely @ 2011-02-13 6:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D531CA0.8050402@gmail.com>
On Wed, Feb 09, 2011 at 05:00:48PM -0600, Rob Herring wrote:
> On 02/09/2011 01:03 PM, Grant Likely wrote:
> >On Wed, Feb 9, 2011 at 11:53 AM, Will Deacon<will.deacon@arm.com> wrote:
> >>Grant,
> >>
> >>>>Following on from Rob's update, it would be nice if you could specify that
> >>>>the PMU is a CPU PMU (as opposed to L2-cache, bus, gpu etc) in the string.
> >>>>That way adding different PMUs in the future seems more natural and it accounts
> >>>>for your concerns above. Is that ok, or does the compatible string have to
> >>>>match that used by the platform bus?
> >>>
> >>>It does make sense to encode the specific implementation into the
> >>>compatible string. A single device driver can bind against multiple
> >>>compatible strings. ie. the match table could include {.compatible =
> >>>"arm,cortex-a9-pmu"},{.compatible = "arm,cortex-a9-l2cache-pmu"}...
> >>
> >>Ok - that's great! Specifying the CPU is probably a little verbose, but
> >>something like "arm,armv7pmu" would be really helpful when it comes to
> >>multiple devices.
> >>
> >>>>As for versioning, the PMU detection is done dynamically at runtime,
> >>>>so knowing that we're poking a CPU is enough.
> >>>
> >>>Fair enough. It is still good practice in the compatible list to
> >>>encode the specific PMU implementation (maybe arm,cortex-a9-pmu?)
> >>>instead of trying to define a 'generic' or wildcard compatible value.
> >>>Newer implementations can always claim compatibility with an older
> >>>implementation so that the kernel doesn't have to be modified to find
> >>>the new devices. "arm,pmu" is probably too generic.
> >>
> >>"arm,pmu" is definitely too generic. If it's good practice to specify
> >>as much as possible, then go for it - I just feel a little uneasy having
> >>to add lots of redundant kernel code but it sounds like you'll hopefully
> >>prove me wrong on that :)
> >
> >Shouldn't be any redundant kernel code. It's just a list of match
> >values, and the driver code already knows how to handle it. The
> >advantage of being specific is that if the driver ever does need
> >information about the specific implementation (say, because the
> >hardware reported value is buggy), then the data is available.
>
> By that argument, we should use "<vendor>,cortex-a9-rXpX-pmu." As a
> particular rev of the A9 PMU or vendor's SOC could be buggy. That
> would quickly grow into a long list. Just with cpu models, we would
> have:
>
> cortex-a9
> cortex-a8
> cortex-a15
> cortex-a5
> arm1136
> arm1176
> l210
> l220
> pl310
> various xscale flavors
> Qualcomm SMP
This is not even remotely an unreasonable list. Also remember that
newer revisions can (and should!) claim compatibility with an older
version if it is indeed compatible.
Typically what happens is one particular implementation ends up
becoming the defacto standard implementation. So instead of each
implementation claiming compatibility with a loosely defined "arm,pmu"
(a definition that can end up changing over time), each of the above
implementation may end up claiming "arm,cortex-a8-pmu" compatibility.
In essence the string "arm,cortex-a8-pmu" *becomes* the generic
version without the ambiguity of what to do if/when arm invents an
incompatible PMU.
> Keep in mind that the whole point of this platform device is just to
> specify the interrupt connection which is the only part that varies
> by SOC (and is not probe-able).
That's tangential to the argument. There is very good reason to
anchor compatible values to specific implementations. Doing it this
way costs nothing. Not doing it causes ambiguity down the road that
is hard to recover from.
g.
^ permalink raw reply
* reboot not working on linux-2.6.37 for ARMv7
From: Rabin Vincent @ 2011-02-13 3:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTikvBRJyDJb4=puFzbTCXNYGSEXDRkAZ=1PZuneD@mail.gmail.com>
On Sat, Feb 12, 2011 at 21:45, viresh kumar <viresh.linux@gmail.com> wrote:
> We are not modifying flush_cache_all. L2 cache is not flushed from
> there. Actually as soon as L1 Cache is flushed and disabled,
> spin_lock is taken from some other thread. This creates problem for
> our platform, as we do not have monitors support at L2 thus failing
> strex instruction.
Do you use writel() in your arch_reset() routine (as plat-spear appears
to be doing)? There's a spinlock in there too (l2x0_cache_sync(), via
the barriers) which will also not work in your situation.
^ permalink raw reply
* [PATCH v2 0/7] clock/dpll autoidle support
From: Paul Walmsley @ 2011-02-13 1:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297329400-5936-1-git-send-email-rnayak@ti.com>
Hi Rajendra
On Thu, 10 Feb 2011, Rajendra Nayak wrote:
> OMAP has various clock nodes and dpll's
> which support hardware level autoidle.
> Enabling hardware level autoidle provides
> better power savings without much software
> intervention.
>
> This series does the following to help enable
> hardware level autoidling using clock framework
> for some of these nodes on OMAP3 and OMAP4
>
> -1- Adds support for providing function pointers
> for enabling/disabling autoidle in clkops
> -2- Populates these in clkops for all OMAP3/4 dplls
> -3- Enables all dpll autoidle late in boot on OMAP3 and OMAP4
> -4- Adds support for mx postdivider autoidle (present
> only on OMAP4) and enables it late in OMAP4 boot
I've reviewed and commented on all of these, asked a few questions, made a
few changes, and queued the results for 2.6.39 in two branches of
git://git.pwsan.com/linux-2.6:
clk_a_2.6.39 (for the autoidle-independent fixes)
clk_autoidle_a_2.6.39 (for the autoidle-specific code and data changes)
Both branches have been added to the 'integration-2.6.39' branch of
git://git.pwsan.com/linux-integration, and are available as part of the
tag "integration-2.6.39-20110212-004".
thanks,
- Paul
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox