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 --- include/linux/devfreq.h | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) (limited to 'include/linux/devfreq.h') diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 281c72a3b9d5..9cdffde74bb5 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 v1.2.3 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 --- drivers/devfreq/devfreq.c | 28 ++++++++++++++++++++++++++++ drivers/devfreq/governor.h | 2 ++ drivers/devfreq/governor_simpleondemand.c | 9 +++++++++ include/linux/devfreq.h | 12 ++++++++++++ 4 files changed, 51 insertions(+) (limited to 'include/linux/devfreq.h') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 1aaf1aeb1f1d..999600da21c7 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 bb3aff32d627..26432ac0a398 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 3716a659122b..b5cf0fb24efe 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 9cdffde74bb5..ee243a3229b8 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 v1.2.3 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 --- Documentation/ABI/testing/sysfs-class-devfreq | 11 ++++++++++- drivers/devfreq/devfreq.c | 14 ++++++++++++++ include/linux/devfreq.h | 3 +++ 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'include/linux/devfreq.h') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 89283b1b0240..e6cf08e6734d 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 999600da21c7..f2f8a976c465 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -463,6 +463,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 ee243a3229b8..7e2e2ea4a70f 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 v1.2.3 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 --- include/linux/devfreq.h | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'include/linux/devfreq.h') diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 7e2e2ea4a70f..1461fb2355ad 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 v1.2.3 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 --- Documentation/ABI/testing/sysfs-class-devfreq | 11 +++ drivers/devfreq/devfreq.c | 101 ++++++++++++++++++++++++++ include/linux/devfreq.h | 15 ++++ 3 files changed, 127 insertions(+) (limited to 'include/linux/devfreq.h') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index e672ccb02e7f..40f98a9428dc 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 c44e562bdfe0..bf6de38190cf 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 1461fb2355ad..bc35c4aee6a3 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 v1.2.3 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 --- drivers/devfreq/devfreq.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/devfreq/governor.h | 4 ++ include/linux/devfreq.h | 3 ++ 3 files changed, 98 insertions(+) (limited to 'include/linux/devfreq.h') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index e0002c5cbadc..679ac424472f 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 26432ac0a398..fad7d6321978 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 bc35c4aee6a3..6484a3f8dda8 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 v1.2.3 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 --- drivers/devfreq/devfreq.c | 95 ++++++++++++++++++++++++++++--- drivers/devfreq/exynos4_bus.c | 2 +- drivers/devfreq/governor_performance.c | 2 +- drivers/devfreq/governor_powersave.c | 2 +- drivers/devfreq/governor_simpleondemand.c | 2 +- drivers/devfreq/governor_userspace.c | 2 +- include/linux/devfreq.h | 21 ++----- 7 files changed, 96 insertions(+), 30 deletions(-) (limited to 'include/linux/devfreq.h') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 679ac424472f..0d7be03d561f 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 68145316c49c..b8ac28497b37 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 db8ff77dbed2..865a36956917 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 30f0fca8d635..8612c0f96b79 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 85f9ed531b1e..a870a24bb56b 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 110f178fec04..34fb80f50cf6 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 6484a3f8dda8..235248cb2c93 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 v1.2.3 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 --- include/linux/devfreq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/devfreq.h') diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 235248cb2c93..e83ef39b3bea 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 v1.2.3