diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 54 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 162 | ||||
-rw-r--r-- | drivers/devfreq/governor.h | 12 | ||||
-rw-r--r-- | drivers/devfreq/governor_simpleondemand.c | 2 | ||||
-rw-r--r-- | drivers/devfreq/tegra30-devfreq.c | 1 |
5 files changed, 159 insertions, 72 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index deefffb3bbe4..67af3f31e17c 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -37,20 +37,6 @@ 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 -Contact: MyungJoo Ham <myungjoo.ham@samsung.com> -Description: - The /sys/class/devfreq/.../polling_interval shows and sets - the requested polling interval of the corresponding devfreq - object. The values are represented in ms. If the value is - less than 1 jiffy, it is considered to be 0, which means - no polling. This value is meaningless if the governor is - not polling; thus. If the governor is not using - devfreq-provided central polling - (/sys/class/devfreq/.../central_polling is 0), this value - may be useless. - What: /sys/class/devfreq/.../trans_stat Date: October 2012 Contact: MyungJoo Ham <myungjoo.ham@samsung.com> @@ -65,14 +51,6 @@ Description: as following: echo 0 > /sys/class/devfreq/.../trans_stat -What: /sys/class/devfreq/.../userspace/set_freq -Date: September 2011 -Contact: MyungJoo Ham <myungjoo.ham@samsung.com> -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 <nm@ti.com> @@ -109,6 +87,35 @@ Description: The max_freq overrides min_freq because max_freq may be used to throttle devices to avoid overheating. +What: /sys/class/devfreq/.../polling_interval +Date: September 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/class/devfreq/.../polling_interval shows and sets + the requested polling interval of the corresponding devfreq + object. The values are represented in ms. If the value is + less than 1 jiffy, it is considered to be 0, which means + no polling. This value is meaningless if the governor is + not polling; thus. If the governor is not using + devfreq-provided central polling + (/sys/class/devfreq/.../central_polling is 0), this value + may be useless. + + A list of governors that support the node: + - simple_ondmenad + - tegra_actmon + +What: /sys/class/devfreq/.../userspace/set_freq +Date: September 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/class/devfreq/.../userspace/set_freq shows and + sets the requested frequency for the devfreq object if + userspace governor is in effect. + + A list of governors that support the node: + - userspace + What: /sys/class/devfreq/.../timer Date: July 2020 Contact: Chanwoo Choi <cw00.choi@samsung.com> @@ -120,3 +127,6 @@ Description: as following: echo deferrable > /sys/class/devfreq/.../timer echo delayed > /sys/class/devfreq/.../timer + + A list of governors that support the node: + - simple_ondemand diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a862acfe987e..02cfc6552913 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -32,6 +32,7 @@ #include <trace/events/devfreq.h> #define IS_SUPPORTED_FLAG(f, name) ((f & DEVFREQ_GOV_FLAG_##name) ? true : false) +#define IS_SUPPORTED_ATTR(f, name) ((f & DEVFREQ_GOV_ATTR_##name) ? true : false) #define HZ_PER_KHZ 1000 static struct class *devfreq_class; @@ -760,6 +761,11 @@ static void devfreq_dev_release(struct device *dev) kfree(devfreq); } +static void create_sysfs_files(struct devfreq *devfreq, + const struct devfreq_governor *gov); +static void remove_sysfs_files(struct devfreq *devfreq, + const struct devfreq_governor *gov); + /** * devfreq_add_device() - Add devfreq feature to the device * @dev: the device to add devfreq feature. @@ -917,6 +923,7 @@ struct devfreq *devfreq_add_device(struct device *dev, __func__); goto err_init; } + create_sysfs_files(devfreq, devfreq->governor); list_add(&devfreq->node, &devfreq_list); @@ -947,9 +954,12 @@ int devfreq_remove_device(struct devfreq *devfreq) if (!devfreq) return -EINVAL; - if (devfreq->governor) + if (devfreq->governor) { devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); + remove_sysfs_files(devfreq, devfreq->governor); + } + device_unregister(&devfreq->dev); return 0; @@ -1378,13 +1388,22 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, goto out; } + /* + * Stop the current governor and remove the specific sysfs files + * which depend on current 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; } + remove_sysfs_files(df, df->governor); + /* + * Start the new governor and create the specific sysfs files + * which depend on the new governor. + */ prev_governor = df->governor; df->governor = governor; strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); @@ -1392,6 +1411,8 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, if (ret) { dev_warn(dev, "%s: Governor %s not started(%d)\n", __func__, df->governor->name, ret); + + /* Restore previous governor */ df->governor = prev_governor; strncpy(df->governor_name, prev_governor->name, DEVFREQ_NAME_LEN); @@ -1401,8 +1422,16 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, "%s: reverting to Governor %s failed (%d)\n", __func__, df->governor_name, ret); df->governor = NULL; + goto out; } } + + /* + * Create the sysfs files for the new governor. But if failed to start + * the new governor, restore the sysfs files of previous governor. + */ + create_sysfs_files(df, df->governor); + out: mutex_unlock(&devfreq_list_lock); @@ -1484,39 +1513,6 @@ static ssize_t target_freq_show(struct device *dev, } static DEVICE_ATTR_RO(target_freq); -static ssize_t polling_interval_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct devfreq *df = to_devfreq(dev); - - if (!df->profile) - return -EINVAL; - - return sprintf(buf, "%d\n", df->profile->polling_ms); -} - -static ssize_t polling_interval_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct devfreq *df = to_devfreq(dev); - unsigned int value; - int ret; - - if (!df->governor) - return -EINVAL; - - ret = sscanf(buf, "%u", &value); - if (ret != 1) - return -EINVAL; - - df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value); - ret = count; - - return ret; -} -static DEVICE_ATTR_RW(polling_interval); - static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1724,6 +1720,53 @@ static ssize_t trans_stat_store(struct device *dev, } static DEVICE_ATTR_RW(trans_stat); +static struct attribute *devfreq_attrs[] = { + &dev_attr_name.attr, + &dev_attr_governor.attr, + &dev_attr_available_governors.attr, + &dev_attr_cur_freq.attr, + &dev_attr_available_frequencies.attr, + &dev_attr_target_freq.attr, + &dev_attr_min_freq.attr, + &dev_attr_max_freq.attr, + &dev_attr_trans_stat.attr, + NULL, +}; +ATTRIBUTE_GROUPS(devfreq); + +static ssize_t polling_interval_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = to_devfreq(dev); + + if (!df->profile) + return -EINVAL; + + return sprintf(buf, "%d\n", df->profile->polling_ms); +} + +static ssize_t polling_interval_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *df = to_devfreq(dev); + unsigned int value; + int ret; + + if (!df->governor) + return -EINVAL; + + ret = sscanf(buf, "%u", &value); + if (ret != 1) + return -EINVAL; + + df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value); + ret = count; + + return ret; +} +static DEVICE_ATTR_RW(polling_interval); + static ssize_t timer_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1787,21 +1830,36 @@ out: } static DEVICE_ATTR_RW(timer); -static struct attribute *devfreq_attrs[] = { - &dev_attr_name.attr, - &dev_attr_governor.attr, - &dev_attr_available_governors.attr, - &dev_attr_cur_freq.attr, - &dev_attr_available_frequencies.attr, - &dev_attr_target_freq.attr, - &dev_attr_polling_interval.attr, - &dev_attr_min_freq.attr, - &dev_attr_max_freq.attr, - &dev_attr_trans_stat.attr, - &dev_attr_timer.attr, - NULL, -}; -ATTRIBUTE_GROUPS(devfreq); +#define CREATE_SYSFS_FILE(df, name) \ +{ \ + int ret; \ + ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \ + if (ret < 0) { \ + dev_warn(&df->dev, \ + "Unable to create attr(%s)\n", "##name"); \ + } \ +} \ + +/* Create the specific sysfs files which depend on each governor. */ +static void create_sysfs_files(struct devfreq *devfreq, + const struct devfreq_governor *gov) +{ + if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL)) + CREATE_SYSFS_FILE(devfreq, polling_interval); + if (IS_SUPPORTED_ATTR(gov->attrs, TIMER)) + CREATE_SYSFS_FILE(devfreq, timer); +} + +/* Remove the specific sysfs files which depend on each governor. */ +static void remove_sysfs_files(struct devfreq *devfreq, + const struct devfreq_governor *gov) +{ + if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL)) + sysfs_remove_file(&devfreq->dev.kobj, + &dev_attr_polling_interval.attr); + if (IS_SUPPORTED_ATTR(gov->attrs, TIMER)) + sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr); +} /** * devfreq_summary_show() - Show the summary of the devfreq devices @@ -1858,8 +1916,12 @@ static int devfreq_summary_show(struct seq_file *s, void *data) mutex_lock(&devfreq->lock); cur_freq = devfreq->previous_freq; get_freq_range(devfreq, &min_freq, &max_freq); - polling_ms = devfreq->profile->polling_ms; timer = devfreq->profile->timer; + + if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL)) + polling_ms = devfreq->profile->polling_ms; + else + polling_ms = 0; mutex_unlock(&devfreq->lock); seq_printf(s, diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 7dbb110a869e..df413b851bb2 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -35,10 +35,21 @@ #define DEVFREQ_GOV_FLAG_IMMUTABLE BIT(0) #define DEVFREQ_GOV_FLAG_IRQ_DRIVEN BIT(1) +/* + * Definition of governor attribute flags except for common sysfs attributes + * - DEVFREQ_GOV_ATTR_POLLING_INTERVAL + * : Indicate polling_interal sysfs attribute + * - DEVFREQ_GOV_ATTR_TIMER + * : Indicate timer sysfs attribute + */ +#define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0) +#define DEVFREQ_GOV_ATTR_TIMER BIT(1) + /** * struct devfreq_governor - Devfreq policy governor * @node: list node - contains registered devfreq governors * @name: Governor's name + * @attrs: Governor's sysfs attribute flags * @flags: Governor's feature flags * @get_target_freq: Returns desired operating frequency for the device. * Basically, get_target_freq will run @@ -57,6 +68,7 @@ struct devfreq_governor { struct list_head node; const char name[DEVFREQ_NAME_LEN]; + const u64 attrs; const u64 flags; int (*get_target_freq)(struct devfreq *this, unsigned long *freq); int (*event_handler)(struct devfreq *devfreq, diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 1b314e1df028..d57b82a2b570 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -117,6 +117,8 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_simple_ondemand = { .name = DEVFREQ_GOV_SIMPLE_ONDEMAND, + .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL + | DEVFREQ_GOV_ATTR_TIMER, .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, }; diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c index faa1aecf2a31..8f5b60bef336 100644 --- a/drivers/devfreq/tegra30-devfreq.c +++ b/drivers/devfreq/tegra30-devfreq.c @@ -765,6 +765,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, static struct devfreq_governor tegra_devfreq_governor = { .name = "tegra_actmon", + .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL, .flags = DEVFREQ_GOV_FLAG_IMMUTABLE | DEVFREQ_GOV_FLAG_IRQ_DRIVEN, .get_target_freq = tegra_governor_get_target, |