From 7e6fdd4bad033fa2d73716377b184fa975b0d985 Mon Sep 17 00:00:00 2001 From: Rajagopal Venkat Date: Fri, 26 Oct 2012 01:50:09 +0200 Subject: 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 Acked-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 23d78b5..89283b1 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -21,14 +21,6 @@ Description: The /sys/class/devfreq/.../cur_freq shows the current frequency of the corresponding devfreq object. -What: /sys/class/devfreq/.../central_polling -Date: September 2011 -Contact: MyungJoo Ham -Description: - The /sys/class/devfreq/.../central_polling shows whether - the devfreq ojbect is using devfreq-provided central - polling mechanism or not. - What: /sys/class/devfreq/.../polling_interval Date: September 2011 Contact: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b146d76..1aaf1ae 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -30,17 +30,11 @@ struct class *devfreq_class; /* - * devfreq_work periodically monitors every registered device. - * The minimum polling interval is one jiffy. The polling interval is - * determined by the minimum polling period among all polling devfreq - * devices. The resolution of polling interval is one jiffy. + * devfreq core provides delayed work based load monitoring helper + * functions. Governors can use these or can implement their own + * monitoring mechanism. */ -static bool polling; static struct workqueue_struct *devfreq_wq; -static struct delayed_work devfreq_work; - -/* wait removing if this is to be removed */ -static struct devfreq *wait_remove_device; /* The list of all device-devfreq */ static LIST_HEAD(devfreq_list); @@ -72,6 +66,8 @@ static struct devfreq *find_device_devfreq(struct device *dev) return ERR_PTR(-ENODEV); } +/* Load monitoring helper functions for governors use */ + /** * update_devfreq() - Reevaluate the device and configure frequency. * @devfreq: the devfreq instance. @@ -121,6 +117,152 @@ int update_devfreq(struct devfreq *devfreq) } /** + * devfreq_monitor() - Periodically poll devfreq objects. + * @work: the work struct used to run devfreq_monitor periodically. + * + */ +static void devfreq_monitor(struct work_struct *work) +{ + int err; + struct devfreq *devfreq = container_of(work, + struct devfreq, work.work); + + mutex_lock(&devfreq->lock); + err = update_devfreq(devfreq); + if (err) + dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err); + + queue_delayed_work(devfreq_wq, &devfreq->work, + msecs_to_jiffies(devfreq->profile->polling_ms)); + mutex_unlock(&devfreq->lock); +} + +/** + * devfreq_monitor_start() - Start load monitoring of devfreq instance + * @devfreq: the devfreq instance. + * + * Helper function for starting devfreq device load monitoing. By + * default delayed work based monitoring is supported. Function + * to be called from governor in response to DEVFREQ_GOV_START + * event when device is added to devfreq framework. + */ +void devfreq_monitor_start(struct devfreq *devfreq) +{ + INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor); + if (devfreq->profile->polling_ms) + queue_delayed_work(devfreq_wq, &devfreq->work, + msecs_to_jiffies(devfreq->profile->polling_ms)); +} + +/** + * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance + * @devfreq: the devfreq instance. + * + * Helper function to stop devfreq device load monitoing. Function + * to be called from governor in response to DEVFREQ_GOV_STOP + * event when device is removed from devfreq framework. + */ +void devfreq_monitor_stop(struct devfreq *devfreq) +{ + cancel_delayed_work_sync(&devfreq->work); +} + +/** + * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance + * @devfreq: the devfreq instance. + * + * Helper function to suspend devfreq device load monitoing. Function + * to be called from governor in response to DEVFREQ_GOV_SUSPEND + * event or when polling interval is set to zero. + * + * Note: Though this function is same as devfreq_monitor_stop(), + * intentionally kept separate to provide hooks for collecting + * transition statistics. + */ +void devfreq_monitor_suspend(struct devfreq *devfreq) +{ + mutex_lock(&devfreq->lock); + if (devfreq->stop_polling) { + mutex_unlock(&devfreq->lock); + return; + } + + devfreq->stop_polling = true; + mutex_unlock(&devfreq->lock); + cancel_delayed_work_sync(&devfreq->work); +} + +/** + * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance + * @devfreq: the devfreq instance. + * + * Helper function to resume devfreq device load monitoing. Function + * to be called from governor in response to DEVFREQ_GOV_RESUME + * event or when polling interval is set to non-zero. + */ +void devfreq_monitor_resume(struct devfreq *devfreq) +{ + mutex_lock(&devfreq->lock); + if (!devfreq->stop_polling) + goto out; + + if (!delayed_work_pending(&devfreq->work) && + devfreq->profile->polling_ms) + queue_delayed_work(devfreq_wq, &devfreq->work, + msecs_to_jiffies(devfreq->profile->polling_ms)); + devfreq->stop_polling = false; + +out: + mutex_unlock(&devfreq->lock); +} + +/** + * devfreq_interval_update() - Update device devfreq monitoring interval + * @devfreq: the devfreq instance. + * @delay: new polling interval to be set. + * + * Helper function to set new load monitoring polling interval. Function + * to be called from governor in response to DEVFREQ_GOV_INTERVAL event. + */ +void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay) +{ + unsigned int cur_delay = devfreq->profile->polling_ms; + unsigned int new_delay = *delay; + + mutex_lock(&devfreq->lock); + devfreq->profile->polling_ms = new_delay; + + if (devfreq->stop_polling) + goto out; + + /* if new delay is zero, stop polling */ + if (!new_delay) { + mutex_unlock(&devfreq->lock); + cancel_delayed_work_sync(&devfreq->work); + return; + } + + /* if current delay is zero, start polling with new delay */ + if (!cur_delay) { + queue_delayed_work(devfreq_wq, &devfreq->work, + msecs_to_jiffies(devfreq->profile->polling_ms)); + goto out; + } + + /* if current delay is greater than new delay, restart polling */ + if (cur_delay > new_delay) { + mutex_unlock(&devfreq->lock); + cancel_delayed_work_sync(&devfreq->work); + mutex_lock(&devfreq->lock); + if (!devfreq->stop_polling) + queue_delayed_work(devfreq_wq, &devfreq->work, + msecs_to_jiffies(devfreq->profile->polling_ms)); + } +out: + mutex_unlock(&devfreq->lock); +} + +/** * devfreq_notifier_call() - Notify that the device frequency requirements * has been changed out of devfreq framework. * @nb the notifier_block (supposed to be devfreq->nb) @@ -143,59 +285,32 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, } /** - * _remove_devfreq() - Remove devfreq from the device. + * _remove_devfreq() - Remove devfreq from the list and release its resources. * @devfreq: the devfreq struct * @skip: skip calling device_unregister(). - * - * Note that the caller should lock devfreq->lock before calling - * this. _remove_devfreq() will unlock it and free devfreq - * internally. devfreq_list_lock should be locked by the caller - * as well (not relased at return) - * - * Lock usage: - * devfreq->lock: locked before call. - * unlocked at return (and freed) - * devfreq_list_lock: locked before call. - * kept locked at return. - * if devfreq is centrally polled. - * - * Freed memory: - * devfreq */ static void _remove_devfreq(struct devfreq *devfreq, bool skip) { - if (!mutex_is_locked(&devfreq->lock)) { - WARN(true, "devfreq->lock must be locked by the caller.\n"); - return; - } - if (!devfreq->governor->no_central_polling && - !mutex_is_locked(&devfreq_list_lock)) { - WARN(true, "devfreq_list_lock must be locked by the caller.\n"); + mutex_lock(&devfreq_list_lock); + if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { + mutex_unlock(&devfreq_list_lock); + dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n"); return; } + list_del(&devfreq->node); + mutex_unlock(&devfreq_list_lock); - if (devfreq->being_removed) - return; - - devfreq->being_removed = true; + devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); - if (devfreq->governor->exit) - devfreq->governor->exit(devfreq); - if (!skip && get_device(&devfreq->dev)) { device_unregister(&devfreq->dev); put_device(&devfreq->dev); } - if (!devfreq->governor->no_central_polling) - list_del(&devfreq->node); - - mutex_unlock(&devfreq->lock); mutex_destroy(&devfreq->lock); - kfree(devfreq); } @@ -210,130 +325,8 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) static void devfreq_dev_release(struct device *dev) { struct devfreq *devfreq = to_devfreq(dev); - bool central_polling = !devfreq->governor->no_central_polling; - - /* - * If devfreq_dev_release() was called by device_unregister() of - * _remove_devfreq(), we cannot mutex_lock(&devfreq->lock) and - * being_removed is already set. This also partially checks the case - * where devfreq_dev_release() is called from a thread other than - * the one called _remove_devfreq(); however, this case is - * dealt completely with another following being_removed check. - * - * Because being_removed is never being - * unset, we do not need to worry about race conditions on - * being_removed. - */ - if (devfreq->being_removed) - return; - if (central_polling) - mutex_lock(&devfreq_list_lock); - - mutex_lock(&devfreq->lock); - - /* - * Check being_removed flag again for the case where - * devfreq_dev_release() was called in a thread other than the one - * possibly called _remove_devfreq(). - */ - if (devfreq->being_removed) { - mutex_unlock(&devfreq->lock); - goto out; - } - - /* devfreq->lock is unlocked and removed in _removed_devfreq() */ _remove_devfreq(devfreq, true); - -out: - if (central_polling) - mutex_unlock(&devfreq_list_lock); -} - -/** - * devfreq_monitor() - Periodically poll devfreq objects. - * @work: the work struct used to run devfreq_monitor periodically. - * - */ -static void devfreq_monitor(struct work_struct *work) -{ - static unsigned long last_polled_at; - struct devfreq *devfreq, *tmp; - int error; - unsigned long jiffies_passed; - unsigned long next_jiffies = ULONG_MAX, now = jiffies; - struct device *dev; - - /* Initially last_polled_at = 0, polling every device at bootup */ - jiffies_passed = now - last_polled_at; - last_polled_at = now; - if (jiffies_passed == 0) - jiffies_passed = 1; - - mutex_lock(&devfreq_list_lock); - list_for_each_entry_safe(devfreq, tmp, &devfreq_list, node) { - mutex_lock(&devfreq->lock); - dev = devfreq->dev.parent; - - /* Do not remove tmp for a while */ - wait_remove_device = tmp; - - if (devfreq->governor->no_central_polling || - devfreq->next_polling == 0) { - mutex_unlock(&devfreq->lock); - continue; - } - mutex_unlock(&devfreq_list_lock); - - /* - * Reduce more next_polling if devfreq_wq took an extra - * delay. (i.e., CPU has been idled.) - */ - if (devfreq->next_polling <= jiffies_passed) { - error = update_devfreq(devfreq); - - /* Remove a devfreq with an error. */ - if (error && error != -EAGAIN) { - - dev_err(dev, "Due to update_devfreq error(%d), devfreq(%s) is removed from the device\n", - error, devfreq->governor->name); - - /* - * Unlock devfreq before locking the list - * in order to avoid deadlock with - * find_device_devfreq or others - */ - mutex_unlock(&devfreq->lock); - mutex_lock(&devfreq_list_lock); - /* Check if devfreq is already removed */ - if (IS_ERR(find_device_devfreq(dev))) - continue; - mutex_lock(&devfreq->lock); - /* This unlocks devfreq->lock and free it */ - _remove_devfreq(devfreq, false); - continue; - } - devfreq->next_polling = devfreq->polling_jiffies; - } else { - devfreq->next_polling -= jiffies_passed; - } - - if (devfreq->next_polling) - next_jiffies = (next_jiffies > devfreq->next_polling) ? - devfreq->next_polling : next_jiffies; - - mutex_unlock(&devfreq->lock); - mutex_lock(&devfreq_list_lock); - } - wait_remove_device = NULL; - mutex_unlock(&devfreq_list_lock); - - if (next_jiffies > 0 && next_jiffies < ULONG_MAX) { - polling = true; - queue_delayed_work(devfreq_wq, &devfreq_work, next_jiffies); - } else { - polling = false; - } } /** @@ -357,16 +350,13 @@ struct devfreq *devfreq_add_device(struct device *dev, return ERR_PTR(-EINVAL); } - - if (!governor->no_central_polling) { - mutex_lock(&devfreq_list_lock); - devfreq = find_device_devfreq(dev); - mutex_unlock(&devfreq_list_lock); - if (!IS_ERR(devfreq)) { - dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__); - err = -EINVAL; - goto err_out; - } + mutex_lock(&devfreq_list_lock); + devfreq = find_device_devfreq(dev); + mutex_unlock(&devfreq_list_lock); + if (!IS_ERR(devfreq)) { + dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__); + err = -EINVAL; + goto err_out; } devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL); @@ -386,48 +376,41 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->governor = governor; devfreq->previous_freq = profile->initial_freq; devfreq->data = data; - devfreq->next_polling = devfreq->polling_jiffies - = msecs_to_jiffies(devfreq->profile->polling_ms); devfreq->nb.notifier_call = devfreq_notifier_call; dev_set_name(&devfreq->dev, dev_name(dev)); err = device_register(&devfreq->dev); if (err) { put_device(&devfreq->dev); + mutex_unlock(&devfreq->lock); goto err_dev; } - if (governor->init) - err = governor->init(devfreq); - if (err) - goto err_init; - mutex_unlock(&devfreq->lock); - if (governor->no_central_polling) - goto out; - mutex_lock(&devfreq_list_lock); - list_add(&devfreq->node, &devfreq_list); + mutex_unlock(&devfreq_list_lock); - if (devfreq_wq && devfreq->next_polling && !polling) { - polling = true; - queue_delayed_work(devfreq_wq, &devfreq_work, - devfreq->next_polling); + err = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_START, NULL); + if (err) { + dev_err(dev, "%s: Unable to start governor for the device\n", + __func__); + goto err_init; } - mutex_unlock(&devfreq_list_lock); -out: + return devfreq; err_init: + list_del(&devfreq->node); device_unregister(&devfreq->dev); err_dev: - mutex_unlock(&devfreq->lock); kfree(devfreq); err_out: return ERR_PTR(err); } +EXPORT_SYMBOL(devfreq_add_device); /** * devfreq_remove_device() - Remove devfreq feature from a device. @@ -435,30 +418,14 @@ err_out: */ int devfreq_remove_device(struct devfreq *devfreq) { - bool central_polling; - if (!devfreq) return -EINVAL; - central_polling = !devfreq->governor->no_central_polling; - - if (central_polling) { - mutex_lock(&devfreq_list_lock); - while (wait_remove_device == devfreq) { - mutex_unlock(&devfreq_list_lock); - schedule(); - mutex_lock(&devfreq_list_lock); - } - } - - mutex_lock(&devfreq->lock); - _remove_devfreq(devfreq, false); /* it unlocks devfreq->lock */ - - if (central_polling) - mutex_unlock(&devfreq_list_lock); + _remove_devfreq(devfreq, false); return 0; } +EXPORT_SYMBOL(devfreq_remove_device); static ssize_t show_governor(struct device *dev, struct device_attribute *attr, char *buf) @@ -490,35 +457,13 @@ static ssize_t store_polling_interval(struct device *dev, if (ret != 1) goto out; - mutex_lock(&df->lock); - df->profile->polling_ms = value; - df->next_polling = df->polling_jiffies - = msecs_to_jiffies(value); - mutex_unlock(&df->lock); - + df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value); ret = count; - if (df->governor->no_central_polling) - goto out; - - mutex_lock(&devfreq_list_lock); - if (df->next_polling > 0 && !polling) { - polling = true; - queue_delayed_work(devfreq_wq, &devfreq_work, - df->next_polling); - } - mutex_unlock(&devfreq_list_lock); out: return ret; } -static ssize_t show_central_polling(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", - !to_devfreq(dev)->governor->no_central_polling); -} - static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -590,7 +535,6 @@ static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr, static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO, show_governor, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), - __ATTR(central_polling, S_IRUGO, show_central_polling, NULL), __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, store_polling_interval), __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), @@ -598,23 +542,6 @@ static struct device_attribute devfreq_attrs[] = { { }, }; -/** - * devfreq_start_polling() - Initialize data structure for devfreq framework and - * start polling registered devfreq devices. - */ -static int __init devfreq_start_polling(void) -{ - mutex_lock(&devfreq_list_lock); - polling = false; - devfreq_wq = create_freezable_workqueue("devfreq_wq"); - INIT_DEFERRABLE_WORK(&devfreq_work, devfreq_monitor); - mutex_unlock(&devfreq_list_lock); - - devfreq_monitor(&devfreq_work.work); - return 0; -} -late_initcall(devfreq_start_polling); - static int __init devfreq_init(void) { devfreq_class = class_create(THIS_MODULE, "devfreq"); @@ -622,7 +549,15 @@ static int __init devfreq_init(void) pr_err("%s: couldn't create class\n", __FILE__); return PTR_ERR(devfreq_class); } + + devfreq_wq = create_freezable_workqueue("devfreq_wq"); + if (IS_ERR(devfreq_wq)) { + class_destroy(devfreq_class); + pr_err("%s: couldn't create workqueue\n", __FILE__); + return PTR_ERR(devfreq_wq); + } devfreq_class->dev_attrs = devfreq_attrs; + return 0; } subsys_initcall(devfreq_init); @@ -630,6 +565,7 @@ subsys_initcall(devfreq_init); static void __exit devfreq_exit(void) { class_destroy(devfreq_class); + destroy_workqueue(devfreq_wq); } module_exit(devfreq_exit); diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index ea7f13c..bb3aff3 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -18,7 +18,18 @@ #define to_devfreq(DEV) container_of((DEV), struct devfreq, dev) +/* Devfreq events */ +#define DEVFREQ_GOV_START 0x1 +#define DEVFREQ_GOV_STOP 0x2 +#define DEVFREQ_GOV_INTERVAL 0x3 + /* Caution: devfreq->lock must be locked before calling update_devfreq */ extern int update_devfreq(struct devfreq *devfreq); +extern void devfreq_monitor_start(struct devfreq *devfreq); +extern void devfreq_monitor_stop(struct devfreq *devfreq); +extern void devfreq_monitor_suspend(struct devfreq *devfreq); +extern void devfreq_monitor_resume(struct devfreq *devfreq); +extern void devfreq_interval_update(struct devfreq *devfreq, + unsigned int *delay); #endif /* _GOVERNOR_H */ diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index af75ddd..eea3f9b 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -26,14 +26,22 @@ static int devfreq_performance_func(struct devfreq *df, return 0; } -static int performance_init(struct devfreq *devfreq) +static int devfreq_performance_handler(struct devfreq *devfreq, + unsigned int event, void *data) { - return update_devfreq(devfreq); + int ret = 0; + + if (event == DEVFREQ_GOV_START) { + mutex_lock(&devfreq->lock); + ret = update_devfreq(devfreq); + mutex_unlock(&devfreq->lock); + } + + return ret; } const struct devfreq_governor devfreq_performance = { .name = "performance", - .init = performance_init, .get_target_freq = devfreq_performance_func, - .no_central_polling = true, + .event_handler = devfreq_performance_handler, }; diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index fec0cdb..2868d98 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -23,14 +23,22 @@ static int devfreq_powersave_func(struct devfreq *df, return 0; } -static int powersave_init(struct devfreq *devfreq) +static int devfreq_powersave_handler(struct devfreq *devfreq, + unsigned int event, void *data) { - return update_devfreq(devfreq); + int ret = 0; + + if (event == DEVFREQ_GOV_START) { + mutex_lock(&devfreq->lock); + ret = update_devfreq(devfreq); + mutex_unlock(&devfreq->lock); + } + + return ret; } const struct devfreq_governor devfreq_powersave = { .name = "powersave", - .init = powersave_init, .get_target_freq = devfreq_powersave_func, - .no_central_polling = true, + .event_handler = devfreq_powersave_handler, }; diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index a2e3eae..3716a65 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -12,6 +12,7 @@ #include #include #include +#include "governor.h" /* Default constants for DevFreq-Simple-Ondemand (DFSO) */ #define DFSO_UPTHRESHOLD (90) @@ -88,7 +89,30 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, return 0; } +static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + switch (event) { + case DEVFREQ_GOV_START: + devfreq_monitor_start(devfreq); + break; + + case DEVFREQ_GOV_STOP: + devfreq_monitor_stop(devfreq); + break; + + case DEVFREQ_GOV_INTERVAL: + devfreq_interval_update(devfreq, (unsigned int *)data); + break; + default: + break; + } + + return 0; +} + const struct devfreq_governor devfreq_simple_ondemand = { .name = "simple_ondemand", .get_target_freq = devfreq_simple_ondemand_func, + .event_handler = devfreq_simple_ondemand_handler, }; diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 0681246..7067555 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -116,10 +116,27 @@ static void userspace_exit(struct devfreq *devfreq) devfreq->data = NULL; } +static int devfreq_userspace_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + int ret = 0; + + switch (event) { + case DEVFREQ_GOV_START: + ret = userspace_init(devfreq); + break; + case DEVFREQ_GOV_STOP: + userspace_exit(devfreq); + break; + default: + break; + } + + return ret; +} + const struct devfreq_governor devfreq_userspace = { .name = "userspace", .get_target_freq = devfreq_userspace_func, - .init = userspace_init, - .exit = userspace_exit, - .no_central_polling = true, + .event_handler = devfreq_userspace_handler, }; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 281c72a..9cdffde 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -91,25 +91,18 @@ struct devfreq_dev_profile { * status of the device (load = busy_time / total_time). * If no_central_polling is set, this callback is called * only with update_devfreq() notified by OPP. - * @init Called when the devfreq is being attached to a device - * @exit Called when the devfreq is being removed from a - * device. Governor should stop any internal routines - * before return because related data may be - * freed after exit(). - * @no_central_polling Do not use devfreq's central polling mechanism. - * When this is set, devfreq will not call - * get_target_freq with devfreq_monitor(). However, - * devfreq will call get_target_freq with - * devfreq_update() notified by OPP framework. + * @event_handler Callback for devfreq core framework to notify events + * to governors. Events include per device governor + * init and exit, opp changes out of devfreq, suspend + * and resume of per device devfreq during device idle. * * Note that the callbacks are called with devfreq->lock locked by devfreq. */ struct devfreq_governor { const char name[DEVFREQ_NAME_LEN]; int (*get_target_freq)(struct devfreq *this, unsigned long *freq); - int (*init)(struct devfreq *this); - void (*exit)(struct devfreq *this); - const bool no_central_polling; + int (*event_handler)(struct devfreq *devfreq, + unsigned int event, void *data); }; /** @@ -124,18 +117,13 @@ struct devfreq_governor { * @nb notifier block used to notify devfreq object that it should * reevaluate operable frequencies. Devfreq users may use * devfreq.nb to the corresponding register notifier call chain. - * @polling_jiffies interval in jiffies. + * @work delayed work for load monitoring. * @previous_freq previously configured frequency value. - * @next_polling the number of remaining jiffies to poll with - * "devfreq_monitor" executions to reevaluate - * frequency/voltage of the device. Set by - * profile's polling_ms interval. * @data Private data of the governor. The devfreq framework does not * touch this. - * @being_removed a flag to mark that this object is being removed in - * order to prevent trying to remove the object multiple times. * @min_freq Limit minimum frequency requested by user (0: none) * @max_freq Limit maximum frequency requested by user (0: none) + * @stop_polling devfreq polling status of a device. * * This structure stores the devfreq information for a give device. * @@ -153,17 +141,15 @@ struct devfreq { struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; struct notifier_block nb; + struct delayed_work work; - unsigned long polling_jiffies; unsigned long previous_freq; - unsigned int next_polling; void *data; /* private data for governors */ - bool being_removed; - unsigned long min_freq; unsigned long max_freq; + bool stop_polling; }; #if defined(CONFIG_PM_DEVFREQ) -- cgit v0.10.2 From 206c30cfeb7c05dfb9fdfd81b1deb933627e43c1 Mon Sep 17 00:00:00 2001 From: Rajagopal Venkat Date: Fri, 26 Oct 2012 01:50:18 +0200 Subject: PM / devfreq: Add suspend and resume apis Add devfreq suspend/resume apis for devfreq users. This patch supports suspend and resume of devfreq load monitoring, required for devices which can idle. Signed-off-by: Rajagopal Venkat Acked-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 1aaf1ae..999600d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -427,6 +427,34 @@ int devfreq_remove_device(struct devfreq *devfreq) } EXPORT_SYMBOL(devfreq_remove_device); +/** + * devfreq_suspend_device() - Suspend devfreq of a device. + * @devfreq: the devfreq instance to be suspended + */ +int devfreq_suspend_device(struct devfreq *devfreq) +{ + if (!devfreq) + return -EINVAL; + + return devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_SUSPEND, NULL); +} +EXPORT_SYMBOL(devfreq_suspend_device); + +/** + * devfreq_resume_device() - Resume devfreq of a device. + * @devfreq: the devfreq instance to be resumed + */ +int devfreq_resume_device(struct devfreq *devfreq) +{ + if (!devfreq) + return -EINVAL; + + return devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_RESUME, NULL); +} +EXPORT_SYMBOL(devfreq_resume_device); + static ssize_t show_governor(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index bb3aff3..26432ac 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -22,6 +22,8 @@ #define DEVFREQ_GOV_START 0x1 #define DEVFREQ_GOV_STOP 0x2 #define DEVFREQ_GOV_INTERVAL 0x3 +#define DEVFREQ_GOV_SUSPEND 0x4 +#define DEVFREQ_GOV_RESUME 0x5 /* Caution: devfreq->lock must be locked before calling update_devfreq */ extern int update_devfreq(struct devfreq *devfreq); diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 3716a65..b5cf0fb 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -104,6 +104,15 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, case DEVFREQ_GOV_INTERVAL: devfreq_interval_update(devfreq, (unsigned int *)data); break; + + case DEVFREQ_GOV_SUSPEND: + devfreq_monitor_suspend(devfreq); + break; + + case DEVFREQ_GOV_RESUME: + devfreq_monitor_resume(devfreq); + break; + default: break; } diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 9cdffde..ee243a3 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -158,6 +158,8 @@ extern struct devfreq *devfreq_add_device(struct device *dev, const struct devfreq_governor *governor, void *data); extern int devfreq_remove_device(struct devfreq *devfreq); +extern int devfreq_suspend_device(struct devfreq *devfreq); +extern int devfreq_resume_device(struct devfreq *devfreq); /* Helper functions for devfreq user device driver with OPP. */ extern struct opp *devfreq_recommended_opp(struct device *dev, @@ -211,6 +213,16 @@ static int devfreq_remove_device(struct devfreq *devfreq) return 0; } +static int devfreq_suspend_device(struct devfreq *devfreq) +{ + return 0; +} + +static int devfreq_resume_device(struct devfreq *devfreq) +{ + return 0; +} + static struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags) { -- cgit v0.10.2 From 7f98a905dca6e4f144cdd4462edeac00c2bdc379 Mon Sep 17 00:00:00 2001 From: Rajagopal Venkat Date: Fri, 26 Oct 2012 01:50:26 +0200 Subject: PM / devfreq: Add current freq callback in device profile Devfreq returns governor predicted frequency as current frequency via sysfs interface. But device may not support all frequencies that governor predicts. So add a callback in device profile to get current freq from driver. Also add a new sysfs node to expose governor predicted next target frequency. Signed-off-by: Rajagopal Venkat Acked-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 89283b1..e6cf08e 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -19,7 +19,16 @@ Date: September 2011 Contact: MyungJoo Ham Description: The /sys/class/devfreq/.../cur_freq shows the current - frequency of the corresponding devfreq object. + frequency of the corresponding devfreq object. Same as + target_freq when get_cur_freq() is not implemented by + devfreq driver. + +What: /sys/class/devfreq/.../target_freq +Date: September 2012 +Contact: Rajagopal Venkat +Description: + The /sys/class/devfreq/.../target_freq shows the next governor + predicted target frequency of the corresponding devfreq object. What: /sys/class/devfreq/.../polling_interval Date: September 2011 diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 999600d..f2f8a97 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -464,6 +464,19 @@ static ssize_t show_governor(struct device *dev, static ssize_t show_freq(struct device *dev, struct device_attribute *attr, char *buf) { + unsigned long freq; + struct devfreq *devfreq = to_devfreq(dev); + + if (devfreq->profile->get_cur_freq && + !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) + return sprintf(buf, "%lu\n", freq); + + return sprintf(buf, "%lu\n", devfreq->previous_freq); +} + +static ssize_t show_target_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ return sprintf(buf, "%lu\n", to_devfreq(dev)->previous_freq); } @@ -563,6 +576,7 @@ static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr, static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO, show_governor, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), + __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, store_polling_interval), __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index ee243a3..7e2e2ea 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -66,6 +66,8 @@ struct devfreq_dev_status { * explained above with "DEVFREQ_FLAG_*" macros. * @get_dev_status The device should provide the current performance * status to devfreq, which is used by governors. + * @get_cur_freq The device should provide the current frequency + * at which it is operating. * @exit An optional callback that is called when devfreq * is removing the devfreq object due to error or * from devfreq_remove_device() call. If the user @@ -79,6 +81,7 @@ struct devfreq_dev_profile { int (*target)(struct device *dev, unsigned long *freq, u32 flags); int (*get_dev_status)(struct device *dev, struct devfreq_dev_status *stat); + int (*get_cur_freq)(struct device *dev, unsigned long *freq); void (*exit)(struct device *dev); }; -- cgit v0.10.2 From c5b4a1c15da3e6f472c6ff1a085a1134d18a1464 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 26 Oct 2012 01:50:35 +0200 Subject: PM / devfreq: kernel-doc typo corrections Parameter documentation needs a ':' for scripts/kernel-doc to parse properly. Minor fixes for ones warned by: ./scripts/kernel-doc -text drivers/devfreq/devfreq.c>/dev/null Signed-off-by: Nishanth Menon Acked-by: Randy Dunlap Acked-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index f2f8a97..a42f69b 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -265,9 +265,9 @@ out: /** * devfreq_notifier_call() - Notify that the device frequency requirements * has been changed out of devfreq framework. - * @nb the notifier_block (supposed to be devfreq->nb) - * @type not used - * @devp not used + * @nb: the notifier_block (supposed to be devfreq->nb) + * @type: not used + * @devp: not used * * Called by a notifier that uses devfreq->nb. */ @@ -414,7 +414,7 @@ EXPORT_SYMBOL(devfreq_add_device); /** * devfreq_remove_device() - Remove devfreq feature from a device. - * @devfreq the devfreq instance to be removed + * @devfreq: the devfreq instance to be removed */ int devfreq_remove_device(struct devfreq *devfreq) { @@ -619,9 +619,9 @@ module_exit(devfreq_exit); /** * devfreq_recommended_opp() - Helper function to get proper OPP for the * freq value given to target callback. - * @dev The devfreq user device. (parent of devfreq) - * @freq The frequency given to target function - * @flags Flags handed from devfreq framework. + * @dev: The devfreq user device. (parent of devfreq) + * @freq: The frequency given to target function + * @flags: Flags handed from devfreq framework. * */ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, @@ -652,8 +652,8 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, * devfreq_register_opp_notifier() - Helper function to get devfreq notified * for any changes in the OPP availability * changes - * @dev The devfreq user device. (parent of devfreq) - * @devfreq The devfreq object. + * @dev: The devfreq user device. (parent of devfreq) + * @devfreq: The devfreq object. */ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) { @@ -668,8 +668,8 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq * notified for any changes in the OPP * availability changes anymore. - * @dev The devfreq user device. (parent of devfreq) - * @devfreq The devfreq object. + * @dev: The devfreq user device. (parent of devfreq) + * @devfreq: The devfreq object. * * At exit() callback of devfreq_dev_profile, this must be included if * devfreq_recommended_opp is used. -- cgit v0.10.2 From 12e26265e6225bf93b2fdc70399774b31e2dd980 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 26 Oct 2012 01:50:43 +0200 Subject: PM / devfreq: fix sscanf handling for writable sysfs entries sscanf returns 0 when an invalid parameter like: echo -n "a">min_freq is attempted. Returning back the return result(0) will cause the command not to return back to command prompt. Instead, just return -EINVAL when sscanf does not return 1. This is done for min_freq, max_freq and polling_interval Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a42f69b..79a7343 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -496,12 +496,11 @@ static ssize_t store_polling_interval(struct device *dev, ret = sscanf(buf, "%u", &value); if (ret != 1) - goto out; + return -EINVAL; df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value); ret = count; -out: return ret; } @@ -515,7 +514,7 @@ static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr, ret = sscanf(buf, "%lu", &value); if (ret != 1) - goto out; + return -EINVAL; mutex_lock(&df->lock); max = df->max_freq; @@ -529,7 +528,6 @@ static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); -out: return ret; } @@ -549,7 +547,7 @@ static ssize_t store_max_freq(struct device *dev, struct device_attribute *attr, ret = sscanf(buf, "%lu", &value); if (ret != 1) - goto out; + return -EINVAL; mutex_lock(&df->lock); min = df->min_freq; @@ -563,7 +561,6 @@ static ssize_t store_max_freq(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); -out: return ret; } -- cgit v0.10.2 From 1a1357ea176670867f347419c3345e2becc07338 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 26 Oct 2012 01:50:53 +0200 Subject: PM / devfreq: make devfreq_class static devfreq_class is used internally by devfreq and has no need to be globally available. This also fixes the following sparse warning: drivers/devfreq/devfreq.c:30:14: warning: symbol 'devfreq_class' was not declared. Should it be static? Signed-off-by: Nishanth Menon Signed-off-by: Rafael J. Wysocki diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 79a7343..789af4f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -27,7 +27,7 @@ #include #include "governor.h" -struct class *devfreq_class; +static struct class *devfreq_class; /* * devfreq core provides delayed work based load monitoring helper -- cgit v0.10.2 From d7895052d97cde63b34e5185da28052384fa8564 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 21 Aug 2012 15:35:32 +0530 Subject: PM / devfreq: Use devm_* functions in exynos4_bus.c devm_* functions are device managed functions and make cleanup code simpler and smaller. devm_kzalloc and devm_regulator_get functions are used. Signed-off-by: Sachin Kamat [renamed the patch title by MyungJoo Ham] Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index 88ddc77..6814531 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -987,7 +987,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int err = 0; - data = kzalloc(sizeof(struct busfreq_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL); if (data == NULL) { dev_err(dev, "Cannot allocate memory.\n"); return -ENOMEM; @@ -1012,22 +1012,18 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) err = -EINVAL; } if (err) - goto err_regulator; + return err; - data->vdd_int = regulator_get(dev, "vdd_int"); + data->vdd_int = devm_regulator_get(dev, "vdd_int"); if (IS_ERR(data->vdd_int)) { dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); - err = PTR_ERR(data->vdd_int); - goto err_regulator; + return PTR_ERR(data->vdd_int); } if (data->type == TYPE_BUSF_EXYNOS4x12) { - data->vdd_mif = regulator_get(dev, "vdd_mif"); + data->vdd_mif = devm_regulator_get(dev, "vdd_mif"); if (IS_ERR(data->vdd_mif)) { dev_err(dev, "Cannot get the regulator \"vdd_mif\"\n"); - err = PTR_ERR(data->vdd_mif); - regulator_put(data->vdd_int); - goto err_regulator; - + return PTR_ERR(data->vdd_mif); } } @@ -1035,8 +1031,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) if (IS_ERR(opp)) { dev_err(dev, "Invalid initial frequency %lu kHz.\n", exynos4_devfreq_profile.initial_freq); - err = PTR_ERR(opp); - goto err_opp_add; + return PTR_ERR(opp); } data->curr_opp = opp; @@ -1046,29 +1041,19 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, &devfreq_simple_ondemand, NULL); - if (IS_ERR(data->devfreq)) { - err = PTR_ERR(data->devfreq); - goto err_opp_add; - } + if (IS_ERR(data->devfreq)) + return PTR_ERR(data->devfreq); devfreq_register_opp_notifier(dev, data->devfreq); err = register_pm_notifier(&data->pm_notifier); if (err) { dev_err(dev, "Failed to setup pm notifier\n"); - goto err_devfreq_add; + devfreq_remove_device(data->devfreq); + return err; } return 0; -err_devfreq_add: - devfreq_remove_device(data->devfreq); -err_opp_add: - if (data->vdd_mif) - regulator_put(data->vdd_mif); - regulator_put(data->vdd_int); -err_regulator: - kfree(data); - return err; } static __devexit int exynos4_busfreq_remove(struct platform_device *pdev) @@ -1077,10 +1062,6 @@ static __devexit int exynos4_busfreq_remove(struct platform_device *pdev) unregister_pm_notifier(&data->pm_notifier); devfreq_remove_device(data->devfreq); - regulator_put(data->vdd_int); - if (data->vdd_mif) - regulator_put(data->vdd_mif); - kfree(data); return 0; } -- cgit v0.10.2 From e09651fcc295a7dc802f38d9494f5b860dd90bca Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 08:02:23 -0500 Subject: PM / devfreq: documentation cleanups for devfreq header struct parameters need to have ':' in documentation for scripts/kernel-doc to parse appropriately. Fix the errors reported by: ./scripts/kernel-doc include/linux/devfreq.h >/dev/null Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: Randy Dunlap Acked-by: MyungJoo Ham Signed-off-by: MyungJoo Ham diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 7e2e2ea..1461fb2 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -25,12 +25,12 @@ struct devfreq; * struct devfreq_dev_status - Data given from devfreq user device to * governors. Represents the performance * statistics. - * @total_time The total time represented by this instance of + * @total_time: The total time represented by this instance of * devfreq_dev_status - * @busy_time The time that the device was working among the + * @busy_time: The time that the device was working among the * total_time. - * @current_frequency The operating frequency. - * @private_data An entry not specified by the devfreq framework. + * @current_frequency: The operating frequency. + * @private_data: An entry not specified by the devfreq framework. * A device and a specific governor may have their * own protocol with private_data. However, because * this is governor-specific, a governor using this @@ -54,21 +54,21 @@ struct devfreq_dev_status { /** * struct devfreq_dev_profile - Devfreq's user device profile - * @initial_freq The operating frequency when devfreq_add_device() is + * @initial_freq: The operating frequency when devfreq_add_device() is * called. - * @polling_ms The polling interval in ms. 0 disables polling. - * @target The device should set its operating frequency at + * @polling_ms: The polling interval in ms. 0 disables polling. + * @target: The device should set its operating frequency at * freq or lowest-upper-than-freq value. If freq is * higher than any operable frequency, set maximum. * Before returning, target function should set * freq at the current frequency. * The "flags" parameter's possible values are * explained above with "DEVFREQ_FLAG_*" macros. - * @get_dev_status The device should provide the current performance + * @get_dev_status: The device should provide the current performance * status to devfreq, which is used by governors. - * @get_cur_freq The device should provide the current frequency + * @get_cur_freq: The device should provide the current frequency * at which it is operating. - * @exit An optional callback that is called when devfreq + * @exit: An optional callback that is called when devfreq * is removing the devfreq object due to error or * from devfreq_remove_device() call. If the user * has registered devfreq->nb at a notifier-head, @@ -87,14 +87,14 @@ struct devfreq_dev_profile { /** * struct devfreq_governor - Devfreq policy governor - * @name Governor's name - * @get_target_freq Returns desired operating frequency for the device. + * @name: Governor's name + * @get_target_freq: Returns desired operating frequency for the device. * Basically, get_target_freq will run * devfreq_dev_profile.get_dev_status() to get the * status of the device (load = busy_time / total_time). * If no_central_polling is set, this callback is called * only with update_devfreq() notified by OPP. - * @event_handler Callback for devfreq core framework to notify events + * @event_handler: Callback for devfreq core framework to notify events * to governors. Events include per device governor * init and exit, opp changes out of devfreq, suspend * and resume of per device devfreq during device idle. @@ -110,23 +110,23 @@ struct devfreq_governor { /** * struct devfreq - Device devfreq structure - * @node list node - contains the devices with devfreq that have been + * @node: list node - contains the devices with devfreq that have been * registered. - * @lock a mutex to protect accessing devfreq. - * @dev device registered by devfreq class. dev.parent is the device + * @lock: a mutex to protect accessing devfreq. + * @dev: device registered by devfreq class. dev.parent is the device * using devfreq. - * @profile device-specific devfreq profile - * @governor method how to choose frequency based on the usage. - * @nb notifier block used to notify devfreq object that it should + * @profile: device-specific devfreq profile + * @governor: method how to choose frequency based on the usage. + * @nb: notifier block used to notify devfreq object that it should * reevaluate operable frequencies. Devfreq users may use * devfreq.nb to the corresponding register notifier call chain. - * @work delayed work for load monitoring. - * @previous_freq previously configured frequency value. - * @data Private data of the governor. The devfreq framework does not + * @work: delayed work for load monitoring. + * @previous_freq: previously configured frequency value. + * @data: Private data of the governor. The devfreq framework does not * touch this. - * @min_freq Limit minimum frequency requested by user (0: none) - * @max_freq Limit maximum frequency requested by user (0: none) - * @stop_polling devfreq polling status of a device. + * @min_freq: Limit minimum frequency requested by user (0: none) + * @max_freq: Limit maximum frequency requested by user (0: none) + * @stop_polling: devfreq polling status of a device. * * This structure stores the devfreq information for a give device. * @@ -186,9 +186,9 @@ extern const struct devfreq_governor devfreq_simple_ondemand; /** * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq * and devfreq_add_device - * @ upthreshold If the load is over this value, the frequency jumps. + * @upthreshold: If the load is over this value, the frequency jumps. * Specify 0 to use the default. Valid value = 0 to 100. - * @ downdifferential If the load is under upthreshold - downdifferential, + * @downdifferential: If the load is under upthreshold - downdifferential, * the governor may consider slowing the frequency down. * Specify 0 to use the default. Valid value = 0 to 100. * downdifferential < upthreshold must hold. -- cgit v0.10.2 From d287de855f97c56ca7146ff627e652bd7cd64f3f Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 25 Oct 2012 19:48:59 -0500 Subject: PM / devfreq: Add sysfs node to expose available frequencies devfreq governors such as ondemand are controlled by a min and max frequency, while governors like userspace governor allow us to set a specific frequency. However, for the same specific device, depending on the SoC, the available frequencies can vary. So expose the available frequencies as a snapshot over sysfs to allow informed decisions. This was inspired by cpufreq framework's equivalent for similar usage sysfs node: scaling_available_frequencies. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Signed-off-by: MyungJoo Ham diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index e6cf08e..e672ccb 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -51,3 +51,12 @@ Description: The /sys/class/devfreq/.../userspace/set_freq shows and sets the requested frequency for the devfreq object if userspace governor is in effect. + +What: /sys/class/devfreq/.../available_frequencies +Date: October 2012 +Contact: Nishanth Menon +Description: + The /sys/class/devfreq/.../available_frequencies shows + the available frequencies of the corresponding devfreq object. + This is a snapshot of available frequencies and not limited + by the min/max frequency restrictions. diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 789af4f..c44e562 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -570,9 +570,41 @@ static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq); } +static ssize_t show_available_freqs(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct devfreq *df = to_devfreq(d); + struct device *dev = df->dev.parent; + struct opp *opp; + ssize_t count = 0; + unsigned long freq = 0; + + rcu_read_lock(); + do { + opp = opp_find_freq_ceil(dev, &freq); + if (IS_ERR(opp)) + break; + + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), + "%lu ", freq); + freq++; + } while (1); + rcu_read_unlock(); + + /* Truncate the trailing space */ + if (count) + count--; + + count += sprintf(&buf[count], "\n"); + + return count; +} + static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO, show_governor, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), + __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL), __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, store_polling_interval), -- cgit v0.10.2 From e552bbaf5b987f57c43e6981a452b8a3c700b1ae Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Thu, 23 Aug 2012 20:00:46 +0900 Subject: PM / devfreq: Add sysfs node for representing frequency transition information. This patch adds sysfs node which can be used to get information of frequency transition. It represents transition table which contains total number of transition of each freqeuncy state and time spent. It is inspired CPUFREQ's status driver. Signed-off-by: Jonghwa Lee [Added Documentation/ABI entry, updated kernel-doc, and resolved merge conflict] Signed-off-by: MyungJoo Ham diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index e672ccb..40f98a9 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -44,6 +44,17 @@ Description: (/sys/class/devfreq/.../central_polling is 0), this value may be useless. +What: /sys/class/devfreq/.../trans_stat +Date: October 2012 +Contact: MyungJoo Ham +Descrtiption: + This ABI shows the statistics of devfreq behavior on a + specific device. It shows the time spent in each state and + the number of transitions between states. + In order to activate this ABI, the devfreq target device + driver should provide the list of available frequencies + with its profile. + What: /sys/class/devfreq/.../userspace/set_freq Date: September 2011 Contact: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c44e562..bf6de38 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -66,6 +66,51 @@ static struct devfreq *find_device_devfreq(struct device *dev) return ERR_PTR(-ENODEV); } +/** + * devfreq_get_freq_level() - Lookup freq_table for the frequency + * @devfreq: the devfreq instance + * @freq: the target frequency + */ +static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) +{ + int lev; + + for (lev = 0; lev < devfreq->profile->max_state; lev++) + if (freq == devfreq->profile->freq_table[lev]) + return lev; + + return -EINVAL; +} + +/** + * devfreq_update_status() - Update statistics of devfreq behavior + * @devfreq: the devfreq instance + * @freq: the update target frequency + */ +static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) +{ + int lev, prev_lev; + unsigned long cur_time; + + lev = devfreq_get_freq_level(devfreq, freq); + if (lev < 0) + return lev; + + cur_time = jiffies; + devfreq->time_in_state[lev] += + cur_time - devfreq->last_stat_updated; + if (freq != devfreq->previous_freq) { + prev_lev = devfreq_get_freq_level(devfreq, + devfreq->previous_freq); + devfreq->trans_table[(prev_lev * + devfreq->profile->max_state) + lev]++; + devfreq->total_trans++; + } + devfreq->last_stat_updated = cur_time; + + return 0; +} + /* Load monitoring helper functions for governors use */ /** @@ -112,6 +157,11 @@ int update_devfreq(struct devfreq *devfreq) if (err) return err; + if (devfreq->profile->freq_table) + if (devfreq_update_status(devfreq, freq)) + dev_err(&devfreq->dev, + "Couldn't update frequency transition information.\n"); + devfreq->previous_freq = freq; return err; } @@ -378,6 +428,15 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->data = data; devfreq->nb.notifier_call = devfreq_notifier_call; + devfreq->trans_table = devm_kzalloc(dev, sizeof(unsigned int) * + devfreq->profile->max_state * + devfreq->profile->max_state, + GFP_KERNEL); + devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) * + devfreq->profile->max_state, + GFP_KERNEL); + devfreq->last_stat_updated = jiffies; + dev_set_name(&devfreq->dev, dev_name(dev)); err = device_register(&devfreq->dev); if (err) { @@ -601,6 +660,47 @@ static ssize_t show_available_freqs(struct device *d, return count; } +static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq *devfreq = to_devfreq(dev); + ssize_t len; + int i, j, err; + unsigned int max_state = devfreq->profile->max_state; + + err = devfreq_update_status(devfreq, devfreq->previous_freq); + if (err) + return 0; + + len = sprintf(buf, " From : To\n"); + len += sprintf(buf + len, " :"); + for (i = 0; i < max_state; i++) + len += sprintf(buf + len, "%8u", + devfreq->profile->freq_table[i]); + + len += sprintf(buf + len, " time(ms)\n"); + + for (i = 0; i < max_state; i++) { + if (devfreq->profile->freq_table[i] + == devfreq->previous_freq) { + len += sprintf(buf + len, "*"); + } else { + len += sprintf(buf + len, " "); + } + len += sprintf(buf + len, "%8u:", + devfreq->profile->freq_table[i]); + for (j = 0; j < max_state; j++) + len += sprintf(buf + len, "%8u", + devfreq->trans_table[(i * max_state) + j]); + len += sprintf(buf + len, "%10u\n", + jiffies_to_msecs(devfreq->time_in_state[i])); + } + + len += sprintf(buf + len, "Total transition : %u\n", + devfreq->total_trans); + return len; +} + static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO, show_governor, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), @@ -610,6 +710,7 @@ static struct device_attribute devfreq_attrs[] = { store_polling_interval), __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), + __ATTR(trans_stat, S_IRUGO, show_trans_table, NULL), { }, }; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 1461fb2..bc35c4a 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -73,6 +73,8 @@ struct devfreq_dev_status { * from devfreq_remove_device() call. If the user * has registered devfreq->nb at a notifier-head, * this is the time to unregister it. + * @freq_table: Optional list of frequencies to support statistics. + * @max_state: The size of freq_table. */ struct devfreq_dev_profile { unsigned long initial_freq; @@ -83,6 +85,9 @@ struct devfreq_dev_profile { struct devfreq_dev_status *stat); int (*get_cur_freq)(struct device *dev, unsigned long *freq); void (*exit)(struct device *dev); + + unsigned int *freq_table; + unsigned int max_state; }; /** @@ -127,6 +132,10 @@ struct devfreq_governor { * @min_freq: Limit minimum frequency requested by user (0: none) * @max_freq: Limit maximum frequency requested by user (0: none) * @stop_polling: devfreq polling status of a device. + * @total_trans: Number of devfreq transitions + * @trans_table: Statistics of devfreq transitions + * @time_in_state: Statistics of devfreq states + * @last_stat_updated: The last time stat updated * * This structure stores the devfreq information for a give device. * @@ -153,6 +162,12 @@ struct devfreq { unsigned long min_freq; unsigned long max_freq; bool stop_polling; + + /* information for device freqeuncy transition */ + unsigned int total_trans; + unsigned int *trans_table; + unsigned long *time_in_state; + unsigned long last_stat_updated; }; #if defined(CONFIG_PM_DEVFREQ) -- cgit v0.10.2 From 2df5021fa9738905e8b1ab92fa0cd685430f54da Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:42 -0500 Subject: PM / devfreq: export update_devfreq Allow update_devfreq to be used by devfreq governor built as modules Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index bf6de38..e0002c5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -165,6 +165,7 @@ int update_devfreq(struct devfreq *devfreq) devfreq->previous_freq = freq; return err; } +EXPORT_SYMBOL(update_devfreq); /** * devfreq_monitor() - Periodically poll devfreq objects. -- cgit v0.10.2 From 3aa173b8db200bb96354481acc0a5b9e123119fe Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:43 -0500 Subject: PM / devfreq: provide hooks for governors to be registered Add devfreq_add_governor and devfreq_remove_governor which can be invoked by governors to register with devfreq. This sets up the stage to dynamically switch governors and allow governors to be dynamically loaded as well. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index e0002c5..679ac42 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -36,6 +36,8 @@ static struct class *devfreq_class; */ static struct workqueue_struct *devfreq_wq; +/* The list of all device-devfreq governors */ +static LIST_HEAD(devfreq_governor_list); /* The list of all device-devfreq */ static LIST_HEAD(devfreq_list); static DEFINE_MUTEX(devfreq_list_lock); @@ -111,6 +113,32 @@ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) return 0; } +/** + * find_devfreq_governor() - find devfreq governor from name + * @name: name of the governor + * + * Search the list of devfreq governors and return the matched + * governor's pointer. devfreq_list_lock should be held by the caller. + */ +static struct devfreq_governor *find_devfreq_governor(const char *name) +{ + struct devfreq_governor *tmp_governor; + + if (unlikely(IS_ERR_OR_NULL(name))) { + pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); + return ERR_PTR(-EINVAL); + } + WARN(!mutex_is_locked(&devfreq_list_lock), + "devfreq_list_lock must be locked."); + + list_for_each_entry(tmp_governor, &devfreq_governor_list, node) { + if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN)) + return tmp_governor; + } + + return ERR_PTR(-ENODEV); +} + /* Load monitoring helper functions for governors use */ /** @@ -515,6 +543,69 @@ int devfreq_resume_device(struct devfreq *devfreq) } EXPORT_SYMBOL(devfreq_resume_device); +/** + * devfreq_add_governor() - Add devfreq governor + * @governor: the devfreq governor to be added + */ +int devfreq_add_governor(struct devfreq_governor *governor) +{ + struct devfreq_governor *g; + int err = 0; + + if (!governor) { + pr_err("%s: Invalid parameters.\n", __func__); + return -EINVAL; + } + + mutex_lock(&devfreq_list_lock); + g = find_devfreq_governor(governor->name); + if (!IS_ERR(g)) { + pr_err("%s: governor %s already registered\n", __func__, + g->name); + err = -EINVAL; + goto err_out; + } + + list_add(&governor->node, &devfreq_governor_list); + +err_out: + mutex_unlock(&devfreq_list_lock); + + return err; +} +EXPORT_SYMBOL(devfreq_add_governor); + +/** + * devfreq_remove_device() - Remove devfreq feature from a device. + * @governor: the devfreq governor to be removed + */ +int devfreq_remove_governor(struct devfreq_governor *governor) +{ + struct devfreq_governor *g; + int err = 0; + + if (!governor) { + pr_err("%s: Invalid parameters.\n", __func__); + return -EINVAL; + } + + mutex_lock(&devfreq_list_lock); + g = find_devfreq_governor(governor->name); + if (IS_ERR(g)) { + pr_err("%s: governor %s not registered\n", __func__, + g->name); + err = -EINVAL; + goto err_out; + } + + list_del(&governor->node); +err_out: + mutex_unlock(&devfreq_list_lock); + + return err; +} +EXPORT_SYMBOL(devfreq_remove_governor); + static ssize_t show_governor(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 26432ac..fad7d63 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -34,4 +34,8 @@ extern void devfreq_monitor_suspend(struct devfreq *devfreq); extern void devfreq_monitor_resume(struct devfreq *devfreq); extern void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay); + +extern int devfreq_add_governor(struct devfreq_governor *governor); +extern int devfreq_remove_governor(struct devfreq_governor *governor); + #endif /* _GOVERNOR_H */ diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index bc35c4a..6484a3f 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -92,6 +92,7 @@ struct devfreq_dev_profile { /** * struct devfreq_governor - Devfreq policy governor + * @node: list node - contains registered devfreq governors * @name: Governor's name * @get_target_freq: Returns desired operating frequency for the device. * Basically, get_target_freq will run @@ -107,6 +108,8 @@ struct devfreq_dev_profile { * Note that the callbacks are called with devfreq->lock locked by devfreq. */ struct devfreq_governor { + struct list_head node; + const char name[DEVFREQ_NAME_LEN]; int (*get_target_freq)(struct devfreq *this, unsigned long *freq); int (*event_handler)(struct devfreq *devfreq, -- cgit v0.10.2 From 83116e66a232184f733ecf09a41817cf893ede98 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:44 -0500 Subject: PM / devfreq: register governors with devfreq framework With the new registration functions, governors can be now registered with devfreq framework. NOTE: generates 'discards qualifiers from pointer target type' build warnings, which the next patche in this series fixes Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index eea3f9b..db8ff77 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -45,3 +45,21 @@ const struct devfreq_governor devfreq_performance = { .get_target_freq = devfreq_performance_func, .event_handler = devfreq_performance_handler, }; + +static int __init devfreq_performance_init(void) +{ + return devfreq_add_governor(&devfreq_performance); +} +subsys_initcall(devfreq_performance_init); + +static void __exit devfreq_performance_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_performance); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + + return; +} +module_exit(devfreq_performance_exit); diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index 2868d98..30f0fca 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -42,3 +42,21 @@ const struct devfreq_governor devfreq_powersave = { .get_target_freq = devfreq_powersave_func, .event_handler = devfreq_powersave_handler, }; + +static int __init devfreq_powersave_init(void) +{ + return devfreq_add_governor(&devfreq_powersave); +} +subsys_initcall(devfreq_powersave_init); + +static void __exit devfreq_powersave_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_powersave); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + + return; +} +module_exit(devfreq_powersave_exit); diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index b5cf0fb..85f9ed5 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -125,3 +125,21 @@ const struct devfreq_governor devfreq_simple_ondemand = { .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, }; + +static int __init devfreq_simple_ondemand_init(void) +{ + return devfreq_add_governor(&devfreq_simple_ondemand); +} +subsys_initcall(devfreq_simple_ondemand_init); + +static void __exit devfreq_simple_ondemand_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_simple_ondemand); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + + return; +} +module_exit(devfreq_simple_ondemand_exit); diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 7067555..110f178 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -140,3 +140,21 @@ const struct devfreq_governor devfreq_userspace = { .get_target_freq = devfreq_userspace_func, .event_handler = devfreq_userspace_handler, }; + +static int __init devfreq_userspace_init(void) +{ + return devfreq_add_governor(&devfreq_userspace); +} +subsys_initcall(devfreq_userspace_init); + +static void __exit devfreq_userspace_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_userspace); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + + return; +} +module_exit(devfreq_userspace_exit); -- cgit v0.10.2 From 1b5c1be2c88e8445a20fa1929e26c37e7ca8c926 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:45 -0500 Subject: PM / devfreq: map devfreq drivers to governor using name Allow devfreq drivers to register a preferred governor name and when the devfreq governor loads itself at a later point required drivers are managed appropriately, at the time of unload of a devfreq governor, stop managing those drivers as well. Since the governor structures do not need to be exposed anymore, remove the definitions and make them static NOTE: devfreq_list_lock is now used to protect governor start and stop - as this allows us to protect governors and devfreq with the proper dependencies as needed. As part of this change, change the registration of exynos bus driver to request for ondemand using the governor name. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon [Merge conflict resolved by MyungJoo Ham] Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 679ac42..0d7be03 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -159,6 +159,9 @@ int update_devfreq(struct devfreq *devfreq) return -EINVAL; } + if (!devfreq->governor) + return -EINVAL; + /* Reevaluate the proper frequency */ err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) @@ -379,7 +382,9 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); - devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); + if (devfreq->governor) + devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); @@ -412,19 +417,20 @@ static void devfreq_dev_release(struct device *dev) * devfreq_add_device() - Add devfreq feature to the device * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. - * @governor: the policy to choose frequency. + * @governor_name: name of the policy to choose frequency. * @data: private data for the governor. The devfreq framework does not * touch this value. */ struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, - const struct devfreq_governor *governor, + const char *governor_name, void *data) { struct devfreq *devfreq; + struct devfreq_governor *governor; int err = 0; - if (!dev || !profile || !governor) { + if (!dev || !profile || !governor_name) { dev_err(dev, "%s: Invalid parameters.\n", __func__); return ERR_PTR(-EINVAL); } @@ -452,7 +458,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->dev.class = devfreq_class; devfreq->dev.release = devfreq_dev_release; devfreq->profile = profile; - devfreq->governor = governor; + strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); devfreq->previous_freq = profile->initial_freq; devfreq->data = data; devfreq->nb.notifier_call = devfreq_notifier_call; @@ -478,10 +484,14 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_lock(&devfreq_list_lock); list_add(&devfreq->node, &devfreq_list); - mutex_unlock(&devfreq_list_lock); - err = devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_START, NULL); + governor = find_devfreq_governor(devfreq->governor_name); + if (!IS_ERR(governor)) + devfreq->governor = governor; + if (devfreq->governor) + err = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_START, NULL); + mutex_unlock(&devfreq_list_lock); if (err) { dev_err(dev, "%s: Unable to start governor for the device\n", __func__); @@ -524,6 +534,9 @@ int devfreq_suspend_device(struct devfreq *devfreq) if (!devfreq) return -EINVAL; + if (!devfreq->governor) + return 0; + return devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_SUSPEND, NULL); } @@ -538,6 +551,9 @@ int devfreq_resume_device(struct devfreq *devfreq) if (!devfreq) return -EINVAL; + if (!devfreq->governor) + return 0; + return devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_RESUME, NULL); } @@ -550,6 +566,7 @@ EXPORT_SYMBOL(devfreq_resume_device); int devfreq_add_governor(struct devfreq_governor *governor) { struct devfreq_governor *g; + struct devfreq *devfreq; int err = 0; if (!governor) { @@ -568,6 +585,38 @@ int devfreq_add_governor(struct devfreq_governor *governor) list_add(&governor->node, &devfreq_governor_list); + list_for_each_entry(devfreq, &devfreq_list, node) { + int ret = 0; + struct device *dev = devfreq->dev.parent; + + if (!strncmp(devfreq->governor_name, governor->name, + DEVFREQ_NAME_LEN)) { + /* The following should never occur */ + if (devfreq->governor) { + dev_warn(dev, + "%s: Governor %s already present\n", + __func__, devfreq->governor->name); + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev, + "%s: Governor %s stop = %d\n", + __func__, + devfreq->governor->name, ret); + } + /* Fall through */ + } + devfreq->governor = governor; + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_START, NULL); + if (ret) { + dev_warn(dev, "%s: Governor %s start=%d\n", + __func__, devfreq->governor->name, + ret); + } + } + } + err_out: mutex_unlock(&devfreq_list_lock); @@ -582,6 +631,7 @@ EXPORT_SYMBOL(devfreq_add_governor); int devfreq_remove_governor(struct devfreq_governor *governor) { struct devfreq_governor *g; + struct devfreq *devfreq; int err = 0; if (!governor) { @@ -597,6 +647,29 @@ int devfreq_remove_governor(struct devfreq_governor *governor) err = -EINVAL; goto err_out; } + list_for_each_entry(devfreq, &devfreq_list, node) { + int ret; + struct device *dev = devfreq->dev.parent; + + if (!strncmp(devfreq->governor_name, governor->name, + DEVFREQ_NAME_LEN)) { + /* we should have a devfreq governor! */ + if (!devfreq->governor) { + dev_warn(dev, "%s: Governor %s NOT present\n", + __func__, governor->name); + continue; + /* Fall through */ + } + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev, "%s: Governor %s stop=%d\n", + __func__, devfreq->governor->name, + ret); + } + devfreq->governor = NULL; + } + } list_del(&governor->node); err_out: @@ -609,6 +682,9 @@ EXPORT_SYMBOL(devfreq_remove_governor); static ssize_t show_governor(struct device *dev, struct device_attribute *attr, char *buf) { + if (!to_devfreq(dev)->governor) + return -EINVAL; + return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name); } @@ -645,6 +721,9 @@ static ssize_t store_polling_interval(struct device *dev, unsigned int value; int ret; + if (!df->governor) + return -EINVAL; + ret = sscanf(buf, "%u", &value); if (ret != 1) return -EINVAL; diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index 6814531..b8ac284 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -1040,7 +1040,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) busfreq_mon_reset(data); data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, - &devfreq_simple_ondemand, NULL); + "simple_ondemand", NULL); if (IS_ERR(data->devfreq)) return PTR_ERR(data->devfreq); diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index db8ff77..865a369 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -40,7 +40,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq, return ret; } -const struct devfreq_governor devfreq_performance = { +static struct devfreq_governor devfreq_performance = { .name = "performance", .get_target_freq = devfreq_performance_func, .event_handler = devfreq_performance_handler, diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index 30f0fca..8612c0f 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -37,7 +37,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq, return ret; } -const struct devfreq_governor devfreq_powersave = { +static struct devfreq_governor devfreq_powersave = { .name = "powersave", .get_target_freq = devfreq_powersave_func, .event_handler = devfreq_powersave_handler, diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 85f9ed5..a870a24 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -120,7 +120,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, return 0; } -const struct devfreq_governor devfreq_simple_ondemand = { +static struct devfreq_governor devfreq_simple_ondemand = { .name = "simple_ondemand", .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 110f178..34fb80f 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -135,7 +135,7 @@ static int devfreq_userspace_handler(struct devfreq *devfreq, return ret; } -const struct devfreq_governor devfreq_userspace = { +static struct devfreq_governor devfreq_userspace = { .name = "userspace", .get_target_freq = devfreq_userspace_func, .event_handler = devfreq_userspace_handler, diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 6484a3f..235248c 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -125,6 +125,7 @@ struct devfreq_governor { * using devfreq. * @profile: device-specific devfreq profile * @governor: method how to choose frequency based on the usage. + * @governor_name: devfreq governor name for use with this devfreq * @nb: notifier block used to notify devfreq object that it should * reevaluate operable frequencies. Devfreq users may use * devfreq.nb to the corresponding register notifier call chain. @@ -155,6 +156,7 @@ struct devfreq { struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; + char governor_name[DEVFREQ_NAME_LEN]; struct notifier_block nb; struct delayed_work work; @@ -176,7 +178,7 @@ struct devfreq { #if defined(CONFIG_PM_DEVFREQ) extern struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, - const struct devfreq_governor *governor, + const char *governor_name, void *data); extern int devfreq_remove_device(struct devfreq *devfreq); extern int devfreq_suspend_device(struct devfreq *devfreq); @@ -190,17 +192,7 @@ extern int devfreq_register_opp_notifier(struct device *dev, extern int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq); -#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE -extern const struct devfreq_governor devfreq_powersave; -#endif -#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE -extern const struct devfreq_governor devfreq_performance; -#endif -#ifdef CONFIG_DEVFREQ_GOV_USERSPACE -extern const struct devfreq_governor devfreq_userspace; -#endif #ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND -extern const struct devfreq_governor devfreq_simple_ondemand; /** * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq * and devfreq_add_device @@ -223,7 +215,7 @@ struct devfreq_simple_ondemand_data { #else /* !CONFIG_PM_DEVFREQ */ static struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, - struct devfreq_governor *governor, + const char *governor_name, void *data) { return NULL; @@ -262,11 +254,6 @@ static int devfreq_unregister_opp_notifier(struct device *dev, return -EINVAL; } -#define devfreq_powersave NULL -#define devfreq_performance NULL -#define devfreq_userspace NULL -#define devfreq_simple_ondemand NULL - #endif /* CONFIG_PM_DEVFREQ */ #endif /* __LINUX_DEVFREQ_H__ */ -- cgit v0.10.2 From eff607fdb1f787da1fedf46ab6e64adc2afd1c5a Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:46 -0500 Subject: PM / devfreq: governors: add GPL module license and allow module build Add GPL module license and remove the static build restrictions for building governors. This allows governors now to be loaded on a need basis and reloaded independently of kernel build Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index f6b0a6e2..0f079be 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -30,7 +30,7 @@ if PM_DEVFREQ comment "DEVFREQ Governors" config DEVFREQ_GOV_SIMPLE_ONDEMAND - bool "Simple Ondemand" + tristate "Simple Ondemand" help Chooses frequency based on the recent load on the device. Works similar as ONDEMAND governor of CPUFREQ does. A device with @@ -39,7 +39,7 @@ config DEVFREQ_GOV_SIMPLE_ONDEMAND values to the governor with data field at devfreq_add_device(). config DEVFREQ_GOV_PERFORMANCE - bool "Performance" + tristate "Performance" help Sets the frequency at the maximum available frequency. This governor always returns UINT_MAX as frequency so that @@ -47,7 +47,7 @@ config DEVFREQ_GOV_PERFORMANCE at any time. config DEVFREQ_GOV_POWERSAVE - bool "Powersave" + tristate "Powersave" help Sets the frequency at the minimum available frequency. This governor always returns 0 as frequency so that @@ -55,7 +55,7 @@ config DEVFREQ_GOV_POWERSAVE at any time. config DEVFREQ_GOV_USERSPACE - bool "Userspace" + tristate "Userspace" help Sets the frequency at the user specified one. This governor returns the user configured frequency if there diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index 865a369..c72f942 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -10,6 +10,7 @@ */ #include +#include #include "governor.h" static int devfreq_performance_func(struct devfreq *df, @@ -63,3 +64,4 @@ static void __exit devfreq_performance_exit(void) return; } module_exit(devfreq_performance_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index 8612c0f..0c6bed5 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -10,6 +10,7 @@ */ #include +#include #include "governor.h" static int devfreq_powersave_func(struct devfreq *df, @@ -60,3 +61,4 @@ static void __exit devfreq_powersave_exit(void) return; } module_exit(devfreq_powersave_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index a870a24..0720ba8 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include "governor.h" @@ -143,3 +144,4 @@ static void __exit devfreq_simple_ondemand_exit(void) return; } module_exit(devfreq_simple_ondemand_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 34fb80f..35de6e8 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "governor.h" struct userspace_data { @@ -158,3 +159,4 @@ static void __exit devfreq_userspace_exit(void) return; } module_exit(devfreq_userspace_exit); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 0359d1afe4013d1a216908b6be4c6695a1db6fd6 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:47 -0500 Subject: PM / devfreq: allow sysfs governor node to switch governor This allows us to select governor runtime from the default configuration without having to rebuild kernel or the devfreq driver using the sysfs node: /sys/class/devfreq/.../governor cat of the governor will return valid governor and an echo 'governor_name'>governor will switch governor Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Acked-by: MyungJoo Ham Signed-off-by: MyungJoo Ham diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 40f98a9..66876de 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -11,7 +11,7 @@ What: /sys/class/devfreq/.../governor Date: September 2011 Contact: MyungJoo Ham Description: - The /sys/class/devfreq/.../governor shows the name of the + The /sys/class/devfreq/.../governor show or set the name of the governor used by the corresponding devfreq object. What: /sys/class/devfreq/.../cur_freq diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0d7be03..ff960f0 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -688,6 +688,49 @@ static ssize_t show_governor(struct device *dev, return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name); } +static ssize_t store_governor(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *df = to_devfreq(dev); + int ret; + char str_governor[DEVFREQ_NAME_LEN + 1]; + struct devfreq_governor *governor; + + ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor); + if (ret != 1) + return -EINVAL; + + mutex_lock(&devfreq_list_lock); + governor = find_devfreq_governor(str_governor); + if (IS_ERR(governor)) { + ret = PTR_ERR(governor); + goto out; + } + if (df->governor == governor) + goto out; + + if (df->governor) { + ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev, "%s: Governor %s not stopped(%d)\n", + __func__, df->governor->name, ret); + goto out; + } + } + df->governor = governor; + strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); + ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); + if (ret) + dev_warn(dev, "%s: Governor %s not started(%d)\n", + __func__, df->governor->name, ret); +out: + mutex_unlock(&devfreq_list_lock); + + if (!ret) + ret = count; + return ret; +} + static ssize_t show_freq(struct device *dev, struct device_attribute *attr, char *buf) { @@ -873,7 +916,7 @@ static ssize_t show_trans_table(struct device *dev, struct device_attribute *att } static struct device_attribute devfreq_attrs[] = { - __ATTR(governor, S_IRUGO, show_governor, NULL), + __ATTR(governor, S_IRUGO | S_IWUSR, show_governor, store_governor), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL), __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), -- cgit v0.10.2 From 50a5b33e0159f8783ef617cdb9d5fbb6a3955b6f Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 29 Oct 2012 15:01:48 -0500 Subject: PM / devfreq: Add sysfs node to expose available governors Now that governor list can be variable, knowing the available governors is useful to be able to select a governor using relevant sysfs node. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Signed-off-by: MyungJoo Ham diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 66876de..0ba6ea2 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -71,3 +71,10 @@ Description: the available frequencies of the corresponding devfreq object. This is a snapshot of available frequencies and not limited by the min/max frequency restrictions. + +What: /sys/class/devfreq/.../available_governors +Date: October 2012 +Contact: Nishanth Menon +Description: + The /sys/class/devfreq/.../available_governors shows + currently available governors in the system. diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index ff960f0..45e053e 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -730,6 +730,27 @@ out: ret = count; return ret; } +static ssize_t show_available_governors(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct devfreq_governor *tmp_governor; + ssize_t count = 0; + + mutex_lock(&devfreq_list_lock); + list_for_each_entry(tmp_governor, &devfreq_governor_list, node) + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), + "%s ", tmp_governor->name); + mutex_unlock(&devfreq_list_lock); + + /* Truncate the trailing space */ + if (count) + count--; + + count += sprintf(&buf[count], "\n"); + + return count; +} static ssize_t show_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -917,6 +938,7 @@ static ssize_t show_trans_table(struct device *dev, struct device_attribute *att static struct device_attribute devfreq_attrs[] = { __ATTR(governor, S_IRUGO | S_IWUSR, show_governor, store_governor), + __ATTR(available_governors, S_IRUGO, show_available_governors, NULL), __ATTR(cur_freq, S_IRUGO, show_freq, NULL), __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL), __ATTR(target_freq, S_IRUGO, show_target_freq, NULL), -- cgit v0.10.2 From dce9dc3a24f03b054dae02ef5cf1df7e97a8f558 Mon Sep 17 00:00:00 2001 From: Sangho Yi Date: Sat, 20 Oct 2012 01:16:34 +0900 Subject: PM / devfreq: exynos4_bus.c: Fixed an alignment of the func call args. I fixed the following check item (via checkpatch.pl --strict option): CHECK: Alignment should match open parenthesis Signed-off-by: Sangho Yi [Merge conflict resolved] Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index b8ac284..7418372 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -1030,7 +1030,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq); if (IS_ERR(opp)) { dev_err(dev, "Invalid initial frequency %lu kHz.\n", - exynos4_devfreq_profile.initial_freq); + exynos4_devfreq_profile.initial_freq); return PTR_ERR(opp); } data->curr_opp = opp; -- cgit v0.10.2 From 883d588e556347c4b3221ac492a8acd8a75e730a Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 21 Nov 2012 17:17:11 +0900 Subject: PM / devfreq: remove compiler error when a governor is module With the intruction of patch, eff607fdb1f787da1fedf46ab6e64adc2afd1c5a, it became possible to include a governor as a module. Thus the #ifdef statement for a governor should become #if IS_ENABLED. Signed-off-by: MyungJoo Ham diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 235248c..e83ef39 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -192,7 +192,7 @@ extern int devfreq_register_opp_notifier(struct device *dev, extern int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq); -#ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND +#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) /** * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq * and devfreq_add_device -- cgit v0.10.2 From 389baed01edd298c9622c6e28de5e7e250c8767d Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 21 Nov 2012 19:04:51 +0900 Subject: PM / devfreq: missing rcu_read_lock() added for find_device_opp() opp_get_notifier() uses find_device_opp(), which requires to held rcu_read_lock. In order to keep the notifier-header valid, we have added rcu_read_lock(). Reported-by: Kees Cook Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 45e053e..1a78ad9 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1023,11 +1023,18 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, */ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) { - struct srcu_notifier_head *nh = opp_get_notifier(dev); + struct srcu_notifier_head *nh; + int ret = 0; + rcu_read_lock(); + nh = opp_get_notifier(dev); if (IS_ERR(nh)) - return PTR_ERR(nh); - return srcu_notifier_chain_register(nh, &devfreq->nb); + ret = PTR_ERR(nh); + rcu_read_unlock(); + if (!ret) + ret = srcu_notifier_chain_register(nh, &devfreq->nb); + + return ret; } /** @@ -1042,11 +1049,18 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) */ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) { - struct srcu_notifier_head *nh = opp_get_notifier(dev); + struct srcu_notifier_head *nh; + int ret = 0; + rcu_read_lock(); + nh = opp_get_notifier(dev); if (IS_ERR(nh)) - return PTR_ERR(nh); - return srcu_notifier_chain_unregister(nh, &devfreq->nb); + ret = PTR_ERR(nh); + rcu_read_unlock(); + if (!ret) + ret = srcu_notifier_chain_unregister(nh, &devfreq->nb); + + return ret; } MODULE_AUTHOR("MyungJoo Ham "); -- cgit v0.10.2 From b9e1c8e821e7768d5b8629ad7748456af6feece3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 21 Nov 2012 10:36:13 +0530 Subject: PM / devfreq: Fix incorrect argument in error message 'g' is cast to the error return code. Hence gives the following error which is fixed by this patch. drivers/devfreq/devfreq.c:645 devfreq_remove_governor() error: 'g' dereferencing possible ERR_PTR() Signed-off-by: Sachin Kamat Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 1a78ad9..ac4334f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -643,7 +643,7 @@ int devfreq_remove_governor(struct devfreq_governor *governor) g = find_devfreq_governor(governor->name); if (IS_ERR(g)) { pr_err("%s: governor %s not registered\n", __func__, - g->name); + governor->name); err = -EINVAL; goto err_out; } -- cgit v0.10.2 From f9c08e2acbbed19ecafc82e4549b7040cc549216 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 21 Nov 2012 10:36:14 +0530 Subject: PM / devfreq: Fix return value in devfreq_remove_governor() Use the value obtained from the function instead of -EINVAL. Signed-off-by: Sachin Kamat Signed-off-by: MyungJoo Ham diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index ac4334f..34c00c5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -644,7 +644,7 @@ int devfreq_remove_governor(struct devfreq_governor *governor) if (IS_ERR(g)) { pr_err("%s: governor %s not registered\n", __func__, governor->name); - err = -EINVAL; + err = PTR_ERR(g); goto err_out; } list_for_each_entry(devfreq, &devfreq_list, node) { -- cgit v0.10.2 From 6dcdd8e3cadd8dfcfe63d231631d70e2670970f9 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 28 Nov 2012 20:29:17 +0100 Subject: PM / devfreq: remove compiler error with module governors (2) Governors compiled as modules may use these functions. Signed-off-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 34c00c5..a8f0173 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -235,6 +235,7 @@ void devfreq_monitor_start(struct devfreq *devfreq) queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); } +EXPORT_SYMBOL(devfreq_monitor_start); /** * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance @@ -248,6 +249,7 @@ void devfreq_monitor_stop(struct devfreq *devfreq) { cancel_delayed_work_sync(&devfreq->work); } +EXPORT_SYMBOL(devfreq_monitor_stop); /** * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance @@ -273,6 +275,7 @@ void devfreq_monitor_suspend(struct devfreq *devfreq) mutex_unlock(&devfreq->lock); cancel_delayed_work_sync(&devfreq->work); } +EXPORT_SYMBOL(devfreq_monitor_suspend); /** * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance @@ -297,6 +300,7 @@ void devfreq_monitor_resume(struct devfreq *devfreq) out: mutex_unlock(&devfreq->lock); } +EXPORT_SYMBOL(devfreq_monitor_resume); /** * devfreq_interval_update() - Update device devfreq monitoring interval @@ -343,6 +347,7 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay) out: mutex_unlock(&devfreq->lock); } +EXPORT_SYMBOL(devfreq_interval_update); /** * devfreq_notifier_call() - Notify that the device frequency requirements -- cgit v0.10.2