aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/cpuidle44xx.c
diff options
context:
space:
mode:
authorSantosh Shilimkar <santosh.shilimkar@ti.com>2011-08-16 08:01:40 -0400
committerKevin Hilman <khilman@ti.com>2011-12-08 14:29:01 -0500
commit98272660970a71e21ad1992f695f75b780de833c (patch)
tree602ff2dc1ba89f46c195a16bf55116d7197288bf /arch/arm/mach-omap2/cpuidle44xx.c
parent137d105d50f6e6c373c1aa759f59045e6239cf66 (diff)
ARM: OMAP4: PM: Add CPUidle support
Add OMAP4 CPUIDLE support. CPU1 is left with defualt idle and the low power state for it is managed via cpu-hotplug. This patch adds MPUSS low power states in cpuidle. C1 - CPU0 ON + CPU1 ON + MPU ON C2 - CPU0 OFF + CPU1 OFF + MPU CSWR C3 - CPU0 OFF + CPU1 OFF + MPU OSWR OMAP4460 onwards, MPUSS power domain doesn't support OFF state any more anymore just like CORE power domain. The deepest state supported is OSWr. Ofcourse when MPUSS and CORE PD transitions to OSWR along with device off mode, even the memory contemts are lost which is as good as the PD off state. On OMAP4 because of hardware constraints, no low power states are targeted when both CPUs are online and in SMP mode. The low power states are attempted only when secondary CPU gets offline to OFF through hotplug infrastructure. Thanks to Nicole Chalhoub <n-chalhoub@ti.com> for doing exhaustive C-state latency profiling. Signed-off-by: Rajendra Nayak <rnayak@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Jean Pihet <j-pihet@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Vishwanath BS <vishwanath.bs@ti.com> Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'arch/arm/mach-omap2/cpuidle44xx.c')
-rw-r--r--arch/arm/mach-omap2/cpuidle44xx.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
new file mode 100644
index 000000000000..81386c6256eb
--- /dev/null
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -0,0 +1,237 @@
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
18#include <asm/proc-fns.h>
19
20#include "common.h"
21#include "pm.h"
22#include "prm.h"
23
24#ifdef CONFIG_CPU_IDLE
25
26/* Machine specific information to be recorded in the C-state driver_data */
27struct omap4_idle_statedata {
28 u32 cpu_state;
29 u32 mpu_logic_state;
30 u32 mpu_state;
31 u8 valid;
32};
33
34static struct cpuidle_params cpuidle_params_table[] = {
35 /* C1 - CPU0 ON + CPU1 ON + MPU ON */
36 {.exit_latency = 2 + 2 , .target_residency = 5, .valid = 1},
37 /* C2- CPU0 OFF + CPU1 OFF + MPU CSWR */
38 {.exit_latency = 328 + 440 , .target_residency = 960, .valid = 1},
39 /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
40 {.exit_latency = 460 + 518 , .target_residency = 1100, .valid = 1},
41};
42
43#define OMAP4_NUM_STATES ARRAY_SIZE(cpuidle_params_table)
44
45struct omap4_idle_statedata omap4_idle_data[OMAP4_NUM_STATES];
46static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
47
48/**
49 * omap4_enter_idle - Programs OMAP4 to enter the specified state
50 * @dev: cpuidle device
51 * @drv: cpuidle driver
52 * @index: the index of state to be entered
53 *
54 * Called from the CPUidle framework to program the device to the
55 * specified low power state selected by the governor.
56 * Returns the amount of time spent in the low power state.
57 */
58static int omap4_enter_idle(struct cpuidle_device *dev,
59 struct cpuidle_driver *drv,
60 int index)
61{
62 struct omap4_idle_statedata *cx =
63 cpuidle_get_statedata(&dev->states_usage[index]);
64 struct timespec ts_preidle, ts_postidle, ts_idle;
65 u32 cpu1_state;
66 int idle_time;
67 int new_state_idx;
68
69 /* Used to keep track of the total time in idle */
70 getnstimeofday(&ts_preidle);
71
72 local_irq_disable();
73 local_fiq_disable();
74
75 /*
76 * CPU0 has to stay ON (i.e in C1) until CPU1 is OFF state.
77 * This is necessary to honour hardware recommondation
78 * of triggeing all the possible low power modes once CPU1 is
79 * out of coherency and in OFF mode.
80 * Update dev->last_state so that governor stats reflects right
81 * data.
82 */
83 cpu1_state = pwrdm_read_pwrst(cpu1_pd);
84 if (cpu1_state != PWRDM_POWER_OFF) {
85 new_state_idx = drv->safe_state_index;
86 cx = cpuidle_get_statedata(&dev->states_usage[new_state_idx]);
87 }
88
89 /*
90 * Call idle CPU PM enter notifier chain so that
91 * VFP and per CPU interrupt context is saved.
92 */
93 if (cx->cpu_state == PWRDM_POWER_OFF)
94 cpu_pm_enter();
95
96 pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
97 omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
98
99 /*
100 * Call idle CPU cluster PM enter notifier chain
101 * to save GIC and wakeupgen context.
102 */
103 if ((cx->mpu_state == PWRDM_POWER_RET) &&
104 (cx->mpu_logic_state == PWRDM_POWER_OFF))
105 cpu_cluster_pm_enter();
106
107 omap4_enter_lowpower(dev->cpu, cx->cpu_state);
108
109 /*
110 * Call idle CPU PM exit notifier chain to restore
111 * VFP and per CPU IRQ context. Only CPU0 state is
112 * considered since CPU1 is managed by CPU hotplug.
113 */
114 if (pwrdm_read_prev_pwrst(cpu0_pd) == PWRDM_POWER_OFF)
115 cpu_pm_exit();
116
117 /*
118 * Call idle CPU cluster PM exit notifier chain
119 * to restore GIC and wakeupgen context.
120 */
121 if (omap4_mpuss_read_prev_context_state())
122 cpu_cluster_pm_exit();
123
124 getnstimeofday(&ts_postidle);
125 ts_idle = timespec_sub(ts_postidle, ts_preidle);
126
127 local_irq_enable();
128 local_fiq_enable();
129
130 idle_time = ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * \
131 USEC_PER_SEC;
132
133 /* Update cpuidle counters */
134 dev->last_residency = idle_time;
135
136 return index;
137}
138
139DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
140
141struct cpuidle_driver omap4_idle_driver = {
142 .name = "omap4_idle",
143 .owner = THIS_MODULE,
144};
145
146static inline void _fill_cstate(struct cpuidle_driver *drv,
147 int idx, const char *descr)
148{
149 struct cpuidle_state *state = &drv->states[idx];
150
151 state->exit_latency = cpuidle_params_table[idx].exit_latency;
152 state->target_residency = cpuidle_params_table[idx].target_residency;
153 state->flags = CPUIDLE_FLAG_TIME_VALID;
154 state->enter = omap4_enter_idle;
155 sprintf(state->name, "C%d", idx + 1);
156 strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
157}
158
159static inline struct omap4_idle_statedata *_fill_cstate_usage(
160 struct cpuidle_device *dev,
161 int idx)
162{
163 struct omap4_idle_statedata *cx = &omap4_idle_data[idx];
164 struct cpuidle_state_usage *state_usage = &dev->states_usage[idx];
165
166 cx->valid = cpuidle_params_table[idx].valid;
167 cpuidle_set_statedata(state_usage, cx);
168
169 return cx;
170}
171
172
173
174/**
175 * omap4_idle_init - Init routine for OMAP4 idle
176 *
177 * Registers the OMAP4 specific cpuidle driver to the cpuidle
178 * framework with the valid set of states.
179 */
180int __init omap4_idle_init(void)
181{
182 struct omap4_idle_statedata *cx;
183 struct cpuidle_device *dev;
184 struct cpuidle_driver *drv = &omap4_idle_driver;
185 unsigned int cpu_id = 0;
186
187 mpu_pd = pwrdm_lookup("mpu_pwrdm");
188 cpu0_pd = pwrdm_lookup("cpu0_pwrdm");
189 cpu1_pd = pwrdm_lookup("cpu1_pwrdm");
190 if ((!mpu_pd) || (!cpu0_pd) || (!cpu1_pd))
191 return -ENODEV;
192
193
194 drv->safe_state_index = -1;
195 dev = &per_cpu(omap4_idle_dev, cpu_id);
196 dev->cpu = cpu_id;
197
198 /* C1 - CPU0 ON + CPU1 ON + MPU ON */
199 _fill_cstate(drv, 0, "MPUSS ON");
200 drv->safe_state_index = 0;
201 cx = _fill_cstate_usage(dev, 0);
202 cx->valid = 1; /* C1 is always valid */
203 cx->cpu_state = PWRDM_POWER_ON;
204 cx->mpu_state = PWRDM_POWER_ON;
205 cx->mpu_logic_state = PWRDM_POWER_RET;
206
207 /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
208 _fill_cstate(drv, 1, "MPUSS CSWR");
209 cx = _fill_cstate_usage(dev, 1);
210 cx->cpu_state = PWRDM_POWER_OFF;
211 cx->mpu_state = PWRDM_POWER_RET;
212 cx->mpu_logic_state = PWRDM_POWER_RET;
213
214 /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
215 _fill_cstate(drv, 2, "MPUSS OSWR");
216 cx = _fill_cstate_usage(dev, 2);
217 cx->cpu_state = PWRDM_POWER_OFF;
218 cx->mpu_state = PWRDM_POWER_RET;
219 cx->mpu_logic_state = PWRDM_POWER_OFF;
220
221 drv->state_count = OMAP4_NUM_STATES;
222 cpuidle_register_driver(&omap4_idle_driver);
223
224 dev->state_count = OMAP4_NUM_STATES;
225 if (cpuidle_register_device(dev)) {
226 pr_err("%s: CPUidle register device failed\n", __func__);
227 return -EIO;
228 }
229
230 return 0;
231}
232#else
233int __init omap4_idle_init(void)
234{
235 return 0;
236}
237#endif /* CONFIG_CPU_IDLE */