PM / devfreq: add min/max_freq limit requested by users.
MyungJoo Ham [Fri, 9 Dec 2011 07:42:19 +0000 (16:42 +0900)]
The frequency requested to devfreq device driver from devfreq governors
is restricted by min_freq and max_freq input.

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

drivers/devfreq/devfreq.c
drivers/devfreq/governor_performance.c
drivers/devfreq/governor_powersave.c
drivers/devfreq/governor_simpleondemand.c
drivers/devfreq/governor_userspace.c
include/linux/devfreq.h

index c189b82..a129a7b 100644 (file)
@@ -501,12 +501,82 @@ static ssize_t show_central_polling(struct device *dev,
                       !to_devfreq(dev)->governor->no_central_polling);
 }
 
+static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct devfreq *df = to_devfreq(dev);
+       unsigned long value;
+       int ret;
+       unsigned long max;
+
+       ret = sscanf(buf, "%lu", &value);
+       if (ret != 1)
+               goto out;
+
+       mutex_lock(&df->lock);
+       max = df->max_freq;
+       if (value && max && value > max) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       df->min_freq = value;
+       update_devfreq(df);
+       ret = count;
+unlock:
+       mutex_unlock(&df->lock);
+out:
+       return ret;
+}
+
+static ssize_t show_min_freq(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       return sprintf(buf, "%lu\n", to_devfreq(dev)->min_freq);
+}
+
+static ssize_t store_max_freq(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct devfreq *df = to_devfreq(dev);
+       unsigned long value;
+       int ret;
+       unsigned long min;
+
+       ret = sscanf(buf, "%lu", &value);
+       if (ret != 1)
+               goto out;
+
+       mutex_lock(&df->lock);
+       min = df->min_freq;
+       if (value && min && value < min) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       df->max_freq = value;
+       update_devfreq(df);
+       ret = count;
+unlock:
+       mutex_unlock(&df->lock);
+out:
+       return ret;
+}
+
+static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq);
+}
+
 static struct device_attribute devfreq_attrs[] = {
        __ATTR(governor, S_IRUGO, show_governor, NULL),
        __ATTR(cur_freq, S_IRUGO, show_freq, NULL),
        __ATTR(central_polling, S_IRUGO, show_central_polling, NULL),
        __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval,
               store_polling_interval),
+       __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq),
+       __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq),
        { },
 };
 
index c0596b2..574a06b 100644 (file)
@@ -18,7 +18,10 @@ static int devfreq_performance_func(struct devfreq *df,
         * target callback should be able to get floor value as
         * said in devfreq.h
         */
-       *freq = UINT_MAX;
+       if (!df->max_freq)
+               *freq = UINT_MAX;
+       else
+               *freq = df->max_freq;
        return 0;
 }
 
index 2483a85..d742d4a 100644 (file)
@@ -18,7 +18,7 @@ static int devfreq_powersave_func(struct devfreq *df,
         * target callback should be able to get ceiling value as
         * said in devfreq.h
         */
-       *freq = 0;
+       *freq = df->min_freq;
        return 0;
 }
 
index efad8dc..a2e3eae 100644 (file)
@@ -25,6 +25,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
        unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
        unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
        struct devfreq_simple_ondemand_data *data = df->data;
+       unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
 
        if (err)
                return err;
@@ -41,7 +42,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
 
        /* Assume MAX if it is going to be divided by zero */
        if (stat.total_time == 0) {
-               *freq = UINT_MAX;
+               *freq = max;
                return 0;
        }
 
@@ -54,13 +55,13 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
        /* Set MAX if it's busy enough */
        if (stat.busy_time * 100 >
            stat.total_time * dfso_upthreshold) {
-               *freq = UINT_MAX;
+               *freq = max;
                return 0;
        }
 
        /* Set MAX if we do not know the initial frequency */
        if (stat.current_frequency == 0) {
-               *freq = UINT_MAX;
+               *freq = max;
                return 0;
        }
 
@@ -79,6 +80,11 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
        b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
        *freq = (unsigned long) b;
 
+       if (df->min_freq && *freq < df->min_freq)
+               *freq = df->min_freq;
+       if (df->max_freq && *freq > df->max_freq)
+               *freq = df->max_freq;
+
        return 0;
 }
 
index 4f8b563..0681246 100644 (file)
@@ -25,10 +25,19 @@ static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
 {
        struct userspace_data *data = df->data;
 
-       if (!data->valid)
+       if (data->valid) {
+               unsigned long adjusted_freq = data->user_frequency;
+
+               if (df->max_freq && adjusted_freq > df->max_freq)
+                       adjusted_freq = df->max_freq;
+
+               if (df->min_freq && adjusted_freq < df->min_freq)
+                       adjusted_freq = df->min_freq;
+
+               *freq = adjusted_freq;
+       } else {
                *freq = df->previous_freq; /* No user freq specified yet */
-       else
-               *freq = data->user_frequency;
+       }
        return 0;
 }
 
index f7eb7d0..5862475 100644 (file)
@@ -124,6 +124,8 @@ struct devfreq_governor {
  *             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)
  *
  * This structure stores the devfreq information for a give device.
  *
@@ -149,6 +151,9 @@ struct devfreq {
        void *data; /* private data for governors */
 
        bool being_removed;
+
+       unsigned long min_freq;
+       unsigned long max_freq;
 };
 
 #if defined(CONFIG_PM_DEVFREQ)