Message ID | 1314086044-24659-5-git-send-email-myungjoo.ham@samsung.com (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
On Tue, Aug 23, 2011 at 12:54 AM, MyungJoo Ham <myungjoo.ham@samsung.com> wrote: > Device specific sysfs interface /sys/devices/.../power/devfreq_* > - governor R: name of governor > - cur_freq R: current frequency > - max_freq R: maximum operable frequency > - min_freq R: minimum operable frequency > - set_freq R: read user specified frequency (0 if not specified yet) > W: set user specified frequency > - polling_interval R: polling interval in ms given with devfreq profile > W: update polling interval. > > Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> > > -- > Changed from v6 > - poling_interval is writable. > > Changed from v5 > - updated devferq_update usage. > > Changed from v4 > - removed system-wide sysfs interface > - removed tickling sysfs interface > - added set_freq for userspace governor (and any other governors that > require user input) > > Changed from v3 > - corrected sysfs API usage > - corrected error messages > - moved sysfs entry location > - added sysfs entries > > Changed from v2 > - add ABI entries for devfreq sysfs interface > --- > Documentation/ABI/testing/sysfs-devices-power | 47 +++++++ > drivers/devfreq/devfreq.c | 185 +++++++++++++++++++++++++ > 2 files changed, 232 insertions(+), 0 deletions(-) > > diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power > index 8ffbc25..ba8bc35 100644 > --- a/Documentation/ABI/testing/sysfs-devices-power > +++ b/Documentation/ABI/testing/sysfs-devices-power > @@ -165,3 +165,50 @@ Description: > > Not all drivers support this attribute. If it isn't supported, > attempts to read or write it will yield I/O errors. > + > +What: /sys/devices/.../power/devfreq_governor > +Date: July 2011 > +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> > +Description: > + The /sys/devices/.../power/devfreq_governor shows the name > + of the governor used by the corresponding device. > + > +What: /sys/devices/.../power/devfreq_cur_freq > +Date: July 2011 > +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> > +Description: > + The /sys/devices/.../power/devfreq_cur_freq shows the current > + frequency of the corresponding device. > + > +What: /sys/devices/.../power/devfreq_max_freq > +Date: July 2011 > +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> > +Description: > + The /sys/devices/.../power/devfreq_cur_freq shows the Copy/paste error? Description should reference devfreq_max_freq not devfreq_cur_freq. > + maximum operable frequency of the corresponding device. > + > +What: /sys/devices/.../power/devfreq_min_freq > +Date: July 2011 > +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> > +Description: > + The /sys/devices/.../power/devfreq_cur_freq shows the > + minimum operable frequency of the corresponding device. Similar to the above. > + > +What: /sys/devices/.../power/devfreq_set_freq > +Date: August 2011 > +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> > +Description: > + The /sys/devices/.../power/devfreq_set_freq sets and shows > + the user specified desired frequency of the device. The > + governor may and may not use the value. With the basic > + governors given with devfreq.c, userspace governor is > + using the value. As I stated in patch 3, this should conditionally exist only if the userspace governor is being used for this device. The existing devfreq_cur_freq covers the read-only case well. Regards, Mike > + > +What: /sys/devices/.../power/devfreq_polling_interval > +Date: July 2011 > +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> > +Description: > + The /sys/devices/.../power/devfreq_polling_interval sets and > + shows the requested polling interval of the corresponding > + device. The values are represented in ms. If the value is less > + than 1 jiffy, it is considered to be 0, which means no polling. > diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c > index df63bdc..3070250 100644 > --- a/drivers/devfreq/devfreq.c > +++ b/drivers/devfreq/devfreq.c > @@ -37,6 +37,8 @@ static struct delayed_work devfreq_work; > static LIST_HEAD(devfreq_list); > static DEFINE_MUTEX(devfreq_list_lock); > > +static struct attribute_group dev_attr_group; > + > /** > * find_device_devfreq() - find devfreq struct using device pointer > * @dev: device pointer used to lookup device devfreq. > @@ -149,6 +151,8 @@ static void devfreq_monitor(struct work_struct *work) > dev_err(devfreq->dev, "Due to devfreq_do error(%d), devfreq(%s) is removed from the device\n", > error, devfreq->governor->name); > > + sysfs_unmerge_group(&devfreq->dev->kobj, > + &dev_attr_group); > list_del(&devfreq->node); > kfree(devfreq); > > @@ -239,6 +243,8 @@ int devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, > queue_delayed_work(devfreq_wq, &devfreq_work, > devfreq->next_polling); > } > + > + sysfs_merge_group(&dev->kobj, &dev_attr_group); > out: > mutex_unlock(&devfreq_list_lock); > > @@ -271,6 +277,8 @@ int devfreq_remove_device(struct device *dev) > goto out; > } > > + sysfs_unmerge_group(&dev->kobj, &dev_attr_group); > + > list_del(&devfreq->node); > srcu_notifier_chain_unregister(nh, &devfreq->nb); > kfree(devfreq); > @@ -279,6 +287,183 @@ out: > return 0; > } > > +static ssize_t show_governor(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct devfreq *df = find_device_devfreq(dev); > + > + if (IS_ERR(df)) > + return PTR_ERR(df); > + if (!df->governor) > + return -EINVAL; > + > + return sprintf(buf, "%s\n", df->governor->name); > +} > + > +static ssize_t show_freq(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct devfreq *df = find_device_devfreq(dev); > + > + if (IS_ERR(df)) > + return PTR_ERR(df); > + > + return sprintf(buf, "%lu\n", df->previous_freq); > +} > + > +static ssize_t show_max_freq(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct devfreq *df = find_device_devfreq(dev); > + unsigned long freq = ULONG_MAX; > + struct opp *opp; > + > + if (IS_ERR(df)) > + return PTR_ERR(df); > + if (!df->dev) > + return -EINVAL; > + > + opp = opp_find_freq_floor(df->dev, &freq); > + if (IS_ERR(opp)) > + return PTR_ERR(opp); > + > + return sprintf(buf, "%lu\n", freq); > +} > + > +static ssize_t show_min_freq(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct devfreq *df = find_device_devfreq(dev); > + unsigned long freq = 0; > + struct opp *opp; > + > + if (IS_ERR(df)) > + return PTR_ERR(df); > + if (!df->dev) > + return -EINVAL; > + > + opp = opp_find_freq_ceil(df->dev, &freq); > + if (IS_ERR(opp)) > + return PTR_ERR(opp); > + > + return sprintf(buf, "%lu\n", freq); > +} > + > +static ssize_t show_polling_interval(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct devfreq *df = find_device_devfreq(dev); > + > + if (IS_ERR(df)) > + return PTR_ERR(df); > + if (!df->profile) > + return -EINVAL; > + > + return sprintf(buf, "%d\n", df->profile->polling_ms); > +} > + > +static ssize_t store_polling_interval(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct devfreq *df = find_device_devfreq(dev); > + unsigned int value; > + int ret; > + > + if (IS_ERR(df)) > + return PTR_ERR(df); > + if (!df->profile) > + return -EINVAL; > + > + ret = sscanf(buf, "%u", &value); > + if (ret != 1) > + return -EINVAL; > + > + df->profile->polling_ms = value; > + df->next_polling = df->polling_jiffies > + = msecs_to_jiffies(value); > + > + 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); > + > + return count; > +} > + > +static ssize_t set_user_frequency(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct devfreq *devfreq; > + unsigned long wanted; > + int err = 0; > + > + sscanf(buf, "%lu", &wanted); > + > + mutex_lock(&devfreq_list_lock); > + devfreq = find_device_devfreq(dev); > + > + if (IS_ERR(devfreq)) { > + err = PTR_ERR(devfreq); > + goto out; > + } > + > + devfreq->user_set_freq = wanted; > + err = count; > +out: > + mutex_unlock(&devfreq_list_lock); > + if (err >= 0) > + devfreq_update(&devfreq->nb, 0, NULL); > + return err; > +} > + > +static ssize_t show_user_frequency(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct devfreq *devfreq; > + int err = 0; > + > + mutex_lock(&devfreq_list_lock); > + devfreq = find_device_devfreq(dev); > + > + if (IS_ERR(devfreq)) { > + err = PTR_ERR(devfreq); > + goto out; > + } > + > + err = sprintf(buf, "%lu\n", devfreq->user_set_freq); > +out: > + mutex_unlock(&devfreq_list_lock); > + return err; > + > +} > + > +static DEVICE_ATTR(devfreq_governor, 0444, show_governor, NULL); > +static DEVICE_ATTR(devfreq_cur_freq, 0444, show_freq, NULL); > +static DEVICE_ATTR(devfreq_max_freq, 0444, show_max_freq, NULL); > +static DEVICE_ATTR(devfreq_min_freq, 0444, show_min_freq, NULL); > +static DEVICE_ATTR(devfreq_polling_interval, 0644, show_polling_interval, > + store_polling_interval); > +static DEVICE_ATTR(devfreq_set_freq, 0644, show_user_frequency, > + set_user_frequency); > +static struct attribute *dev_entries[] = { > + &dev_attr_devfreq_governor.attr, > + &dev_attr_devfreq_cur_freq.attr, > + &dev_attr_devfreq_max_freq.attr, > + &dev_attr_devfreq_min_freq.attr, > + &dev_attr_devfreq_polling_interval.attr, > + &dev_attr_devfreq_set_freq.attr, > + NULL, > +}; > +static struct attribute_group dev_attr_group = { > + .name = power_group_name, > + .attrs = dev_entries, > +}; > + > /** > * devfreq_init() - Initialize data structure for devfreq framework and > * start polling registered devfreq devices. > -- > 1.7.4.1 > >
On Wed, Aug 24, 2011 at 2:34 AM, Turquette, Mike <mturquette@ti.com> wrote: > On Tue, Aug 23, 2011 at 12:54 AM, MyungJoo Ham <myungjoo.ham@samsung.com> wrote: >> + The /sys/devices/.../power/devfreq_cur_freq shows the > > Copy/paste error? Description should reference devfreq_max_freq not > devfreq_cur_freq. > >> + minimum operable frequency of the corresponding device. > > Similar to the above. > Thank you. I'll resubmit soon with corrections (and w/ some further changes to allow governors to have their own sysfs entries and access more on the DEVFREQ core). >> + >> +What: /sys/devices/.../power/devfreq_set_freq >> +Date: August 2011 >> +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> >> +Description: >> + The /sys/devices/.../power/devfreq_set_freq sets and shows >> + the user specified desired frequency of the device. The >> + governor may and may not use the value. With the basic >> + governors given with devfreq.c, userspace governor is >> + using the value. > > As I stated in patch 3, this should conditionally exist only if the > userspace governor is being used for this device. Ok, in the next revision, this entry will be removed and replaced with an entry created by userspace governor. > > The existing devfreq_cur_freq covers the read-only case well. It could be a bit different especially with another device driver enabling and disabling OPPs and/or PM QoS. For example, when user states "100MHz" on a device with "50/100/200MHz", it will use 100. However, if someone has disabled 100MHz with opp_disable, devfreq_cur_freq will automatically changed to 200 while the user value should remain at 100 in case where 100 is later re-enabled. > > Regards, > Mike > Thank you. Cheers! MyungJoo
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 8ffbc25..ba8bc35 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -165,3 +165,50 @@ Description: Not all drivers support this attribute. If it isn't supported, attempts to read or write it will yield I/O errors. + +What: /sys/devices/.../power/devfreq_governor +Date: July 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/devices/.../power/devfreq_governor shows the name + of the governor used by the corresponding device. + +What: /sys/devices/.../power/devfreq_cur_freq +Date: July 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/devices/.../power/devfreq_cur_freq shows the current + frequency of the corresponding device. + +What: /sys/devices/.../power/devfreq_max_freq +Date: July 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/devices/.../power/devfreq_cur_freq shows the + maximum operable frequency of the corresponding device. + +What: /sys/devices/.../power/devfreq_min_freq +Date: July 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/devices/.../power/devfreq_cur_freq shows the + minimum operable frequency of the corresponding device. + +What: /sys/devices/.../power/devfreq_set_freq +Date: August 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/devices/.../power/devfreq_set_freq sets and shows + the user specified desired frequency of the device. The + governor may and may not use the value. With the basic + governors given with devfreq.c, userspace governor is + using the value. + +What: /sys/devices/.../power/devfreq_polling_interval +Date: July 2011 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/devices/.../power/devfreq_polling_interval sets and + shows the requested polling interval of the corresponding + device. The values are represented in ms. If the value is less + than 1 jiffy, it is considered to be 0, which means no polling. diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index df63bdc..3070250 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -37,6 +37,8 @@ static struct delayed_work devfreq_work; static LIST_HEAD(devfreq_list); static DEFINE_MUTEX(devfreq_list_lock); +static struct attribute_group dev_attr_group; + /** * find_device_devfreq() - find devfreq struct using device pointer * @dev: device pointer used to lookup device devfreq. @@ -149,6 +151,8 @@ static void devfreq_monitor(struct work_struct *work) dev_err(devfreq->dev, "Due to devfreq_do error(%d), devfreq(%s) is removed from the device\n", error, devfreq->governor->name); + sysfs_unmerge_group(&devfreq->dev->kobj, + &dev_attr_group); list_del(&devfreq->node); kfree(devfreq); @@ -239,6 +243,8 @@ int devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, queue_delayed_work(devfreq_wq, &devfreq_work, devfreq->next_polling); } + + sysfs_merge_group(&dev->kobj, &dev_attr_group); out: mutex_unlock(&devfreq_list_lock); @@ -271,6 +277,8 @@ int devfreq_remove_device(struct device *dev) goto out; } + sysfs_unmerge_group(&dev->kobj, &dev_attr_group); + list_del(&devfreq->node); srcu_notifier_chain_unregister(nh, &devfreq->nb); kfree(devfreq); @@ -279,6 +287,183 @@ out: return 0; } +static ssize_t show_governor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = find_device_devfreq(dev); + + if (IS_ERR(df)) + return PTR_ERR(df); + if (!df->governor) + return -EINVAL; + + return sprintf(buf, "%s\n", df->governor->name); +} + +static ssize_t show_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = find_device_devfreq(dev); + + if (IS_ERR(df)) + return PTR_ERR(df); + + return sprintf(buf, "%lu\n", df->previous_freq); +} + +static ssize_t show_max_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = find_device_devfreq(dev); + unsigned long freq = ULONG_MAX; + struct opp *opp; + + if (IS_ERR(df)) + return PTR_ERR(df); + if (!df->dev) + return -EINVAL; + + opp = opp_find_freq_floor(df->dev, &freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + return sprintf(buf, "%lu\n", freq); +} + +static ssize_t show_min_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = find_device_devfreq(dev); + unsigned long freq = 0; + struct opp *opp; + + if (IS_ERR(df)) + return PTR_ERR(df); + if (!df->dev) + return -EINVAL; + + opp = opp_find_freq_ceil(df->dev, &freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + return sprintf(buf, "%lu\n", freq); +} + +static ssize_t show_polling_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = find_device_devfreq(dev); + + if (IS_ERR(df)) + return PTR_ERR(df); + if (!df->profile) + return -EINVAL; + + return sprintf(buf, "%d\n", df->profile->polling_ms); +} + +static ssize_t store_polling_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *df = find_device_devfreq(dev); + unsigned int value; + int ret; + + if (IS_ERR(df)) + return PTR_ERR(df); + if (!df->profile) + return -EINVAL; + + ret = sscanf(buf, "%u", &value); + if (ret != 1) + return -EINVAL; + + df->profile->polling_ms = value; + df->next_polling = df->polling_jiffies + = msecs_to_jiffies(value); + + 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); + + return count; +} + +static ssize_t set_user_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *devfreq; + unsigned long wanted; + int err = 0; + + sscanf(buf, "%lu", &wanted); + + mutex_lock(&devfreq_list_lock); + devfreq = find_device_devfreq(dev); + + if (IS_ERR(devfreq)) { + err = PTR_ERR(devfreq); + goto out; + } + + devfreq->user_set_freq = wanted; + err = count; +out: + mutex_unlock(&devfreq_list_lock); + if (err >= 0) + devfreq_update(&devfreq->nb, 0, NULL); + return err; +} + +static ssize_t show_user_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *devfreq; + int err = 0; + + mutex_lock(&devfreq_list_lock); + devfreq = find_device_devfreq(dev); + + if (IS_ERR(devfreq)) { + err = PTR_ERR(devfreq); + goto out; + } + + err = sprintf(buf, "%lu\n", devfreq->user_set_freq); +out: + mutex_unlock(&devfreq_list_lock); + return err; + +} + +static DEVICE_ATTR(devfreq_governor, 0444, show_governor, NULL); +static DEVICE_ATTR(devfreq_cur_freq, 0444, show_freq, NULL); +static DEVICE_ATTR(devfreq_max_freq, 0444, show_max_freq, NULL); +static DEVICE_ATTR(devfreq_min_freq, 0444, show_min_freq, NULL); +static DEVICE_ATTR(devfreq_polling_interval, 0644, show_polling_interval, + store_polling_interval); +static DEVICE_ATTR(devfreq_set_freq, 0644, show_user_frequency, + set_user_frequency); +static struct attribute *dev_entries[] = { + &dev_attr_devfreq_governor.attr, + &dev_attr_devfreq_cur_freq.attr, + &dev_attr_devfreq_max_freq.attr, + &dev_attr_devfreq_min_freq.attr, + &dev_attr_devfreq_polling_interval.attr, + &dev_attr_devfreq_set_freq.attr, + NULL, +}; +static struct attribute_group dev_attr_group = { + .name = power_group_name, + .attrs = dev_entries, +}; + /** * devfreq_init() - Initialize data structure for devfreq framework and * start polling registered devfreq devices.