diff options
Diffstat (limited to 'arch/arm/mach-tegra/pm.c')
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 150 |
1 files changed, 81 insertions, 69 deletions
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 523604de666f..d0b7400e4606 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c | |||
@@ -22,7 +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/clk.h> | 25 | #include <linux/suspend.h> |
26 | #include <linux/err.h> | 26 | #include <linux/err.h> |
27 | #include <linux/clk/tegra.h> | 27 | #include <linux/clk/tegra.h> |
28 | 28 | ||
@@ -37,67 +37,13 @@ | |||
37 | #include "reset.h" | 37 | #include "reset.h" |
38 | #include "flowctrl.h" | 38 | #include "flowctrl.h" |
39 | #include "fuse.h" | 39 | #include "fuse.h" |
40 | #include "pmc.h" | ||
40 | #include "sleep.h" | 41 | #include "sleep.h" |
41 | 42 | ||
42 | #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ | ||
43 | |||
44 | #define PMC_CTRL 0x0 | ||
45 | #define PMC_CPUPWRGOOD_TIMER 0xc8 | ||
46 | #define PMC_CPUPWROFF_TIMER 0xcc | ||
47 | |||
48 | #ifdef CONFIG_PM_SLEEP | 43 | #ifdef CONFIG_PM_SLEEP |
49 | static unsigned int g_diag_reg; | ||
50 | static DEFINE_SPINLOCK(tegra_lp2_lock); | 44 | static DEFINE_SPINLOCK(tegra_lp2_lock); |
51 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); | ||
52 | static struct clk *tegra_pclk; | ||
53 | void (*tegra_tear_down_cpu)(void); | 45 | void (*tegra_tear_down_cpu)(void); |
54 | 46 | ||
55 | void save_cpu_arch_register(void) | ||
56 | { | ||
57 | /* read diagnostic register */ | ||
58 | asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); | ||
59 | return; | ||
60 | } | ||
61 | |||
62 | void restore_cpu_arch_register(void) | ||
63 | { | ||
64 | /* write diagnostic register */ | ||
65 | asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | static void set_power_timers(unsigned long us_on, unsigned long us_off) | ||
70 | { | ||
71 | unsigned long long ticks; | ||
72 | unsigned long long pclk; | ||
73 | unsigned long rate; | ||
74 | static unsigned long tegra_last_pclk; | ||
75 | |||
76 | if (tegra_pclk == NULL) { | ||
77 | tegra_pclk = clk_get_sys(NULL, "pclk"); | ||
78 | WARN_ON(IS_ERR(tegra_pclk)); | ||
79 | } | ||
80 | |||
81 | rate = clk_get_rate(tegra_pclk); | ||
82 | |||
83 | if (WARN_ON_ONCE(rate <= 0)) | ||
84 | pclk = 100000000; | ||
85 | else | ||
86 | pclk = rate; | ||
87 | |||
88 | if ((rate != tegra_last_pclk)) { | ||
89 | ticks = (us_on * pclk) + 999999ull; | ||
90 | do_div(ticks, 1000000); | ||
91 | writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER); | ||
92 | |||
93 | ticks = (us_off * pclk) + 999999ull; | ||
94 | do_div(ticks, 1000000); | ||
95 | writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER); | ||
96 | wmb(); | ||
97 | } | ||
98 | tegra_last_pclk = pclk; | ||
99 | } | ||
100 | |||
101 | /* | 47 | /* |
102 | * restore_cpu_complex | 48 | * restore_cpu_complex |
103 | * | 49 | * |
@@ -119,8 +65,6 @@ static void restore_cpu_complex(void) | |||
119 | tegra_cpu_clock_resume(); | 65 | tegra_cpu_clock_resume(); |
120 | 66 | ||
121 | flowctrl_cpu_suspend_exit(cpu); | 67 | flowctrl_cpu_suspend_exit(cpu); |
122 | |||
123 | restore_cpu_arch_register(); | ||
124 | } | 68 | } |
125 | 69 | ||
126 | /* | 70 | /* |
@@ -145,8 +89,6 @@ static void suspend_cpu_complex(void) | |||
145 | tegra_cpu_clock_suspend(); | 89 | tegra_cpu_clock_suspend(); |
146 | 90 | ||
147 | flowctrl_cpu_suspend_enter(cpu); | 91 | flowctrl_cpu_suspend_enter(cpu); |
148 | |||
149 | save_cpu_arch_register(); | ||
150 | } | 92 | } |
151 | 93 | ||
152 | void tegra_clear_cpu_in_lp2(int phy_cpu_id) | 94 | void tegra_clear_cpu_in_lp2(int phy_cpu_id) |
@@ -197,16 +139,9 @@ static int tegra_sleep_cpu(unsigned long v2p) | |||
197 | return 0; | 139 | return 0; |
198 | } | 140 | } |
199 | 141 | ||
200 | void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) | 142 | void tegra_idle_lp2_last(void) |
201 | { | 143 | { |
202 | u32 mode; | 144 | tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); |
203 | |||
204 | /* Only the last cpu down does the final suspend steps */ | ||
205 | mode = readl(pmc + PMC_CTRL); | ||
206 | mode |= TEGRA_POWER_CPU_PWRREQ_OE; | ||
207 | writel(mode, pmc + PMC_CTRL); | ||
208 | |||
209 | set_power_timers(cpu_on_time, cpu_off_time); | ||
210 | 145 | ||
211 | cpu_cluster_pm_enter(); | 146 | cpu_cluster_pm_enter(); |
212 | suspend_cpu_complex(); | 147 | suspend_cpu_complex(); |
@@ -216,4 +151,81 @@ void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) | |||
216 | restore_cpu_complex(); | 151 | restore_cpu_complex(); |
217 | cpu_cluster_pm_exit(); | 152 | cpu_cluster_pm_exit(); |
218 | } | 153 | } |
154 | |||
155 | enum tegra_suspend_mode tegra_pm_validate_suspend_mode( | ||
156 | enum tegra_suspend_mode mode) | ||
157 | { | ||
158 | /* Tegra114 didn't support any suspending mode yet. */ | ||
159 | if (tegra_chip_id == TEGRA114) | ||
160 | return TEGRA_SUSPEND_NONE; | ||
161 | |||
162 | /* | ||
163 | * The Tegra devices only support suspending to LP2 currently. | ||
164 | */ | ||
165 | if (mode > TEGRA_SUSPEND_LP2) | ||
166 | return TEGRA_SUSPEND_LP2; | ||
167 | |||
168 | return mode; | ||
169 | } | ||
170 | |||
171 | static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { | ||
172 | [TEGRA_SUSPEND_NONE] = "none", | ||
173 | [TEGRA_SUSPEND_LP2] = "LP2", | ||
174 | [TEGRA_SUSPEND_LP1] = "LP1", | ||
175 | [TEGRA_SUSPEND_LP0] = "LP0", | ||
176 | }; | ||
177 | |||
178 | static int __cpuinit tegra_suspend_enter(suspend_state_t state) | ||
179 | { | ||
180 | enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); | ||
181 | |||
182 | if (WARN_ON(mode < TEGRA_SUSPEND_NONE || | ||
183 | mode >= TEGRA_MAX_SUSPEND_MODE)) | ||
184 | return -EINVAL; | ||
185 | |||
186 | pr_info("Entering suspend state %s\n", lp_state[mode]); | ||
187 | |||
188 | tegra_pmc_pm_set(mode); | ||
189 | |||
190 | local_fiq_disable(); | ||
191 | |||
192 | suspend_cpu_complex(); | ||
193 | switch (mode) { | ||
194 | case TEGRA_SUSPEND_LP2: | ||
195 | tegra_set_cpu_in_lp2(0); | ||
196 | break; | ||
197 | default: | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); | ||
202 | |||
203 | switch (mode) { | ||
204 | case TEGRA_SUSPEND_LP2: | ||
205 | tegra_clear_cpu_in_lp2(0); | ||
206 | break; | ||
207 | default: | ||
208 | break; | ||
209 | } | ||
210 | restore_cpu_complex(); | ||
211 | |||
212 | local_fiq_enable(); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static const struct platform_suspend_ops tegra_suspend_ops = { | ||
218 | .valid = suspend_valid_only_mem, | ||
219 | .enter = tegra_suspend_enter, | ||
220 | }; | ||
221 | |||
222 | void __init tegra_init_suspend(void) | ||
223 | { | ||
224 | if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) | ||
225 | return; | ||
226 | |||
227 | tegra_pmc_suspend_init(); | ||
228 | |||
229 | suspend_set_ops(&tegra_suspend_ops); | ||
230 | } | ||
219 | #endif | 231 | #endif |