aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/pm.c
diff options
context:
space:
mode:
authorKevin Hilman <khilman@linaro.org>2013-08-21 13:16:55 -0400
committerKevin Hilman <khilman@linaro.org>2013-08-21 13:17:18 -0400
commitbfa664f21b0357f2ad9cdf519f594ece36ec8f64 (patch)
treef377028eba58633d917d65c1141977b2e8ca9529 /arch/arm/mach-tegra/pm.c
parent5515d9981f5f30e82d096921f86ba016911c9ea8 (diff)
parentb4f173752a56187bd55752b0474429202f2ab1d3 (diff)
Merge tag 'tegra-for-3.12-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc
From: Stephen Warren: ARM: tegra: core SoC enhancements for 3.12 This branch includes a number of enhancements to core SoC support for Tegra devices. The major new features are: * Adds a new CPU-power-gated cpuidle state for Tegra114. * Adds initial system suspend support for Tegra114, initially supporting just CPU-power-gating during suspend. * Adds "LP1" suspend mode support for all of Tegra20/30/114. This mode both gates CPU power, and places the DRAM into self-refresh mode. * A new DT-driven PCIe driver to Tegra20/30. The driver is also moved from arch/arm/mach-tegra/ to drivers/pci/host/. The PCIe driver work depends on the following tag from Thomas Petazzoni: git://git.infradead.org/linux-mvebu.git mis-3.12.2 ... which is merged into the middle of this pull request. * tag 'tegra-for-3.12-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra: (33 commits) ARM: tegra: disable LP2 cpuidle state if PCIe is enabled MAINTAINERS: Add myself as Tegra PCIe maintainer PCI: tegra: set up PADS_REFCLK_CFG1 PCI: tegra: Add Tegra 30 PCIe support PCI: tegra: Move PCIe driver to drivers/pci/host PCI: msi: add default MSI operations for !HAVE_GENERIC_HARDIRQS platforms ARM: tegra: add LP1 suspend support for Tegra114 ARM: tegra: add LP1 suspend support for Tegra20 ARM: tegra: add LP1 suspend support for Tegra30 ARM: tegra: add common LP1 suspend support clk: tegra114: add LP1 suspend/resume support ARM: tegra: config the polarity of the request of sys clock ARM: tegra: add common resume handling code for LP1 resuming ARM: pci: add ->add_bus() and ->remove_bus() hooks to hw_pci of: pci: add registry of MSI chips PCI: Introduce new MSI chip infrastructure PCI: remove ARCH_SUPPORTS_MSI kconfig option PCI: use weak functions for MSI arch-specific functions ARM: tegra: unify Tegra's Kconfig a bit more ARM: tegra: remove the limitation that Tegra114 can't support suspend ... Signed-off-by: Kevin Hilman <khilman@linaro.org>
Diffstat (limited to 'arch/arm/mach-tegra/pm.c')
-rw-r--r--arch/arm/mach-tegra/pm.c148
1 files changed, 138 insertions, 10 deletions
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 261fec140c06..ed294a04e1d3 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -37,12 +37,18 @@
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 "pm.h"
40#include "pmc.h" 41#include "pmc.h"
41#include "sleep.h" 42#include "sleep.h"
42 43
43#ifdef CONFIG_PM_SLEEP 44#ifdef CONFIG_PM_SLEEP
44static DEFINE_SPINLOCK(tegra_lp2_lock); 45static DEFINE_SPINLOCK(tegra_lp2_lock);
46static u32 iram_save_size;
47static void *iram_save_addr;
48struct tegra_lp1_iram tegra_lp1_iram;
45void (*tegra_tear_down_cpu)(void); 49void (*tegra_tear_down_cpu)(void);
50void (*tegra_sleep_core_finish)(unsigned long v2p);
51static int (*tegra_sleep_func)(unsigned long v2p);
46 52
47static void tegra_tear_down_cpu_init(void) 53static void tegra_tear_down_cpu_init(void)
48{ 54{
@@ -52,7 +58,9 @@ static void tegra_tear_down_cpu_init(void)
52 tegra_tear_down_cpu = tegra20_tear_down_cpu; 58 tegra_tear_down_cpu = tegra20_tear_down_cpu;
53 break; 59 break;
54 case TEGRA30: 60 case TEGRA30:
55 if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC)) 61 case TEGRA114:
62 if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) ||
63 IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC))
56 tegra_tear_down_cpu = tegra30_tear_down_cpu; 64 tegra_tear_down_cpu = tegra30_tear_down_cpu;
57 break; 65 break;
58 } 66 }
@@ -171,19 +179,109 @@ void tegra_idle_lp2_last(void)
171enum tegra_suspend_mode tegra_pm_validate_suspend_mode( 179enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
172 enum tegra_suspend_mode mode) 180 enum tegra_suspend_mode mode)
173{ 181{
174 /* Tegra114 didn't support any suspending mode yet. */
175 if (tegra_chip_id == TEGRA114)
176 return TEGRA_SUSPEND_NONE;
177
178 /* 182 /*
179 * The Tegra devices only support suspending to LP2 currently. 183 * The Tegra devices support suspending to LP1 or lower currently.
180 */ 184 */
181 if (mode > TEGRA_SUSPEND_LP2) 185 if (mode > TEGRA_SUSPEND_LP1)
182 return TEGRA_SUSPEND_LP2; 186 return TEGRA_SUSPEND_LP1;
183 187
184 return mode; 188 return mode;
185} 189}
186 190
191static int tegra_sleep_core(unsigned long v2p)
192{
193 setup_mm_for_reboot();
194 tegra_sleep_core_finish(v2p);
195
196 /* should never here */
197 BUG();
198
199 return 0;
200}
201
202/*
203 * tegra_lp1_iram_hook
204 *
205 * Hooking the address of LP1 reset vector and SDRAM self-refresh code in
206 * SDRAM. These codes not be copied to IRAM in this fuction. We need to
207 * copy these code to IRAM before LP0/LP1 suspend and restore the content
208 * of IRAM after resume.
209 */
210static bool tegra_lp1_iram_hook(void)
211{
212 switch (tegra_chip_id) {
213 case TEGRA20:
214 if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
215 tegra20_lp1_iram_hook();
216 break;
217 case TEGRA30:
218 case TEGRA114:
219 if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) ||
220 IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC))
221 tegra30_lp1_iram_hook();
222 break;
223 default:
224 break;
225 }
226
227 if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr)
228 return false;
229
230 iram_save_size = tegra_lp1_iram.end_addr - tegra_lp1_iram.start_addr;
231 iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL);
232 if (!iram_save_addr)
233 return false;
234
235 return true;
236}
237
238static bool tegra_sleep_core_init(void)
239{
240 switch (tegra_chip_id) {
241 case TEGRA20:
242 if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
243 tegra20_sleep_core_init();
244 break;
245 case TEGRA30:
246 case TEGRA114:
247 if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) ||
248 IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC))
249 tegra30_sleep_core_init();
250 break;
251 default:
252 break;
253 }
254
255 if (!tegra_sleep_core_finish)
256 return false;
257
258 return true;
259}
260
261static void tegra_suspend_enter_lp1(void)
262{
263 tegra_pmc_suspend();
264
265 /* copy the reset vector & SDRAM shutdown code into IRAM */
266 memcpy(iram_save_addr, IO_ADDRESS(TEGRA_IRAM_CODE_AREA),
267 iram_save_size);
268 memcpy(IO_ADDRESS(TEGRA_IRAM_CODE_AREA), tegra_lp1_iram.start_addr,
269 iram_save_size);
270
271 *((u32 *)tegra_cpu_lp1_mask) = 1;
272}
273
274static void tegra_suspend_exit_lp1(void)
275{
276 tegra_pmc_resume();
277
278 /* restore IRAM */
279 memcpy(IO_ADDRESS(TEGRA_IRAM_CODE_AREA), iram_save_addr,
280 iram_save_size);
281
282 *(u32 *)tegra_cpu_lp1_mask = 0;
283}
284
187static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { 285static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = {
188 [TEGRA_SUSPEND_NONE] = "none", 286 [TEGRA_SUSPEND_NONE] = "none",
189 [TEGRA_SUSPEND_LP2] = "LP2", 287 [TEGRA_SUSPEND_LP2] = "LP2",
@@ -207,6 +305,9 @@ static int tegra_suspend_enter(suspend_state_t state)
207 305
208 suspend_cpu_complex(); 306 suspend_cpu_complex();
209 switch (mode) { 307 switch (mode) {
308 case TEGRA_SUSPEND_LP1:
309 tegra_suspend_enter_lp1();
310 break;
210 case TEGRA_SUSPEND_LP2: 311 case TEGRA_SUSPEND_LP2:
211 tegra_set_cpu_in_lp2(); 312 tegra_set_cpu_in_lp2();
212 break; 313 break;
@@ -214,9 +315,12 @@ static int tegra_suspend_enter(suspend_state_t state)
214 break; 315 break;
215 } 316 }
216 317
217 cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); 318 cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, tegra_sleep_func);
218 319
219 switch (mode) { 320 switch (mode) {
321 case TEGRA_SUSPEND_LP1:
322 tegra_suspend_exit_lp1();
323 break;
220 case TEGRA_SUSPEND_LP2: 324 case TEGRA_SUSPEND_LP2:
221 tegra_clear_cpu_in_lp2(); 325 tegra_clear_cpu_in_lp2();
222 break; 326 break;
@@ -237,12 +341,36 @@ static const struct platform_suspend_ops tegra_suspend_ops = {
237 341
238void __init tegra_init_suspend(void) 342void __init tegra_init_suspend(void)
239{ 343{
240 if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) 344 enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
345
346 if (mode == TEGRA_SUSPEND_NONE)
241 return; 347 return;
242 348
243 tegra_tear_down_cpu_init(); 349 tegra_tear_down_cpu_init();
244 tegra_pmc_suspend_init(); 350 tegra_pmc_suspend_init();
245 351
352 if (mode >= TEGRA_SUSPEND_LP1) {
353 if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) {
354 pr_err("%s: unable to allocate memory for SDRAM"
355 "self-refresh -- LP0/LP1 unavailable\n",
356 __func__);
357 tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2);
358 mode = TEGRA_SUSPEND_LP2;
359 }
360 }
361
362 /* set up sleep function for cpu_suspend */
363 switch (mode) {
364 case TEGRA_SUSPEND_LP1:
365 tegra_sleep_func = tegra_sleep_core;
366 break;
367 case TEGRA_SUSPEND_LP2:
368 tegra_sleep_func = tegra_sleep_cpu;
369 break;
370 default:
371 break;
372 }
373
246 suspend_set_ops(&tegra_suspend_ops); 374 suspend_set_ops(&tegra_suspend_ops);
247} 375}
248#endif 376#endif