diff options
author | Alex Frid <afrid@nvidia.com> | 2011-03-13 00:41:14 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:42:27 -0800 |
commit | 4f325a029dc651dd924002a456b039a7bc00385b (patch) | |
tree | fc46cbc51917b61184ba5392446916ef04423336 /arch/arm/mach-tegra/pm-t3.c | |
parent | e630c3d716c0a883995c0b7a2938c35d5de25b72 (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.c | 63 |
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))); } |