diff options
author | Arnd Bergmann <arnd@arndb.de> | 2018-03-07 16:21:59 +0100 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2018-03-07 16:21:59 +0100 |
commit | 18b4788badeae8ed3a9bd3c240d83dffe5db8f37 (patch) | |
tree | 664dacce5739cfe87b791994c37ada5bb99b2fa2 /arch/arm/mach-omap2 | |
parent | 6f566c4f304de8a90d520f7ca1925b4ea7dc3581 (diff) | |
parent | afe761f8d3e99155b76833421e76553a3ac69577 (diff) |
Merge tag 'omap-for-v4.17/am-pm-signed' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/soc
Pull "Add am335x and am437x PM code for v4.17" from Tony Lindgren:
This series of changes from Dave Gerlach adds the PM related
code to allow low-power suspend states. The code consists of
the SoC specific assembly code and a related PM driver.
* tag 'omap-for-v4.17/am-pm-signed' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
soc: ti: Add pm33xx driver for basic suspend support
ARM: OMAP2+: pm33xx-core: Add platform code needed for PM
ARM: OMAP2+: Introduce low-level suspend code for AM43XX
ARM: OMAP2+: Introduce low-level suspend code for AM33XX
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r-- | arch/arm/mach-omap2/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 16 | ||||
-rw-r--r-- | arch/arm/mach-omap2/common.h | 7 | ||||
-rw-r--r-- | arch/arm/mach-omap2/io.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm-asm-offsets.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm33xx-core.c | 189 | ||||
-rw-r--r-- | arch/arm/mach-omap2/sleep33xx.S | 214 | ||||
-rw-r--r-- | arch/arm/mach-omap2/sleep43xx.S | 387 |
9 files changed, 850 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 00b1f17f8d44..9f27b486a536 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -72,6 +72,7 @@ config SOC_AM43XX select ARM_ERRATA_754322 select ARM_ERRATA_775420 select OMAP_INTERCONNECT + select ARM_CPU_SUSPEND if PM config SOC_DRA7XX bool "TI DRA7XX" diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index c15bbcad5f67..4603c30fef73 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -88,6 +88,8 @@ omap-4-5-pm-common += pm44xx.o obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-pm-common) +obj-$(CONFIG_SOC_AM33XX) += pm33xx-core.o sleep33xx.o +obj-$(CONFIG_SOC_AM43XX) += pm33xx-core.o sleep43xx.o obj-$(CONFIG_PM_DEBUG) += pm-debug.o obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o @@ -95,6 +97,8 @@ obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o AFLAGS_sleep24xx.o :=-Wa,-march=armv6 AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec) +AFLAGS_sleep33xx.o :=-Wa,-march=armv7-a$(plus_sec) +AFLAGS_sleep43xx.o :=-Wa,-march=armv7-a$(plus_sec) endif @@ -232,3 +236,15 @@ obj-y += $(omap-hsmmc-m) $(omap-hsmmc-y) obj-y += omap_phy_internal.o obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o + +arch/arm/mach-omap2/pm-asm-offsets.s: arch/arm/mach-omap2/pm-asm-offsets.c + $(call if_changed_dep,cc_s_c) + +include/generated/ti-pm-asm-offsets.h: arch/arm/mach-omap2/pm-asm-offsets.s FORCE + $(call filechk,offsets,__TI_PM_ASM_OFFSETS_H__) + +# For rule to generate ti-emif-asm-offsets.h dependency +include drivers/memory/Makefile.asm-offsets + +arch/arm/mach-omap2/sleep33xx.o: include/generated/ti-pm-asm-offsets.h include/generated/ti-emif-asm-offsets.h +arch/arm/mach-omap2/sleep43xx.o: include/generated/ti-pm-asm-offsets.h include/generated/ti-emif-asm-offsets.h diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index bc202835371b..fbe0b78bf489 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -77,6 +77,13 @@ static inline int omap4_pm_init_early(void) } #endif +#if defined(CONFIG_PM) && (defined(CONFIG_SOC_AM33XX) || \ + defined(CONFIG_SOC_AM43XX)) +void amx3_common_pm_init(void); +#else +static inline void amx3_common_pm_init(void) { } +#endif + extern void omap2_init_common_infrastructure(void); extern void omap_init_time(void); diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index cb5d7314cf99..cf546dfe3b32 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -622,6 +622,7 @@ void __init am33xx_init_early(void) void __init am33xx_init_late(void) { omap_common_late_init(); + amx3_common_pm_init(); } #endif @@ -646,6 +647,7 @@ void __init am43xx_init_late(void) { omap_common_late_init(); omap2_clk_enable_autoidle_all(); + amx3_common_pm_init(); } #endif diff --git a/arch/arm/mach-omap2/pm-asm-offsets.c b/arch/arm/mach-omap2/pm-asm-offsets.c new file mode 100644 index 000000000000..6d4392da7c11 --- /dev/null +++ b/arch/arm/mach-omap2/pm-asm-offsets.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI AM33XX and AM43XX PM Assembly Offsets + * + * Copyright (C) 2017-2018 Texas Instruments Inc. + */ + +#include <linux/kbuild.h> +#include <linux/platform_data/pm33xx.h> + +int main(void) +{ + DEFINE(AMX3_PM_WFI_FLAGS_OFFSET, + offsetof(struct am33xx_pm_sram_data, wfi_flags)); + DEFINE(AMX3_PM_L2_AUX_CTRL_VAL_OFFSET, + offsetof(struct am33xx_pm_sram_data, l2_aux_ctrl_val)); + DEFINE(AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET, + offsetof(struct am33xx_pm_sram_data, l2_prefetch_ctrl_val)); + DEFINE(AMX3_PM_SRAM_DATA_SIZE, sizeof(struct am33xx_pm_sram_data)); + + BLANK(); + + DEFINE(AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET, + offsetof(struct am33xx_pm_ro_sram_data, amx3_pm_sram_data_virt)); + DEFINE(AMX3_PM_RO_SRAM_DATA_PHYS_OFFSET, + offsetof(struct am33xx_pm_ro_sram_data, amx3_pm_sram_data_phys)); + DEFINE(AMX3_PM_RO_SRAM_DATA_SIZE, + sizeof(struct am33xx_pm_ro_sram_data)); + + return 0; +} diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 8e30772cfe32..c73776b82348 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -81,6 +81,9 @@ extern unsigned int omap3_do_wfi_sz; /* ... and its pointer from SRAM after copy */ extern void (*omap3_do_wfi_sram)(void); +extern struct am33xx_pm_sram_addr am33xx_pm_sram; +extern struct am33xx_pm_sram_addr am43xx_pm_sram; + extern void omap3_save_scratchpad_contents(void); #define PM_RTA_ERRATUM_i608 (1 << 0) diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c new file mode 100644 index 000000000000..93c0b5ba9f09 --- /dev/null +++ b/arch/arm/mach-omap2/pm33xx-core.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AM33XX Arch Power Management Routines + * + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach + */ + +#include <asm/smp_scu.h> +#include <asm/suspend.h> +#include <linux/errno.h> +#include <linux/platform_data/pm33xx.h> + +#include "cm33xx.h" +#include "common.h" +#include "control.h" +#include "clockdomain.h" +#include "iomap.h" +#include "omap_hwmod.h" +#include "pm.h" +#include "powerdomain.h" +#include "prm33xx.h" +#include "soc.h" +#include "sram.h" + +static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm; +static struct clockdomain *gfx_l4ls_clkdm; +static void __iomem *scu_base; + +static int __init am43xx_map_scu(void) +{ + scu_base = ioremap(scu_a9_get_base(), SZ_256); + + if (!scu_base) + return -ENOMEM; + + return 0; +} + +static int amx3_common_init(void) +{ + gfx_pwrdm = pwrdm_lookup("gfx_pwrdm"); + per_pwrdm = pwrdm_lookup("per_pwrdm"); + mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); + + if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm)) + return -ENODEV; + + (void)clkdm_for_each(omap_pm_clkdms_setup, NULL); + + /* CEFUSE domain can be turned off post bootup */ + cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm"); + if (cefuse_pwrdm) + omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF); + else + pr_err("PM: Failed to get cefuse_pwrdm\n"); + + return 0; +} + +static int am33xx_suspend_init(void) +{ + int ret; + + gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm"); + + if (!gfx_l4ls_clkdm) { + pr_err("PM: Cannot lookup gfx_l4ls_clkdm clockdomains\n"); + return -ENODEV; + } + + ret = amx3_common_init(); + + return ret; +} + +static int am43xx_suspend_init(void) +{ + int ret = 0; + + ret = am43xx_map_scu(); + if (ret) { + pr_err("PM: Could not ioremap SCU\n"); + return ret; + } + + ret = amx3_common_init(); + + return ret; +} + +static void amx3_pre_suspend_common(void) +{ + omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF); +} + +static void amx3_post_suspend_common(void) +{ + int status; + /* + * Because gfx_pwrdm is the only one under MPU control, + * comment on transition status + */ + status = pwrdm_read_pwrst(gfx_pwrdm); + if (status != PWRDM_POWER_OFF) + pr_err("PM: GFX domain did not transition: %x\n", status); +} + +static int am33xx_suspend(unsigned int state, int (*fn)(unsigned long)) +{ + int ret = 0; + + amx3_pre_suspend_common(); + ret = cpu_suspend(0, fn); + amx3_post_suspend_common(); + + /* + * BUG: GFX_L4LS clock domain needs to be woken up to + * ensure thet L4LS clock domain does not get stuck in + * transition. If that happens L3 module does not get + * disabled, thereby leading to PER power domain + * transition failing + */ + + clkdm_wakeup(gfx_l4ls_clkdm); + clkdm_sleep(gfx_l4ls_clkdm); + + return ret; +} + +static int am43xx_suspend(unsigned int state, int (*fn)(unsigned long)) +{ + int ret = 0; + + amx3_pre_suspend_common(); + scu_power_mode(scu_base, SCU_PM_POWEROFF); + ret = cpu_suspend(0, fn); + scu_power_mode(scu_base, SCU_PM_NORMAL); + amx3_post_suspend_common(); + + return ret; +} + +static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void) +{ + if (soc_is_am33xx()) + return &am33xx_pm_sram; + else if (soc_is_am437x()) + return &am43xx_pm_sram; + else + return NULL; +} + +static struct am33xx_pm_platform_data am33xx_ops = { + .init = am33xx_suspend_init, + .soc_suspend = am33xx_suspend, + .get_sram_addrs = amx3_get_sram_addrs, +}; + +static struct am33xx_pm_platform_data am43xx_ops = { + .init = am43xx_suspend_init, + .soc_suspend = am43xx_suspend, + .get_sram_addrs = amx3_get_sram_addrs, +}; + +static struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void) +{ + if (soc_is_am33xx()) + return &am33xx_ops; + else if (soc_is_am437x()) + return &am43xx_ops; + else + return NULL; +} + +void __init amx3_common_pm_init(void) +{ + struct am33xx_pm_platform_data *pdata; + struct platform_device_info devinfo; + + pdata = am33xx_pm_get_pdata(); + + memset(&devinfo, 0, sizeof(devinfo)); + devinfo.name = "pm33xx"; + devinfo.data = pdata; + devinfo.size_data = sizeof(*pdata); + devinfo.id = -1; + platform_device_register_full(&devinfo); +} diff --git a/arch/arm/mach-omap2/sleep33xx.S b/arch/arm/mach-omap2/sleep33xx.S new file mode 100644 index 000000000000..218d79930b04 --- /dev/null +++ b/arch/arm/mach-omap2/sleep33xx.S @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low level suspend code for AM33XX SoCs + * + * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach, Vaibhav Bedia + */ + +#include <generated/ti-emif-asm-offsets.h> +#include <generated/ti-pm-asm-offsets.h> +#include <linux/linkage.h> +#include <linux/ti-emif-sram.h> +#include <asm/assembler.h> +#include <asm/memory.h> + +#include "iomap.h" +#include "cm33xx.h" + +#define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000 +#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003 +#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002 + + .arm + .align 3 + +ENTRY(am33xx_do_wfi) + stmfd sp!, {r4 - r11, lr} @ save registers on stack + + /* + * Flush all data from the L1 and L2 data cache before disabling + * SCTLR.C bit. + */ + ldr r1, kernel_flush + blx r1 + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + /* + * Invalidate L1 and L2 data cache. + */ + ldr r1, kernel_flush + blx r1 + + adr r9, am33xx_emif_sram_table + + ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET] + blx r3 + + ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET] + blx r3 + + /* Disable EMIF */ + ldr r1, virt_emif_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + + ldr r1, virt_emif_clkctrl +wait_emif_disable: + ldr r2, [r1] + mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED + cmp r2, r3 + bne wait_emif_disable + + /* + * For the MPU WFI to be registered as an interrupt + * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set + * to DISABLED + */ + ldr r1, virt_mpu_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + + /* + * Execute an ISB instruction to ensure that all of the + * CP15 register changes have been committed. + */ + isb + + /* + * Execute a barrier instruction to ensure that all cache, + * TLB and branch predictor maintenance operations issued + * have completed. + */ + dsb + dmb + + /* + * Execute a WFI instruction and wait until the + * STANDBYWFI output is asserted to indicate that the + * CPU is in idle and low power state. CPU can specualatively + * prefetch the instructions so add NOPs after WFI. Thirteen + * NOPs as per Cortex-A8 pipeline. + */ + wfi + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + /* We come here in case of an abort due to a late interrupt */ + + /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */ + ldr r1, virt_mpu_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] + + /* Re-enable EMIF */ + ldr r1, virt_emif_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] +wait_emif_enable: + ldr r3, [r1] + cmp r2, r3 + bne wait_emif_enable + + + ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET] + blx r1 + + /* + * Set SCTLR.C bit to allow data cache allocation + */ + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 2) @ Enable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + /* Let the suspend code know about the abort */ + mov r0, #1 + ldmfd sp!, {r4 - r11, pc} @ restore regs and return +ENDPROC(am33xx_do_wfi) + + .align +ENTRY(am33xx_resume_offset) + .word . - am33xx_do_wfi + +ENTRY(am33xx_resume_from_deep_sleep) + /* Re-enable EMIF */ + ldr r0, phys_emif_clkctrl + mov r1, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r1, [r0] +wait_emif_enable1: + ldr r2, [r0] + cmp r1, r2 + bne wait_emif_enable1 + + adr r9, am33xx_emif_sram_table + + ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET] + blx r1 + + ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET] + blx r1 + +resume_to_ddr: + /* We are back. Branch to the common CPU resume routine */ + mov r0, #0 + ldr pc, resume_addr +ENDPROC(am33xx_resume_from_deep_sleep) + +/* + * Local variables + */ + .align +resume_addr: + .word cpu_resume - PAGE_OFFSET + 0x80000000 +kernel_flush: + .word v7_flush_dcache_all +virt_mpu_clkctrl: + .word AM33XX_CM_MPU_MPU_CLKCTRL +virt_emif_clkctrl: + .word AM33XX_CM_PER_EMIF_CLKCTRL +phys_emif_clkctrl: + .word (AM33XX_CM_BASE + AM33XX_CM_PER_MOD + \ + AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET) + +.align 3 +/* DDR related defines */ +am33xx_emif_sram_table: + .space EMIF_PM_FUNCTIONS_SIZE + +ENTRY(am33xx_pm_sram) + .word am33xx_do_wfi + .word am33xx_do_wfi_sz + .word am33xx_resume_offset + .word am33xx_emif_sram_table + .word am33xx_pm_ro_sram_data + +.align 3 +ENTRY(am33xx_pm_ro_sram_data) + .space AMX3_PM_RO_SRAM_DATA_SIZE + +ENTRY(am33xx_do_wfi_sz) + .word . - am33xx_do_wfi diff --git a/arch/arm/mach-omap2/sleep43xx.S b/arch/arm/mach-omap2/sleep43xx.S new file mode 100644 index 000000000000..59770a69396b --- /dev/null +++ b/arch/arm/mach-omap2/sleep43xx.S @@ -0,0 +1,387 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low level suspend code for AM43XX SoCs + * + * Copyright (C) 2013-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach, Vaibhav Bedia + */ + +#include <generated/ti-emif-asm-offsets.h> +#include <generated/ti-pm-asm-offsets.h> +#include <linux/linkage.h> +#include <linux/ti-emif-sram.h> + +#include <asm/assembler.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/memory.h> + +#include "cm33xx.h" +#include "common.h" +#include "iomap.h" +#include "omap-secure.h" +#include "omap44xx.h" +#include "prm33xx.h" +#include "prcm43xx.h" + +#define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000 +#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003 +#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002 + +#define AM43XX_EMIF_POWEROFF_ENABLE 0x1 +#define AM43XX_EMIF_POWEROFF_DISABLE 0x0 + +#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP 0x1 +#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO 0x3 + +#define AM43XX_CM_BASE 0x44DF0000 + +#define AM43XX_CM_REGADDR(inst, reg) \ + AM33XX_L4_WK_IO_ADDRESS(AM43XX_CM_BASE + (inst) + (reg)) + +#define AM43XX_CM_MPU_CLKSTCTRL AM43XX_CM_REGADDR(AM43XX_CM_MPU_INST, \ + AM43XX_CM_MPU_MPU_CDOFFS) +#define AM43XX_CM_MPU_MPU_CLKCTRL AM43XX_CM_REGADDR(AM43XX_CM_MPU_INST, \ + AM43XX_CM_MPU_MPU_CLKCTRL_OFFSET) +#define AM43XX_CM_PER_EMIF_CLKCTRL AM43XX_CM_REGADDR(AM43XX_CM_PER_INST, \ + AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET) +#define AM43XX_PRM_EMIF_CTRL_OFFSET 0x0030 + + .arm + .align 3 + +ENTRY(am43xx_do_wfi) + stmfd sp!, {r4 - r11, lr} @ save registers on stack + + /* Retrieve l2 cache virt address BEFORE we shut off EMIF */ + ldr r1, get_l2cache_base + blx r1 + mov r8, r0 + + /* + * Flush all data from the L1 and L2 data cache before disabling + * SCTLR.C bit. + */ + ldr r1, kernel_flush + blx r1 + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + dsb + + /* + * Invalidate L1 and L2 data cache. + */ + ldr r1, kernel_flush + blx r1 + +#ifdef CONFIG_CACHE_L2X0 + /* + * Clean and invalidate the L2 cache. + */ +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x03 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + dsb + smc #0 + dsb +#endif + mov r0, r8 + adr r4, am43xx_pm_ro_sram_data + ldr r3, [r4, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET] + + mov r2, r0 + ldr r0, [r2, #L2X0_AUX_CTRL] + str r0, [r3, #AMX3_PM_L2_AUX_CTRL_VAL_OFFSET] + ldr r0, [r2, #L310_PREFETCH_CTRL] + str r0, [r3, #AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET] + + ldr r0, l2_val + str r0, [r2, #L2X0_CLEAN_INV_WAY] +wait: + ldr r0, [r2, #L2X0_CLEAN_INV_WAY] + ldr r1, l2_val + ands r0, r0, r1 + bne wait +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x00 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + dsb + smc #0 + dsb +#endif +l2x_sync: + mov r0, r8 + mov r2, r0 + mov r0, #0x0 + str r0, [r2, #L2X0_CACHE_SYNC] +sync: + ldr r0, [r2, #L2X0_CACHE_SYNC] + ands r0, r0, #0x1 + bne sync +#endif + + adr r9, am43xx_emif_sram_table + + ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET] + blx r3 + + ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET] + blx r3 + + /* Disable EMIF */ + ldr r1, am43xx_virt_emif_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + +wait_emif_disable: + ldr r2, [r1] + mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED + cmp r2, r3 + bne wait_emif_disable + + /* + * For the MPU WFI to be registered as an interrupt + * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set + * to DISABLED + */ + ldr r1, am43xx_virt_mpu_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + + /* + * Put MPU CLKDM to SW_SLEEP + */ + ldr r1, am43xx_virt_mpu_clkstctrl + mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP + str r2, [r1] + + /* + * Execute a barrier instruction to ensure that all cache, + * TLB and branch predictor maintenance operations issued + * have completed. + */ + dsb + dmb + + /* + * Execute a WFI instruction and wait until the + * STANDBYWFI output is asserted to indicate that the + * CPU is in idle and low power state. CPU can specualatively + * prefetch the instructions so add NOPs after WFI. Sixteen + * NOPs as per Cortex-A9 pipeline. + */ + wfi + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + /* We come here in case of an abort due to a late interrupt */ + ldr r1, am43xx_virt_mpu_clkstctrl + mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO + str r2, [r1] + + /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */ + ldr r1, am43xx_virt_mpu_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] + + /* Re-enable EMIF */ + ldr r1, am43xx_virt_emif_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] +wait_emif_enable: + ldr r3, [r1] + cmp r2, r3 + bne wait_emif_enable + + /* + * Set SCTLR.C bit to allow data cache allocation + */ + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 2) @ Enable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET] + blx r1 + + /* Let the suspend code know about the abort */ + mov r0, #1 + ldmfd sp!, {r4 - r11, pc} @ restore regs and return +ENDPROC(am43xx_do_wfi) + + .align +ENTRY(am43xx_resume_offset) + .word . - am43xx_do_wfi + +ENTRY(am43xx_resume_from_deep_sleep) + /* Set MPU CLKSTCTRL to HW AUTO so that CPUidle works properly */ + ldr r1, am43xx_virt_mpu_clkstctrl + mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO + str r2, [r1] + + /* For AM43xx, use EMIF power down until context is restored */ + ldr r2, am43xx_phys_emif_poweroff + mov r1, #AM43XX_EMIF_POWEROFF_ENABLE + str r1, [r2, #0x0] + + /* Re-enable EMIF */ + ldr r1, am43xx_phys_emif_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] +wait_emif_enable1: + ldr r3, [r1] + cmp r2, r3 + bne wait_emif_enable1 + + adr r9, am43xx_emif_sram_table + + ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET] + blx r1 + + ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET] + blx r1 + + ldr r2, am43xx_phys_emif_poweroff + mov r1, #AM43XX_EMIF_POWEROFF_DISABLE + str r1, [r2, #0x0] + +#ifdef CONFIG_CACHE_L2X0 + ldr r2, l2_cache_base + ldr r0, [r2, #L2X0_CTRL] + and r0, #0x0f + cmp r0, #1 + beq skip_l2en @ Skip if already enabled + + adr r4, am43xx_pm_ro_sram_data + ldr r3, [r4, #AMX3_PM_RO_SRAM_DATA_PHYS_OFFSET] + ldr r0, [r3, #AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET] + + ldr r12, l2_smc1 + dsb + smc #0 + dsb +set_aux_ctrl: + ldr r0, [r3, #AMX3_PM_L2_AUX_CTRL_VAL_OFFSET] + ldr r12, l2_smc2 + dsb + smc #0 + dsb + + /* L2 invalidate on resume */ + ldr r0, l2_val + ldr r2, l2_cache_base + str r0, [r2, #L2X0_INV_WAY] +wait2: + ldr r0, [r2, #L2X0_INV_WAY] + ldr r1, l2_val + ands r0, r0, r1 + bne wait2 +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x00 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + dsb + smc #0 + dsb +#endif +l2x_sync2: + ldr r2, l2_cache_base + mov r0, #0x0 + str r0, [r2, #L2X0_CACHE_SYNC] +sync2: + ldr r0, [r2, #L2X0_CACHE_SYNC] + ands r0, r0, #0x1 + bne sync2 + + mov r0, #0x1 + ldr r12, l2_smc3 + dsb + smc #0 + dsb +#endif +skip_l2en: + /* We are back. Branch to the common CPU resume routine */ + mov r0, #0 + ldr pc, resume_addr +ENDPROC(am43xx_resume_from_deep_sleep) + +/* + * Local variables + */ + .align +resume_addr: + .word cpu_resume - PAGE_OFFSET + 0x80000000 +get_l2cache_base: + .word omap4_get_l2cache_base +kernel_flush: + .word v7_flush_dcache_all +ddr_start: + .word PAGE_OFFSET + +am43xx_phys_emif_poweroff: + .word (AM43XX_CM_BASE + AM43XX_PRM_DEVICE_INST + \ + AM43XX_PRM_EMIF_CTRL_OFFSET) +am43xx_virt_mpu_clkstctrl: + .word (AM43XX_CM_MPU_CLKSTCTRL) +am43xx_virt_mpu_clkctrl: + .word (AM43XX_CM_MPU_MPU_CLKCTRL) +am43xx_virt_emif_clkctrl: + .word (AM43XX_CM_PER_EMIF_CLKCTRL) +am43xx_phys_emif_clkctrl: + .word (AM43XX_CM_BASE + AM43XX_CM_PER_INST + \ + AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET) + +/* L2 cache related defines for AM437x */ +l2_cache_base: + .word OMAP44XX_L2CACHE_BASE +l2_smc1: + .word OMAP4_MON_L2X0_PREFETCH_INDEX +l2_smc2: + .word OMAP4_MON_L2X0_AUXCTRL_INDEX +l2_smc3: + .word OMAP4_MON_L2X0_CTRL_INDEX +l2_val: + .word 0xffff + +.align 3 +/* DDR related defines */ +ENTRY(am43xx_emif_sram_table) + .space EMIF_PM_FUNCTIONS_SIZE + +ENTRY(am43xx_pm_sram) + .word am43xx_do_wfi + .word am43xx_do_wfi_sz + .word am43xx_resume_offset + .word am43xx_emif_sram_table + .word am43xx_pm_ro_sram_data + +.align 3 + +ENTRY(am43xx_pm_ro_sram_data) + .space AMX3_PM_RO_SRAM_DATA_SIZE + +ENTRY(am43xx_do_wfi_sz) + .word . - am43xx_do_wfi |