aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/cpuidle/cpuidle.c96
-rw-r--r--include/linux/cpuidle.h19
2 files changed, 94 insertions, 21 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 09d05ab262be..1506d69b3f0f 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -65,6 +65,26 @@ int cpuidle_play_dead(void)
65} 65}
66 66
67/** 67/**
68 * cpuidle_enabled - check if the cpuidle framework is ready
69 * @dev: cpuidle device for this cpu
70 * @drv: cpuidle driver for this cpu
71 *
72 * Return 0 on success, otherwise:
73 * -NODEV : the cpuidle framework is not available
74 * -EBUSY : the cpuidle framework is not initialized
75 */
76int cpuidle_enabled(struct cpuidle_driver *drv, struct cpuidle_device *dev)
77{
78 if (off || !initialized)
79 return -ENODEV;
80
81 if (!drv || !dev || !dev->enabled)
82 return -EBUSY;
83
84 return 0;
85}
86
87/**
68 * cpuidle_enter_state - enter the state and update stats 88 * cpuidle_enter_state - enter the state and update stats
69 * @dev: cpuidle device for this cpu 89 * @dev: cpuidle device for this cpu
70 * @drv: cpuidle driver for this cpu 90 * @drv: cpuidle driver for this cpu
@@ -108,6 +128,51 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
108} 128}
109 129
110/** 130/**
131 * cpuidle_select - ask the cpuidle framework to choose an idle state
132 *
133 * @drv: the cpuidle driver
134 * @dev: the cpuidle device
135 *
136 * Returns the index of the idle state.
137 */
138int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
139{
140 return cpuidle_curr_governor->select(drv, dev);
141}
142
143/**
144 * cpuidle_enter - enter into the specified idle state
145 *
146 * @drv: the cpuidle driver tied with the cpu
147 * @dev: the cpuidle device
148 * @index: the index in the idle state table
149 *
150 * Returns the index in the idle state, < 0 in case of error.
151 * The error code depends on the backend driver
152 */
153int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
154 int index)
155{
156 if (cpuidle_state_is_coupled(dev, drv, index))
157 return cpuidle_enter_state_coupled(dev, drv, index);
158 return cpuidle_enter_state(dev, drv, index);
159}
160
161/**
162 * cpuidle_reflect - tell the underlying governor what was the state
163 * we were in
164 *
165 * @dev : the cpuidle device
166 * @index: the index in the idle state table
167 *
168 */
169void cpuidle_reflect(struct cpuidle_device *dev, int index)
170{
171 if (cpuidle_curr_governor->reflect)
172 cpuidle_curr_governor->reflect(dev, index);
173}
174
175/**
111 * cpuidle_idle_call - the main idle loop 176 * cpuidle_idle_call - the main idle loop
112 * 177 *
113 * NOTE: no locks or semaphores should be used here 178 * NOTE: no locks or semaphores should be used here
@@ -116,26 +181,21 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
116int cpuidle_idle_call(void) 181int cpuidle_idle_call(void)
117{ 182{
118 struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); 183 struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
119 struct cpuidle_driver *drv; 184 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
120 int next_state, entered_state; 185 int next_state, entered_state, ret;
121 bool broadcast; 186 bool broadcast;
122 187
123 if (off || !initialized) 188 ret = cpuidle_enabled(drv, dev);
124 return -ENODEV; 189 if (ret < 0)
125 190 return ret;
126 /* check if the device is ready */
127 if (!dev || !dev->enabled)
128 return -EBUSY;
129
130 drv = cpuidle_get_cpu_driver(dev);
131 191
132 /* ask the governor for the next state */ 192 /* ask the governor for the next state */
133 next_state = cpuidle_curr_governor->select(drv, dev); 193 next_state = cpuidle_select(drv, dev);
194
134 if (need_resched()) { 195 if (need_resched()) {
135 dev->last_residency = 0; 196 dev->last_residency = 0;
136 /* give the governor an opportunity to reflect on the outcome */ 197 /* give the governor an opportunity to reflect on the outcome */
137 if (cpuidle_curr_governor->reflect) 198 cpuidle_reflect(dev, next_state);
138 cpuidle_curr_governor->reflect(dev, next_state);
139 local_irq_enable(); 199 local_irq_enable();
140 return 0; 200 return 0;
141 } 201 }
@@ -146,14 +206,9 @@ int cpuidle_idle_call(void)
146 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu)) 206 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu))
147 return -EBUSY; 207 return -EBUSY;
148 208
149
150 trace_cpu_idle_rcuidle(next_state, dev->cpu); 209 trace_cpu_idle_rcuidle(next_state, dev->cpu);
151 210
152 if (cpuidle_state_is_coupled(dev, drv, next_state)) 211 entered_state = cpuidle_enter(drv, dev, next_state);
153 entered_state = cpuidle_enter_state_coupled(dev, drv,
154 next_state);
155 else
156 entered_state = cpuidle_enter_state(dev, drv, next_state);
157 212
158 trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); 213 trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
159 214
@@ -161,8 +216,7 @@ int cpuidle_idle_call(void)
161 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); 216 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
162 217
163 /* give the governor an opportunity to reflect on the outcome */ 218 /* give the governor an opportunity to reflect on the outcome */
164 if (cpuidle_curr_governor->reflect) 219 cpuidle_reflect(dev, entered_state);
165 cpuidle_curr_governor->reflect(dev, entered_state);
166 220
167 return 0; 221 return 0;
168} 222}
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 50fcbb0ac4e7..accc2dd72049 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -119,6 +119,15 @@ struct cpuidle_driver {
119 119
120#ifdef CONFIG_CPU_IDLE 120#ifdef CONFIG_CPU_IDLE
121extern void disable_cpuidle(void); 121extern void disable_cpuidle(void);
122
123extern int cpuidle_enabled(struct cpuidle_driver *drv,
124 struct cpuidle_device *dev);
125extern int cpuidle_select(struct cpuidle_driver *drv,
126 struct cpuidle_device *dev);
127extern int cpuidle_enter(struct cpuidle_driver *drv,
128 struct cpuidle_device *dev, int index);
129extern void cpuidle_reflect(struct cpuidle_device *dev, int index);
130
122extern int cpuidle_idle_call(void); 131extern int cpuidle_idle_call(void);
123extern int cpuidle_register_driver(struct cpuidle_driver *drv); 132extern int cpuidle_register_driver(struct cpuidle_driver *drv);
124extern struct cpuidle_driver *cpuidle_get_driver(void); 133extern struct cpuidle_driver *cpuidle_get_driver(void);
@@ -141,6 +150,16 @@ extern int cpuidle_play_dead(void);
141extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); 150extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
142#else 151#else
143static inline void disable_cpuidle(void) { } 152static inline void disable_cpuidle(void) { }
153static inline int cpuidle_enabled(struct cpuidle_driver *drv,
154 struct cpuidle_device *dev)
155{return -ENODEV; }
156static inline int cpuidle_select(struct cpuidle_driver *drv,
157 struct cpuidle_device *dev)
158{return -ENODEV; }
159static inline int cpuidle_enter(struct cpuidle_driver *drv,
160 struct cpuidle_device *dev, int index)
161{return -ENODEV; }
162static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { }
144static inline int cpuidle_idle_call(void) { return -ENODEV; } 163static inline int cpuidle_idle_call(void) { return -ENODEV; }
145static inline int cpuidle_register_driver(struct cpuidle_driver *drv) 164static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
146{return -ENODEV; } 165{return -ENODEV; }