PM / devfreq: provide hooks for governors to be registered
authorNishanth Menon <nm@ti.com>
Mon, 29 Oct 2012 20:01:43 +0000 (15:01 -0500)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Tue, 20 Nov 2012 09:46:12 +0000 (18:46 +0900)
Add devfreq_add_governor and devfreq_remove_governor which
can be invoked by governors to register with devfreq.

This sets up the stage to dynamically switch governors and
allow governors to be dynamically loaded as well.

Cc: Rajagopal Venkat <rajagopal.venkat@linaro.org>
Cc: MyungJoo Ham <myungjoo.ham@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Kevin Hilman <khilman@ti.com>
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
drivers/devfreq/devfreq.c
drivers/devfreq/governor.h
include/linux/devfreq.h

index e0002c5cbadcd3f120aad0f67b9fdf1994147b1d..679ac424472f6567f5588b0e90de1d062afcf0ae 100644 (file)
@@ -36,6 +36,8 @@ static struct class *devfreq_class;
  */
 static struct workqueue_struct *devfreq_wq;
 
+/* The list of all device-devfreq governors */
+static LIST_HEAD(devfreq_governor_list);
 /* The list of all device-devfreq */
 static LIST_HEAD(devfreq_list);
 static DEFINE_MUTEX(devfreq_list_lock);
@@ -111,6 +113,32 @@ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
        return 0;
 }
 
+/**
+ * find_devfreq_governor() - find devfreq governor from name
+ * @name:      name of the governor
+ *
+ * Search the list of devfreq governors and return the matched
+ * governor's pointer. devfreq_list_lock should be held by the caller.
+ */
+static struct devfreq_governor *find_devfreq_governor(const char *name)
+{
+       struct devfreq_governor *tmp_governor;
+
+       if (unlikely(IS_ERR_OR_NULL(name))) {
+               pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
+               return ERR_PTR(-EINVAL);
+       }
+       WARN(!mutex_is_locked(&devfreq_list_lock),
+            "devfreq_list_lock must be locked.");
+
+       list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
+               if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
+                       return tmp_governor;
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+
 /* Load monitoring helper functions for governors use */
 
 /**
@@ -515,6 +543,69 @@ int devfreq_resume_device(struct devfreq *devfreq)
 }
 EXPORT_SYMBOL(devfreq_resume_device);
 
+/**
+ * devfreq_add_governor() - Add devfreq governor
+ * @governor:  the devfreq governor to be added
+ */
+int devfreq_add_governor(struct devfreq_governor *governor)
+{
+       struct devfreq_governor *g;
+       int err = 0;
+
+       if (!governor) {
+               pr_err("%s: Invalid parameters.\n", __func__);
+               return -EINVAL;
+       }
+
+       mutex_lock(&devfreq_list_lock);
+       g = find_devfreq_governor(governor->name);
+       if (!IS_ERR(g)) {
+               pr_err("%s: governor %s already registered\n", __func__,
+                      g->name);
+               err = -EINVAL;
+               goto err_out;
+       }
+
+       list_add(&governor->node, &devfreq_governor_list);
+
+err_out:
+       mutex_unlock(&devfreq_list_lock);
+
+       return err;
+}
+EXPORT_SYMBOL(devfreq_add_governor);
+
+/**
+ * devfreq_remove_device() - Remove devfreq feature from a device.
+ * @governor:  the devfreq governor to be removed
+ */
+int devfreq_remove_governor(struct devfreq_governor *governor)
+{
+       struct devfreq_governor *g;
+       int err = 0;
+
+       if (!governor) {
+               pr_err("%s: Invalid parameters.\n", __func__);
+               return -EINVAL;
+       }
+
+       mutex_lock(&devfreq_list_lock);
+       g = find_devfreq_governor(governor->name);
+       if (IS_ERR(g)) {
+               pr_err("%s: governor %s not registered\n", __func__,
+                      g->name);
+               err = -EINVAL;
+               goto err_out;
+       }
+
+       list_del(&governor->node);
+err_out:
+       mutex_unlock(&devfreq_list_lock);
+
+       return err;
+}
+EXPORT_SYMBOL(devfreq_remove_governor);
+
 static ssize_t show_governor(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
index 26432ac0a398f069c747a16cda78e6241ac7cfc3..fad7d63219786387e191ea4130bf5a0b7b37001b 100644 (file)
@@ -34,4 +34,8 @@ extern void devfreq_monitor_suspend(struct devfreq *devfreq);
 extern void devfreq_monitor_resume(struct devfreq *devfreq);
 extern void devfreq_interval_update(struct devfreq *devfreq,
                                        unsigned int *delay);
+
+extern int devfreq_add_governor(struct devfreq_governor *governor);
+extern int devfreq_remove_governor(struct devfreq_governor *governor);
+
 #endif /* _GOVERNOR_H */
index bc35c4aee6a38b420fffc9ece7a5fa94dd0f439c..6484a3f8dda8d6c5710a37b545d968fdc253b67e 100644 (file)
@@ -92,6 +92,7 @@ struct devfreq_dev_profile {
 
 /**
  * struct devfreq_governor - Devfreq policy governor
+ * @node:              list node - contains registered devfreq governors
  * @name:              Governor's name
  * @get_target_freq:   Returns desired operating frequency for the device.
  *                     Basically, get_target_freq will run
@@ -107,6 +108,8 @@ struct devfreq_dev_profile {
  * Note that the callbacks are called with devfreq->lock locked by devfreq.
  */
 struct devfreq_governor {
+       struct list_head node;
+
        const char name[DEVFREQ_NAME_LEN];
        int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
        int (*event_handler)(struct devfreq *devfreq,