diff mbox

[v6,4/4] PM / DEVFREQ: add sysfs interface

Message ID 1313663262-15308-5-git-send-email-myungjoo.ham@samsung.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

MyungJoo Ham Aug. 18, 2011, 10:27 a.m. UTC
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

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
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 |   45 +++++++
 drivers/devfreq/devfreq.c                     |  152 +++++++++++++++++++++++++
 2 files changed, 197 insertions(+), 0 deletions(-)

Comments

Mike Turquette Aug. 19, 2011, 3:15 a.m. UTC | #1
On Thu, Aug 18, 2011 at 3:27 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

This code still does not export up_threshold/down_threshold entries
for simple-ondemand which I requested in V5.  However there is a much
bigger issue here brought out in patch 3.  Those values shouldn't be
global, since devices might want different thresholds.

The solution is not to put those tunables in struct devfreq.
up_threshold and down_threshold must be parameters of the governor,
and there must be a per-device instance of the governor.

Regards,
Mike

>
> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>
> --
> 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 |   45 +++++++
>  drivers/devfreq/devfreq.c                     |  152 +++++++++++++++++++++++++
>  2 files changed, 197 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
> index 8ffbc25..6e77604 100644
> --- a/Documentation/ABI/testing/sysfs-devices-power
> +++ b/Documentation/ABI/testing/sysfs-devices-power
> @@ -165,3 +165,48 @@ 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 shows the
> +               requested polling interval of the corresponding device.
> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index 2036f2c..13b422f 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.
> @@ -154,6 +156,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);
>
> @@ -244,6 +248,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);
>
> @@ -276,6 +282,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);
> @@ -284,6 +292,150 @@ 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 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, 0444, show_polling_interval, NULL);
> +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
>
>
Mike Turquette Aug. 22, 2011, 6:28 p.m. UTC | #2
On Fri, Aug 19, 2011 at 12:28 AM, MyungJoo Ham <myungjoo.ham@samsung.com> wrote:
> On Fri, Aug 19, 2011 at 12:15 PM, Turquette, Mike <mturquette@ti.com> wrote:
>> On Thu, Aug 18, 2011 at 3:27 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
>>
>> This code still does not export up_threshold/down_threshold entries
>> for simple-ondemand which I requested in V5.  However there is a much
>> bigger issue here brought out in patch 3.  Those values shouldn't be
>> global, since devices might want different thresholds.
>>
>> The solution is not to put those tunables in struct devfreq.
>> up_threshold and down_threshold must be parameters of the governor,
>> and there must be a per-device instance of the governor.
>>
>> Regards,
>> Mike
>
> As in the example in the reply to the thread of patch 3/4 of v6
> patchset, we do not need to create another per-device object in
> DEVFREQ in order to allow governors to be tuned per device. The global
> values you've mentioned are only used as default and each instance of
> devfreq (per device) can specify its own tuneable values and they can
> alter the values in run-time.
>
> For the sysfs interfaces of governor-specific tunable values, I'd like
> to see that as a "future work" and that would be implemented
> per-governor basis; i.e., probably under
> /sysfs/devices/.../power/NAME_OF_GOVERNOR/variable_name. Or should I
> regard the sysfs interface to tune governors at userland is somewhat
> essential and critical for the initial release?

I guess it is not critical but I feel that implementing all of the
sysfs bits will reveal some designs issues.

E.g. by pushing threshold data into the device's private_data you're
crossing data paths with the governors: the private data tunable must
be associated per-device in sysfs, but the read/write functions must
come from the governor.  If the core code assumes too much about what
private data looks like (up_threshold, down_threshold, etc) then it
might be limiting to future governor authors.

The patches are shaping up however and I won't consider sysfs support
a blocker.  Rafael's point about revisiting design at a later date is
good guidance here.

Regards,
Mike

> Cheers!
>
> MyungJoo
>
>>
>>>
>>> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>>
>>> --
>>> 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 |   45 +++++++
>>>  drivers/devfreq/devfreq.c                     |  152 +++++++++++++++++++++++++
>>>  2 files changed, 197 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
>>> index 8ffbc25..6e77604 100644
>>> --- a/Documentation/ABI/testing/sysfs-devices-power
>>> +++ b/Documentation/ABI/testing/sysfs-devices-power
>>> @@ -165,3 +165,48 @@ 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 shows the
>>> +               requested polling interval of the corresponding device.
>>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>>> index 2036f2c..13b422f 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.
>>> @@ -154,6 +156,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);
>>>
>>> @@ -244,6 +248,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);
>>>
>>> @@ -276,6 +282,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);
>>> @@ -284,6 +292,150 @@ 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 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, 0444, show_polling_interval, NULL);
>>> +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
>>>
>>>
>>
>
>
>
> --
> MyungJoo Ham (???), Ph.D.
> Mobile Software Platform Lab,
> Digital Media and Communications (DMC) Business
> Samsung Electronics
> cell: 82-10-6714-2858
>
diff mbox

Patch

diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
index 8ffbc25..6e77604 100644
--- a/Documentation/ABI/testing/sysfs-devices-power
+++ b/Documentation/ABI/testing/sysfs-devices-power
@@ -165,3 +165,48 @@  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 shows the
+		requested polling interval of the corresponding device.
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 2036f2c..13b422f 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.
@@ -154,6 +156,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);
 
@@ -244,6 +248,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);
 
@@ -276,6 +282,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);
@@ -284,6 +292,150 @@  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 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, 0444, show_polling_interval, NULL);
+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.