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>
This commit is contained in:
Joseph Lo 2012-10-31 17:41:19 +08:00 committed by Stephen Warren
parent fe508d7769
commit a6e293eef2
2 changed files with 145 additions and 0 deletions

View File

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

View File

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