summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/pm-t3.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-03-13 00:41:14 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:42:27 -0800
commit4f325a029dc651dd924002a456b039a7bc00385b (patch)
treefc46cbc51917b61184ba5392446916ef04423336 /arch/arm/mach-tegra/pm-t3.c
parente630c3d716c0a883995c0b7a2938c35d5de25b72 (diff)
ARM: tegra: clock: Re-factor Tegra3 cpu clocks
Added second level virtualization (on top of virtual cpu rate control) to support different Tegra3 CPU power modes: low power (LP) mode and geared performance (G) mode. Virtual cpu complex (cpu_cmplx) clock is defined as a child with two parents: virtual cpu_lp and virtual cpu_g clocks for the respective modes. Mode switch sequence was integrated into cpu_cmplx set parent implementation. (Before this commit mode switch was triggered outside the clock framework, which created cpu clock/mode synchronization problems). Each mode clock is derived from its own super clock mux (cclk_lp and cclk_g) to statically match Tegra3 h/w layout. (Before this commit the code had to dynamically synchronize CPU mode and active mux selection). This change also allowed to support PLLX output divider for low power mode as fixed 1:2 divider with bypass control embedded into cclk_lp parent section. Updated auto and sysfs CPU mode switch calls to use new clock framework, and removed clock manipulation from the low level mode switch implementation. Original-Change-Id: Ibc3cc495b2ff29e2d3417eff2bfd45535cbd015b Reviewed-on: http://git-master/r/24734 Reviewed-by: Aleksandr Frid <afrid@nvidia.com> Tested-by: Aleksandr Frid <afrid@nvidia.com> Tested-by: Jin Qian <jqian@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Original-Change-Id: I23ae80edbf14fb22727a6fc317cd9e5baf8bd6be Rebase-Id: Rdcd4a2165ebd92bf4caa35d68ca81d19a3789351
Diffstat (limited to 'arch/arm/mach-tegra/pm-t3.c')
-rw-r--r--arch/arm/mach-tegra/pm-t3.c63
1 files changed, 21 insertions, 42 deletions
diff --git a/arch/arm/mach-tegra/pm-t3.c b/arch/arm/mach-tegra/pm-t3.c
index 1c57a019a143..92209dc308fa 100644
--- a/arch/arm/mach-tegra/pm-t3.c
+++ b/arch/arm/mach-tegra/pm-t3.c
@@ -94,29 +94,32 @@ static int cluster_switch_prolog_clock(unsigned int flags)
u32 CclkBurstPolicy;
u32 SuperCclkDivier;
- /* Read the CPU clock settings for the currently active CPU. */
- CclkBurstPolicy = readl(CAR_CCLK_BURST_POLICY);
- SuperCclkDivier = readl(CAR_SUPER_CCLK_DIVIDER);
-
/* Read the bond out register containing the G and LP CPUs. */
reg = readl(CAR_BOND_OUT_V);
+ /* Sync G-PLLX divider bypass with LP (no effect on G, just to prevent
+ LP settings overwrite by save/restore code */
+ CclkBurstPolicy = ~PLLX_DIV2_BYPASS_LP & readl(CAR_CCLKG_BURST_POLICY);
+ CclkBurstPolicy |= PLLX_DIV2_BYPASS_LP & readl(CAR_CCLKLP_BURST_POLICY);
+ writel(CclkBurstPolicy, CAR_CCLKG_BURST_POLICY);
+
/* Switching to G? */
if (flags & TEGRA_POWER_CLUSTER_G) {
/* Do the G CPUs exist? */
if (reg & CAR_BOND_OUT_V_CPU_G)
return -ENXIO;
+ /* Keep G CPU clock policy set by upper laayer, with the
+ exception of the transition via LP1 */
if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
/* In LP1 power mode come up on CLKM (oscillator) */
+ CclkBurstPolicy = readl(CAR_CCLKG_BURST_POLICY);
CclkBurstPolicy |= ~0xF;
SuperCclkDivier = 0;
- }
- /* We will be running on the G CPU after the switch.
- Set up the G clock policy. */
- writel(CclkBurstPolicy, CAR_CCLKG_BURST_POLICY);
- writel(SuperCclkDivier, CAR_SUPER_CCLKG_DIVIDER);
+ writel(CclkBurstPolicy, CAR_CCLKG_BURST_POLICY);
+ writel(SuperCclkDivier, CAR_SUPER_CCLKG_DIVIDER);
+ }
/* Hold G CPUs 1-3 in reset after the switch */
reg = CPU_RESET(1) | CPU_RESET(2) | CPU_RESET(3);
@@ -144,43 +147,17 @@ static int cluster_switch_prolog_clock(unsigned int flags)
if (reg & CAR_BOND_OUT_V_CPU_LP)
return -ENXIO;
+ /* Keep LP CPU clock policy set by upper layer, with the
+ exception of the transition via LP1 */
if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
/* In LP1 power mode come up on CLKM (oscillator) */
+ CclkBurstPolicy = readl(CAR_CCLKLP_BURST_POLICY);
CclkBurstPolicy |= ~0xF;
SuperCclkDivier = 0;
- } else {
- /* It is possible that PLLX frequency is too high
- for the LP CPU. Reduce the frequency if necessary
- to prevent over-clocking when we switch. PLLX
- has an implied divide-by-2 when the LP CPU is
- active unless PLLX_DIV2_BYPASS_LP is selected. */
-
- struct clk *c = tegra_get_clock_by_name("cpu");
- unsigned long cur_rate = clk_get_rate(c);
- unsigned long max_rate = tegra_get_lpcpu_max_rate();
- int err;
-
- BUG_ON(max_rate == 0);
- if (cur_rate/2 > max_rate) {
- /* PLLX is running too fast for the LP CPU.
- Reduce it to LP maximum rate which must
- be multipled by 2 because of the LP CPU's
- implied divied-by-2. */
-
- DEBUG_CLUSTER(("%s: G freq %lu\r\n", __func__,
- cur_rate));
- err = clk_set_rate(c, max_rate * 2);
- BUG_ON(err);
- DEBUG_CLUSTER(("%s: G freq %lu\r\n", __func__,
- clk_get_rate(c)));
- }
- }
- /* We will be running on the LP CPU after the switch.
- Set up the LP clock policy. */
- CclkBurstPolicy &= ~PLLX_DIV2_BYPASS_LP;
- writel(CclkBurstPolicy, CAR_CCLKLP_BURST_POLICY);
- writel(SuperCclkDivier, CAR_SUPER_CCLKLP_DIVIDER);
+ writel(CclkBurstPolicy, CAR_CCLKLP_BURST_POLICY);
+ writel(SuperCclkDivier, CAR_SUPER_CCLKLP_DIVIDER);
+ }
/* Take the LP CPU ut of reset after the switch */
reg = CPU_RESET(0);
@@ -280,7 +257,9 @@ void tegra_cluster_switch_epilog(unsigned int flags)
#if DEBUG_CLUSTER_SWITCH
{
- struct clk *c = tegra_get_clock_by_name("cpu");
+ /* FIXME: clock functions below are taking mutex */
+ struct clk *c = tegra_get_clock_by_name(
+ is_lp_cluster() ? "cpu_lp" : "cpu_g");
DEBUG_CLUSTER(("%s: %s freq %lu\r\n", __func__,
is_lp_cluster() ? "LP" : "G", clk_get_rate(c)));
}