diff options
author | Joseph Lo <josephl@nvidia.com> | 2012-10-31 17:41:19 +0800 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2012-11-15 15:09:21 -0700 |
commit | a6e293eef2eafc31dbe008301182e7124bd87755 (patch) | |
tree | b4feec69bd092c576fc17584961d84a9c2e6dfe4 /arch/arm/mach-tegra | |
parent | fe508d776908b8512c6d936eb29e40bef1f4b8fc (diff) | |
download | talos-obmc-linux-a6e293eef2eafc31dbe008301182e7124bd87755.tar.gz talos-obmc-linux-a6e293eef2eafc31dbe008301182e7124bd87755.zip |
ARM: tegra30: clocks: add CPU low-power function into tegra_cpu_car_ops
Add suspend, resume and rail_off_ready API into tegra_cpu_car_ops. These
functions were used for CPU powered-down state maintenance. One thing
needs to notice the rail_off_ready API only availalbe for cpu_g cluster
not cpu_lp cluster.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/tegra30_clocks.c | 108 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_cpu_car.h | 37 |
2 files changed, 145 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c index f5b453f4bf4d..efc000e32e1c 100644 --- a/arch/arm/mach-tegra/tegra30_clocks.c +++ b/arch/arm/mach-tegra/tegra30_clocks.c @@ -31,6 +31,8 @@ #include <asm/clkdev.h> +#include <mach/powergate.h> + #include "clock.h" #include "fuse.h" #include "iomap.h" @@ -309,6 +311,31 @@ #define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) #define CPU_RESET(cpu) (0x1111ul << (cpu)) +#define CLK_RESET_CCLK_BURST 0x20 +#define CLK_RESET_CCLK_DIVIDER 0x24 +#define CLK_RESET_PLLX_BASE 0xe0 +#define CLK_RESET_PLLX_MISC 0xe4 + +#define CLK_RESET_SOURCE_CSITE 0x1d4 + +#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28 +#define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4 +#define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0 +#define CLK_RESET_CCLK_IDLE_POLICY 1 +#define CLK_RESET_CCLK_RUN_POLICY 2 +#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 + +#ifdef CONFIG_PM_SLEEP +static struct cpu_clk_suspend_context { + u32 pllx_misc; + u32 pllx_base; + + u32 cpu_burst; + u32 clk_csite_src; + u32 cclk_divider; +} tegra30_cpu_clk_sctx; +#endif + /** * Structure defining the fields for USB UTMI clocks Parameters. */ @@ -2386,12 +2413,93 @@ static void tegra30_disable_cpu_clock(u32 cpu) reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); } +#ifdef CONFIG_PM_SLEEP +static bool tegra30_cpu_rail_off_ready(void) +{ + unsigned int cpu_rst_status; + int cpu_pwr_status; + + cpu_rst_status = readl(reg_clk_base + + TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); + cpu_pwr_status = tegra_powergate_is_powered(TEGRA_POWERGATE_CPU1) || + tegra_powergate_is_powered(TEGRA_POWERGATE_CPU2) || + tegra_powergate_is_powered(TEGRA_POWERGATE_CPU3); + + if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status) + return false; + + return true; +} + +static void tegra30_cpu_clock_suspend(void) +{ + /* switch coresite to clk_m, save off original source */ + tegra30_cpu_clk_sctx.clk_csite_src = + readl(reg_clk_base + CLK_RESET_SOURCE_CSITE); + writel(3<<30, reg_clk_base + CLK_RESET_SOURCE_CSITE); + + tegra30_cpu_clk_sctx.cpu_burst = + readl(reg_clk_base + CLK_RESET_CCLK_BURST); + tegra30_cpu_clk_sctx.pllx_base = + readl(reg_clk_base + CLK_RESET_PLLX_BASE); + tegra30_cpu_clk_sctx.pllx_misc = + readl(reg_clk_base + CLK_RESET_PLLX_MISC); + tegra30_cpu_clk_sctx.cclk_divider = + readl(reg_clk_base + CLK_RESET_CCLK_DIVIDER); +} + +static void tegra30_cpu_clock_resume(void) +{ + unsigned int reg, policy; + + /* Is CPU complex already running on PLLX? */ + reg = readl(reg_clk_base + CLK_RESET_CCLK_BURST); + policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF; + + if (policy == CLK_RESET_CCLK_IDLE_POLICY) + reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF; + else if (policy == CLK_RESET_CCLK_RUN_POLICY) + reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF; + else + BUG(); + + if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) { + /* restore PLLX settings if CPU is on different PLL */ + writel(tegra30_cpu_clk_sctx.pllx_misc, + reg_clk_base + CLK_RESET_PLLX_MISC); + writel(tegra30_cpu_clk_sctx.pllx_base, + reg_clk_base + CLK_RESET_PLLX_BASE); + + /* wait for PLL stabilization if PLLX was enabled */ + if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30)) + udelay(300); + } + + /* + * Restore original burst policy setting for calls resulting from CPU + * LP2 in idle or system suspend. + */ + writel(tegra30_cpu_clk_sctx.cclk_divider, + reg_clk_base + CLK_RESET_CCLK_DIVIDER); + writel(tegra30_cpu_clk_sctx.cpu_burst, + reg_clk_base + CLK_RESET_CCLK_BURST); + + writel(tegra30_cpu_clk_sctx.clk_csite_src, + reg_clk_base + CLK_RESET_SOURCE_CSITE); +} +#endif + static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { .wait_for_reset = tegra30_wait_cpu_in_reset, .put_in_reset = tegra30_put_cpu_in_reset, .out_of_reset = tegra30_cpu_out_of_reset, .enable_clock = tegra30_enable_cpu_clock, .disable_clock = tegra30_disable_cpu_clock, +#ifdef CONFIG_PM_SLEEP + .rail_off_ready = tegra30_cpu_rail_off_ready, + .suspend = tegra30_cpu_clock_suspend, + .resume = tegra30_cpu_clock_resume, +#endif }; void __init tegra30_cpu_car_ops_init(void) diff --git a/arch/arm/mach-tegra/tegra_cpu_car.h b/arch/arm/mach-tegra/tegra_cpu_car.h index 30d063ad2bef..9764d31032b7 100644 --- a/arch/arm/mach-tegra/tegra_cpu_car.h +++ b/arch/arm/mach-tegra/tegra_cpu_car.h @@ -30,6 +30,12 @@ * CPU clock un-gate * disable_clock: * CPU clock gate + * rail_off_ready: + * CPU is ready for rail off + * suspend: + * save the clock settings when CPU go into low-power state + * resume: + * restore the clock settings when CPU exit low-power state */ struct tegra_cpu_car_ops { void (*wait_for_reset)(u32 cpu); @@ -37,6 +43,11 @@ struct tegra_cpu_car_ops { void (*out_of_reset)(u32 cpu); void (*enable_clock)(u32 cpu); void (*disable_clock)(u32 cpu); +#ifdef CONFIG_PM_SLEEP + bool (*rail_off_ready)(void); + void (*suspend)(void); + void (*resume)(void); +#endif }; extern struct tegra_cpu_car_ops *tegra_cpu_car_ops; @@ -81,6 +92,32 @@ static inline void tegra_disable_cpu_clock(u32 cpu) tegra_cpu_car_ops->disable_clock(cpu); } +#ifdef CONFIG_PM_SLEEP +static inline bool tegra_cpu_rail_off_ready(void) +{ + if (WARN_ON(!tegra_cpu_car_ops->rail_off_ready)) + return false; + + return tegra_cpu_car_ops->rail_off_ready(); +} + +static inline void tegra_cpu_clock_suspend(void) +{ + if (WARN_ON(!tegra_cpu_car_ops->suspend)) + return; + + tegra_cpu_car_ops->suspend(); +} + +static inline void tegra_cpu_clock_resume(void) +{ + if (WARN_ON(!tegra_cpu_car_ops->resume)) + return; + + tegra_cpu_car_ops->resume(); +} +#endif + void tegra20_cpu_car_ops_init(void); void tegra30_cpu_car_ops_init(void); |