1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * OMAP4+ CPU idle Routines |
4 | * |
5 | * Copyright (C) 2011-2013 Texas Instruments, Inc. |
6 | * Santosh Shilimkar <santosh.shilimkar@ti.com> |
7 | * Rajendra Nayak <rnayak@ti.com> |
8 | */ |
9 | |
10 | #include <linux/sched.h> |
11 | #include <linux/cpuidle.h> |
12 | #include <linux/cpu_pm.h> |
13 | #include <linux/export.h> |
14 | #include <linux/tick.h> |
15 | |
16 | #include <asm/cpuidle.h> |
17 | |
18 | #include "common.h" |
19 | #include "pm.h" |
20 | #include "prm.h" |
21 | #include "soc.h" |
22 | #include "clockdomain.h" |
23 | |
24 | #define MAX_CPUS 2 |
25 | |
26 | /* Machine specific information */ |
27 | struct idle_statedata { |
28 | u32 cpu_state; |
29 | u32 mpu_logic_state; |
30 | u32 mpu_state; |
31 | u32 mpu_state_vote; |
32 | }; |
33 | |
34 | static struct idle_statedata omap4_idle_data[] = { |
35 | { |
36 | .cpu_state = PWRDM_POWER_ON, |
37 | .mpu_state = PWRDM_POWER_ON, |
38 | .mpu_logic_state = PWRDM_POWER_RET, |
39 | }, |
40 | { |
41 | .cpu_state = PWRDM_POWER_OFF, |
42 | .mpu_state = PWRDM_POWER_RET, |
43 | .mpu_logic_state = PWRDM_POWER_RET, |
44 | }, |
45 | { |
46 | .cpu_state = PWRDM_POWER_OFF, |
47 | .mpu_state = PWRDM_POWER_RET, |
48 | .mpu_logic_state = PWRDM_POWER_OFF, |
49 | }, |
50 | }; |
51 | |
52 | static struct idle_statedata omap5_idle_data[] = { |
53 | { |
54 | .cpu_state = PWRDM_POWER_ON, |
55 | .mpu_state = PWRDM_POWER_ON, |
56 | .mpu_logic_state = PWRDM_POWER_ON, |
57 | }, |
58 | { |
59 | .cpu_state = PWRDM_POWER_RET, |
60 | .mpu_state = PWRDM_POWER_RET, |
61 | .mpu_logic_state = PWRDM_POWER_RET, |
62 | }, |
63 | }; |
64 | |
65 | static struct powerdomain *mpu_pd, *cpu_pd[MAX_CPUS]; |
66 | static struct clockdomain *cpu_clkdm[MAX_CPUS]; |
67 | |
68 | static atomic_t abort_barrier; |
69 | static bool cpu_done[MAX_CPUS]; |
70 | static struct idle_statedata *state_ptr = &omap4_idle_data[0]; |
71 | static DEFINE_RAW_SPINLOCK(mpu_lock); |
72 | |
73 | /* Private functions */ |
74 | |
75 | /** |
76 | * omap_enter_idle_[simple/coupled] - OMAP4PLUS cpuidle entry functions |
77 | * @dev: cpuidle device |
78 | * @drv: cpuidle driver |
79 | * @index: the index of state to be entered |
80 | * |
81 | * Called from the CPUidle framework to program the device to the |
82 | * specified low power state selected by the governor. |
83 | * Returns the amount of time spent in the low power state. |
84 | */ |
85 | static int omap_enter_idle_simple(struct cpuidle_device *dev, |
86 | struct cpuidle_driver *drv, |
87 | int index) |
88 | { |
89 | omap_do_wfi(); |
90 | return index; |
91 | } |
92 | |
93 | static int omap_enter_idle_smp(struct cpuidle_device *dev, |
94 | struct cpuidle_driver *drv, |
95 | int index) |
96 | { |
97 | struct idle_statedata *cx = state_ptr + index; |
98 | unsigned long flag; |
99 | |
100 | raw_spin_lock_irqsave(&mpu_lock, flag); |
101 | cx->mpu_state_vote++; |
102 | if (cx->mpu_state_vote == num_online_cpus()) { |
103 | pwrdm_set_logic_retst(pwrdm: mpu_pd, pwrst: cx->mpu_logic_state); |
104 | omap_set_pwrdm_state(pwrdm: mpu_pd, state: cx->mpu_state); |
105 | } |
106 | raw_spin_unlock_irqrestore(&mpu_lock, flag); |
107 | |
108 | omap4_enter_lowpower(cpu: dev->cpu, power_state: cx->cpu_state, rcuidle: true); |
109 | |
110 | raw_spin_lock_irqsave(&mpu_lock, flag); |
111 | if (cx->mpu_state_vote == num_online_cpus()) |
112 | omap_set_pwrdm_state(pwrdm: mpu_pd, PWRDM_POWER_ON); |
113 | cx->mpu_state_vote--; |
114 | raw_spin_unlock_irqrestore(&mpu_lock, flag); |
115 | |
116 | return index; |
117 | } |
118 | |
119 | static int omap_enter_idle_coupled(struct cpuidle_device *dev, |
120 | struct cpuidle_driver *drv, |
121 | int index) |
122 | { |
123 | struct idle_statedata *cx = state_ptr + index; |
124 | u32 mpuss_can_lose_context = 0; |
125 | int error; |
126 | |
127 | /* |
128 | * CPU0 has to wait and stay ON until CPU1 is OFF state. |
129 | * This is necessary to honour hardware recommondation |
130 | * of triggeing all the possible low power modes once CPU1 is |
131 | * out of coherency and in OFF mode. |
132 | */ |
133 | if (dev->cpu == 0 && cpumask_test_cpu(cpu: 1, cpu_online_mask)) { |
134 | while (pwrdm_read_pwrst(pwrdm: cpu_pd[1]) != PWRDM_POWER_OFF) { |
135 | cpu_relax(); |
136 | |
137 | /* |
138 | * CPU1 could have already entered & exited idle |
139 | * without hitting off because of a wakeup |
140 | * or a failed attempt to hit off mode. Check for |
141 | * that here, otherwise we could spin forever |
142 | * waiting for CPU1 off. |
143 | */ |
144 | if (cpu_done[1]) |
145 | goto fail; |
146 | |
147 | } |
148 | } |
149 | |
150 | mpuss_can_lose_context = (cx->mpu_state == PWRDM_POWER_RET) && |
151 | (cx->mpu_logic_state == PWRDM_POWER_OFF); |
152 | |
153 | /* Enter broadcast mode for periodic timers */ |
154 | tick_broadcast_enable(); |
155 | |
156 | /* Enter broadcast mode for one-shot timers */ |
157 | tick_broadcast_enter(); |
158 | |
159 | /* |
160 | * Call idle CPU PM enter notifier chain so that |
161 | * VFP and per CPU interrupt context is saved. |
162 | */ |
163 | error = cpu_pm_enter(); |
164 | if (error) |
165 | goto cpu_pm_out; |
166 | |
167 | if (dev->cpu == 0) { |
168 | pwrdm_set_logic_retst(pwrdm: mpu_pd, pwrst: cx->mpu_logic_state); |
169 | omap_set_pwrdm_state(pwrdm: mpu_pd, state: cx->mpu_state); |
170 | |
171 | /* |
172 | * Call idle CPU cluster PM enter notifier chain |
173 | * to save GIC and wakeupgen context. |
174 | */ |
175 | if (mpuss_can_lose_context) { |
176 | error = cpu_cluster_pm_enter(); |
177 | if (error) { |
178 | index = 0; |
179 | cx = state_ptr + index; |
180 | pwrdm_set_logic_retst(pwrdm: mpu_pd, pwrst: cx->mpu_logic_state); |
181 | omap_set_pwrdm_state(pwrdm: mpu_pd, state: cx->mpu_state); |
182 | mpuss_can_lose_context = 0; |
183 | } |
184 | } |
185 | } |
186 | |
187 | omap4_enter_lowpower(cpu: dev->cpu, power_state: cx->cpu_state, rcuidle: true); |
188 | cpu_done[dev->cpu] = true; |
189 | |
190 | /* Wakeup CPU1 only if it is not offlined */ |
191 | if (dev->cpu == 0 && cpumask_test_cpu(cpu: 1, cpu_online_mask)) { |
192 | |
193 | if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) && |
194 | mpuss_can_lose_context) |
195 | gic_dist_disable(); |
196 | |
197 | clkdm_deny_idle(clkdm: cpu_clkdm[1]); |
198 | omap_set_pwrdm_state(pwrdm: cpu_pd[1], PWRDM_POWER_ON); |
199 | clkdm_allow_idle(clkdm: cpu_clkdm[1]); |
200 | |
201 | if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) && |
202 | mpuss_can_lose_context) { |
203 | while (gic_dist_disabled()) { |
204 | udelay(1); |
205 | cpu_relax(); |
206 | } |
207 | gic_timer_retrigger(); |
208 | } |
209 | } |
210 | |
211 | /* |
212 | * Call idle CPU cluster PM exit notifier chain |
213 | * to restore GIC and wakeupgen context. |
214 | */ |
215 | if (dev->cpu == 0 && mpuss_can_lose_context) |
216 | cpu_cluster_pm_exit(); |
217 | |
218 | /* |
219 | * Call idle CPU PM exit notifier chain to restore |
220 | * VFP and per CPU IRQ context. |
221 | */ |
222 | cpu_pm_exit(); |
223 | |
224 | cpu_pm_out: |
225 | tick_broadcast_exit(); |
226 | |
227 | fail: |
228 | cpuidle_coupled_parallel_barrier(dev, a: &abort_barrier); |
229 | cpu_done[dev->cpu] = false; |
230 | |
231 | return index; |
232 | } |
233 | |
234 | static struct cpuidle_driver omap4_idle_driver = { |
235 | .name = "omap4_idle" , |
236 | .owner = THIS_MODULE, |
237 | .states = { |
238 | { |
239 | /* C1 - CPU0 ON + CPU1 ON + MPU ON */ |
240 | .exit_latency = 2 + 2, |
241 | .target_residency = 5, |
242 | .enter = omap_enter_idle_simple, |
243 | .name = "C1" , |
244 | .desc = "CPUx ON, MPUSS ON" |
245 | }, |
246 | { |
247 | /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */ |
248 | .exit_latency = 328 + 440, |
249 | .target_residency = 960, |
250 | .flags = CPUIDLE_FLAG_COUPLED | |
251 | CPUIDLE_FLAG_RCU_IDLE, |
252 | .enter = omap_enter_idle_coupled, |
253 | .name = "C2" , |
254 | .desc = "CPUx OFF, MPUSS CSWR" , |
255 | }, |
256 | { |
257 | /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */ |
258 | .exit_latency = 460 + 518, |
259 | .target_residency = 1100, |
260 | .flags = CPUIDLE_FLAG_COUPLED | |
261 | CPUIDLE_FLAG_RCU_IDLE, |
262 | .enter = omap_enter_idle_coupled, |
263 | .name = "C3" , |
264 | .desc = "CPUx OFF, MPUSS OSWR" , |
265 | }, |
266 | }, |
267 | .state_count = ARRAY_SIZE(omap4_idle_data), |
268 | .safe_state_index = 0, |
269 | }; |
270 | |
271 | static struct cpuidle_driver omap5_idle_driver = { |
272 | .name = "omap5_idle" , |
273 | .owner = THIS_MODULE, |
274 | .states = { |
275 | { |
276 | /* C1 - CPU0 ON + CPU1 ON + MPU ON */ |
277 | .exit_latency = 2 + 2, |
278 | .target_residency = 5, |
279 | .enter = omap_enter_idle_simple, |
280 | .name = "C1" , |
281 | .desc = "CPUx WFI, MPUSS ON" |
282 | }, |
283 | { |
284 | /* C2 - CPU0 RET + CPU1 RET + MPU CSWR */ |
285 | .exit_latency = 48 + 60, |
286 | .target_residency = 100, |
287 | .flags = CPUIDLE_FLAG_TIMER_STOP | |
288 | CPUIDLE_FLAG_RCU_IDLE, |
289 | .enter = omap_enter_idle_smp, |
290 | .name = "C2" , |
291 | .desc = "CPUx CSWR, MPUSS CSWR" , |
292 | }, |
293 | }, |
294 | .state_count = ARRAY_SIZE(omap5_idle_data), |
295 | .safe_state_index = 0, |
296 | }; |
297 | |
298 | /* Public functions */ |
299 | |
300 | /** |
301 | * omap4_idle_init - Init routine for OMAP4+ idle |
302 | * |
303 | * Registers the OMAP4+ specific cpuidle driver to the cpuidle |
304 | * framework with the valid set of states. |
305 | */ |
306 | int __init omap4_idle_init(void) |
307 | { |
308 | struct cpuidle_driver *idle_driver; |
309 | |
310 | if (soc_is_omap54xx()) { |
311 | state_ptr = &omap5_idle_data[0]; |
312 | idle_driver = &omap5_idle_driver; |
313 | } else { |
314 | state_ptr = &omap4_idle_data[0]; |
315 | idle_driver = &omap4_idle_driver; |
316 | } |
317 | |
318 | mpu_pd = pwrdm_lookup(name: "mpu_pwrdm" ); |
319 | cpu_pd[0] = pwrdm_lookup(name: "cpu0_pwrdm" ); |
320 | cpu_pd[1] = pwrdm_lookup(name: "cpu1_pwrdm" ); |
321 | if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1])) |
322 | return -ENODEV; |
323 | |
324 | cpu_clkdm[0] = clkdm_lookup(name: "mpu0_clkdm" ); |
325 | cpu_clkdm[1] = clkdm_lookup(name: "mpu1_clkdm" ); |
326 | if (!cpu_clkdm[0] || !cpu_clkdm[1]) |
327 | return -ENODEV; |
328 | |
329 | return cpuidle_register(drv: idle_driver, cpu_online_mask); |
330 | } |
331 | |