aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSantosh Shilimkar <santosh.shilimkar@ti.com>2010-06-16 12:49:48 -0400
committerKevin Hilman <khilman@ti.com>2011-12-08 14:29:00 -0500
commitb2b9762f76981c16a8768255284efeae7f27e4f1 (patch)
tree3538b853da88eedeb2c64646b9555736000ba73a /arch
parentfcf6efa3ffbc3cc19e7abe39e0b90f497df2fc42 (diff)
ARM: OMAP4: PM: Add CPUX OFF mode support
This patch adds the CPU0 and CPU1 off mode support. CPUX close switch retention (CSWR) is not supported by hardware design. The CPUx OFF mode isn't supported on OMAP4430 ES1.0 CPUx sleep code is common for hotplug, suspend and CPUilde. Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Jean Pihet <j-pihet@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Vishwanath BS <vishwanath.bs@ti.com> Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/Makefile6
-rw-r--r--arch/arm/mach-omap2/common.h30
-rw-r--r--arch/arm/mach-omap2/include/mach/omap-secure.h9
-rw-r--r--arch/arm/mach-omap2/omap-mpuss-lowpower.c248
-rw-r--r--arch/arm/mach-omap2/omap-smp.c13
-rw-r--r--arch/arm/mach-omap2/omap4-sar-layout.h9
-rw-r--r--arch/arm/mach-omap2/pm44xx.c6
-rw-r--r--arch/arm/mach-omap2/sleep44xx.S276
8 files changed, 595 insertions, 2 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 19c29d569d82..58de1f6df27c 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -25,11 +25,13 @@ obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
25obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o 25obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o
26obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o 26obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o
27obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o 27obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o
28obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o omap-wakeupgen.o 28obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o omap-wakeupgen.o \
29 sleep44xx.o
29 30
30plus_sec := $(call as-instr,.arch_extension sec,+sec) 31plus_sec := $(call as-instr,.arch_extension sec,+sec)
31AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec) 32AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec)
32AFLAGS_omap-smc.o :=-Wa,-march=armv7-a$(plus_sec) 33AFLAGS_omap-smc.o :=-Wa,-march=armv7-a$(plus_sec)
34AFLAGS_sleep44xx.o :=-Wa,-march=armv7-a$(plus_sec)
33 35
34# Functions loaded to SRAM 36# Functions loaded to SRAM
35obj-$(CONFIG_SOC_OMAP2420) += sram242x.o 37obj-$(CONFIG_SOC_OMAP2420) += sram242x.o
@@ -63,7 +65,7 @@ obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o
63obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o 65obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o
64obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \ 66obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \
65 cpuidle34xx.o 67 cpuidle34xx.o
66obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o 68obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o omap-mpuss-lowpower.o
67obj-$(CONFIG_PM_DEBUG) += pm-debug.o 69obj-$(CONFIG_PM_DEBUG) += pm-debug.o
68obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o 70obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
69obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o 71obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 7ebcb6a9b73e..36cdba7727f2 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -24,9 +24,11 @@
24 24
25#ifndef __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H 25#ifndef __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H
26#define __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H 26#define __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H
27#ifndef __ASSEMBLER__
27 28
28#include <linux/delay.h> 29#include <linux/delay.h>
29#include <plat/common.h> 30#include <plat/common.h>
31#include <asm/proc-fns.h>
30 32
31#ifdef CONFIG_SOC_OMAP2420 33#ifdef CONFIG_SOC_OMAP2420
32extern void omap242x_map_common_io(void); 34extern void omap242x_map_common_io(void);
@@ -183,6 +185,7 @@ static inline void __iomem *omap4_get_scu_base(void)
183extern void __init gic_init_irq(void); 185extern void __init gic_init_irq(void);
184extern void omap_smc1(u32 fn, u32 arg); 186extern void omap_smc1(u32 fn, u32 arg);
185extern void __iomem *omap4_get_sar_ram_base(void); 187extern void __iomem *omap4_get_sar_ram_base(void);
188extern void omap_do_wfi(void);
186 189
187#ifdef CONFIG_SMP 190#ifdef CONFIG_SMP
188/* Needed for secondary core boot */ 191/* Needed for secondary core boot */
@@ -192,4 +195,31 @@ extern void omap_auxcoreboot_addr(u32 cpu_addr);
192extern u32 omap_read_auxcoreboot0(void); 195extern u32 omap_read_auxcoreboot0(void);
193#endif 196#endif
194 197
198#if defined(CONFIG_SMP) && defined(CONFIG_PM)
199extern int omap4_mpuss_init(void);
200extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
201extern int omap4_finish_suspend(unsigned long cpu_state);
202extern void omap4_cpu_resume(void);
203#else
204static inline int omap4_enter_lowpower(unsigned int cpu,
205 unsigned int power_state)
206{
207 cpu_do_idle();
208 return 0;
209}
210
211static inline int omap4_mpuss_init(void)
212{
213 return 0;
214}
215
216static inline int omap4_finish_suspend(unsigned long cpu_state)
217{
218 return 0;
219}
220
221static inline void omap4_cpu_resume(void)
222{}
223#endif
224#endif /* __ASSEMBLER__ */
195#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */ 225#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
diff --git a/arch/arm/mach-omap2/include/mach/omap-secure.h b/arch/arm/mach-omap2/include/mach/omap-secure.h
index 29f60cae45e9..5f0763dd5664 100644
--- a/arch/arm/mach-omap2/include/mach/omap-secure.h
+++ b/arch/arm/mach-omap2/include/mach/omap-secure.h
@@ -35,9 +35,18 @@
35#define OMAP4_HAL_SAVEALL_INDEX 0x1c 35#define OMAP4_HAL_SAVEALL_INDEX 0x1c
36#define OMAP4_HAL_SAVEGIC_INDEX 0x1d 36#define OMAP4_HAL_SAVEGIC_INDEX 0x1d
37 37
38/* Secure Monitor mode APIs */
39#define OMAP4_MON_SCU_PWR_INDEX 0x108
40
41/* Secure PPA(Primary Protected Application) APIs */
42#define OMAP4_PPA_CPU_ACTRL_SMP_INDEX 0x25
43
44#ifndef __ASSEMBLER__
45
38extern u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs, 46extern u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs,
39 u32 arg1, u32 arg2, u32 arg3, u32 arg4); 47 u32 arg1, u32 arg2, u32 arg3, u32 arg4);
40extern u32 omap_smc2(u32 id, u32 falg, u32 pargs); 48extern u32 omap_smc2(u32 id, u32 falg, u32 pargs);
41extern phys_addr_t omap_secure_ram_mempool_base(void); 49extern phys_addr_t omap_secure_ram_mempool_base(void);
42 50
51#endif /* __ASSEMBLER__ */
43#endif /* OMAP_ARCH_OMAP_SECURE_H */ 52#endif /* OMAP_ARCH_OMAP_SECURE_H */
diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
new file mode 100644
index 000000000000..867fee51e42c
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
@@ -0,0 +1,248 @@
1/*
2 * OMAP MPUSS low power code
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 *
7 * OMAP4430 MPUSS mainly consists of dual Cortex-A9 with per-CPU
8 * Local timer and Watchdog, GIC, SCU, PL310 L2 cache controller,
9 * CPU0 and CPU1 LPRM modules.
10 * CPU0, CPU1 and MPUSS each have there own power domain and
11 * hence multiple low power combinations of MPUSS are possible.
12 *
13 * The CPU0 and CPU1 can't support Closed switch Retention (CSWR)
14 * because the mode is not supported by hw constraints of dormant
15 * mode. While waking up from the dormant mode, a reset signal
16 * to the Cortex-A9 processor must be asserted by the external
17 * power controller.
18 *
19 * With architectural inputs and hardware recommendations, only
20 * below modes are supported from power gain vs latency point of view.
21 *
22 * CPU0 CPU1 MPUSS
23 * ----------------------------------------------
24 * ON ON ON
25 * ON(Inactive) OFF ON(Inactive)
26 * OFF OFF CSWR
27 * OFF OFF OSWR (*TBD)
28 * OFF OFF OFF* (*TBD)
29 * ----------------------------------------------
30 *
31 * Note: CPU0 is the master core and it is the last CPU to go down
32 * and first to wake-up when MPUSS low power states are excercised
33 *
34 *
35 * This program is free software; you can redistribute it and/or modify
36 * it under the terms of the GNU General Public License version 2 as
37 * published by the Free Software Foundation.
38 */
39
40#include <linux/kernel.h>
41#include <linux/io.h>
42#include <linux/errno.h>
43#include <linux/linkage.h>
44#include <linux/smp.h>
45
46#include <asm/cacheflush.h>
47#include <asm/tlbflush.h>
48#include <asm/smp_scu.h>
49#include <asm/system.h>
50#include <asm/pgalloc.h>
51#include <asm/suspend.h>
52
53#include <plat/omap44xx.h>
54
55#include "common.h"
56#include "omap4-sar-layout.h"
57#include "pm.h"
58#include "powerdomain.h"
59
60#ifdef CONFIG_SMP
61
62struct omap4_cpu_pm_info {
63 struct powerdomain *pwrdm;
64 void __iomem *scu_sar_addr;
65 void __iomem *wkup_sar_addr;
66};
67
68static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
69
70/*
71 * Program the wakeup routine address for the CPU0 and CPU1
72 * used for OFF or DORMANT wakeup.
73 */
74static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr)
75{
76 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
77
78 __raw_writel(addr, pm_info->wkup_sar_addr);
79}
80
81/*
82 * Set the CPUx powerdomain's previous power state
83 */
84static inline void set_cpu_next_pwrst(unsigned int cpu_id,
85 unsigned int power_state)
86{
87 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
88
89 pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
90}
91
92/*
93 * Read CPU's previous power state
94 */
95static inline unsigned int read_cpu_prev_pwrst(unsigned int cpu_id)
96{
97 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
98
99 return pwrdm_read_prev_pwrst(pm_info->pwrdm);
100}
101
102/*
103 * Clear the CPUx powerdomain's previous power state
104 */
105static inline void clear_cpu_prev_pwrst(unsigned int cpu_id)
106{
107 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
108
109 pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
110}
111
112/*
113 * Store the SCU power status value to scratchpad memory
114 */
115static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
116{
117 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
118 u32 scu_pwr_st;
119
120 switch (cpu_state) {
121 case PWRDM_POWER_RET:
122 scu_pwr_st = SCU_PM_DORMANT;
123 break;
124 case PWRDM_POWER_OFF:
125 scu_pwr_st = SCU_PM_POWEROFF;
126 break;
127 case PWRDM_POWER_ON:
128 case PWRDM_POWER_INACTIVE:
129 default:
130 scu_pwr_st = SCU_PM_NORMAL;
131 break;
132 }
133
134 __raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
135}
136
137/**
138 * omap4_enter_lowpower: OMAP4 MPUSS Low Power Entry Function
139 * The purpose of this function is to manage low power programming
140 * of OMAP4 MPUSS subsystem
141 * @cpu : CPU ID
142 * @power_state: Low power state.
143 */
144int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
145{
146 unsigned int save_state = 0;
147 unsigned int wakeup_cpu;
148
149 if (omap_rev() == OMAP4430_REV_ES1_0)
150 return -ENXIO;
151
152 switch (power_state) {
153 case PWRDM_POWER_ON:
154 case PWRDM_POWER_INACTIVE:
155 save_state = 0;
156 break;
157 case PWRDM_POWER_OFF:
158 save_state = 1;
159 break;
160 case PWRDM_POWER_RET:
161 default:
162 /*
163 * CPUx CSWR is invalid hardware state. Also CPUx OSWR
164 * doesn't make much scense, since logic is lost and $L1
165 * needs to be cleaned because of coherency. This makes
166 * CPUx OSWR equivalent to CPUX OFF and hence not supported
167 */
168 WARN_ON(1);
169 return -ENXIO;
170 }
171
172 clear_cpu_prev_pwrst(cpu);
173 set_cpu_next_pwrst(cpu, power_state);
174 set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume));
175 scu_pwrst_prepare(cpu, power_state);
176
177 /*
178 * Call low level function with targeted low power state.
179 */
180 cpu_suspend(save_state, omap4_finish_suspend);
181
182 /*
183 * Restore the CPUx power state to ON otherwise CPUx
184 * power domain can transitions to programmed low power
185 * state while doing WFI outside the low powe code. On
186 * secure devices, CPUx does WFI which can result in
187 * domain transition
188 */
189 wakeup_cpu = smp_processor_id();
190 set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
191
192 return 0;
193}
194
195/*
196 * Initialise OMAP4 MPUSS
197 */
198int __init omap4_mpuss_init(void)
199{
200 struct omap4_cpu_pm_info *pm_info;
201 void __iomem *sar_base = omap4_get_sar_ram_base();
202
203 if (omap_rev() == OMAP4430_REV_ES1_0) {
204 WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
205 return -ENODEV;
206 }
207
208 /* Initilaise per CPU PM information */
209 pm_info = &per_cpu(omap4_pm_info, 0x0);
210 pm_info->scu_sar_addr = sar_base + SCU_OFFSET0;
211 pm_info->wkup_sar_addr = sar_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
212 pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
213 if (!pm_info->pwrdm) {
214 pr_err("Lookup failed for CPU0 pwrdm\n");
215 return -ENODEV;
216 }
217
218 /* Clear CPU previous power domain state */
219 pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
220
221 /* Initialise CPU0 power domain state to ON */
222 pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
223
224 pm_info = &per_cpu(omap4_pm_info, 0x1);
225 pm_info->scu_sar_addr = sar_base + SCU_OFFSET1;
226 pm_info->wkup_sar_addr = sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
227 pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm");
228 if (!pm_info->pwrdm) {
229 pr_err("Lookup failed for CPU1 pwrdm\n");
230 return -ENODEV;
231 }
232
233 /* Clear CPU previous power domain state */
234 pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
235
236 /* Initialise CPU1 power domain state to ON */
237 pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
238
239 /* Save device type on scratchpad for low level code to use */
240 if (omap_type() != OMAP2_DEVICE_TYPE_GP)
241 __raw_writel(1, sar_base + OMAP_TYPE_OFFSET);
242 else
243 __raw_writel(0, sar_base + OMAP_TYPE_OFFSET);
244
245 return 0;
246}
247
248#endif
diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c
index 74e90b40a0c7..ee83808de0ff 100644
--- a/arch/arm/mach-omap2/omap-smp.c
+++ b/arch/arm/mach-omap2/omap-smp.c
@@ -24,6 +24,7 @@
24#include <asm/hardware/gic.h> 24#include <asm/hardware/gic.h>
25#include <asm/smp_scu.h> 25#include <asm/smp_scu.h>
26#include <mach/hardware.h> 26#include <mach/hardware.h>
27#include <mach/omap-secure.h>
27 28
28#include "common.h" 29#include "common.h"
29 30
@@ -40,6 +41,18 @@ void __iomem *omap4_get_scu_base(void)
40void __cpuinit platform_secondary_init(unsigned int cpu) 41void __cpuinit platform_secondary_init(unsigned int cpu)
41{ 42{
42 /* 43 /*
44 * Configure ACTRL and enable NS SMP bit access on CPU1 on HS device.
45 * OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA
46 * init and for CPU1, a secure PPA API provided. CPU0 must be ON
47 * while executing NS_SMP API on CPU1 and PPA version must be 1.4.0+.
48 * OMAP443X GP devices- SMP bit isn't accessible.
49 * OMAP446X GP devices - SMP bit access is enabled on both CPUs.
50 */
51 if (cpu_is_omap443x() && (omap_type() != OMAP2_DEVICE_TYPE_GP))
52 omap_secure_dispatcher(OMAP4_PPA_CPU_ACTRL_SMP_INDEX,
53 4, 0, 0, 0, 0, 0);
54
55 /*
43 * If any interrupts are already enabled for the primary 56 * If any interrupts are already enabled for the primary
44 * core (e.g. timer irq), then they will not have been enabled 57 * core (e.g. timer irq), then they will not have been enabled
45 * for us: do so 58 * for us: do so
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
index 7781ea4dacbc..970a2eef3ab9 100644
--- a/arch/arm/mach-omap2/omap4-sar-layout.h
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -19,4 +19,13 @@
19#define SAR_BANK3_OFFSET 0x2000 19#define SAR_BANK3_OFFSET 0x2000
20#define SAR_BANK4_OFFSET 0x3000 20#define SAR_BANK4_OFFSET 0x3000
21 21
22/* Scratch pad memory offsets from SAR_BANK1 */
23#define SCU_OFFSET0 0xd00
24#define SCU_OFFSET1 0xd04
25#define OMAP_TYPE_OFFSET 0xd10
26
27/* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK3 */
28#define CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xa04
29#define CPU1_WAKEUP_NS_PA_ADDR_OFFSET 0xa08
30
22#endif 31#endif
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index c34139dc8d8c..781aadf98e32 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -163,6 +163,12 @@ static int __init omap4_pm_init(void)
163 goto err2; 163 goto err2;
164 } 164 }
165 165
166 ret = omap4_mpuss_init();
167 if (ret) {
168 pr_err("Failed to initialise OMAP4 MPUSS\n");
169 goto err2;
170 }
171
166 (void) clkdm_for_each(clkdms_setup, NULL); 172 (void) clkdm_for_each(clkdms_setup, NULL);
167 173
168#ifdef CONFIG_SUSPEND 174#ifdef CONFIG_SUSPEND
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
new file mode 100644
index 000000000000..e5521945ba8e
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -0,0 +1,276 @@
1/*
2 * OMAP44xx sleep code.
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 *
7 * This program is free software,you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/linkage.h>
13#include <asm/system.h>
14#include <asm/smp_scu.h>
15#include <asm/memory.h>
16#include <asm/hardware/cache-l2x0.h>
17
18#include <plat/omap44xx.h>
19#include <mach/omap-secure.h>
20
21#include "common.h"
22#include "omap4-sar-layout.h"
23
24#if defined(CONFIG_SMP) && defined(CONFIG_PM)
25
26.macro DO_SMC
27 dsb
28 smc #0
29 dsb
30.endm
31
32ppa_zero_params:
33 .word 0x0
34
35/*
36 * =============================
37 * == CPU suspend finisher ==
38 * =============================
39 *
40 * void omap4_finish_suspend(unsigned long cpu_state)
41 *
42 * This function code saves the CPU context and performs the CPU
43 * power down sequence. Calling WFI effectively changes the CPU
44 * power domains states to the desired target power state.
45 *
46 * @cpu_state : contains context save state (r0)
47 * 0 - No context lost
48 * 1 - CPUx L1 and logic lost: MPUSS CSWR
49 * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
50 * 3 - CPUx L1 and logic lost + GIC + L2 lost: MPUSS OFF
51 * @return: This function never returns for CPU OFF and DORMANT power states.
52 * Post WFI, CPU transitions to DORMANT or OFF power state and on wake-up
53 * from this follows a full CPU reset path via ROM code to CPU restore code.
54 * The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET.
55 * It returns to the caller for CPU INACTIVE and ON power states or in case
56 * CPU failed to transition to targeted OFF/DORMANT state.
57 */
58ENTRY(omap4_finish_suspend)
59 stmfd sp!, {lr}
60 cmp r0, #0x0
61 beq do_WFI @ No lowpower state, jump to WFI
62
63 /*
64 * Flush all data from the L1 data cache before disabling
65 * SCTLR.C bit.
66 */
67 bl omap4_get_sar_ram_base
68 ldr r9, [r0, #OMAP_TYPE_OFFSET]
69 cmp r9, #0x1 @ Check for HS device
70 bne skip_secure_l1_clean
71 mov r0, #SCU_PM_NORMAL
72 mov r1, #0xFF @ clean seucre L1
73 stmfd r13!, {r4-r12, r14}
74 ldr r12, =OMAP4_MON_SCU_PWR_INDEX
75 DO_SMC
76 ldmfd r13!, {r4-r12, r14}
77skip_secure_l1_clean:
78 bl v7_flush_dcache_all
79
80 /*
81 * Clear the SCTLR.C bit to prevent further data cache
82 * allocation. Clearing SCTLR.C would make all the data accesses
83 * strongly ordered and would not hit the cache.
84 */
85 mrc p15, 0, r0, c1, c0, 0
86 bic r0, r0, #(1 << 2) @ Disable the C bit
87 mcr p15, 0, r0, c1, c0, 0
88 isb
89
90 /*
91 * Invalidate L1 data cache. Even though only invalidate is
92 * necessary exported flush API is used here. Doing clean
93 * on already clean cache would be almost NOP.
94 */
95 bl v7_flush_dcache_all
96
97 /*
98 * Switch the CPU from Symmetric Multiprocessing (SMP) mode
99 * to AsymmetricMultiprocessing (AMP) mode by programming
100 * the SCU power status to DORMANT or OFF mode.
101 * This enables the CPU to be taken out of coherency by
102 * preventing the CPU from receiving cache, TLB, or BTB
103 * maintenance operations broadcast by other CPUs in the cluster.
104 */
105 bl omap4_get_sar_ram_base
106 mov r8, r0
107 ldr r9, [r8, #OMAP_TYPE_OFFSET]
108 cmp r9, #0x1 @ Check for HS device
109 bne scu_gp_set
110 mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR
111 ands r0, r0, #0x0f
112 ldreq r0, [r8, #SCU_OFFSET0]
113 ldrne r0, [r8, #SCU_OFFSET1]
114 mov r1, #0x00
115 stmfd r13!, {r4-r12, r14}
116 ldr r12, =OMAP4_MON_SCU_PWR_INDEX
117 DO_SMC
118 ldmfd r13!, {r4-r12, r14}
119 b skip_scu_gp_set
120scu_gp_set:
121 mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR
122 ands r0, r0, #0x0f
123 ldreq r1, [r8, #SCU_OFFSET0]
124 ldrne r1, [r8, #SCU_OFFSET1]
125 bl omap4_get_scu_base
126 bl scu_power_mode
127skip_scu_gp_set:
128 mrc p15, 0, r0, c1, c1, 2 @ Read NSACR data
129 tst r0, #(1 << 18)
130 mrcne p15, 0, r0, c1, c0, 1
131 bicne r0, r0, #(1 << 6) @ Disable SMP bit
132 mcrne p15, 0, r0, c1, c0, 1
133 isb
134 dsb
135
136do_WFI:
137 bl omap_do_wfi
138
139 /*
140 * CPU is here when it failed to enter OFF/DORMANT or
141 * no low power state was attempted.
142 */
143 mrc p15, 0, r0, c1, c0, 0
144 tst r0, #(1 << 2) @ Check C bit enabled?
145 orreq r0, r0, #(1 << 2) @ Enable the C bit
146 mcreq p15, 0, r0, c1, c0, 0
147 isb
148
149 /*
150 * Ensure the CPU power state is set to NORMAL in
151 * SCU power state so that CPU is back in coherency.
152 * In non-coherent mode CPU can lock-up and lead to
153 * system deadlock.
154 */
155 mrc p15, 0, r0, c1, c0, 1
156 tst r0, #(1 << 6) @ Check SMP bit enabled?
157 orreq r0, r0, #(1 << 6)
158 mcreq p15, 0, r0, c1, c0, 1
159 isb
160 bl omap4_get_sar_ram_base
161 mov r8, r0
162 ldr r9, [r8, #OMAP_TYPE_OFFSET]
163 cmp r9, #0x1 @ Check for HS device
164 bne scu_gp_clear
165 mov r0, #SCU_PM_NORMAL
166 mov r1, #0x00
167 stmfd r13!, {r4-r12, r14}
168 ldr r12, =OMAP4_MON_SCU_PWR_INDEX
169 DO_SMC
170 ldmfd r13!, {r4-r12, r14}
171 b skip_scu_gp_clear
172scu_gp_clear:
173 bl omap4_get_scu_base
174 mov r1, #SCU_PM_NORMAL
175 bl scu_power_mode
176skip_scu_gp_clear:
177 isb
178 dsb
179 ldmfd sp!, {pc}
180ENDPROC(omap4_finish_suspend)
181
182/*
183 * ============================
184 * == CPU resume entry point ==
185 * ============================
186 *
187 * void omap4_cpu_resume(void)
188 *
189 * ROM code jumps to this function while waking up from CPU
190 * OFF or DORMANT state. Physical address of the function is
191 * stored in the SAR RAM while entering to OFF or DORMANT mode.
192 * The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET.
193 */
194ENTRY(omap4_cpu_resume)
195 /*
196 * Configure ACTRL and enable NS SMP bit access on CPU1 on HS device.
197 * OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA
198 * init and for CPU1, a secure PPA API provided. CPU0 must be ON
199 * while executing NS_SMP API on CPU1 and PPA version must be 1.4.0+.
200 * OMAP443X GP devices- SMP bit isn't accessible.
201 * OMAP446X GP devices - SMP bit access is enabled on both CPUs.
202 */
203 ldr r8, =OMAP44XX_SAR_RAM_BASE
204 ldr r9, [r8, #OMAP_TYPE_OFFSET]
205 cmp r9, #0x1 @ Skip if GP device
206 bne skip_ns_smp_enable
207 mrc p15, 0, r0, c0, c0, 5
208 ands r0, r0, #0x0f
209 beq skip_ns_smp_enable
210ppa_actrl_retry:
211 mov r0, #OMAP4_PPA_CPU_ACTRL_SMP_INDEX
212 adr r3, ppa_zero_params @ Pointer to parameters
213 mov r1, #0x0 @ Process ID
214 mov r2, #0x4 @ Flag
215 mov r6, #0xff
216 mov r12, #0x00 @ Secure Service ID
217 DO_SMC
218 cmp r0, #0x0 @ API returns 0 on success.
219 beq enable_smp_bit
220 b ppa_actrl_retry
221enable_smp_bit:
222 mrc p15, 0, r0, c1, c0, 1
223 tst r0, #(1 << 6) @ Check SMP bit enabled?
224 orreq r0, r0, #(1 << 6)
225 mcreq p15, 0, r0, c1, c0, 1
226 isb
227skip_ns_smp_enable:
228
229 b cpu_resume @ Jump to generic resume
230ENDPROC(omap4_cpu_resume)
231#endif
232
233ENTRY(omap_do_wfi)
234 stmfd sp!, {lr}
235
236 /*
237 * Execute an ISB instruction to ensure that all of the
238 * CP15 register changes have been committed.
239 */
240 isb
241
242 /*
243 * Execute a barrier instruction to ensure that all cache,
244 * TLB and branch predictor maintenance operations issued
245 * by any CPU in the cluster have completed.
246 */
247 dsb
248 dmb
249
250 /*
251 * Execute a WFI instruction and wait until the
252 * STANDBYWFI output is asserted to indicate that the
253 * CPU is in idle and low power state. CPU can specualatively
254 * prefetch the instructions so add NOPs after WFI. Sixteen
255 * NOPs as per Cortex-A9 pipeline.
256 */
257 wfi @ Wait For Interrupt
258 nop
259 nop
260 nop
261 nop
262 nop
263 nop
264 nop
265 nop
266 nop
267 nop
268 nop
269 nop
270 nop
271 nop
272 nop
273 nop
274
275 ldmfd sp!, {pc}
276ENDPROC(omap_do_wfi)