]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/devfreq/devfreq.c
Merge branches 'pm-core', 'pm-qos', 'pm-domains' and 'pm-opp'
[linux.git] / drivers / devfreq / devfreq.c
index 47206a21bb901f424c4e331c9db6eaedef69d93d..551a271353d2a49f98221a03af5c235405d50fe5 100644 (file)
@@ -111,18 +111,16 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
                return;
        }
 
-       rcu_read_lock();
        for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
                opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);
                if (IS_ERR(opp)) {
                        devm_kfree(devfreq->dev.parent, profile->freq_table);
                        profile->max_state = 0;
-                       rcu_read_unlock();
                        return;
                }
+               dev_pm_opp_put(opp);
                profile->freq_table[i] = freq;
        }
-       rcu_read_unlock();
 }
 
 /**
@@ -130,7 +128,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
  * @devfreq:   the devfreq instance
  * @freq:      the update target frequency
  */
-static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
+int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 {
        int lev, prev_lev, ret = 0;
        unsigned long cur_time;
@@ -166,6 +164,7 @@ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
        devfreq->last_stat_updated = cur_time;
        return ret;
 }
+EXPORT_SYMBOL(devfreq_update_status);
 
 /**
  * find_devfreq_governor() - find devfreq governor from name
@@ -474,11 +473,15 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
 }
 
 /**
- * _remove_devfreq() - Remove devfreq from the list and release its resources.
- * @devfreq:   the devfreq struct
+ * devfreq_dev_release() - Callback for struct device to release the device.
+ * @dev:       the devfreq device
+ *
+ * Remove devfreq from the list and release its resources.
  */
-static void _remove_devfreq(struct devfreq *devfreq)
+static void devfreq_dev_release(struct device *dev)
 {
+       struct devfreq *devfreq = to_devfreq(dev);
+
        mutex_lock(&devfreq_list_lock);
        if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
                mutex_unlock(&devfreq_list_lock);
@@ -499,19 +502,6 @@ static void _remove_devfreq(struct devfreq *devfreq)
        kfree(devfreq);
 }
 
-/**
- * devfreq_dev_release() - Callback for struct device to release the device.
- * @dev:       the devfreq device
- *
- * This calls _remove_devfreq() if _remove_devfreq() is not called.
- */
-static void devfreq_dev_release(struct device *dev)
-{
-       struct devfreq *devfreq = to_devfreq(dev);
-
-       _remove_devfreq(devfreq);
-}
-
 /**
  * devfreq_add_device() - Add devfreq feature to the device
  * @dev:       the device to add devfreq feature.
@@ -527,6 +517,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
 {
        struct devfreq *devfreq;
        struct devfreq_governor *governor;
+       static atomic_t devfreq_no = ATOMIC_INIT(-1);
        int err = 0;
 
        if (!dev || !profile || !governor_name) {
@@ -538,15 +529,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
        devfreq = find_device_devfreq(dev);
        mutex_unlock(&devfreq_list_lock);
        if (!IS_ERR(devfreq)) {
-               dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__);
+               dev_err(dev, "%s: Unable to create devfreq for the device.\n",
+                       __func__);
                err = -EINVAL;
                goto err_out;
        }
 
        devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
        if (!devfreq) {
-               dev_err(dev, "%s: Unable to create devfreq for the device\n",
-                       __func__);
                err = -ENOMEM;
                goto err_out;
        }
@@ -569,18 +559,21 @@ struct devfreq *devfreq_add_device(struct device *dev,
                mutex_lock(&devfreq->lock);
        }
 
-       dev_set_name(&devfreq->dev, "%s", dev_name(dev));
+       dev_set_name(&devfreq->dev, "devfreq%d",
+                               atomic_inc_return(&devfreq_no));
        err = device_register(&devfreq->dev);
        if (err) {
                mutex_unlock(&devfreq->lock);
                goto err_out;
        }
 
-       devfreq->trans_table =  devm_kzalloc(&devfreq->dev, sizeof(unsigned int) *
+       devfreq->trans_table =  devm_kzalloc(&devfreq->dev,
+                                               sizeof(unsigned int) *
                                                devfreq->profile->max_state *
                                                devfreq->profile->max_state,
                                                GFP_KERNEL);
-       devfreq->time_in_state = devm_kzalloc(&devfreq->dev, sizeof(unsigned long) *
+       devfreq->time_in_state = devm_kzalloc(&devfreq->dev,
+                                               sizeof(unsigned long) *
                                                devfreq->profile->max_state,
                                                GFP_KERNEL);
        devfreq->last_stat_updated = jiffies;
@@ -939,6 +932,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
        if (df->governor == governor) {
                ret = 0;
                goto out;
+       } else if (df->governor->immutable || governor->immutable) {
+               ret = -EINVAL;
+               goto out;
        }
 
        if (df->governor) {
@@ -968,13 +964,33 @@ static ssize_t available_governors_show(struct device *d,
                                        struct device_attribute *attr,
                                        char *buf)
 {
-       struct devfreq_governor *tmp_governor;
+       struct devfreq *df = to_devfreq(d);
        ssize_t count = 0;
 
        mutex_lock(&devfreq_list_lock);
-       list_for_each_entry(tmp_governor, &devfreq_governor_list, node)
-               count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
-                                  "%s ", tmp_governor->name);
+
+       /*
+        * The devfreq with immutable governor (e.g., passive) shows
+        * only own governor.
+        */
+       if (df->governor->immutable) {
+               count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
+                                  "%s ", df->governor_name);
+       /*
+        * The devfreq device shows the registered governor except for
+        * immutable governors such as passive governor .
+        */
+       } else {
+               struct devfreq_governor *governor;
+
+               list_for_each_entry(governor, &devfreq_governor_list, node) {
+                       if (governor->immutable)
+                               continue;
+                       count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
+                                          "%s ", governor->name);
+               }
+       }
+
        mutex_unlock(&devfreq_list_lock);
 
        /* Truncate the trailing space */
@@ -995,7 +1011,7 @@ static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr,
 
        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", freq);
 
        return sprintf(buf, "%lu\n", devfreq->previous_freq);
 }
@@ -1112,17 +1128,16 @@ static ssize_t available_frequencies_show(struct device *d,
        ssize_t count = 0;
        unsigned long freq = 0;
 
-       rcu_read_lock();
        do {
                opp = dev_pm_opp_find_freq_ceil(dev, &freq);
                if (IS_ERR(opp))
                        break;
 
+               dev_pm_opp_put(opp);
                count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
                                   "%lu ", freq);
                freq++;
        } while (1);
-       rcu_read_unlock();
 
        /* Truncate the trailing space */
        if (count)
@@ -1224,11 +1239,8 @@ subsys_initcall(devfreq_init);
  * @freq:      The frequency given to target function
  * @flags:     Flags handed from devfreq framework.
  *
- * Locking: This function must be called under rcu_read_lock(). opp is a rcu
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
- * under the locked area. The pointer returned must be used prior to unlocking
- * with rcu_read_unlock() to maintain the integrity of the pointer.
+ * The callers are required to call dev_pm_opp_put() for the returned OPP after
+ * use.
  */
 struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
                                           unsigned long *freq,
@@ -1265,18 +1277,7 @@ EXPORT_SYMBOL(devfreq_recommended_opp);
  */
 int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
 {
-       struct srcu_notifier_head *nh;
-       int ret = 0;
-
-       rcu_read_lock();
-       nh = dev_pm_opp_get_notifier(dev);
-       if (IS_ERR(nh))
-               ret = PTR_ERR(nh);
-       rcu_read_unlock();
-       if (!ret)
-               ret = srcu_notifier_chain_register(nh, &devfreq->nb);
-
-       return ret;
+       return dev_pm_opp_register_notifier(dev, &devfreq->nb);
 }
 EXPORT_SYMBOL(devfreq_register_opp_notifier);
 
@@ -1292,18 +1293,7 @@ EXPORT_SYMBOL(devfreq_register_opp_notifier);
  */
 int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
 {
-       struct srcu_notifier_head *nh;
-       int ret = 0;
-
-       rcu_read_lock();
-       nh = dev_pm_opp_get_notifier(dev);
-       if (IS_ERR(nh))
-               ret = PTR_ERR(nh);
-       rcu_read_unlock();
-       if (!ret)
-               ret = srcu_notifier_chain_unregister(nh, &devfreq->nb);
-
-       return ret;
+       return dev_pm_opp_unregister_notifier(dev, &devfreq->nb);
 }
 EXPORT_SYMBOL(devfreq_unregister_opp_notifier);