diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-06 13:30:06 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-06 13:30:06 -0700 |
commit | b4b50fd78b1e31989940dfc647e64453d0f7176a (patch) | |
tree | 1a55f110e021c02963b63759f3f18ea7ba3aa228 /arch/arm/mach-tegra/pm.c | |
parent | dccfd1e439c11422d7aca0d834b0430d24650e85 (diff) | |
parent | f97c43bbdf8a1ea42477b1a804a48e7e368cb13c (diff) |
Merge tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform changes from Olof Johansson:
"This branch contains mostly additions and changes to platform
enablement and SoC-level drivers. Since there's sometimes a
dependency on device-tree changes, there's also a fair amount of
those in this branch.
Pieces worth mentioning are:
- Mbus driver for Marvell platforms, allowing kernel configuration
and resource allocation of on-chip peripherals.
- Enablement of the mbus infrastructure from Marvell PCI-e drivers.
- Preparation of MSI support for Marvell platforms.
- Addition of new PCI-e host controller driver for Tegra platforms
- Some churn caused by sharing of macro names between i.MX 6Q and 6DL
platforms in the device tree sources and header files.
- Various suspend/PM updates for Tegra, including LP1 support.
- Versatile Express support for MCPM, part of big little support.
- Allwinner platform support for A20 and A31 SoCs (dual and quad
Cortex-A7)
- OMAP2+ support for DRA7, a new Cortex-A15-based SoC.
The code that touches other architectures are patches moving MSI
arch-specific functions over to weak symbols and removal of
ARCH_SUPPORTS_MSI, acked by PCI maintainers"
* tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (266 commits)
tegra-cpuidle: provide stub when !CONFIG_CPU_IDLE
PCI: tegra: replace devm_request_and_ioremap by devm_ioremap_resource
ARM: tegra: Drop ARCH_SUPPORTS_MSI and sort list
ARM: dts: vf610-twr: enable i2c0 device
ARM: dts: i.MX51: Add one more I2C2 pinmux entry
ARM: dts: i.MX51: Move pins configuration under "iomuxc" label
ARM: dtsi: imx6qdl-sabresd: Add USB OTG vbus pin to pinctrl_hog
ARM: dtsi: imx6qdl-sabresd: Add USB host 1 VBUS regulator
ARM: dts: imx27-phytec-phycore-som: Enable AUDMUX
ARM: dts: i.MX27: Disable AUDMUX in the template
ARM: dts: wandboard: Add support for SDIO bcm4329
ARM: i.MX5 clocks: Remove optional clock setup (CKIH1) from i.MX51 template
ARM: dts: imx53-qsb: Make USBH1 functional
ARM i.MX6Q: dts: Enable I2C1 with EEPROM and PMIC on Phytec phyFLEX-i.MX6 Ouad module
ARM i.MX6Q: dts: Enable SPI NOR flash on Phytec phyFLEX-i.MX6 Ouad module
ARM: dts: imx6qdl-sabresd: Add touchscreen support
ARM: imx: add ocram clock for imx53
ARM: dts: imx: ocram size is different between imx6q and imx6dl
ARM: dts: imx27-phytec-phycore-som: Fix regulator settings
ARM: dts: i.MX27: Remove clock name from CPU node
...
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 @@ #include "reset.h" #include "flowctrl.h" #include "fuse.h" +#include "pm.h" #include "pmc.h" #include "sleep.h" #ifdef CONFIG_PM_SLEEP static DEFINE_SPINLOCK(tegra_lp2_lock); +static u32 iram_save_size; +static void *iram_save_addr; +struct tegra_lp1_iram tegra_lp1_iram; void (*tegra_tear_down_cpu)(void); +void (*tegra_sleep_core_finish)(unsigned long v2p); +static int (*tegra_sleep_func)(unsigned long v2p); static void tegra_tear_down_cpu_init(void) { @@ -52,7 +58,9 @@ static void tegra_tear_down_cpu_init(void) tegra_tear_down_cpu = tegra20_tear_down_cpu; break; case TEGRA30: - if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC)) + case TEGRA114: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) tegra_tear_down_cpu = tegra30_tear_down_cpu; break; } @@ -171,19 +179,109 @@ void tegra_idle_lp2_last(void) enum tegra_suspend_mode tegra_pm_validate_suspend_mode( enum tegra_suspend_mode mode) { - /* Tegra114 didn't support any suspending mode yet. */ - if (tegra_chip_id == TEGRA114) - return TEGRA_SUSPEND_NONE; - /* - * The Tegra devices only support suspending to LP2 currently. + * The Tegra devices support suspending to LP1 or lower currently. */ - if (mode > TEGRA_SUSPEND_LP2) - return TEGRA_SUSPEND_LP2; + if (mode > TEGRA_SUSPEND_LP1) + return TEGRA_SUSPEND_LP1; return mode; } +static int tegra_sleep_core(unsigned long v2p) +{ + setup_mm_for_reboot(); + tegra_sleep_core_finish(v2p); + + /* should never here */ + BUG(); + + return 0; +} + +/* + * tegra_lp1_iram_hook + * + * Hooking the address of LP1 reset vector and SDRAM self-refresh code in + * SDRAM. These codes not be copied to IRAM in this fuction. We need to + * copy these code to IRAM before LP0/LP1 suspend and restore the content + * of IRAM after resume. + */ +static bool tegra_lp1_iram_hook(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra20_lp1_iram_hook(); + break; + case TEGRA30: + case TEGRA114: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) + tegra30_lp1_iram_hook(); + break; + default: + break; + } + + if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr) + return false; + + iram_save_size = tegra_lp1_iram.end_addr - tegra_lp1_iram.start_addr; + iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL); + if (!iram_save_addr) + return false; + + return true; +} + +static bool tegra_sleep_core_init(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra20_sleep_core_init(); + break; + case TEGRA30: + case TEGRA114: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) + tegra30_sleep_core_init(); + break; + default: + break; + } + + if (!tegra_sleep_core_finish) + return false; + + return true; +} + +static void tegra_suspend_enter_lp1(void) +{ + tegra_pmc_suspend(); + + /* copy the reset vector & SDRAM shutdown code into IRAM */ + memcpy(iram_save_addr, IO_ADDRESS(TEGRA_IRAM_CODE_AREA), + iram_save_size); + memcpy(IO_ADDRESS(TEGRA_IRAM_CODE_AREA), tegra_lp1_iram.start_addr, + iram_save_size); + + *((u32 *)tegra_cpu_lp1_mask) = 1; +} + +static void tegra_suspend_exit_lp1(void) +{ + tegra_pmc_resume(); + + /* restore IRAM */ + memcpy(IO_ADDRESS(TEGRA_IRAM_CODE_AREA), iram_save_addr, + iram_save_size); + + *(u32 *)tegra_cpu_lp1_mask = 0; +} + static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { [TEGRA_SUSPEND_NONE] = "none", [TEGRA_SUSPEND_LP2] = "LP2", @@ -207,6 +305,9 @@ static int tegra_suspend_enter(suspend_state_t state) suspend_cpu_complex(); switch (mode) { + case TEGRA_SUSPEND_LP1: + tegra_suspend_enter_lp1(); + break; case TEGRA_SUSPEND_LP2: tegra_set_cpu_in_lp2(); break; @@ -214,9 +315,12 @@ static int tegra_suspend_enter(suspend_state_t state) break; } - cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); + cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, tegra_sleep_func); switch (mode) { + case TEGRA_SUSPEND_LP1: + tegra_suspend_exit_lp1(); + break; case TEGRA_SUSPEND_LP2: tegra_clear_cpu_in_lp2(); break; @@ -237,12 +341,36 @@ static const struct platform_suspend_ops tegra_suspend_ops = { void __init tegra_init_suspend(void) { - if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) + enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); + + if (mode == TEGRA_SUSPEND_NONE) return; tegra_tear_down_cpu_init(); tegra_pmc_suspend_init(); + if (mode >= TEGRA_SUSPEND_LP1) { + if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) { + pr_err("%s: unable to allocate memory for SDRAM" + "self-refresh -- LP0/LP1 unavailable\n", + __func__); + tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2); + mode = TEGRA_SUSPEND_LP2; + } + } + + /* set up sleep function for cpu_suspend */ + switch (mode) { + case TEGRA_SUSPEND_LP1: + tegra_sleep_func = tegra_sleep_core; + break; + case TEGRA_SUSPEND_LP2: + tegra_sleep_func = tegra_sleep_cpu; + break; + default: + break; + } + suspend_set_ops(&tegra_suspend_ops); } #endif |