aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2011-12-08 16:22:57 -0500
committerTony Lindgren <tony@atomide.com>2011-12-08 16:22:57 -0500
commit4c89aad9f4803875f7065e825badc9ba61922091 (patch)
treed0c60e673e527085aaddd76fb512311aa6159e40 /arch/arm
parentdeee6d5359969a0ce4e2760cfd7b9f379bd5698a (diff)
parentff819da44258ca12b9f60dfd589884106e5a3129 (diff)
Merge branch 'for_3.3/pm/omap4-mpuss' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-omap-pm into omap4
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-omap2/Kconfig21
-rw-r--r--arch/arm/mach-omap2/Makefile16
-rw-r--r--arch/arm/mach-omap2/common.h64
-rw-r--r--arch/arm/mach-omap2/cpuidle34xx.c15
-rw-r--r--arch/arm/mach-omap2/cpuidle44xx.c245
-rw-r--r--arch/arm/mach-omap2/include/mach/barriers.h31
-rw-r--r--arch/arm/mach-omap2/include/mach/omap-secure.h57
-rw-r--r--arch/arm/mach-omap2/include/mach/omap-wakeupgen.h39
-rw-r--r--arch/arm/mach-omap2/io.c9
-rw-r--r--arch/arm/mach-omap2/omap-headsmp.S5
-rw-r--r--arch/arm/mach-omap2/omap-hotplug.c14
-rw-r--r--arch/arm/mach-omap2/omap-mpuss-lowpower.c398
-rw-r--r--arch/arm/mach-omap2/omap-secure.c81
-rw-r--r--arch/arm/mach-omap2/omap-smc.S (renamed from arch/arm/mach-omap2/omap44xx-smc.S)23
-rw-r--r--arch/arm/mach-omap2/omap-smp.c45
-rw-r--r--arch/arm/mach-omap2/omap-wakeupgen.c389
-rw-r--r--arch/arm/mach-omap2/omap4-common.c94
-rw-r--r--arch/arm/mach-omap2/omap4-sar-layout.h50
-rw-r--r--arch/arm/mach-omap2/pm.h1
-rw-r--r--arch/arm/mach-omap2/pm44xx.c153
-rw-r--r--arch/arm/mach-omap2/sleep44xx.S379
-rw-r--r--arch/arm/plat-omap/common.c3
-rw-r--r--arch/arm/plat-omap/include/plat/omap-secure.h13
-rw-r--r--arch/arm/plat-omap/include/plat/omap44xx.h1
-rw-r--r--arch/arm/plat-omap/include/plat/sram.h6
-rw-r--r--arch/arm/plat-omap/sram.c8
26 files changed, 2126 insertions, 34 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index b6625130831d..50f43942c1aa 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -353,6 +353,27 @@ config OMAP3_SDRC_AC_TIMING
353 wish to say no. Selecting yes without understanding what is 353 wish to say no. Selecting yes without understanding what is
354 going on could result in system crashes; 354 going on could result in system crashes;
355 355
356config OMAP4_ERRATA_I688
357 bool "OMAP4 errata: Async Bridge Corruption"
358 depends on ARCH_OMAP4
359 select ARCH_HAS_BARRIERS
360 help
361 If a data is stalled inside asynchronous bridge because of back
362 pressure, it may be accepted multiple times, creating pointer
363 misalignment that will corrupt next transfers on that data path
364 until next reset of the system (No recovery procedure once the
365 issue is hit, the path remains consistently broken). Async bridge
366 can be found on path between MPU to EMIF and MPU to L3 interconnect.
367 This situation can happen only when the idle is initiated by a
368 Master Request Disconnection (which is trigged by software when
369 executing WFI on CPU).
370 The work-around for this errata needs all the initiators connected
371 through async bridge must ensure that data path is properly drained
372 before issuing WFI. This condition will be met if one Strongly ordered
373 access is performed to the target right before executing the WFI.
374 In MPU case, L3 T2ASYNC FIFO and DDR T2ASYNC FIFO needs to be drained.
375 IO barrier ensure that there is no synchronisation loss on initiators
376 operating on both interconnect port simultaneously.
356endmenu 377endmenu
357 378
358endif 379endif
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index b009f17dee56..9a6da52661ce 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -11,10 +11,11 @@ hwmod-common = omap_hwmod.o \
11 omap_hwmod_common_data.o 11 omap_hwmod_common_data.o
12clock-common = clock.o clock_common_data.o \ 12clock-common = clock.o clock_common_data.o \
13 clkt_dpll.o clkt_clksel.o 13 clkt_dpll.o clkt_clksel.o
14secure-common = omap-smc.o omap-secure.o
14 15
15obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) 16obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) $(secure-common)
16obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) 17obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common)
17obj-$(CONFIG_ARCH_OMAP4) += prm44xx.o $(hwmod-common) 18obj-$(CONFIG_ARCH_OMAP4) += prm44xx.o $(hwmod-common) $(secure-common)
18 19
19obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o 20obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
20 21
@@ -24,11 +25,13 @@ obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
24obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o 25obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o
25obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o 26obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o
26obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o 27obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o
27obj-$(CONFIG_ARCH_OMAP4) += omap44xx-smc.o omap4-common.o 28obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o omap-wakeupgen.o \
29 sleep44xx.o
28 30
29plus_sec := $(call as-instr,.arch_extension sec,+sec) 31plus_sec := $(call as-instr,.arch_extension sec,+sec)
30AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec) 32AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec)
31AFLAGS_omap44xx-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)
32 35
33# Functions loaded to SRAM 36# Functions loaded to SRAM
34obj-$(CONFIG_SOC_OMAP2420) += sram242x.o 37obj-$(CONFIG_SOC_OMAP2420) += sram242x.o
@@ -62,7 +65,8 @@ obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o
62obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o 65obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o
63obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \ 66obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \
64 cpuidle34xx.o 67 cpuidle34xx.o
65obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o 68obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o omap-mpuss-lowpower.o \
69 cpuidle44xx.o
66obj-$(CONFIG_PM_DEBUG) += pm-debug.o 70obj-$(CONFIG_PM_DEBUG) += pm-debug.o
67obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o 71obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
68obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o 72obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 012bac7d56a5..0911e843f079 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);
@@ -156,23 +158,23 @@ void omap3_intc_resume_idle(void);
156void omap2_intc_handle_irq(struct pt_regs *regs); 158void omap2_intc_handle_irq(struct pt_regs *regs);
157void omap3_intc_handle_irq(struct pt_regs *regs); 159void omap3_intc_handle_irq(struct pt_regs *regs);
158 160
159/* 161#ifdef CONFIG_CACHE_L2X0
160 * wfi used in low power code. Directly opcode is used instead 162extern void __iomem *omap4_get_l2cache_base(void);
161 * of instruction to avoid mulit-omap build break
162 */
163#ifdef CONFIG_THUMB2_KERNEL
164#define do_wfi() __asm__ __volatile__ ("wfi" : : : "memory")
165#else
166#define do_wfi() \
167 __asm__ __volatile__ (".word 0xe320f003" : : : "memory")
168#endif 163#endif
169 164
170#ifdef CONFIG_CACHE_L2X0 165#ifdef CONFIG_SMP
171extern void __iomem *l2cache_base; 166extern void __iomem *omap4_get_scu_base(void);
167#else
168static inline void __iomem *omap4_get_scu_base(void)
169{
170 return NULL;
171}
172#endif 172#endif
173 173
174extern void __init gic_init_irq(void); 174extern void __init gic_init_irq(void);
175extern void omap_smc1(u32 fn, u32 arg); 175extern void omap_smc1(u32 fn, u32 arg);
176extern void __iomem *omap4_get_sar_ram_base(void);
177extern void omap_do_wfi(void);
176 178
177#ifdef CONFIG_SMP 179#ifdef CONFIG_SMP
178/* Needed for secondary core boot */ 180/* Needed for secondary core boot */
@@ -182,4 +184,44 @@ extern void omap_auxcoreboot_addr(u32 cpu_addr);
182extern u32 omap_read_auxcoreboot0(void); 184extern u32 omap_read_auxcoreboot0(void);
183#endif 185#endif
184 186
187#if defined(CONFIG_SMP) && defined(CONFIG_PM)
188extern int omap4_mpuss_init(void);
189extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
190extern int omap4_finish_suspend(unsigned long cpu_state);
191extern void omap4_cpu_resume(void);
192extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state);
193extern u32 omap4_mpuss_read_prev_context_state(void);
194#else
195static inline int omap4_enter_lowpower(unsigned int cpu,
196 unsigned int power_state)
197{
198 cpu_do_idle();
199 return 0;
200}
201
202static inline int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
203{
204 cpu_do_idle();
205 return 0;
206}
207
208static inline int omap4_mpuss_init(void)
209{
210 return 0;
211}
212
213static inline int omap4_finish_suspend(unsigned long cpu_state)
214{
215 return 0;
216}
217
218static inline void omap4_cpu_resume(void)
219{}
220
221static inline u32 omap4_mpuss_read_prev_context_state(void)
222{
223 return 0;
224}
225#endif
226#endif /* __ASSEMBLER__ */
185#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */ 227#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index e20332f4abdc..1f71ebb6c12c 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -25,6 +25,7 @@
25#include <linux/sched.h> 25#include <linux/sched.h>
26#include <linux/cpuidle.h> 26#include <linux/cpuidle.h>
27#include <linux/export.h> 27#include <linux/export.h>
28#include <linux/cpu_pm.h>
28 29
29#include <plat/prcm.h> 30#include <plat/prcm.h>
30#include <plat/irqs.h> 31#include <plat/irqs.h>
@@ -124,9 +125,23 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
124 pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle); 125 pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle);
125 } 126 }
126 127
128 /*
129 * Call idle CPU PM enter notifier chain so that
130 * VFP context is saved.
131 */
132 if (mpu_state == PWRDM_POWER_OFF)
133 cpu_pm_enter();
134
127 /* Execute ARM wfi */ 135 /* Execute ARM wfi */
128 omap_sram_idle(); 136 omap_sram_idle();
129 137
138 /*
139 * Call idle CPU PM enter notifier chain to restore
140 * VFP context.
141 */
142 if (pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
143 cpu_pm_exit();
144
130 /* Re-allow idle for C1 */ 145 /* Re-allow idle for C1 */
131 if (index == 0) { 146 if (index == 0) {
132 pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle); 147 pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle);
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
new file mode 100644
index 000000000000..cfdbb86bc84e
--- /dev/null
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -0,0 +1,245 @@
1/*
2 * OMAP4 CPU idle Routines
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 * Rajendra Nayak <rnayak@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/sched.h>
14#include <linux/cpuidle.h>
15#include <linux/cpu_pm.h>
16#include <linux/export.h>
17#include <linux/clockchips.h>
18
19#include <asm/proc-fns.h>
20
21#include "common.h"
22#include "pm.h"
23#include "prm.h"
24
25#ifdef CONFIG_CPU_IDLE
26
27/* Machine specific information to be recorded in the C-state driver_data */
28struct omap4_idle_statedata {
29 u32 cpu_state;
30 u32 mpu_logic_state;
31 u32 mpu_state;
32 u8 valid;
33};
34
35static struct cpuidle_params cpuidle_params_table[] = {
36 /* C1 - CPU0 ON + CPU1 ON + MPU ON */
37 {.exit_latency = 2 + 2 , .target_residency = 5, .valid = 1},
38 /* C2- CPU0 OFF + CPU1 OFF + MPU CSWR */
39 {.exit_latency = 328 + 440 , .target_residency = 960, .valid = 1},
40 /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
41 {.exit_latency = 460 + 518 , .target_residency = 1100, .valid = 1},
42};
43
44#define OMAP4_NUM_STATES ARRAY_SIZE(cpuidle_params_table)
45
46struct omap4_idle_statedata omap4_idle_data[OMAP4_NUM_STATES];
47static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
48
49/**
50 * omap4_enter_idle - Programs OMAP4 to enter the specified state
51 * @dev: cpuidle device
52 * @drv: cpuidle driver
53 * @index: the index of state to be entered
54 *
55 * Called from the CPUidle framework to program the device to the
56 * specified low power state selected by the governor.
57 * Returns the amount of time spent in the low power state.
58 */
59static int omap4_enter_idle(struct cpuidle_device *dev,
60 struct cpuidle_driver *drv,
61 int index)
62{
63 struct omap4_idle_statedata *cx =
64 cpuidle_get_statedata(&dev->states_usage[index]);
65 struct timespec ts_preidle, ts_postidle, ts_idle;
66 u32 cpu1_state;
67 int idle_time;
68 int new_state_idx;
69 int cpu_id = smp_processor_id();
70
71 /* Used to keep track of the total time in idle */
72 getnstimeofday(&ts_preidle);
73
74 local_irq_disable();
75 local_fiq_disable();
76
77 /*
78 * CPU0 has to stay ON (i.e in C1) until CPU1 is OFF state.
79 * This is necessary to honour hardware recommondation
80 * of triggeing all the possible low power modes once CPU1 is
81 * out of coherency and in OFF mode.
82 * Update dev->last_state so that governor stats reflects right
83 * data.
84 */
85 cpu1_state = pwrdm_read_pwrst(cpu1_pd);
86 if (cpu1_state != PWRDM_POWER_OFF) {
87 new_state_idx = drv->safe_state_index;
88 cx = cpuidle_get_statedata(&dev->states_usage[new_state_idx]);
89 }
90
91 if (index > 0)
92 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
93
94 /*
95 * Call idle CPU PM enter notifier chain so that
96 * VFP and per CPU interrupt context is saved.
97 */
98 if (cx->cpu_state == PWRDM_POWER_OFF)
99 cpu_pm_enter();
100
101 pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
102 omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
103
104 /*
105 * Call idle CPU cluster PM enter notifier chain
106 * to save GIC and wakeupgen context.
107 */
108 if ((cx->mpu_state == PWRDM_POWER_RET) &&
109 (cx->mpu_logic_state == PWRDM_POWER_OFF))
110 cpu_cluster_pm_enter();
111
112 omap4_enter_lowpower(dev->cpu, cx->cpu_state);
113
114 /*
115 * Call idle CPU PM exit notifier chain to restore
116 * VFP and per CPU IRQ context. Only CPU0 state is
117 * considered since CPU1 is managed by CPU hotplug.
118 */
119 if (pwrdm_read_prev_pwrst(cpu0_pd) == PWRDM_POWER_OFF)
120 cpu_pm_exit();
121
122 /*
123 * Call idle CPU cluster PM exit notifier chain
124 * to restore GIC and wakeupgen context.
125 */
126 if (omap4_mpuss_read_prev_context_state())
127 cpu_cluster_pm_exit();
128
129 if (index > 0)
130 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
131
132 getnstimeofday(&ts_postidle);
133 ts_idle = timespec_sub(ts_postidle, ts_preidle);
134
135 local_irq_enable();
136 local_fiq_enable();
137
138 idle_time = ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * \
139 USEC_PER_SEC;
140
141 /* Update cpuidle counters */
142 dev->last_residency = idle_time;
143
144 return index;
145}
146
147DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
148
149struct cpuidle_driver omap4_idle_driver = {
150 .name = "omap4_idle",
151 .owner = THIS_MODULE,
152};
153
154static inline void _fill_cstate(struct cpuidle_driver *drv,
155 int idx, const char *descr)
156{
157 struct cpuidle_state *state = &drv->states[idx];
158
159 state->exit_latency = cpuidle_params_table[idx].exit_latency;
160 state->target_residency = cpuidle_params_table[idx].target_residency;
161 state->flags = CPUIDLE_FLAG_TIME_VALID;
162 state->enter = omap4_enter_idle;
163 sprintf(state->name, "C%d", idx + 1);
164 strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
165}
166
167static inline struct omap4_idle_statedata *_fill_cstate_usage(
168 struct cpuidle_device *dev,
169 int idx)
170{
171 struct omap4_idle_statedata *cx = &omap4_idle_data[idx];
172 struct cpuidle_state_usage *state_usage = &dev->states_usage[idx];
173
174 cx->valid = cpuidle_params_table[idx].valid;
175 cpuidle_set_statedata(state_usage, cx);
176
177 return cx;
178}
179
180
181
182/**
183 * omap4_idle_init - Init routine for OMAP4 idle
184 *
185 * Registers the OMAP4 specific cpuidle driver to the cpuidle
186 * framework with the valid set of states.
187 */
188int __init omap4_idle_init(void)
189{
190 struct omap4_idle_statedata *cx;
191 struct cpuidle_device *dev;
192 struct cpuidle_driver *drv = &omap4_idle_driver;
193 unsigned int cpu_id = 0;
194
195 mpu_pd = pwrdm_lookup("mpu_pwrdm");
196 cpu0_pd = pwrdm_lookup("cpu0_pwrdm");
197 cpu1_pd = pwrdm_lookup("cpu1_pwrdm");
198 if ((!mpu_pd) || (!cpu0_pd) || (!cpu1_pd))
199 return -ENODEV;
200
201
202 drv->safe_state_index = -1;
203 dev = &per_cpu(omap4_idle_dev, cpu_id);
204 dev->cpu = cpu_id;
205
206 /* C1 - CPU0 ON + CPU1 ON + MPU ON */
207 _fill_cstate(drv, 0, "MPUSS ON");
208 drv->safe_state_index = 0;
209 cx = _fill_cstate_usage(dev, 0);
210 cx->valid = 1; /* C1 is always valid */
211 cx->cpu_state = PWRDM_POWER_ON;
212 cx->mpu_state = PWRDM_POWER_ON;
213 cx->mpu_logic_state = PWRDM_POWER_RET;
214
215 /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
216 _fill_cstate(drv, 1, "MPUSS CSWR");
217 cx = _fill_cstate_usage(dev, 1);
218 cx->cpu_state = PWRDM_POWER_OFF;
219 cx->mpu_state = PWRDM_POWER_RET;
220 cx->mpu_logic_state = PWRDM_POWER_RET;
221
222 /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
223 _fill_cstate(drv, 2, "MPUSS OSWR");
224 cx = _fill_cstate_usage(dev, 2);
225 cx->cpu_state = PWRDM_POWER_OFF;
226 cx->mpu_state = PWRDM_POWER_RET;
227 cx->mpu_logic_state = PWRDM_POWER_OFF;
228
229 drv->state_count = OMAP4_NUM_STATES;
230 cpuidle_register_driver(&omap4_idle_driver);
231
232 dev->state_count = OMAP4_NUM_STATES;
233 if (cpuidle_register_device(dev)) {
234 pr_err("%s: CPUidle register device failed\n", __func__);
235 return -EIO;
236 }
237
238 return 0;
239}
240#else
241int __init omap4_idle_init(void)
242{
243 return 0;
244}
245#endif /* CONFIG_CPU_IDLE */
diff --git a/arch/arm/mach-omap2/include/mach/barriers.h b/arch/arm/mach-omap2/include/mach/barriers.h
new file mode 100644
index 000000000000..4fa72c7cc7cd
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/barriers.h
@@ -0,0 +1,31 @@
1/*
2 * OMAP memory barrier header.
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 * Richard Woodruff <r-woodruff2@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#ifndef __MACH_BARRIERS_H
23#define __MACH_BARRIERS_H
24
25extern void omap_bus_sync(void);
26
27#define rmb() dsb()
28#define wmb() do { dsb(); outer_sync(); omap_bus_sync(); } while (0)
29#define mb() wmb()
30
31#endif /* __MACH_BARRIERS_H */
diff --git a/arch/arm/mach-omap2/include/mach/omap-secure.h b/arch/arm/mach-omap2/include/mach/omap-secure.h
new file mode 100644
index 000000000000..c90a43589abe
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/omap-secure.h
@@ -0,0 +1,57 @@
1/*
2 * omap-secure.h: OMAP Secure infrastructure header.
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#ifndef OMAP_ARCH_OMAP_SECURE_H
12#define OMAP_ARCH_OMAP_SECURE_H
13
14/* Monitor error code */
15#define API_HAL_RET_VALUE_NS2S_CONVERSION_ERROR 0xFFFFFFFE
16#define API_HAL_RET_VALUE_SERVICE_UNKNWON 0xFFFFFFFF
17
18/* HAL API error codes */
19#define API_HAL_RET_VALUE_OK 0x00
20#define API_HAL_RET_VALUE_FAIL 0x01
21
22/* Secure HAL API flags */
23#define FLAG_START_CRITICAL 0x4
24#define FLAG_IRQFIQ_MASK 0x3
25#define FLAG_IRQ_ENABLE 0x2
26#define FLAG_FIQ_ENABLE 0x1
27#define NO_FLAG 0x0
28
29/* Maximum Secure memory storage size */
30#define OMAP_SECURE_RAM_STORAGE (88 * SZ_1K)
31
32/* Secure low power HAL API index */
33#define OMAP4_HAL_SAVESECURERAM_INDEX 0x1a
34#define OMAP4_HAL_SAVEHW_INDEX 0x1b
35#define OMAP4_HAL_SAVEALL_INDEX 0x1c
36#define OMAP4_HAL_SAVEGIC_INDEX 0x1d
37
38/* Secure Monitor mode APIs */
39#define OMAP4_MON_SCU_PWR_INDEX 0x108
40#define OMAP4_MON_L2X0_DBG_CTRL_INDEX 0x100
41#define OMAP4_MON_L2X0_CTRL_INDEX 0x102
42#define OMAP4_MON_L2X0_AUXCTRL_INDEX 0x109
43#define OMAP4_MON_L2X0_PREFETCH_INDEX 0x113
44
45/* Secure PPA(Primary Protected Application) APIs */
46#define OMAP4_PPA_L2_POR_INDEX 0x23
47#define OMAP4_PPA_CPU_ACTRL_SMP_INDEX 0x25
48
49#ifndef __ASSEMBLER__
50
51extern u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs,
52 u32 arg1, u32 arg2, u32 arg3, u32 arg4);
53extern u32 omap_smc2(u32 id, u32 falg, u32 pargs);
54extern phys_addr_t omap_secure_ram_mempool_base(void);
55
56#endif /* __ASSEMBLER__ */
57#endif /* OMAP_ARCH_OMAP_SECURE_H */
diff --git a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
new file mode 100644
index 000000000000..d79321b0f2a2
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
@@ -0,0 +1,39 @@
1/*
2 * OMAP WakeupGen header file
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#ifndef OMAP_ARCH_WAKEUPGEN_H
12#define OMAP_ARCH_WAKEUPGEN_H
13
14#define OMAP_WKG_CONTROL_0 0x00
15#define OMAP_WKG_ENB_A_0 0x10
16#define OMAP_WKG_ENB_B_0 0x14
17#define OMAP_WKG_ENB_C_0 0x18
18#define OMAP_WKG_ENB_D_0 0x1c
19#define OMAP_WKG_ENB_SECURE_A_0 0x20
20#define OMAP_WKG_ENB_SECURE_B_0 0x24
21#define OMAP_WKG_ENB_SECURE_C_0 0x28
22#define OMAP_WKG_ENB_SECURE_D_0 0x2c
23#define OMAP_WKG_ENB_A_1 0x410
24#define OMAP_WKG_ENB_B_1 0x414
25#define OMAP_WKG_ENB_C_1 0x418
26#define OMAP_WKG_ENB_D_1 0x41c
27#define OMAP_WKG_ENB_SECURE_A_1 0x420
28#define OMAP_WKG_ENB_SECURE_B_1 0x424
29#define OMAP_WKG_ENB_SECURE_C_1 0x428
30#define OMAP_WKG_ENB_SECURE_D_1 0x42c
31#define OMAP_AUX_CORE_BOOT_0 0x800
32#define OMAP_AUX_CORE_BOOT_1 0x804
33#define OMAP_PTMSYNCREQ_MASK 0xc00
34#define OMAP_PTMSYNCREQ_EN 0xc04
35#define OMAP_TIMESTAMPCYCLELO 0xc08
36#define OMAP_TIMESTAMPCYCLEHI 0xc0c
37
38extern int __init omap_wakeupgen_init(void);
39#endif
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 3f565dd2ea8d..65843390e7f0 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -237,6 +237,15 @@ static struct map_desc omap44xx_io_desc[] __initdata = {
237 .length = L4_EMU_44XX_SIZE, 237 .length = L4_EMU_44XX_SIZE,
238 .type = MT_DEVICE, 238 .type = MT_DEVICE,
239 }, 239 },
240#ifdef CONFIG_OMAP4_ERRATA_I688
241 {
242 .virtual = OMAP4_SRAM_VA,
243 .pfn = __phys_to_pfn(OMAP4_SRAM_PA),
244 .length = PAGE_SIZE,
245 .type = MT_MEMORY_SO,
246 },
247#endif
248
240}; 249};
241#endif 250#endif
242 251
diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S
index 4ee6aeca885a..b13ef7ef5ef4 100644
--- a/arch/arm/mach-omap2/omap-headsmp.S
+++ b/arch/arm/mach-omap2/omap-headsmp.S
@@ -18,11 +18,6 @@
18#include <linux/linkage.h> 18#include <linux/linkage.h>
19#include <linux/init.h> 19#include <linux/init.h>
20 20
21/* Physical address needed since MMU not enabled yet on secondary core */
22#define OMAP4_AUX_CORE_BOOT1_PA 0x48281804
23
24 __INIT
25
26/* 21/*
27 * OMAP4 specific entry point for secondary CPU to jump from ROM 22 * OMAP4 specific entry point for secondary CPU to jump from ROM
28 * code. This routine also provides a holding flag into which 23 * code. This routine also provides a holding flag into which
diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c
index e5a1c3f40a86..adbe4d8c7caf 100644
--- a/arch/arm/mach-omap2/omap-hotplug.c
+++ b/arch/arm/mach-omap2/omap-hotplug.c
@@ -22,6 +22,8 @@
22 22
23#include "common.h" 23#include "common.h"
24 24
25#include "powerdomain.h"
26
25int platform_cpu_kill(unsigned int cpu) 27int platform_cpu_kill(unsigned int cpu)
26{ 28{
27 return 1; 29 return 1;
@@ -33,6 +35,8 @@ int platform_cpu_kill(unsigned int cpu)
33 */ 35 */
34void platform_cpu_die(unsigned int cpu) 36void platform_cpu_die(unsigned int cpu)
35{ 37{
38 unsigned int this_cpu;
39
36 flush_cache_all(); 40 flush_cache_all();
37 dsb(); 41 dsb();
38 42
@@ -40,15 +44,15 @@ void platform_cpu_die(unsigned int cpu)
40 * we're ready for shutdown now, so do it 44 * we're ready for shutdown now, so do it
41 */ 45 */
42 if (omap_modify_auxcoreboot0(0x0, 0x200) != 0x0) 46 if (omap_modify_auxcoreboot0(0x0, 0x200) != 0x0)
43 printk(KERN_CRIT "Secure clear status failed\n"); 47 pr_err("Secure clear status failed\n");
44 48
45 for (;;) { 49 for (;;) {
46 /* 50 /*
47 * Execute WFI 51 * Enter into low power state
48 */ 52 */
49 do_wfi(); 53 omap4_hotplug_cpu(cpu, PWRDM_POWER_OFF);
50 54 this_cpu = smp_processor_id();
51 if (omap_read_auxcoreboot0() == cpu) { 55 if (omap_read_auxcoreboot0() == this_cpu) {
52 /* 56 /*
53 * OK, proper wakeup, we're done 57 * OK, proper wakeup, we're done
54 */ 58 */
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..1d5d01056558
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
@@ -0,0 +1,398 @@
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
28 * OFF OFF OFF(Device 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#include <asm/hardware/cache-l2x0.h>
53
54#include <plat/omap44xx.h>
55
56#include "common.h"
57#include "omap4-sar-layout.h"
58#include "pm.h"
59#include "prcm_mpu44xx.h"
60#include "prminst44xx.h"
61#include "prcm44xx.h"
62#include "prm44xx.h"
63#include "prm-regbits-44xx.h"
64
65#ifdef CONFIG_SMP
66
67struct omap4_cpu_pm_info {
68 struct powerdomain *pwrdm;
69 void __iomem *scu_sar_addr;
70 void __iomem *wkup_sar_addr;
71 void __iomem *l2x0_sar_addr;
72};
73
74static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
75static struct powerdomain *mpuss_pd;
76static void __iomem *sar_base;
77
78/*
79 * Program the wakeup routine address for the CPU0 and CPU1
80 * used for OFF or DORMANT wakeup.
81 */
82static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr)
83{
84 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
85
86 __raw_writel(addr, pm_info->wkup_sar_addr);
87}
88
89/*
90 * Set the CPUx powerdomain's previous power state
91 */
92static inline void set_cpu_next_pwrst(unsigned int cpu_id,
93 unsigned int power_state)
94{
95 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
96
97 pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
98}
99
100/*
101 * Read CPU's previous power state
102 */
103static inline unsigned int read_cpu_prev_pwrst(unsigned int cpu_id)
104{
105 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
106
107 return pwrdm_read_prev_pwrst(pm_info->pwrdm);
108}
109
110/*
111 * Clear the CPUx powerdomain's previous power state
112 */
113static inline void clear_cpu_prev_pwrst(unsigned int cpu_id)
114{
115 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
116
117 pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
118}
119
120/*
121 * Store the SCU power status value to scratchpad memory
122 */
123static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
124{
125 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
126 u32 scu_pwr_st;
127
128 switch (cpu_state) {
129 case PWRDM_POWER_RET:
130 scu_pwr_st = SCU_PM_DORMANT;
131 break;
132 case PWRDM_POWER_OFF:
133 scu_pwr_st = SCU_PM_POWEROFF;
134 break;
135 case PWRDM_POWER_ON:
136 case PWRDM_POWER_INACTIVE:
137 default:
138 scu_pwr_st = SCU_PM_NORMAL;
139 break;
140 }
141
142 __raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
143}
144
145/* Helper functions for MPUSS OSWR */
146static inline void mpuss_clear_prev_logic_pwrst(void)
147{
148 u32 reg;
149
150 reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
151 OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
152 omap4_prminst_write_inst_reg(reg, OMAP4430_PRM_PARTITION,
153 OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
154}
155
156static inline void cpu_clear_prev_logic_pwrst(unsigned int cpu_id)
157{
158 u32 reg;
159
160 if (cpu_id) {
161 reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU1_INST,
162 OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET);
163 omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU1_INST,
164 OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET);
165 } else {
166 reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU0_INST,
167 OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET);
168 omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU0_INST,
169 OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET);
170 }
171}
172
173/**
174 * omap4_mpuss_read_prev_context_state:
175 * Function returns the MPUSS previous context state
176 */
177u32 omap4_mpuss_read_prev_context_state(void)
178{
179 u32 reg;
180
181 reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
182 OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
183 reg &= OMAP4430_LOSTCONTEXT_DFF_MASK;
184 return reg;
185}
186
187/*
188 * Store the CPU cluster state for L2X0 low power operations.
189 */
190static void l2x0_pwrst_prepare(unsigned int cpu_id, unsigned int save_state)
191{
192 struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
193
194 __raw_writel(save_state, pm_info->l2x0_sar_addr);
195}
196
197/*
198 * Save the L2X0 AUXCTRL and POR value to SAR memory. Its used to
199 * in every restore MPUSS OFF path.
200 */
201#ifdef CONFIG_CACHE_L2X0
202static void save_l2x0_context(void)
203{
204 u32 val;
205 void __iomem *l2x0_base = omap4_get_l2cache_base();
206
207 val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
208 __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
209 val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
210 __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
211}
212#else
213static void save_l2x0_context(void)
214{}
215#endif
216
217/**
218 * omap4_enter_lowpower: OMAP4 MPUSS Low Power Entry Function
219 * The purpose of this function is to manage low power programming
220 * of OMAP4 MPUSS subsystem
221 * @cpu : CPU ID
222 * @power_state: Low power state.
223 *
224 * MPUSS states for the context save:
225 * save_state =
226 * 0 - Nothing lost and no need to save: MPUSS INACTIVE
227 * 1 - CPUx L1 and logic lost: MPUSS CSWR
228 * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
229 * 3 - CPUx L1 and logic lost + GIC + L2 lost: DEVICE OFF
230 */
231int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
232{
233 unsigned int save_state = 0;
234 unsigned int wakeup_cpu;
235
236 if (omap_rev() == OMAP4430_REV_ES1_0)
237 return -ENXIO;
238
239 switch (power_state) {
240 case PWRDM_POWER_ON:
241 case PWRDM_POWER_INACTIVE:
242 save_state = 0;
243 break;
244 case PWRDM_POWER_OFF:
245 save_state = 1;
246 break;
247 case PWRDM_POWER_RET:
248 default:
249 /*
250 * CPUx CSWR is invalid hardware state. Also CPUx OSWR
251 * doesn't make much scense, since logic is lost and $L1
252 * needs to be cleaned because of coherency. This makes
253 * CPUx OSWR equivalent to CPUX OFF and hence not supported
254 */
255 WARN_ON(1);
256 return -ENXIO;
257 }
258
259 pwrdm_pre_transition();
260
261 /*
262 * Check MPUSS next state and save interrupt controller if needed.
263 * In MPUSS OSWR or device OFF, interrupt controller contest is lost.
264 */
265 mpuss_clear_prev_logic_pwrst();
266 pwrdm_clear_all_prev_pwrst(mpuss_pd);
267 if ((pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_RET) &&
268 (pwrdm_read_logic_retst(mpuss_pd) == PWRDM_POWER_OFF))
269 save_state = 2;
270
271 clear_cpu_prev_pwrst(cpu);
272 cpu_clear_prev_logic_pwrst(cpu);
273 set_cpu_next_pwrst(cpu, power_state);
274 set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume));
275 scu_pwrst_prepare(cpu, power_state);
276 l2x0_pwrst_prepare(cpu, save_state);
277
278 /*
279 * Call low level function with targeted low power state.
280 */
281 cpu_suspend(save_state, omap4_finish_suspend);
282
283 /*
284 * Restore the CPUx power state to ON otherwise CPUx
285 * power domain can transitions to programmed low power
286 * state while doing WFI outside the low powe code. On
287 * secure devices, CPUx does WFI which can result in
288 * domain transition
289 */
290 wakeup_cpu = smp_processor_id();
291 set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
292
293 pwrdm_post_transition();
294
295 return 0;
296}
297
298/**
299 * omap4_hotplug_cpu: OMAP4 CPU hotplug entry
300 * @cpu : CPU ID
301 * @power_state: CPU low power state.
302 */
303int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
304{
305 unsigned int cpu_state = 0;
306
307 if (omap_rev() == OMAP4430_REV_ES1_0)
308 return -ENXIO;
309
310 if (power_state == PWRDM_POWER_OFF)
311 cpu_state = 1;
312
313 clear_cpu_prev_pwrst(cpu);
314 set_cpu_next_pwrst(cpu, power_state);
315 set_cpu_wakeup_addr(cpu, virt_to_phys(omap_secondary_startup));
316 scu_pwrst_prepare(cpu, power_state);
317
318 /*
319 * CPU never retuns back if targetted power state is OFF mode.
320 * CPU ONLINE follows normal CPU ONLINE ptah via
321 * omap_secondary_startup().
322 */
323 omap4_finish_suspend(cpu_state);
324
325 set_cpu_next_pwrst(cpu, PWRDM_POWER_ON);
326 return 0;
327}
328
329
330/*
331 * Initialise OMAP4 MPUSS
332 */
333int __init omap4_mpuss_init(void)
334{
335 struct omap4_cpu_pm_info *pm_info;
336
337 if (omap_rev() == OMAP4430_REV_ES1_0) {
338 WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
339 return -ENODEV;
340 }
341
342 sar_base = omap4_get_sar_ram_base();
343
344 /* Initilaise per CPU PM information */
345 pm_info = &per_cpu(omap4_pm_info, 0x0);
346 pm_info->scu_sar_addr = sar_base + SCU_OFFSET0;
347 pm_info->wkup_sar_addr = sar_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
348 pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET0;
349 pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
350 if (!pm_info->pwrdm) {
351 pr_err("Lookup failed for CPU0 pwrdm\n");
352 return -ENODEV;
353 }
354
355 /* Clear CPU previous power domain state */
356 pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
357 cpu_clear_prev_logic_pwrst(0);
358
359 /* Initialise CPU0 power domain state to ON */
360 pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
361
362 pm_info = &per_cpu(omap4_pm_info, 0x1);
363 pm_info->scu_sar_addr = sar_base + SCU_OFFSET1;
364 pm_info->wkup_sar_addr = sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
365 pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET1;
366 pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm");
367 if (!pm_info->pwrdm) {
368 pr_err("Lookup failed for CPU1 pwrdm\n");
369 return -ENODEV;
370 }
371
372 /* Clear CPU previous power domain state */
373 pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
374 cpu_clear_prev_logic_pwrst(1);
375
376 /* Initialise CPU1 power domain state to ON */
377 pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
378
379 mpuss_pd = pwrdm_lookup("mpu_pwrdm");
380 if (!mpuss_pd) {
381 pr_err("Failed to lookup MPUSS power domain\n");
382 return -ENODEV;
383 }
384 pwrdm_clear_all_prev_pwrst(mpuss_pd);
385 mpuss_clear_prev_logic_pwrst();
386
387 /* Save device type on scratchpad for low level code to use */
388 if (omap_type() != OMAP2_DEVICE_TYPE_GP)
389 __raw_writel(1, sar_base + OMAP_TYPE_OFFSET);
390 else
391 __raw_writel(0, sar_base + OMAP_TYPE_OFFSET);
392
393 save_l2x0_context();
394
395 return 0;
396}
397
398#endif
diff --git a/arch/arm/mach-omap2/omap-secure.c b/arch/arm/mach-omap2/omap-secure.c
new file mode 100644
index 000000000000..69f3c72d959b
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-secure.c
@@ -0,0 +1,81 @@
1/*
2 * OMAP Secure API infrastructure.
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 *
7 *
8 * This program is free software,you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/io.h>
16#include <linux/memblock.h>
17
18#include <asm/cacheflush.h>
19
20#include <mach/omap-secure.h>
21
22static phys_addr_t omap_secure_memblock_base;
23
24/**
25 * omap_sec_dispatcher: Routine to dispatch low power secure
26 * service routines
27 * @idx: The HAL API index
28 * @flag: The flag indicating criticality of operation
29 * @nargs: Number of valid arguments out of four.
30 * @arg1, arg2, arg3 args4: Parameters passed to secure API
31 *
32 * Return the non-zero error value on failure.
33 */
34u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs, u32 arg1, u32 arg2,
35 u32 arg3, u32 arg4)
36{
37 u32 ret;
38 u32 param[5];
39
40 param[0] = nargs;
41 param[1] = arg1;
42 param[2] = arg2;
43 param[3] = arg3;
44 param[4] = arg4;
45
46 /*
47 * Secure API needs physical address
48 * pointer for the parameters
49 */
50 flush_cache_all();
51 outer_clean_range(__pa(param), __pa(param + 5));
52 ret = omap_smc2(idx, flag, __pa(param));
53
54 return ret;
55}
56
57/* Allocate the memory to save secure ram */
58int __init omap_secure_ram_reserve_memblock(void)
59{
60 phys_addr_t paddr;
61 u32 size = OMAP_SECURE_RAM_STORAGE;
62
63 size = ALIGN(size, SZ_1M);
64 paddr = memblock_alloc(size, SZ_1M);
65 if (!paddr) {
66 pr_err("%s: failed to reserve %x bytes\n",
67 __func__, size);
68 return -ENOMEM;
69 }
70 memblock_free(paddr, size);
71 memblock_remove(paddr, size);
72
73 omap_secure_memblock_base = paddr;
74
75 return 0;
76}
77
78phys_addr_t omap_secure_ram_mempool_base(void)
79{
80 return omap_secure_memblock_base;
81}
diff --git a/arch/arm/mach-omap2/omap44xx-smc.S b/arch/arm/mach-omap2/omap-smc.S
index e69d37d95204..f6441c13cd8c 100644
--- a/arch/arm/mach-omap2/omap44xx-smc.S
+++ b/arch/arm/mach-omap2/omap-smc.S
@@ -31,6 +31,29 @@ ENTRY(omap_smc1)
31 ldmfd sp!, {r2-r12, pc} 31 ldmfd sp!, {r2-r12, pc}
32ENDPROC(omap_smc1) 32ENDPROC(omap_smc1)
33 33
34/**
35 * u32 omap_smc2(u32 id, u32 falg, u32 pargs)
36 * Low level common routine for secure HAL and PPA APIs.
37 * @id: Application ID of HAL APIs
38 * @flag: Flag to indicate the criticality of operation
39 * @pargs: Physical address of parameter list starting
40 * with number of parametrs
41 */
42ENTRY(omap_smc2)
43 stmfd sp!, {r4-r12, lr}
44 mov r3, r2
45 mov r2, r1
46 mov r1, #0x0 @ Process ID
47 mov r6, #0xff
48 mov r12, #0x00 @ Secure Service ID
49 mov r7, #0
50 mcr p15, 0, r7, c7, c5, 6
51 dsb
52 dmb
53 smc #0
54 ldmfd sp!, {r4-r12, pc}
55ENDPROC(omap_smc2)
56
34ENTRY(omap_modify_auxcoreboot0) 57ENTRY(omap_modify_auxcoreboot0)
35 stmfd sp!, {r1-r12, lr} 58 stmfd sp!, {r1-r12, lr}
36 ldr r12, =0x104 59 ldr r12, =0x104
diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c
index e99bc6cd4714..c1bf3ef0ba02 100644
--- a/arch/arm/mach-omap2/omap-smp.c
+++ b/arch/arm/mach-omap2/omap-smp.c
@@ -24,17 +24,37 @@
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
31#include "clockdomain.h"
32
30/* SCU base address */ 33/* SCU base address */
31static void __iomem *scu_base; 34static void __iomem *scu_base;
32 35
33static DEFINE_SPINLOCK(boot_lock); 36static DEFINE_SPINLOCK(boot_lock);
34 37
38void __iomem *omap4_get_scu_base(void)
39{
40 return scu_base;
41}
42
35void __cpuinit platform_secondary_init(unsigned int cpu) 43void __cpuinit platform_secondary_init(unsigned int cpu)
36{ 44{
37 /* 45 /*
46 * Configure ACTRL and enable NS SMP bit access on CPU1 on HS device.
47 * OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA
48 * init and for CPU1, a secure PPA API provided. CPU0 must be ON
49 * while executing NS_SMP API on CPU1 and PPA version must be 1.4.0+.
50 * OMAP443X GP devices- SMP bit isn't accessible.
51 * OMAP446X GP devices - SMP bit access is enabled on both CPUs.
52 */
53 if (cpu_is_omap443x() && (omap_type() != OMAP2_DEVICE_TYPE_GP))
54 omap_secure_dispatcher(OMAP4_PPA_CPU_ACTRL_SMP_INDEX,
55 4, 0, 0, 0, 0, 0);
56
57 /*
38 * If any interrupts are already enabled for the primary 58 * If any interrupts are already enabled for the primary
39 * core (e.g. timer irq), then they will not have been enabled 59 * core (e.g. timer irq), then they will not have been enabled
40 * for us: do so 60 * for us: do so
@@ -50,6 +70,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
50 70
51int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) 71int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
52{ 72{
73 static struct clockdomain *cpu1_clkdm;
74 static bool booted;
53 /* 75 /*
54 * Set synchronisation state between this boot processor 76 * Set synchronisation state between this boot processor
55 * and the secondary one 77 * and the secondary one
@@ -65,6 +87,29 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
65 omap_modify_auxcoreboot0(0x200, 0xfffffdff); 87 omap_modify_auxcoreboot0(0x200, 0xfffffdff);
66 flush_cache_all(); 88 flush_cache_all();
67 smp_wmb(); 89 smp_wmb();
90
91 if (!cpu1_clkdm)
92 cpu1_clkdm = clkdm_lookup("mpu1_clkdm");
93
94 /*
95 * The SGI(Software Generated Interrupts) are not wakeup capable
96 * from low power states. This is known limitation on OMAP4 and
97 * needs to be worked around by using software forced clockdomain
98 * wake-up. To wakeup CPU1, CPU0 forces the CPU1 clockdomain to
99 * software force wakeup. The clockdomain is then put back to
100 * hardware supervised mode.
101 * More details can be found in OMAP4430 TRM - Version J
102 * Section :
103 * 4.3.4.2 Power States of CPU0 and CPU1
104 */
105 if (booted) {
106 clkdm_wakeup(cpu1_clkdm);
107 clkdm_allow_idle(cpu1_clkdm);
108 } else {
109 dsb_sev();
110 booted = true;
111 }
112
68 gic_raise_softirq(cpumask_of(cpu), 1); 113 gic_raise_softirq(cpumask_of(cpu), 1);
69 114
70 /* 115 /*
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
new file mode 100644
index 000000000000..d3d8971d7f30
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -0,0 +1,389 @@
1/*
2 * OMAP WakeupGen Source file
3 *
4 * OMAP WakeupGen is the interrupt controller extension used along
5 * with ARM GIC to wake the CPU out from low power states on
6 * external interrupts. It is responsible for generating wakeup
7 * event from the incoming interrupts and enable bits. It is
8 * implemented in MPU always ON power domain. During normal operation,
9 * WakeupGen delivers external interrupts directly to the GIC.
10 *
11 * Copyright (C) 2011 Texas Instruments, Inc.
12 * Santosh Shilimkar <santosh.shilimkar@ti.com>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
17 */
18
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/io.h>
22#include <linux/irq.h>
23#include <linux/platform_device.h>
24#include <linux/cpu.h>
25#include <linux/notifier.h>
26#include <linux/cpu_pm.h>
27
28#include <asm/hardware/gic.h>
29
30#include <mach/omap-wakeupgen.h>
31#include <mach/omap-secure.h>
32
33#include "omap4-sar-layout.h"
34#include "common.h"
35
36#define NR_REG_BANKS 4
37#define MAX_IRQS 128
38#define WKG_MASK_ALL 0x00000000
39#define WKG_UNMASK_ALL 0xffffffff
40#define CPU_ENA_OFFSET 0x400
41#define CPU0_ID 0x0
42#define CPU1_ID 0x1
43
44static void __iomem *wakeupgen_base;
45static void __iomem *sar_base;
46static DEFINE_PER_CPU(u32 [NR_REG_BANKS], irqmasks);
47static DEFINE_SPINLOCK(wakeupgen_lock);
48static unsigned int irq_target_cpu[NR_IRQS];
49
50/*
51 * Static helper functions.
52 */
53static inline u32 wakeupgen_readl(u8 idx, u32 cpu)
54{
55 return __raw_readl(wakeupgen_base + OMAP_WKG_ENB_A_0 +
56 (cpu * CPU_ENA_OFFSET) + (idx * 4));
57}
58
59static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu)
60{
61 __raw_writel(val, wakeupgen_base + OMAP_WKG_ENB_A_0 +
62 (cpu * CPU_ENA_OFFSET) + (idx * 4));
63}
64
65static inline void sar_writel(u32 val, u32 offset, u8 idx)
66{
67 __raw_writel(val, sar_base + offset + (idx * 4));
68}
69
70static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
71{
72 u8 i;
73
74 for (i = 0; i < NR_REG_BANKS; i++)
75 wakeupgen_writel(reg, i, cpu);
76}
77
78static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index)
79{
80 unsigned int spi_irq;
81
82 /*
83 * PPIs and SGIs are not supported.
84 */
85 if (irq < OMAP44XX_IRQ_GIC_START)
86 return -EINVAL;
87
88 /*
89 * Subtract the GIC offset.
90 */
91 spi_irq = irq - OMAP44XX_IRQ_GIC_START;
92 if (spi_irq > MAX_IRQS) {
93 pr_err("omap wakeupGen: Invalid IRQ%d\n", irq);
94 return -EINVAL;
95 }
96
97 /*
98 * Each WakeupGen register controls 32 interrupt.
99 * i.e. 1 bit per SPI IRQ
100 */
101 *reg_index = spi_irq >> 5;
102 *bit_posn = spi_irq %= 32;
103
104 return 0;
105}
106
107static void _wakeupgen_clear(unsigned int irq, unsigned int cpu)
108{
109 u32 val, bit_number;
110 u8 i;
111
112 if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
113 return;
114
115 val = wakeupgen_readl(i, cpu);
116 val &= ~BIT(bit_number);
117 wakeupgen_writel(val, i, cpu);
118}
119
120static void _wakeupgen_set(unsigned int irq, unsigned int cpu)
121{
122 u32 val, bit_number;
123 u8 i;
124
125 if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
126 return;
127
128 val = wakeupgen_readl(i, cpu);
129 val |= BIT(bit_number);
130 wakeupgen_writel(val, i, cpu);
131}
132
133static void _wakeupgen_save_masks(unsigned int cpu)
134{
135 u8 i;
136
137 for (i = 0; i < NR_REG_BANKS; i++)
138 per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu);
139}
140
141static void _wakeupgen_restore_masks(unsigned int cpu)
142{
143 u8 i;
144
145 for (i = 0; i < NR_REG_BANKS; i++)
146 wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu);
147}
148
149/*
150 * Architecture specific Mask extension
151 */
152static void wakeupgen_mask(struct irq_data *d)
153{
154 unsigned long flags;
155
156 spin_lock_irqsave(&wakeupgen_lock, flags);
157 _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]);
158 spin_unlock_irqrestore(&wakeupgen_lock, flags);
159}
160
161/*
162 * Architecture specific Unmask extension
163 */
164static void wakeupgen_unmask(struct irq_data *d)
165{
166 unsigned long flags;
167
168 spin_lock_irqsave(&wakeupgen_lock, flags);
169 _wakeupgen_set(d->irq, irq_target_cpu[d->irq]);
170 spin_unlock_irqrestore(&wakeupgen_lock, flags);
171}
172
173/*
174 * Mask or unmask all interrupts on given CPU.
175 * 0 = Mask all interrupts on the 'cpu'
176 * 1 = Unmask all interrupts on the 'cpu'
177 * Ensure that the initial mask is maintained. This is faster than
178 * iterating through GIC registers to arrive at the correct masks.
179 */
180static void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set)
181{
182 unsigned long flags;
183
184 spin_lock_irqsave(&wakeupgen_lock, flags);
185 if (set) {
186 _wakeupgen_save_masks(cpu);
187 _wakeupgen_set_all(cpu, WKG_MASK_ALL);
188 } else {
189 _wakeupgen_set_all(cpu, WKG_UNMASK_ALL);
190 _wakeupgen_restore_masks(cpu);
191 }
192 spin_unlock_irqrestore(&wakeupgen_lock, flags);
193}
194
195#ifdef CONFIG_CPU_PM
196/*
197 * Save WakeupGen interrupt context in SAR BANK3. Restore is done by
198 * ROM code. WakeupGen IP is integrated along with GIC to manage the
199 * interrupt wakeups from CPU low power states. It manages
200 * masking/unmasking of Shared peripheral interrupts(SPI). So the
201 * interrupt enable/disable control should be in sync and consistent
202 * at WakeupGen and GIC so that interrupts are not lost.
203 */
204static void irq_save_context(void)
205{
206 u32 i, val;
207
208 if (omap_rev() == OMAP4430_REV_ES1_0)
209 return;
210
211 if (!sar_base)
212 sar_base = omap4_get_sar_ram_base();
213
214 for (i = 0; i < NR_REG_BANKS; i++) {
215 /* Save the CPUx interrupt mask for IRQ 0 to 127 */
216 val = wakeupgen_readl(i, 0);
217 sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i);
218 val = wakeupgen_readl(i, 1);
219 sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i);
220
221 /*
222 * Disable the secure interrupts for CPUx. The restore
223 * code blindly restores secure and non-secure interrupt
224 * masks from SAR RAM. Secure interrupts are not suppose
225 * to be enabled from HLOS. So overwrite the SAR location
226 * so that the secure interrupt remains disabled.
227 */
228 sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
229 sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
230 }
231
232 /* Save AuxBoot* registers */
233 val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
234 __raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
235 val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
236 __raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
237
238 /* Save SyncReq generation logic */
239 val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
240 __raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
241 val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
242 __raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
243
244 /* Save SyncReq generation logic */
245 val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_MASK);
246 __raw_writel(val, sar_base + PTMSYNCREQ_MASK_OFFSET);
247 val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_EN);
248 __raw_writel(val, sar_base + PTMSYNCREQ_EN_OFFSET);
249
250 /* Set the Backup Bit Mask status */
251 val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET);
252 val |= SAR_BACKUP_STATUS_WAKEUPGEN;
253 __raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET);
254}
255
256/*
257 * Clear WakeupGen SAR backup status.
258 */
259void irq_sar_clear(void)
260{
261 u32 val;
262 val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET);
263 val &= ~SAR_BACKUP_STATUS_WAKEUPGEN;
264 __raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET);
265}
266
267/*
268 * Save GIC and Wakeupgen interrupt context using secure API
269 * for HS/EMU devices.
270 */
271static void irq_save_secure_context(void)
272{
273 u32 ret;
274 ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX,
275 FLAG_START_CRITICAL,
276 0, 0, 0, 0, 0);
277 if (ret != API_HAL_RET_VALUE_OK)
278 pr_err("GIC and Wakeupgen context save failed\n");
279}
280#endif
281
282#ifdef CONFIG_HOTPLUG_CPU
283static int __cpuinit irq_cpu_hotplug_notify(struct notifier_block *self,
284 unsigned long action, void *hcpu)
285{
286 unsigned int cpu = (unsigned int)hcpu;
287
288 switch (action) {
289 case CPU_ONLINE:
290 wakeupgen_irqmask_all(cpu, 0);
291 break;
292 case CPU_DEAD:
293 wakeupgen_irqmask_all(cpu, 1);
294 break;
295 }
296 return NOTIFY_OK;
297}
298
299static struct notifier_block __refdata irq_hotplug_notifier = {
300 .notifier_call = irq_cpu_hotplug_notify,
301};
302
303static void __init irq_hotplug_init(void)
304{
305 register_hotcpu_notifier(&irq_hotplug_notifier);
306}
307#else
308static void __init irq_hotplug_init(void)
309{}
310#endif
311
312#ifdef CONFIG_CPU_PM
313static int irq_notifier(struct notifier_block *self, unsigned long cmd, void *v)
314{
315 switch (cmd) {
316 case CPU_CLUSTER_PM_ENTER:
317 if (omap_type() == OMAP2_DEVICE_TYPE_GP)
318 irq_save_context();
319 else
320 irq_save_secure_context();
321 break;
322 case CPU_CLUSTER_PM_EXIT:
323 if (omap_type() == OMAP2_DEVICE_TYPE_GP)
324 irq_sar_clear();
325 break;
326 }
327 return NOTIFY_OK;
328}
329
330static struct notifier_block irq_notifier_block = {
331 .notifier_call = irq_notifier,
332};
333
334static void __init irq_pm_init(void)
335{
336 cpu_pm_register_notifier(&irq_notifier_block);
337}
338#else
339static void __init irq_pm_init(void)
340{}
341#endif
342
343/*
344 * Initialise the wakeupgen module.
345 */
346int __init omap_wakeupgen_init(void)
347{
348 int i;
349 unsigned int boot_cpu = smp_processor_id();
350
351 /* Not supported on OMAP4 ES1.0 silicon */
352 if (omap_rev() == OMAP4430_REV_ES1_0) {
353 WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n");
354 return -EPERM;
355 }
356
357 /* Static mapping, never released */
358 wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
359 if (WARN_ON(!wakeupgen_base))
360 return -ENOMEM;
361
362 /* Clear all IRQ bitmasks at wakeupGen level */
363 for (i = 0; i < NR_REG_BANKS; i++) {
364 wakeupgen_writel(0, i, CPU0_ID);
365 wakeupgen_writel(0, i, CPU1_ID);
366 }
367
368 /*
369 * Override GIC architecture specific functions to add
370 * OMAP WakeupGen interrupt controller along with GIC
371 */
372 gic_arch_extn.irq_mask = wakeupgen_mask;
373 gic_arch_extn.irq_unmask = wakeupgen_unmask;
374 gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE;
375
376 /*
377 * FIXME: Add support to set_smp_affinity() once the core
378 * GIC code has necessary hooks in place.
379 */
380
381 /* Associate all the IRQs to boot CPU like GIC init does. */
382 for (i = 0; i < NR_IRQS; i++)
383 irq_target_cpu[i] = boot_cpu;
384
385 irq_hotplug_init();
386 irq_pm_init();
387
388 return 0;
389}
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index beecfdd56ea3..bc16c818c6b7 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -15,18 +15,73 @@
15#include <linux/init.h> 15#include <linux/init.h>
16#include <linux/io.h> 16#include <linux/io.h>
17#include <linux/platform_device.h> 17#include <linux/platform_device.h>
18#include <linux/memblock.h>
18 19
19#include <asm/hardware/gic.h> 20#include <asm/hardware/gic.h>
20#include <asm/hardware/cache-l2x0.h> 21#include <asm/hardware/cache-l2x0.h>
22#include <asm/mach/map.h>
21 23
22#include <plat/irqs.h> 24#include <plat/irqs.h>
25#include <plat/sram.h>
23 26
24#include <mach/hardware.h> 27#include <mach/hardware.h>
28#include <mach/omap-wakeupgen.h>
25 29
26#include "common.h" 30#include "common.h"
31#include "omap4-sar-layout.h"
27 32
28#ifdef CONFIG_CACHE_L2X0 33#ifdef CONFIG_CACHE_L2X0
29void __iomem *l2cache_base; 34static void __iomem *l2cache_base;
35#endif
36
37static void __iomem *sar_ram_base;
38
39#ifdef CONFIG_OMAP4_ERRATA_I688
40/* Used to implement memory barrier on DRAM path */
41#define OMAP4_DRAM_BARRIER_VA 0xfe600000
42
43void __iomem *dram_sync, *sram_sync;
44
45void omap_bus_sync(void)
46{
47 if (dram_sync && sram_sync) {
48 writel_relaxed(readl_relaxed(dram_sync), dram_sync);
49 writel_relaxed(readl_relaxed(sram_sync), sram_sync);
50 isb();
51 }
52}
53
54static int __init omap_barriers_init(void)
55{
56 struct map_desc dram_io_desc[1];
57 phys_addr_t paddr;
58 u32 size;
59
60 if (!cpu_is_omap44xx())
61 return -ENODEV;
62
63 size = ALIGN(PAGE_SIZE, SZ_1M);
64 paddr = memblock_alloc(size, SZ_1M);
65 if (!paddr) {
66 pr_err("%s: failed to reserve 4 Kbytes\n", __func__);
67 return -ENOMEM;
68 }
69 memblock_free(paddr, size);
70 memblock_remove(paddr, size);
71 dram_io_desc[0].virtual = OMAP4_DRAM_BARRIER_VA;
72 dram_io_desc[0].pfn = __phys_to_pfn(paddr);
73 dram_io_desc[0].length = size;
74 dram_io_desc[0].type = MT_MEMORY_SO;
75 iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc));
76 dram_sync = (void __iomem *) dram_io_desc[0].virtual;
77 sram_sync = (void __iomem *) OMAP4_SRAM_VA;
78
79 pr_info("OMAP4: Map 0x%08llx to 0x%08lx for dram barrier\n",
80 (long long) paddr, dram_io_desc[0].virtual);
81
82 return 0;
83}
84core_initcall(omap_barriers_init);
30#endif 85#endif
31 86
32void __init gic_init_irq(void) 87void __init gic_init_irq(void)
@@ -42,11 +97,18 @@ void __init gic_init_irq(void)
42 omap_irq_base = ioremap(OMAP44XX_GIC_CPU_BASE, SZ_512); 97 omap_irq_base = ioremap(OMAP44XX_GIC_CPU_BASE, SZ_512);
43 BUG_ON(!omap_irq_base); 98 BUG_ON(!omap_irq_base);
44 99
100 omap_wakeupgen_init();
101
45 gic_init(0, 29, gic_dist_base_addr, omap_irq_base); 102 gic_init(0, 29, gic_dist_base_addr, omap_irq_base);
46} 103}
47 104
48#ifdef CONFIG_CACHE_L2X0 105#ifdef CONFIG_CACHE_L2X0
49 106
107void __iomem *omap4_get_l2cache_base(void)
108{
109 return l2cache_base;
110}
111
50static void omap4_l2x0_disable(void) 112static void omap4_l2x0_disable(void)
51{ 113{
52 /* Disable PL310 L2 Cache controller */ 114 /* Disable PL310 L2 Cache controller */
@@ -72,7 +134,8 @@ static int __init omap_l2_cache_init(void)
72 134
73 /* Static mapping, never released */ 135 /* Static mapping, never released */
74 l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K); 136 l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K);
75 BUG_ON(!l2cache_base); 137 if (WARN_ON(!l2cache_base))
138 return -ENOMEM;
76 139
77 /* 140 /*
78 * 16-way associativity, parity disabled 141 * 16-way associativity, parity disabled
@@ -112,3 +175,30 @@ static int __init omap_l2_cache_init(void)
112} 175}
113early_initcall(omap_l2_cache_init); 176early_initcall(omap_l2_cache_init);
114#endif 177#endif
178
179void __iomem *omap4_get_sar_ram_base(void)
180{
181 return sar_ram_base;
182}
183
184/*
185 * SAR RAM used to save and restore the HW
186 * context in low power modes
187 */
188static int __init omap4_sar_ram_init(void)
189{
190 /*
191 * To avoid code running on other OMAPs in
192 * multi-omap builds
193 */
194 if (!cpu_is_omap44xx())
195 return -ENOMEM;
196
197 /* Static mapping, never released */
198 sar_ram_base = ioremap(OMAP44XX_SAR_RAM_BASE, SZ_16K);
199 if (WARN_ON(!sar_ram_base))
200 return -ENOMEM;
201
202 return 0;
203}
204early_initcall(omap4_sar_ram_init);
diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h
new file mode 100644
index 000000000000..fe5b545ad443
--- /dev/null
+++ b/arch/arm/mach-omap2/omap4-sar-layout.h
@@ -0,0 +1,50 @@
1/*
2 * omap4-sar-layout.h: OMAP4 SAR RAM layout header file
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#ifndef OMAP_ARCH_OMAP4_SAR_LAYOUT_H
12#define OMAP_ARCH_OMAP4_SAR_LAYOUT_H
13
14/*
15 * SAR BANK offsets from base address OMAP44XX_SAR_RAM_BASE
16 */
17#define SAR_BANK1_OFFSET 0x0000
18#define SAR_BANK2_OFFSET 0x1000
19#define SAR_BANK3_OFFSET 0x2000
20#define SAR_BANK4_OFFSET 0x3000
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#define L2X0_SAVE_OFFSET0 0xd14
27#define L2X0_SAVE_OFFSET1 0xd18
28#define L2X0_AUXCTRL_OFFSET 0xd1c
29#define L2X0_PREFETCH_CTRL_OFFSET 0xd20
30
31/* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK3 */
32#define CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xa04
33#define CPU1_WAKEUP_NS_PA_ADDR_OFFSET 0xa08
34
35#define SAR_BACKUP_STATUS_OFFSET (SAR_BANK3_OFFSET + 0x500)
36#define SAR_SECURE_RAM_SIZE_OFFSET (SAR_BANK3_OFFSET + 0x504)
37#define SAR_SECRAM_SAVED_AT_OFFSET (SAR_BANK3_OFFSET + 0x508)
38
39/* WakeUpGen save restore offset from OMAP44XX_SAR_RAM_BASE */
40#define WAKEUPGENENB_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x684)
41#define WAKEUPGENENB_SECURE_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x694)
42#define WAKEUPGENENB_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0x6a4)
43#define WAKEUPGENENB_SECURE_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0x6b4)
44#define AUXCOREBOOT0_OFFSET (SAR_BANK3_OFFSET + 0x6c4)
45#define AUXCOREBOOT1_OFFSET (SAR_BANK3_OFFSET + 0x6c8)
46#define PTMSYNCREQ_MASK_OFFSET (SAR_BANK3_OFFSET + 0x6cc)
47#define PTMSYNCREQ_EN_OFFSET (SAR_BANK3_OFFSET + 0x6d0)
48#define SAR_BACKUP_STATUS_WAKEUPGEN 0x10
49
50#endif
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 4e166add2f35..b737b11e4499 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -21,6 +21,7 @@ extern void omap_sram_idle(void);
21extern int omap3_can_sleep(void); 21extern int omap3_can_sleep(void);
22extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state); 22extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state);
23extern int omap3_idle_init(void); 23extern int omap3_idle_init(void);
24extern int omap4_idle_init(void);
24 25
25#if defined(CONFIG_PM_OPP) 26#if defined(CONFIG_PM_OPP)
26extern int omap3_opp_init(void); 27extern int omap3_opp_init(void);
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 8edb015f5618..c264ef7219c1 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -1,8 +1,9 @@
1/* 1/*
2 * OMAP4 Power Management Routines 2 * OMAP4 Power Management Routines
3 * 3 *
4 * Copyright (C) 2010 Texas Instruments, Inc. 4 * Copyright (C) 2010-2011 Texas Instruments, Inc.
5 * Rajendra Nayak <rnayak@ti.com> 5 * Rajendra Nayak <rnayak@ti.com>
6 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 * 7 *
7 * This program is free software; you can redistribute it and/or modify 8 * 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 * it under the terms of the GNU General Public License version 2 as
@@ -17,13 +18,16 @@
17#include <linux/slab.h> 18#include <linux/slab.h>
18 19
19#include "common.h" 20#include "common.h"
21#include "clockdomain.h"
20#include "powerdomain.h" 22#include "powerdomain.h"
23#include "pm.h"
21 24
22struct power_state { 25struct power_state {
23 struct powerdomain *pwrdm; 26 struct powerdomain *pwrdm;
24 u32 next_state; 27 u32 next_state;
25#ifdef CONFIG_SUSPEND 28#ifdef CONFIG_SUSPEND
26 u32 saved_state; 29 u32 saved_state;
30 u32 saved_logic_state;
27#endif 31#endif
28 struct list_head node; 32 struct list_head node;
29}; 33};
@@ -33,7 +37,50 @@ static LIST_HEAD(pwrst_list);
33#ifdef CONFIG_SUSPEND 37#ifdef CONFIG_SUSPEND
34static int omap4_pm_suspend(void) 38static int omap4_pm_suspend(void)
35{ 39{
36 do_wfi(); 40 struct power_state *pwrst;
41 int state, ret = 0;
42 u32 cpu_id = smp_processor_id();
43
44 /* Save current powerdomain state */
45 list_for_each_entry(pwrst, &pwrst_list, node) {
46 pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
47 pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm);
48 }
49
50 /* Set targeted power domain states by suspend */
51 list_for_each_entry(pwrst, &pwrst_list, node) {
52 omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
53 pwrdm_set_logic_retst(pwrst->pwrdm, PWRDM_POWER_OFF);
54 }
55
56 /*
57 * For MPUSS to hit power domain retention(CSWR or OSWR),
58 * CPU0 and CPU1 power domains need to be in OFF or DORMANT state,
59 * since CPU power domain CSWR is not supported by hardware
60 * Only master CPU follows suspend path. All other CPUs follow
61 * CPU hotplug path in system wide suspend. On OMAP4, CPU power
62 * domain CSWR is not supported by hardware.
63 * More details can be found in OMAP4430 TRM section 4.3.4.2.
64 */
65 omap4_enter_lowpower(cpu_id, PWRDM_POWER_OFF);
66
67 /* Restore next powerdomain state */
68 list_for_each_entry(pwrst, &pwrst_list, node) {
69 state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
70 if (state > pwrst->next_state) {
71 pr_info("Powerdomain (%s) didn't enter "
72 "target state %d\n",
73 pwrst->pwrdm->name, pwrst->next_state);
74 ret = -1;
75 }
76 omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
77 pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state);
78 }
79 if (ret)
80 pr_crit("Could not enter target state in pm_suspend\n");
81 else
82 pr_info("Successfully put all powerdomains to target state\n");
83
37 return 0; 84 return 0;
38} 85}
39 86
@@ -73,6 +120,22 @@ static const struct platform_suspend_ops omap_pm_ops = {
73}; 120};
74#endif /* CONFIG_SUSPEND */ 121#endif /* CONFIG_SUSPEND */
75 122
123/*
124 * Enable hardware supervised mode for all clockdomains if it's
125 * supported. Initiate sleep transition for other clockdomains, if
126 * they are not used
127 */
128static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
129{
130 if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO)
131 clkdm_allow_idle(clkdm);
132 else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
133 atomic_read(&clkdm->usecount) == 0)
134 clkdm_sleep(clkdm);
135 return 0;
136}
137
138
76static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) 139static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
77{ 140{
78 struct power_state *pwrst; 141 struct power_state *pwrst;
@@ -80,14 +143,48 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
80 if (!pwrdm->pwrsts) 143 if (!pwrdm->pwrsts)
81 return 0; 144 return 0;
82 145
146 /*
147 * Skip CPU0 and CPU1 power domains. CPU1 is programmed
148 * through hotplug path and CPU0 explicitly programmed
149 * further down in the code path
150 */
151 if (!strncmp(pwrdm->name, "cpu", 3))
152 return 0;
153
154 /*
155 * FIXME: Remove this check when core retention is supported
156 * Only MPUSS power domain is added in the list.
157 */
158 if (strcmp(pwrdm->name, "mpu_pwrdm"))
159 return 0;
160
83 pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); 161 pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC);
84 if (!pwrst) 162 if (!pwrst)
85 return -ENOMEM; 163 return -ENOMEM;
164
86 pwrst->pwrdm = pwrdm; 165 pwrst->pwrdm = pwrdm;
87 pwrst->next_state = PWRDM_POWER_ON; 166 pwrst->next_state = PWRDM_POWER_RET;
88 list_add(&pwrst->node, &pwrst_list); 167 list_add(&pwrst->node, &pwrst_list);
89 168
90 return pwrdm_set_next_pwrst(pwrst->pwrdm, pwrst->next_state); 169 return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
170}
171
172/**
173 * omap_default_idle - OMAP4 default ilde routine.'
174 *
175 * Implements OMAP4 memory, IO ordering requirements which can't be addressed
176 * with default arch_idle() hook. Used by all CPUs with !CONFIG_CPUIDLE and
177 * by secondary CPU with CONFIG_CPUIDLE.
178 */
179static void omap_default_idle(void)
180{
181 local_irq_disable();
182 local_fiq_disable();
183
184 omap_do_wfi();
185
186 local_fiq_enable();
187 local_irq_enable();
91} 188}
92 189
93/** 190/**
@@ -99,10 +196,17 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
99static int __init omap4_pm_init(void) 196static int __init omap4_pm_init(void)
100{ 197{
101 int ret; 198 int ret;
199 struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm;
200 struct clockdomain *ducati_clkdm, *l3_2_clkdm, *l4_per_clkdm;
102 201
103 if (!cpu_is_omap44xx()) 202 if (!cpu_is_omap44xx())
104 return -ENODEV; 203 return -ENODEV;
105 204
205 if (omap_rev() == OMAP4430_REV_ES1_0) {
206 WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
207 return -ENODEV;
208 }
209
106 pr_err("Power Management for TI OMAP4.\n"); 210 pr_err("Power Management for TI OMAP4.\n");
107 211
108 ret = pwrdm_for_each(pwrdms_setup, NULL); 212 ret = pwrdm_for_each(pwrdms_setup, NULL);
@@ -111,10 +215,51 @@ static int __init omap4_pm_init(void)
111 goto err2; 215 goto err2;
112 } 216 }
113 217
218 /*
219 * The dynamic dependency between MPUSS -> MEMIF and
220 * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as
221 * expected. The hardware recommendation is to enable static
222 * dependencies for these to avoid system lock ups or random crashes.
223 */
224 mpuss_clkdm = clkdm_lookup("mpuss_clkdm");
225 emif_clkdm = clkdm_lookup("l3_emif_clkdm");
226 l3_1_clkdm = clkdm_lookup("l3_1_clkdm");
227 l3_2_clkdm = clkdm_lookup("l3_2_clkdm");
228 l4_per_clkdm = clkdm_lookup("l4_per_clkdm");
229 ducati_clkdm = clkdm_lookup("ducati_clkdm");
230 if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) ||
231 (!l3_2_clkdm) || (!ducati_clkdm) || (!l4_per_clkdm))
232 goto err2;
233
234 ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm);
235 ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm);
236 ret |= clkdm_add_wkdep(mpuss_clkdm, l3_2_clkdm);
237 ret |= clkdm_add_wkdep(mpuss_clkdm, l4_per_clkdm);
238 ret |= clkdm_add_wkdep(ducati_clkdm, l3_1_clkdm);
239 ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm);
240 if (ret) {
241 pr_err("Failed to add MPUSS -> L3/EMIF/L4PER, DUCATI -> L3 "
242 "wakeup dependency\n");
243 goto err2;
244 }
245
246 ret = omap4_mpuss_init();
247 if (ret) {
248 pr_err("Failed to initialise OMAP4 MPUSS\n");
249 goto err2;
250 }
251
252 (void) clkdm_for_each(clkdms_setup, NULL);
253
114#ifdef CONFIG_SUSPEND 254#ifdef CONFIG_SUSPEND
115 suspend_set_ops(&omap_pm_ops); 255 suspend_set_ops(&omap_pm_ops);
116#endif /* CONFIG_SUSPEND */ 256#endif /* CONFIG_SUSPEND */
117 257
258 /* Overwrite the default arch_idle() */
259 pm_idle = omap_default_idle;
260
261 omap4_idle_init();
262
118err2: 263err2:
119 return ret; 264 return ret;
120} 265}
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
new file mode 100644
index 000000000000..abd283400490
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -0,0 +1,379 @@
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
35ppa_por_params:
36 .word 1, 0
37
38/*
39 * =============================
40 * == CPU suspend finisher ==
41 * =============================
42 *
43 * void omap4_finish_suspend(unsigned long cpu_state)
44 *
45 * This function code saves the CPU context and performs the CPU
46 * power down sequence. Calling WFI effectively changes the CPU
47 * power domains states to the desired target power state.
48 *
49 * @cpu_state : contains context save state (r0)
50 * 0 - No context lost
51 * 1 - CPUx L1 and logic lost: MPUSS CSWR
52 * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR
53 * 3 - CPUx L1 and logic lost + GIC + L2 lost: MPUSS OFF
54 * @return: This function never returns for CPU OFF and DORMANT power states.
55 * Post WFI, CPU transitions to DORMANT or OFF power state and on wake-up
56 * from this follows a full CPU reset path via ROM code to CPU restore code.
57 * The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET.
58 * It returns to the caller for CPU INACTIVE and ON power states or in case
59 * CPU failed to transition to targeted OFF/DORMANT state.
60 */
61ENTRY(omap4_finish_suspend)
62 stmfd sp!, {lr}
63 cmp r0, #0x0
64 beq do_WFI @ No lowpower state, jump to WFI
65
66 /*
67 * Flush all data from the L1 data cache before disabling
68 * SCTLR.C bit.
69 */
70 bl omap4_get_sar_ram_base
71 ldr r9, [r0, #OMAP_TYPE_OFFSET]
72 cmp r9, #0x1 @ Check for HS device
73 bne skip_secure_l1_clean
74 mov r0, #SCU_PM_NORMAL
75 mov r1, #0xFF @ clean seucre L1
76 stmfd r13!, {r4-r12, r14}
77 ldr r12, =OMAP4_MON_SCU_PWR_INDEX
78 DO_SMC
79 ldmfd r13!, {r4-r12, r14}
80skip_secure_l1_clean:
81 bl v7_flush_dcache_all
82
83 /*
84 * Clear the SCTLR.C bit to prevent further data cache
85 * allocation. Clearing SCTLR.C would make all the data accesses
86 * strongly ordered and would not hit the cache.
87 */
88 mrc p15, 0, r0, c1, c0, 0
89 bic r0, r0, #(1 << 2) @ Disable the C bit
90 mcr p15, 0, r0, c1, c0, 0
91 isb
92
93 /*
94 * Invalidate L1 data cache. Even though only invalidate is
95 * necessary exported flush API is used here. Doing clean
96 * on already clean cache would be almost NOP.
97 */
98 bl v7_flush_dcache_all
99
100 /*
101 * Switch the CPU from Symmetric Multiprocessing (SMP) mode
102 * to AsymmetricMultiprocessing (AMP) mode by programming
103 * the SCU power status to DORMANT or OFF mode.
104 * This enables the CPU to be taken out of coherency by
105 * preventing the CPU from receiving cache, TLB, or BTB
106 * maintenance operations broadcast by other CPUs in the cluster.
107 */
108 bl omap4_get_sar_ram_base
109 mov r8, r0
110 ldr r9, [r8, #OMAP_TYPE_OFFSET]
111 cmp r9, #0x1 @ Check for HS device
112 bne scu_gp_set
113 mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR
114 ands r0, r0, #0x0f
115 ldreq r0, [r8, #SCU_OFFSET0]
116 ldrne r0, [r8, #SCU_OFFSET1]
117 mov r1, #0x00
118 stmfd r13!, {r4-r12, r14}
119 ldr r12, =OMAP4_MON_SCU_PWR_INDEX
120 DO_SMC
121 ldmfd r13!, {r4-r12, r14}
122 b skip_scu_gp_set
123scu_gp_set:
124 mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR
125 ands r0, r0, #0x0f
126 ldreq r1, [r8, #SCU_OFFSET0]
127 ldrne r1, [r8, #SCU_OFFSET1]
128 bl omap4_get_scu_base
129 bl scu_power_mode
130skip_scu_gp_set:
131 mrc p15, 0, r0, c1, c1, 2 @ Read NSACR data
132 tst r0, #(1 << 18)
133 mrcne p15, 0, r0, c1, c0, 1
134 bicne r0, r0, #(1 << 6) @ Disable SMP bit
135 mcrne p15, 0, r0, c1, c0, 1
136 isb
137 dsb
138#ifdef CONFIG_CACHE_L2X0
139 /*
140 * Clean and invalidate the L2 cache.
141 * Common cache-l2x0.c functions can't be used here since it
142 * uses spinlocks. We are out of coherency here with data cache
143 * disabled. The spinlock implementation uses exclusive load/store
144 * instruction which can fail without data cache being enabled.
145 * OMAP4 hardware doesn't support exclusive monitor which can
146 * overcome exclusive access issue. Because of this, CPU can
147 * lead to deadlock.
148 */
149 bl omap4_get_sar_ram_base
150 mov r8, r0
151 mrc p15, 0, r5, c0, c0, 5 @ Read MPIDR
152 ands r5, r5, #0x0f
153 ldreq r0, [r8, #L2X0_SAVE_OFFSET0] @ Retrieve L2 state from SAR
154 ldrne r0, [r8, #L2X0_SAVE_OFFSET1] @ memory.
155 cmp r0, #3
156 bne do_WFI
157#ifdef CONFIG_PL310_ERRATA_727915
158 mov r0, #0x03
159 mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
160 DO_SMC
161#endif
162 bl omap4_get_l2cache_base
163 mov r2, r0
164 ldr r0, =0xffff
165 str r0, [r2, #L2X0_CLEAN_INV_WAY]
166wait:
167 ldr r0, [r2, #L2X0_CLEAN_INV_WAY]
168 ldr r1, =0xffff
169 ands r0, r0, r1
170 bne wait
171#ifdef CONFIG_PL310_ERRATA_727915
172 mov r0, #0x00
173 mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
174 DO_SMC
175#endif
176l2x_sync:
177 bl omap4_get_l2cache_base
178 mov r2, r0
179 mov r0, #0x0
180 str r0, [r2, #L2X0_CACHE_SYNC]
181sync:
182 ldr r0, [r2, #L2X0_CACHE_SYNC]
183 ands r0, r0, #0x1
184 bne sync
185#endif
186
187do_WFI:
188 bl omap_do_wfi
189
190 /*
191 * CPU is here when it failed to enter OFF/DORMANT or
192 * no low power state was attempted.
193 */
194 mrc p15, 0, r0, c1, c0, 0
195 tst r0, #(1 << 2) @ Check C bit enabled?
196 orreq r0, r0, #(1 << 2) @ Enable the C bit
197 mcreq p15, 0, r0, c1, c0, 0
198 isb
199
200 /*
201 * Ensure the CPU power state is set to NORMAL in
202 * SCU power state so that CPU is back in coherency.
203 * In non-coherent mode CPU can lock-up and lead to
204 * system deadlock.
205 */
206 mrc p15, 0, r0, c1, c0, 1
207 tst r0, #(1 << 6) @ Check SMP bit enabled?
208 orreq r0, r0, #(1 << 6)
209 mcreq p15, 0, r0, c1, c0, 1
210 isb
211 bl omap4_get_sar_ram_base
212 mov r8, r0
213 ldr r9, [r8, #OMAP_TYPE_OFFSET]
214 cmp r9, #0x1 @ Check for HS device
215 bne scu_gp_clear
216 mov r0, #SCU_PM_NORMAL
217 mov r1, #0x00
218 stmfd r13!, {r4-r12, r14}
219 ldr r12, =OMAP4_MON_SCU_PWR_INDEX
220 DO_SMC
221 ldmfd r13!, {r4-r12, r14}
222 b skip_scu_gp_clear
223scu_gp_clear:
224 bl omap4_get_scu_base
225 mov r1, #SCU_PM_NORMAL
226 bl scu_power_mode
227skip_scu_gp_clear:
228 isb
229 dsb
230 ldmfd sp!, {pc}
231ENDPROC(omap4_finish_suspend)
232
233/*
234 * ============================
235 * == CPU resume entry point ==
236 * ============================
237 *
238 * void omap4_cpu_resume(void)
239 *
240 * ROM code jumps to this function while waking up from CPU
241 * OFF or DORMANT state. Physical address of the function is
242 * stored in the SAR RAM while entering to OFF or DORMANT mode.
243 * The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET.
244 */
245ENTRY(omap4_cpu_resume)
246 /*
247 * Configure ACTRL and enable NS SMP bit access on CPU1 on HS device.
248 * OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA
249 * init and for CPU1, a secure PPA API provided. CPU0 must be ON
250 * while executing NS_SMP API on CPU1 and PPA version must be 1.4.0+.
251 * OMAP443X GP devices- SMP bit isn't accessible.
252 * OMAP446X GP devices - SMP bit access is enabled on both CPUs.
253 */
254 ldr r8, =OMAP44XX_SAR_RAM_BASE
255 ldr r9, [r8, #OMAP_TYPE_OFFSET]
256 cmp r9, #0x1 @ Skip if GP device
257 bne skip_ns_smp_enable
258 mrc p15, 0, r0, c0, c0, 5
259 ands r0, r0, #0x0f
260 beq skip_ns_smp_enable
261ppa_actrl_retry:
262 mov r0, #OMAP4_PPA_CPU_ACTRL_SMP_INDEX
263 adr r3, ppa_zero_params @ Pointer to parameters
264 mov r1, #0x0 @ Process ID
265 mov r2, #0x4 @ Flag
266 mov r6, #0xff
267 mov r12, #0x00 @ Secure Service ID
268 DO_SMC
269 cmp r0, #0x0 @ API returns 0 on success.
270 beq enable_smp_bit
271 b ppa_actrl_retry
272enable_smp_bit:
273 mrc p15, 0, r0, c1, c0, 1
274 tst r0, #(1 << 6) @ Check SMP bit enabled?
275 orreq r0, r0, #(1 << 6)
276 mcreq p15, 0, r0, c1, c0, 1
277 isb
278skip_ns_smp_enable:
279#ifdef CONFIG_CACHE_L2X0
280 /*
281 * Restore the L2 AUXCTRL and enable the L2 cache.
282 * OMAP4_MON_L2X0_AUXCTRL_INDEX = Program the L2X0 AUXCTRL
283 * OMAP4_MON_L2X0_CTRL_INDEX = Enable the L2 using L2X0 CTRL
284 * register r0 contains value to be programmed.
285 * L2 cache is already invalidate by ROM code as part
286 * of MPUSS OFF wakeup path.
287 */
288 ldr r2, =OMAP44XX_L2CACHE_BASE
289 ldr r0, [r2, #L2X0_CTRL]
290 and r0, #0x0f
291 cmp r0, #1
292 beq skip_l2en @ Skip if already enabled
293 ldr r3, =OMAP44XX_SAR_RAM_BASE
294 ldr r1, [r3, #OMAP_TYPE_OFFSET]
295 cmp r1, #0x1 @ Check for HS device
296 bne set_gp_por
297 ldr r0, =OMAP4_PPA_L2_POR_INDEX
298 ldr r1, =OMAP44XX_SAR_RAM_BASE
299 ldr r4, [r1, #L2X0_PREFETCH_CTRL_OFFSET]
300 adr r3, ppa_por_params
301 str r4, [r3, #0x04]
302 mov r1, #0x0 @ Process ID
303 mov r2, #0x4 @ Flag
304 mov r6, #0xff
305 mov r12, #0x00 @ Secure Service ID
306 DO_SMC
307 b set_aux_ctrl
308set_gp_por:
309 ldr r1, =OMAP44XX_SAR_RAM_BASE
310 ldr r0, [r1, #L2X0_PREFETCH_CTRL_OFFSET]
311 ldr r12, =OMAP4_MON_L2X0_PREFETCH_INDEX @ Setup L2 PREFETCH
312 DO_SMC
313set_aux_ctrl:
314 ldr r1, =OMAP44XX_SAR_RAM_BASE
315 ldr r0, [r1, #L2X0_AUXCTRL_OFFSET]
316 ldr r12, =OMAP4_MON_L2X0_AUXCTRL_INDEX @ Setup L2 AUXCTRL
317 DO_SMC
318 mov r0, #0x1
319 ldr r12, =OMAP4_MON_L2X0_CTRL_INDEX @ Enable L2 cache
320 DO_SMC
321skip_l2en:
322#endif
323
324 b cpu_resume @ Jump to generic resume
325ENDPROC(omap4_cpu_resume)
326#endif
327
328#ifndef CONFIG_OMAP4_ERRATA_I688
329ENTRY(omap_bus_sync)
330 mov pc, lr
331ENDPROC(omap_bus_sync)
332#endif
333
334ENTRY(omap_do_wfi)
335 stmfd sp!, {lr}
336 /* Drain interconnect write buffers. */
337 bl omap_bus_sync
338
339 /*
340 * Execute an ISB instruction to ensure that all of the
341 * CP15 register changes have been committed.
342 */
343 isb
344
345 /*
346 * Execute a barrier instruction to ensure that all cache,
347 * TLB and branch predictor maintenance operations issued
348 * by any CPU in the cluster have completed.
349 */
350 dsb
351 dmb
352
353 /*
354 * Execute a WFI instruction and wait until the
355 * STANDBYWFI output is asserted to indicate that the
356 * CPU is in idle and low power state. CPU can specualatively
357 * prefetch the instructions so add NOPs after WFI. Sixteen
358 * NOPs as per Cortex-A9 pipeline.
359 */
360 wfi @ Wait For Interrupt
361 nop
362 nop
363 nop
364 nop
365 nop
366 nop
367 nop
368 nop
369 nop
370 nop
371 nop
372 nop
373 nop
374 nop
375 nop
376 nop
377
378 ldmfd sp!, {pc}
379ENDPROC(omap_do_wfi)
diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c
index 2ee6341fffdb..06383b51e655 100644
--- a/arch/arm/plat-omap/common.c
+++ b/arch/arm/plat-omap/common.c
@@ -22,6 +22,8 @@
22#include <plat/vram.h> 22#include <plat/vram.h>
23#include <plat/dsp.h> 23#include <plat/dsp.h>
24 24
25#include <plat/omap-secure.h>
26
25 27
26#define NO_LENGTH_CHECK 0xffffffff 28#define NO_LENGTH_CHECK 0xffffffff
27 29
@@ -66,6 +68,7 @@ void __init omap_reserve(void)
66 omapfb_reserve_sdram_memblock(); 68 omapfb_reserve_sdram_memblock();
67 omap_vram_reserve_sdram_memblock(); 69 omap_vram_reserve_sdram_memblock();
68 omap_dsp_reserve_sdram_memblock(); 70 omap_dsp_reserve_sdram_memblock();
71 omap_secure_ram_reserve_memblock();
69} 72}
70 73
71void __init omap_init_consistent_dma_size(void) 74void __init omap_init_consistent_dma_size(void)
diff --git a/arch/arm/plat-omap/include/plat/omap-secure.h b/arch/arm/plat-omap/include/plat/omap-secure.h
new file mode 100644
index 000000000000..64f9d1c7f1bb
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/omap-secure.h
@@ -0,0 +1,13 @@
1#ifndef __OMAP_SECURE_H__
2#define __OMAP_SECURE_H__
3
4#include <linux/types.h>
5
6#ifdef CONFIG_ARCH_OMAP2PLUS
7extern int omap_secure_ram_reserve_memblock(void);
8#else
9static inline void omap_secure_ram_reserve_memblock(void)
10{ }
11#endif
12
13#endif /* __OMAP_SECURE_H__ */
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index ea2b8a6306e7..c0d478e55c84 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -45,6 +45,7 @@
45#define OMAP44XX_WKUPGEN_BASE 0x48281000 45#define OMAP44XX_WKUPGEN_BASE 0x48281000
46#define OMAP44XX_MCPDM_BASE 0x40132000 46#define OMAP44XX_MCPDM_BASE 0x40132000
47#define OMAP44XX_MCPDM_L3_BASE 0x49032000 47#define OMAP44XX_MCPDM_L3_BASE 0x49032000
48#define OMAP44XX_SAR_RAM_BASE 0x4a326000
48 49
49#define OMAP44XX_MAILBOX_BASE (L4_44XX_BASE + 0xF4000) 50#define OMAP44XX_MAILBOX_BASE (L4_44XX_BASE + 0xF4000)
50#define OMAP44XX_HSUSB_OTG_BASE (L4_44XX_BASE + 0xAB000) 51#define OMAP44XX_HSUSB_OTG_BASE (L4_44XX_BASE + 0xAB000)
diff --git a/arch/arm/plat-omap/include/plat/sram.h b/arch/arm/plat-omap/include/plat/sram.h
index f500fc34d065..75aa1b2bef51 100644
--- a/arch/arm/plat-omap/include/plat/sram.h
+++ b/arch/arm/plat-omap/include/plat/sram.h
@@ -95,6 +95,10 @@ static inline void omap_push_sram_idle(void) {}
95 */ 95 */
96#define OMAP2_SRAM_PA 0x40200000 96#define OMAP2_SRAM_PA 0x40200000
97#define OMAP3_SRAM_PA 0x40200000 97#define OMAP3_SRAM_PA 0x40200000
98#ifdef CONFIG_OMAP4_ERRATA_I688
99#define OMAP4_SRAM_PA 0x40304000
100#define OMAP4_SRAM_VA 0xfe404000
101#else
98#define OMAP4_SRAM_PA 0x40300000 102#define OMAP4_SRAM_PA 0x40300000
99 103#endif
100#endif 104#endif
diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c
index 8b28664d1c62..ad6a71a00cef 100644
--- a/arch/arm/plat-omap/sram.c
+++ b/arch/arm/plat-omap/sram.c
@@ -40,7 +40,11 @@
40#define OMAP1_SRAM_PA 0x20000000 40#define OMAP1_SRAM_PA 0x20000000
41#define OMAP2_SRAM_PUB_PA (OMAP2_SRAM_PA + 0xf800) 41#define OMAP2_SRAM_PUB_PA (OMAP2_SRAM_PA + 0xf800)
42#define OMAP3_SRAM_PUB_PA (OMAP3_SRAM_PA + 0x8000) 42#define OMAP3_SRAM_PUB_PA (OMAP3_SRAM_PA + 0x8000)
43#ifdef CONFIG_OMAP4_ERRATA_I688
44#define OMAP4_SRAM_PUB_PA OMAP4_SRAM_PA
45#else
43#define OMAP4_SRAM_PUB_PA (OMAP4_SRAM_PA + 0x4000) 46#define OMAP4_SRAM_PUB_PA (OMAP4_SRAM_PA + 0x4000)
47#endif
44 48
45#if defined(CONFIG_ARCH_OMAP2PLUS) 49#if defined(CONFIG_ARCH_OMAP2PLUS)
46#define SRAM_BOOTLOADER_SZ 0x00 50#define SRAM_BOOTLOADER_SZ 0x00
@@ -163,6 +167,10 @@ static void __init omap_map_sram(void)
163 if (omap_sram_size == 0) 167 if (omap_sram_size == 0)
164 return; 168 return;
165 169
170#ifdef CONFIG_OMAP4_ERRATA_I688
171 omap_sram_start += PAGE_SIZE;
172 omap_sram_size -= SZ_16K;
173#endif
166 if (cpu_is_omap34xx()) { 174 if (cpu_is_omap34xx()) {
167 /* 175 /*
168 * SRAM must be marked as non-cached on OMAP3 since the 176 * SRAM must be marked as non-cached on OMAP3 since the