diff options
| author | Rajagopal Venkat <rajagopal.venkat@linaro.org> | 2012-10-25 19:50:09 -0400 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-14 18:35:04 -0500 |
| commit | 7e6fdd4bad033fa2d73716377b184fa975b0d985 (patch) | |
| tree | 171f13c8aadd833965a9cb0b83a84fe3eb74c1da /drivers/devfreq | |
| parent | 77b67063bb6bce6d475e910d3b886a606d0d91f7 (diff) | |
PM / devfreq: Core updates to support devices which can idle
Prepare devfreq core framework to support devices which
can idle. When device idleness is detected perhaps through
runtime-pm, need some mechanism to suspend devfreq load
monitoring and resume back when device is online. Present
code continues monitoring unless device is removed from
devfreq core.
This patch introduces following design changes,
- use per device work instead of global work to monitor device
load. This enables suspend/resume of device devfreq and
reduces monitoring code complexity.
- decouple delayed work based load monitoring logic from core
by introducing helpers functions to be used by governors. This
provides flexibility for governors either to use delayed work
based monitoring functions or to implement their own mechanism.
- devfreq core interacts with governors via events to perform
specific actions. These events include start/stop devfreq.
This sets ground for adding suspend/resume events.
The devfreq apis are not modified and are kept intact.
Signed-off-by: Rajagopal Venkat <rajagopal.venkat@linaro.org>
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/devfreq')
| -rw-r--r-- | drivers/devfreq/devfreq.c | 442 | ||||
| -rw-r--r-- | drivers/devfreq/governor.h | 11 | ||||
| -rw-r--r-- | drivers/devfreq/governor_performance.c | 16 | ||||
| -rw-r--r-- | drivers/devfreq/governor_powersave.c | 16 | ||||
| -rw-r--r-- | drivers/devfreq/governor_simpleondemand.c | 24 | ||||
| -rw-r--r-- | drivers/devfreq/governor_userspace.c | 23 |
6 files changed, 268 insertions, 264 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b146d76f04cf..1aaf1aeb1f1d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
| @@ -30,17 +30,11 @@ | |||
| 30 | struct class *devfreq_class; | 30 | struct class *devfreq_class; |
| 31 | 31 | ||
| 32 | /* | 32 | /* |
| 33 | * devfreq_work periodically monitors every registered device. | 33 | * devfreq core provides delayed work based load monitoring helper |
| 34 | * The minimum polling interval is one jiffy. The polling interval is | 34 | * functions. Governors can use these or can implement their own |
| 35 | * determined by the minimum polling period among all polling devfreq | 35 | * monitoring mechanism. |
| 36 | * devices. The resolution of polling interval is one jiffy. | ||
| 37 | */ | 36 | */ |
| 38 | static bool polling; | ||
| 39 | static struct workqueue_struct *devfreq_wq; | 37 | static struct workqueue_struct *devfreq_wq; |
| 40 | static struct delayed_work devfreq_work; | ||
| 41 | |||
| 42 | /* wait removing if this is to be removed */ | ||
| 43 | static struct devfreq *wait_remove_device; | ||
| 44 | 38 | ||
| 45 | /* The list of all device-devfreq */ | 39 | /* The list of all device-devfreq */ |
| 46 | static LIST_HEAD(devfreq_list); | 40 | static LIST_HEAD(devfreq_list); |
| @@ -72,6 +66,8 @@ static struct devfreq *find_device_devfreq(struct device *dev) | |||
| 72 | return ERR_PTR(-ENODEV); | 66 | return ERR_PTR(-ENODEV); |
| 73 | } | 67 | } |
| 74 | 68 | ||
| 69 | /* Load monitoring helper functions for governors use */ | ||
| 70 | |||
| 75 | /** | 71 | /** |
| 76 | * update_devfreq() - Reevaluate the device and configure frequency. | 72 | * update_devfreq() - Reevaluate the device and configure frequency. |
| 77 | * @devfreq: the devfreq instance. | 73 | * @devfreq: the devfreq instance. |
| @@ -121,6 +117,152 @@ int update_devfreq(struct devfreq *devfreq) | |||
| 121 | } | 117 | } |
| 122 | 118 | ||
| 123 | /** | 119 | /** |
| 120 | * devfreq_monitor() - Periodically poll devfreq objects. | ||
| 121 | * @work: the work struct used to run devfreq_monitor periodically. | ||
| 122 | * | ||
| 123 | */ | ||
| 124 | static void devfreq_monitor(struct work_struct *work) | ||
| 125 | { | ||
| 126 | int err; | ||
| 127 | struct devfreq *devfreq = container_of(work, | ||
| 128 | struct devfreq, work.work); | ||
| 129 | |||
| 130 | mutex_lock(&devfreq->lock); | ||
| 131 | err = update_devfreq(devfreq); | ||
| 132 | if (err) | ||
| 133 | dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err); | ||
| 134 | |||
| 135 | queue_delayed_work(devfreq_wq, &devfreq->work, | ||
| 136 | msecs_to_jiffies(devfreq->profile->polling_ms)); | ||
| 137 | mutex_unlock(&devfreq->lock); | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * devfreq_monitor_start() - Start load monitoring of devfreq instance | ||
| 142 | * @devfreq: the devfreq instance. | ||
| 143 | * | ||
| 144 | * Helper function for starting devfreq device load monitoing. By | ||
| 145 | * default delayed work based monitoring is supported. Function | ||
| 146 | * to be called from governor in response to DEVFREQ_GOV_START | ||
| 147 | * event when device is added to devfreq framework. | ||
| 148 | */ | ||
| 149 | void devfreq_monitor_start(struct devfreq *devfreq) | ||
| 150 | { | ||
| 151 | INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor); | ||
| 152 | if (devfreq->profile->polling_ms) | ||
| 153 | queue_delayed_work(devfreq_wq, &devfreq->work, | ||
| 154 | msecs_to_jiffies(devfreq->profile->polling_ms)); | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 158 | * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance | ||
| 159 | * @devfreq: the devfreq instance. | ||
| 160 | * | ||
| 161 | * Helper function to stop devfreq device load monitoing. Function | ||
| 162 | * to be called from governor in response to DEVFREQ_GOV_STOP | ||
| 163 | * event when device is removed from devfreq framework. | ||
| 164 | */ | ||
| 165 | void devfreq_monitor_stop(struct devfreq *devfreq) | ||
| 166 | { | ||
| 167 | cancel_delayed_work_sync(&devfreq->work); | ||
| 168 | } | ||
| 169 | |||
| 170 | /** | ||
| 171 | * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance | ||
| 172 | * @devfreq: the devfreq instance. | ||
| 173 | * | ||
| 174 | * Helper function to suspend devfreq device load monitoing. Function | ||
| 175 | * to be called from governor in response to DEVFREQ_GOV_SUSPEND | ||
| 176 | * event or when polling interval is set to zero. | ||
| 177 | * | ||
| 178 | * Note: Though this function is same as devfreq_monitor_stop(), | ||
| 179 | * intentionally kept separate to provide hooks for collecting | ||
| 180 | * transition statistics. | ||
| 181 | */ | ||
| 182 | void devfreq_monitor_suspend(struct devfreq *devfreq) | ||
| 183 | { | ||
| 184 | mutex_lock(&devfreq->lock); | ||
| 185 | if (devfreq->stop_polling) { | ||
| 186 | mutex_unlock(&devfreq->lock); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | |||
| 190 | devfreq->stop_polling = true; | ||
| 191 | mutex_unlock(&devfreq->lock); | ||
| 192 | cancel_delayed_work_sync(&devfreq->work); | ||
| 193 | } | ||
| 194 | |||
| 195 | /** | ||
| 196 | * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance | ||
| 197 | * @devfreq: the devfreq instance. | ||
| 198 | * | ||
| 199 | * Helper function to resume devfreq device load monitoing. Function | ||
| 200 | * to be called from governor in response to DEVFREQ_GOV_RESUME | ||
| 201 | * event or when polling interval is set to non-zero. | ||
| 202 | */ | ||
| 203 | void devfreq_monitor_resume(struct devfreq *devfreq) | ||
| 204 | { | ||
| 205 | mutex_lock(&devfreq->lock); | ||
| 206 | if (!devfreq->stop_polling) | ||
| 207 | goto out; | ||
| 208 | |||
| 209 | if (!delayed_work_pending(&devfreq->work) && | ||
| 210 | devfreq->profile->polling_ms) | ||
| 211 | queue_delayed_work(devfreq_wq, &devfreq->work, | ||
| 212 | msecs_to_jiffies(devfreq->profile->polling_ms)); | ||
| 213 | devfreq->stop_polling = false; | ||
| 214 | |||
| 215 | out: | ||
| 216 | mutex_unlock(&devfreq->lock); | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * devfreq_interval_update() - Update device devfreq monitoring interval | ||
| 221 | * @devfreq: the devfreq instance. | ||
| 222 | * @delay: new polling interval to be set. | ||
| 223 | * | ||
| 224 | * Helper function to set new load monitoring polling interval. Function | ||
| 225 | * to be called from governor in response to DEVFREQ_GOV_INTERVAL event. | ||
| 226 | */ | ||
| 227 | void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay) | ||
| 228 | { | ||
| 229 | unsigned int cur_delay = devfreq->profile->polling_ms; | ||
| 230 | unsigned int new_delay = *delay; | ||
| 231 | |||
| 232 | mutex_lock(&devfreq->lock); | ||
| 233 | devfreq->profile->polling_ms = new_delay; | ||
| 234 | |||
| 235 | if (devfreq->stop_polling) | ||
| 236 | goto out; | ||
| 237 | |||
| 238 | /* if new delay is zero, stop polling */ | ||
| 239 | if (!new_delay) { | ||
| 240 | mutex_unlock(&devfreq->lock); | ||
| 241 | cancel_delayed_work_sync(&devfreq->work); | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | /* if current delay is zero, start polling with new delay */ | ||
| 246 | if (!cur_delay) { | ||
| 247 | queue_delayed_work(devfreq_wq, &devfreq->work, | ||
| 248 | msecs_to_jiffies(devfreq->profile->polling_ms)); | ||
| 249 | goto out; | ||
| 250 | } | ||
| 251 | |||
| 252 | /* if current delay is greater than new delay, restart polling */ | ||
| 253 | if (cur_delay > new_delay) { | ||
| 254 | mutex_unlock(&devfreq->lock); | ||
| 255 | cancel_delayed_work_sync(&devfreq->work); | ||
| 256 | mutex_lock(&devfreq->lock); | ||
| 257 | if (!devfreq->stop_polling) | ||
| 258 | queue_delayed_work(devfreq_wq, &devfreq->work, | ||
| 259 | msecs_to_jiffies(devfreq->profile->polling_ms)); | ||
| 260 | } | ||
