diff options
author | Kevin Hilman <khilman@linaro.org> | 2013-08-21 13:16:55 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@linaro.org> | 2013-08-21 13:17:18 -0400 |
commit | bfa664f21b0357f2ad9cdf519f594ece36ec8f64 (patch) | |
tree | f377028eba58633d917d65c1141977b2e8ca9529 /arch/arm/mach-tegra/pm.c | |
parent | 5515d9981f5f30e82d096921f86ba016911c9ea8 (diff) | |
parent | b4f173752a56187bd55752b0474429202f2ab1d3 (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.c | 148 |
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 |
44 | static DEFINE_SPINLOCK(tegra_lp2_lock); | 45 | static DEFINE_SPINLOCK(tegra_lp2_lock); |
46 | static u32 iram_save_size; | ||
47 | static void *iram_save_addr; | ||
48 | struct tegra_lp1_iram tegra_lp1_iram; | ||
45 | void (*tegra_tear_down_cpu)(void); | 49 | void (*tegra_tear_down_cpu)(void); |
50 | void (*tegra_sleep_core_finish)(unsigned long v2p); | ||
51 | static int (*tegra_sleep_func)(unsigned long v2p); | ||
46 | 52 | ||
47 | static void tegra_tear_down_cpu_init(void) | 53 | static 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) | |||
171 | enum tegra_suspend_mode tegra_pm_validate_suspend_mode( | 179 | enum 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 | ||
191 | static 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 | */ | ||
210 | static 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 | |||
238 | static 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 | |||
261 | static 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 | |||
274 | static 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 | |||
187 | static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { | 285 | static 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 | ||
238 | void __init tegra_init_suspend(void) | 342 | void __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 |