diff options
author | Joseph Lo <josephl@nvidia.com> | 2013-04-03 07:31:47 -0400 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2013-04-03 16:31:41 -0400 |
commit | c8c2e6069065fdecfb195a2c438c7faa964aef22 (patch) | |
tree | 43f1528c665ec9b68891fcbcb345ffa1b0d51dcd | |
parent | 4b51ccbc469facb7b589a71c2a4ae47d3e425d02 (diff) |
ARM: tegra: pm: add platform suspend support
Adding suspend to RAM support for Tegra platform. There are three suspend
mode for Tegra. The difference were below.
* LP2: CPU voltage off
* LP1: CPU voltage off, DRAM in self-refresh
* LP0: CPU + Core voltage off, DRAM in self-refresh
After this patch, the LP2 suspend mode will be supported.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/common.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 93 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.h | 15 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pmc.c | 50 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pmc.h | 4 |
5 files changed, 146 insertions, 17 deletions
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index c84505c1f644..eb1f3c8c74cc 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c | |||
@@ -109,5 +109,6 @@ void __init tegra_init_early(void) | |||
109 | 109 | ||
110 | void __init tegra_init_late(void) | 110 | void __init tegra_init_late(void) |
111 | { | 111 | { |
112 | tegra_init_suspend(); | ||
112 | tegra_powergate_debugfs_init(); | 113 | tegra_powergate_debugfs_init(); |
113 | } | 114 | } |
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 5f5611f40b43..3a3318a83ad3 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/cpumask.h> | 22 | #include <linux/cpumask.h> |
23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <linux/cpu_pm.h> | 24 | #include <linux/cpu_pm.h> |
25 | #include <linux/suspend.h> | ||
25 | #include <linux/err.h> | 26 | #include <linux/err.h> |
26 | #include <linux/clk/tegra.h> | 27 | #include <linux/clk/tegra.h> |
27 | 28 | ||
@@ -38,14 +39,10 @@ | |||
38 | #include "fuse.h" | 39 | #include "fuse.h" |
39 | #include "pmc.h" | 40 | #include "pmc.h" |
40 | #include "sleep.h" | 41 | #include "sleep.h" |
41 | 42 | #include "pmc.h" | |
42 | #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ | ||
43 | |||
44 | #define PMC_CTRL 0x0 | ||
45 | 43 | ||
46 | #ifdef CONFIG_PM_SLEEP | 44 | #ifdef CONFIG_PM_SLEEP |
47 | static DEFINE_SPINLOCK(tegra_lp2_lock); | 45 | static DEFINE_SPINLOCK(tegra_lp2_lock); |
48 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); | ||
49 | void (*tegra_tear_down_cpu)(void); | 46 | void (*tegra_tear_down_cpu)(void); |
50 | 47 | ||
51 | /* | 48 | /* |
@@ -145,14 +142,7 @@ static int tegra_sleep_cpu(unsigned long v2p) | |||
145 | 142 | ||
146 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) | 143 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) |
147 | { | 144 | { |
148 | u32 mode; | 145 | tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); |
149 | |||
150 | /* Only the last cpu down does the final suspend steps */ | ||
151 | mode = readl(pmc + PMC_CTRL); | ||
152 | mode |= TEGRA_POWER_CPU_PWRREQ_OE; | ||
153 | writel(mode, pmc + PMC_CTRL); | ||
154 | |||
155 | set_power_timers(cpu_on_time, cpu_off_time); | ||
156 | 146 | ||
157 | cpu_cluster_pm_enter(); | 147 | cpu_cluster_pm_enter(); |
158 | suspend_cpu_complex(); | 148 | suspend_cpu_complex(); |
@@ -162,4 +152,81 @@ void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) | |||
162 | restore_cpu_complex(); | 152 | restore_cpu_complex(); |
163 | cpu_cluster_pm_exit(); | 153 | cpu_cluster_pm_exit(); |
164 | } | 154 | } |
155 | |||
156 | enum tegra_suspend_mode tegra_pm_validate_suspend_mode( | ||
157 | enum tegra_suspend_mode mode) | ||
158 | { | ||
159 | /* Tegra114 didn't support any suspending mode yet. */ | ||
160 | if (tegra_chip_id == TEGRA114) | ||
161 | return TEGRA_SUSPEND_NONE; | ||
162 | |||
163 | /* | ||
164 | * The Tegra devices only support suspending to LP2 currently. | ||
165 | */ | ||
166 | if (mode > TEGRA_SUSPEND_LP2) | ||
167 | return TEGRA_SUSPEND_LP2; | ||
168 | |||
169 | return mode; | ||
170 | } | ||
171 | |||
172 | static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { | ||
173 | [TEGRA_SUSPEND_NONE] = "none", | ||
174 | [TEGRA_SUSPEND_LP2] = "LP2", | ||
175 | [TEGRA_SUSPEND_LP1] = "LP1", | ||
176 | [TEGRA_SUSPEND_LP0] = "LP0", | ||
177 | }; | ||
178 | |||
179 | static int __cpuinit tegra_suspend_enter(suspend_state_t state) | ||
180 | { | ||
181 | enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); | ||
182 | |||
183 | if (WARN_ON(mode < TEGRA_SUSPEND_NONE || | ||
184 | mode >= TEGRA_MAX_SUSPEND_MODE)) | ||
185 | return -EINVAL; | ||
186 | |||
187 | pr_info("Entering suspend state %s\n", lp_state[mode]); | ||
188 | |||
189 | tegra_pmc_pm_set(mode); | ||
190 | |||
191 | local_fiq_disable(); | ||
192 | |||
193 | suspend_cpu_complex(); | ||
194 | switch (mode) { | ||
195 | case TEGRA_SUSPEND_LP2: | ||
196 | tegra_set_cpu_in_lp2(0); | ||
197 | break; | ||
198 | default: | ||
199 | break; | ||
200 | } | ||
201 | |||
202 | cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); | ||
203 | |||
204 | switch (mode) { | ||
205 | case TEGRA_SUSPEND_LP2: | ||
206 | tegra_clear_cpu_in_lp2(0); | ||
207 | break; | ||
208 | default: | ||
209 | break; | ||
210 | } | ||
211 | restore_cpu_complex(); | ||
212 | |||
213 | local_fiq_enable(); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static const struct platform_suspend_ops tegra_suspend_ops = { | ||
219 | .valid = suspend_valid_only_mem, | ||
220 | .enter = tegra_suspend_enter, | ||
221 | }; | ||
222 | |||
223 | void __init tegra_init_suspend(void) | ||
224 | { | ||
225 | if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) | ||
226 | return; | ||
227 | |||
228 | tegra_pmc_suspend_init(); | ||
229 | |||
230 | suspend_set_ops(&tegra_suspend_ops); | ||
231 | } | ||
165 | #endif | 232 | #endif |
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 787335cc964c..73a45f181fd9 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h | |||
@@ -21,6 +21,8 @@ | |||
21 | #ifndef _MACH_TEGRA_PM_H_ | 21 | #ifndef _MACH_TEGRA_PM_H_ |
22 | #define _MACH_TEGRA_PM_H_ | 22 | #define _MACH_TEGRA_PM_H_ |
23 | 23 | ||
24 | #include "pmc.h" | ||
25 | |||
24 | extern unsigned long l2x0_saved_regs_addr; | 26 | extern unsigned long l2x0_saved_regs_addr; |
25 | 27 | ||
26 | void save_cpu_arch_register(void); | 28 | void save_cpu_arch_register(void); |
@@ -32,4 +34,17 @@ bool tegra_set_cpu_in_lp2(int phy_cpu_id); | |||
32 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); | 34 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); |
33 | extern void (*tegra_tear_down_cpu)(void); | 35 | extern void (*tegra_tear_down_cpu)(void); |
34 | 36 | ||
37 | #ifdef CONFIG_PM_SLEEP | ||
38 | enum tegra_suspend_mode tegra_pm_validate_suspend_mode( | ||
39 | enum tegra_suspend_mode mode); | ||
40 | void tegra_init_suspend(void); | ||
41 | #else | ||
42 | enum tegra_suspend_mode tegra_pm_validate_suspend_mode( | ||
43 | enum tegra_suspend_mode mode) | ||
44 | { | ||
45 | return TEGRA_SUSPEND_NONE; | ||
46 | } | ||
47 | static inline void tegra_init_suspend(void) {} | ||
48 | #endif | ||
49 | |||
35 | #endif /* _MACH_TEGRA_PM_H_ */ | 50 | #endif /* _MACH_TEGRA_PM_H_ */ |
diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index e896826d7d0f..32360e540ce6 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c | |||
@@ -21,7 +21,14 @@ | |||
21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/of_address.h> | 22 | #include <linux/of_address.h> |
23 | 23 | ||
24 | #include "fuse.h" | ||
25 | #include "pm.h" | ||
24 | #include "pmc.h" | 26 | #include "pmc.h" |
27 | #include "sleep.h" | ||
28 | |||
29 | #define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ | ||
30 | #define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ | ||
31 | #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ | ||
25 | 32 | ||
26 | #define PMC_CTRL 0x0 | 33 | #define PMC_CTRL 0x0 |
27 | #define PMC_CTRL_INTR_LOW (1 << 17) | 34 | #define PMC_CTRL_INTR_LOW (1 << 17) |
@@ -157,14 +164,12 @@ int tegra_pmc_cpu_remove_clamping(int cpuid) | |||
157 | } | 164 | } |
158 | 165 | ||
159 | #ifdef CONFIG_PM_SLEEP | 166 | #ifdef CONFIG_PM_SLEEP |
160 | void set_power_timers(unsigned long us_on, unsigned long us_off) | 167 | static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) |
161 | { | 168 | { |
162 | unsigned long long ticks; | 169 | unsigned long long ticks; |
163 | unsigned long long pclk; | 170 | unsigned long long pclk; |
164 | unsigned long rate; | ||
165 | static unsigned long tegra_last_pclk; | 171 | static unsigned long tegra_last_pclk; |
166 | 172 | ||
167 | rate = clk_get_rate(tegra_pclk); | ||
168 | if (WARN_ON_ONCE(rate <= 0)) | 173 | if (WARN_ON_ONCE(rate <= 0)) |
169 | pclk = 100000000; | 174 | pclk = 100000000; |
170 | else | 175 | else |
@@ -182,6 +187,44 @@ void set_power_timers(unsigned long us_on, unsigned long us_off) | |||
182 | } | 187 | } |
183 | tegra_last_pclk = pclk; | 188 | tegra_last_pclk = pclk; |
184 | } | 189 | } |
190 | |||
191 | enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) | ||
192 | { | ||
193 | return pmc_pm_data.suspend_mode; | ||
194 | } | ||
195 | |||
196 | void tegra_pmc_pm_set(enum tegra_suspend_mode mode) | ||
197 | { | ||
198 | u32 reg; | ||
199 | unsigned long rate = 0; | ||
200 | |||
201 | reg = tegra_pmc_readl(PMC_CTRL); | ||
202 | reg |= TEGRA_POWER_CPU_PWRREQ_OE; | ||
203 | reg &= ~TEGRA_POWER_EFFECT_LP0; | ||
204 | |||
205 | switch (mode) { | ||
206 | case TEGRA_SUSPEND_LP2: | ||
207 | rate = clk_get_rate(tegra_pclk); | ||
208 | break; | ||
209 | default: | ||
210 | break; | ||
211 | } | ||
212 | |||
213 | set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time, | ||
214 | rate); | ||
215 | |||
216 | tegra_pmc_writel(reg, PMC_CTRL); | ||
217 | } | ||
218 | |||
219 | void tegra_pmc_suspend_init(void) | ||
220 | { | ||
221 | u32 reg; | ||
222 | |||
223 | /* Always enable CPU power request */ | ||
224 | reg = tegra_pmc_readl(PMC_CTRL); | ||
225 | reg |= TEGRA_POWER_CPU_PWRREQ_OE; | ||
226 | tegra_pmc_writel(reg, PMC_CTRL); | ||
227 | } | ||
185 | #endif | 228 | #endif |
186 | 229 | ||
187 | static const struct of_device_id matches[] __initconst = { | 230 | static const struct of_device_id matches[] __initconst = { |
@@ -228,6 +271,7 @@ static void tegra_pmc_parse_dt(void) | |||
228 | break; | 271 | break; |
229 | } | 272 | } |
230 | } | 273 | } |
274 | suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode); | ||
231 | 275 | ||
232 | if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) | 276 | if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) |
233 | suspend_mode = TEGRA_SUSPEND_NONE; | 277 | suspend_mode = TEGRA_SUSPEND_NONE; |
diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 6bc0fc095269..e1c2df272f7d 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h | |||
@@ -27,7 +27,9 @@ enum tegra_suspend_mode { | |||
27 | }; | 27 | }; |
28 | 28 | ||
29 | #ifdef CONFIG_PM_SLEEP | 29 | #ifdef CONFIG_PM_SLEEP |
30 | void set_power_timers(unsigned long us_on, unsigned long us_off); | 30 | enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); |
31 | void tegra_pmc_pm_set(enum tegra_suspend_mode mode); | ||
32 | void tegra_pmc_suspend_init(void); | ||
31 | #endif | 33 | #endif |
32 | 34 | ||
33 | bool tegra_pmc_cpu_is_powered(int cpuid); | 35 | bool tegra_pmc_cpu_is_powered(int cpuid); |