aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/common.c1
-rw-r--r--arch/arm/mach-tegra/pm.c93
-rw-r--r--arch/arm/mach-tegra/pm.h15
-rw-r--r--arch/arm/mach-tegra/pmc.c50
-rw-r--r--arch/arm/mach-tegra/pmc.h4
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
110void __init tegra_init_late(void) 110void __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
47static DEFINE_SPINLOCK(tegra_lp2_lock); 45static DEFINE_SPINLOCK(tegra_lp2_lock);
48static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
49void (*tegra_tear_down_cpu)(void); 46void (*tegra_tear_down_cpu)(void);
50 47
51/* 48/*
@@ -145,14 +142,7 @@ static int tegra_sleep_cpu(unsigned long v2p)
145 142
146void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) 143void 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
156enum 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
172static 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
179static 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
218static const struct platform_suspend_ops tegra_suspend_ops = {
219 .valid = suspend_valid_only_mem,
220 .enter = tegra_suspend_enter,
221};
222
223void __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
24extern unsigned long l2x0_saved_regs_addr; 26extern unsigned long l2x0_saved_regs_addr;
25 27
26void save_cpu_arch_register(void); 28void save_cpu_arch_register(void);
@@ -32,4 +34,17 @@ bool tegra_set_cpu_in_lp2(int phy_cpu_id);
32void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); 34void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time);
33extern void (*tegra_tear_down_cpu)(void); 35extern void (*tegra_tear_down_cpu)(void);
34 36
37#ifdef CONFIG_PM_SLEEP
38enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
39 enum tegra_suspend_mode mode);
40void tegra_init_suspend(void);
41#else
42enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
43 enum tegra_suspend_mode mode)
44{
45 return TEGRA_SUSPEND_NONE;
46}
47static 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
160void set_power_timers(unsigned long us_on, unsigned long us_off) 167static 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
191enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
192{
193 return pmc_pm_data.suspend_mode;
194}
195
196void 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
219void 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
187static const struct of_device_id matches[] __initconst = { 230static 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
30void set_power_timers(unsigned long us_on, unsigned long us_off); 30enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
31void tegra_pmc_pm_set(enum tegra_suspend_mode mode);
32void tegra_pmc_suspend_init(void);
31#endif 33#endif
32 34
33bool tegra_pmc_cpu_is_powered(int cpuid); 35bool tegra_pmc_cpu_is_powered(int cpuid);