]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'pm-devfreq'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 7 Dec 2012 22:13:36 +0000 (23:13 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 7 Dec 2012 22:13:36 +0000 (23:13 +0100)
* pm-devfreq: (23 commits)
  PM / devfreq: remove compiler error with module governors (2)
  PM / devfreq: Fix return value in devfreq_remove_governor()
  PM / devfreq: Fix incorrect argument in error message
  PM / devfreq: missing rcu_read_lock() added for find_device_opp()
  PM / devfreq: remove compiler error when a governor is module
  PM / devfreq: exynos4_bus.c: Fixed an alignment of the func call args.
  PM / devfreq: Add sysfs node to expose available governors
  PM / devfreq: allow sysfs governor node to switch governor
  PM / devfreq: governors: add GPL module license and allow module build
  PM / devfreq: map devfreq drivers to governor using name
  PM / devfreq: register governors with devfreq framework
  PM / devfreq: provide hooks for governors to be registered
  PM / devfreq: export update_devfreq
  PM / devfreq: Add sysfs node for representing frequency transition information.
  PM / devfreq: Add sysfs node to expose available frequencies
  PM / devfreq: documentation cleanups for devfreq header
  PM / devfreq: Use devm_* functions in exynos4_bus.c
  PM / devfreq: make devfreq_class static
  PM / devfreq: fix sscanf handling for writable sysfs entries
  PM / devfreq: kernel-doc typo corrections
  ...

Documentation/ABI/testing/sysfs-class-devfreq
drivers/devfreq/Kconfig
drivers/devfreq/devfreq.c
drivers/devfreq/exynos4_bus.c
drivers/devfreq/governor.h
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 23d78b5aab11d140f1f47e29c098d44051793ef8..0ba6ea2f89d9b831bdebdcc2419727464a59d88c 100644 (file)
@@ -11,7 +11,7 @@ What:         /sys/class/devfreq/.../governor
 Date:          September 2011
 Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
-               The /sys/class/devfreq/.../governor shows the name of the
+               The /sys/class/devfreq/.../governor show or set the name of the
                governor used by the corresponding devfreq object.
 
 What:          /sys/class/devfreq/.../cur_freq
@@ -19,15 +19,16 @@ Date:               September 2011
 Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
                The /sys/class/devfreq/.../cur_freq shows the current
-               frequency of the corresponding devfreq object.
+               frequency of the corresponding devfreq object. Same as
+               target_freq when get_cur_freq() is not implemented by
+               devfreq driver.
 
-What:          /sys/class/devfreq/.../central_polling
-Date:          September 2011
-Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
+What:          /sys/class/devfreq/.../target_freq
+Date:          September 2012
+Contact:       Rajagopal Venkat <rajagopal.venkat@linaro.org>
 Description:
-               The /sys/class/devfreq/.../central_polling shows whether
-               the devfreq ojbect is using devfreq-provided central
-               polling mechanism or not.
+               The /sys/class/devfreq/.../target_freq shows the next governor
+               predicted target frequency of the corresponding devfreq object.
 
 What:          /sys/class/devfreq/.../polling_interval
 Date:          September 2011
@@ -43,6 +44,17 @@ Description:
                (/sys/class/devfreq/.../central_polling is 0), this value
                may be useless.
 
+What:          /sys/class/devfreq/.../trans_stat
+Date:          October 2012
+Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
+Descrtiption:
+               This ABI shows the statistics of devfreq behavior on a
+               specific device. It shows the time spent in each state and
+               the number of transitions between states.
+               In order to activate this ABI, the devfreq target device
+               driver should provide the list of available frequencies
+               with its profile.
+
 What:          /sys/class/devfreq/.../userspace/set_freq
 Date:          September 2011
 Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
@@ -50,3 +62,19 @@ Description:
                The /sys/class/devfreq/.../userspace/set_freq shows and
                sets the requested frequency for the devfreq object if
                userspace governor is in effect.
+
+What:          /sys/class/devfreq/.../available_frequencies
+Date:          October 2012
+Contact:       Nishanth Menon <nm@ti.com>
+Description:
+               The /sys/class/devfreq/.../available_frequencies shows
+               the available frequencies of the corresponding devfreq object.
+               This is a snapshot of available frequencies and not limited
+               by the min/max frequency restrictions.
+
+What:          /sys/class/devfreq/.../available_governors
+Date:          October 2012
+Contact:       Nishanth Menon <nm@ti.com>
+Description:
+               The /sys/class/devfreq/.../available_governors shows
+               currently available governors in the system.
index f6b0a6e2ea501ff295c38953c4aeda870c497859..0f079be133051540c62d8c83d03579e132f7d6bd 100644 (file)
@@ -30,7 +30,7 @@ if PM_DEVFREQ
 comment "DEVFREQ Governors"
 
 config DEVFREQ_GOV_SIMPLE_ONDEMAND
-       bool "Simple Ondemand"
+       tristate "Simple Ondemand"
        help
          Chooses frequency based on the recent load on the device. Works
          similar as ONDEMAND governor of CPUFREQ does. A device with
@@ -39,7 +39,7 @@ config DEVFREQ_GOV_SIMPLE_ONDEMAND
          values to the governor with data field at devfreq_add_device().
 
 config DEVFREQ_GOV_PERFORMANCE
-       bool "Performance"
+       tristate "Performance"
        help
          Sets the frequency at the maximum available frequency.
          This governor always returns UINT_MAX as frequency so that
@@ -47,7 +47,7 @@ config DEVFREQ_GOV_PERFORMANCE
          at any time.
 
 config DEVFREQ_GOV_POWERSAVE
-       bool "Powersave"
+       tristate "Powersave"
        help
          Sets the frequency at the minimum available frequency.
          This governor always returns 0 as frequency so that
@@ -55,7 +55,7 @@ config DEVFREQ_GOV_POWERSAVE
          at any time.
 
 config DEVFREQ_GOV_USERSPACE
-       bool "Userspace"
+       tristate "Userspace"
        help
          Sets the frequency at the user specified one.
          This governor returns the user configured frequency if there
index 4fa1a22c55eabe578cb8e285d0630290c957a891..53766f39aaddfd5db9147842b40825fb1399453b 100644 (file)
 #include <linux/hrtimer.h>
 #include "governor.h"
 
-struct class *devfreq_class;
+static struct class *devfreq_class;
 
 /*
- * devfreq_work periodically monitors every registered device.
- * The minimum polling interval is one jiffy. The polling interval is
- * determined by the minimum polling period among all polling devfreq
- * devices. The resolution of polling interval is one jiffy.
+ * devfreq core provides delayed work based load monitoring helper
+ * functions. Governors can use these or can implement their own
+ * monitoring mechanism.
  */
-static bool polling;
 static struct workqueue_struct *devfreq_wq;
-static struct delayed_work devfreq_work;
-
-/* wait removing if this is to be removed */
-static struct devfreq *wait_remove_device;
 
+/* 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);
@@ -72,6 +68,79 @@ static struct devfreq *find_device_devfreq(struct device *dev)
        return ERR_PTR(-ENODEV);
 }
 
+/**
+ * devfreq_get_freq_level() - Lookup freq_table for the frequency
+ * @devfreq:   the devfreq instance
+ * @freq:      the target frequency
+ */
+static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
+{
+       int lev;
+
+       for (lev = 0; lev < devfreq->profile->max_state; lev++)
+               if (freq == devfreq->profile->freq_table[lev])
+                       return lev;
+
+       return -EINVAL;
+}
+
+/**
+ * devfreq_update_status() - Update statistics of devfreq behavior
+ * @devfreq:   the devfreq instance
+ * @freq:      the update target frequency
+ */
+static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
+{
+       int lev, prev_lev;
+       unsigned long cur_time;
+
+       lev = devfreq_get_freq_level(devfreq, freq);
+       if (lev < 0)
+               return lev;
+
+       cur_time = jiffies;
+       devfreq->time_in_state[lev] +=
+                        cur_time - devfreq->last_stat_updated;
+       if (freq != devfreq->previous_freq) {
+               prev_lev = devfreq_get_freq_level(devfreq,
+                                               devfreq->previous_freq);
+               devfreq->trans_table[(prev_lev *
+                               devfreq->profile->max_state) + lev]++;
+               devfreq->total_trans++;
+       }
+       devfreq->last_stat_updated = cur_time;
+
+       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 */
+
 /**
  * update_devfreq() - Reevaluate the device and configure frequency.
  * @devfreq:   the devfreq instance.
@@ -90,6 +159,9 @@ int update_devfreq(struct devfreq *devfreq)
                return -EINVAL;
        }
 
+       if (!devfreq->governor)
+               return -EINVAL;
+
        /* Reevaluate the proper frequency */
        err = devfreq->governor->get_target_freq(devfreq, &freq);
        if (err)
@@ -116,16 +188,173 @@ int update_devfreq(struct devfreq *devfreq)
        if (err)
                return err;
 
+       if (devfreq->profile->freq_table)
+               if (devfreq_update_status(devfreq, freq))
+                       dev_err(&devfreq->dev,
+                               "Couldn't update frequency transition information.\n");
+
        devfreq->previous_freq = freq;
        return err;
 }
+EXPORT_SYMBOL(update_devfreq);
+
+/**
+ * devfreq_monitor() - Periodically poll devfreq objects.
+ * @work:      the work struct used to run devfreq_monitor periodically.
+ *
+ */
+static void devfreq_monitor(struct work_struct *work)
+{
+       int err;
+       struct devfreq *devfreq = container_of(work,
+                                       struct devfreq, work.work);
+
+       mutex_lock(&devfreq->lock);
+       err = update_devfreq(devfreq);
+       if (err)
+               dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err);
+
+       queue_delayed_work(devfreq_wq, &devfreq->work,
+                               msecs_to_jiffies(devfreq->profile->polling_ms));
+       mutex_unlock(&devfreq->lock);
+}
+
+/**
+ * devfreq_monitor_start() - Start load monitoring of devfreq instance
+ * @devfreq:   the devfreq instance.
+ *
+ * Helper function for starting devfreq device load monitoing. By
+ * default delayed work based monitoring is supported. Function
+ * to be called from governor in response to DEVFREQ_GOV_START
+ * event when device is added to devfreq framework.
+ */
+void devfreq_monitor_start(struct devfreq *devfreq)
+{
+       INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
+       if (devfreq->profile->polling_ms)
+               queue_delayed_work(devfreq_wq, &devfreq->work,
+                       msecs_to_jiffies(devfreq->profile->polling_ms));
+}
+EXPORT_SYMBOL(devfreq_monitor_start);
+
+/**
+ * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance
+ * @devfreq:   the devfreq instance.
+ *
+ * Helper function to stop devfreq device load monitoing. Function
+ * to be called from governor in response to DEVFREQ_GOV_STOP
+ * event when device is removed from devfreq framework.
+ */
+void devfreq_monitor_stop(struct devfreq *devfreq)
+{
+       cancel_delayed_work_sync(&devfreq->work);
+}
+EXPORT_SYMBOL(devfreq_monitor_stop);
+
+/**
+ * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance
+ * @devfreq:   the devfreq instance.
+ *
+ * Helper function to suspend devfreq device load monitoing. Function
+ * to be called from governor in response to DEVFREQ_GOV_SUSPEND
+ * event or when polling interval is set to zero.
+ *
+ * Note: Though this function is same as devfreq_monitor_stop(),
+ * intentionally kept separate to provide hooks for collecting
+ * transition statistics.
+ */
+void devfreq_monitor_suspend(struct devfreq *devfreq)
+{
+       mutex_lock(&devfreq->lock);
+       if (devfreq->stop_polling) {
+               mutex_unlock(&devfreq->lock);
+               return;
+       }
+
+       devfreq->stop_polling = true;
+       mutex_unlock(&devfreq->lock);
+       cancel_delayed_work_sync(&devfreq->work);
+}
+EXPORT_SYMBOL(devfreq_monitor_suspend);
+
+/**
+ * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance
+ * @devfreq:    the devfreq instance.
+ *
+ * Helper function to resume devfreq device load monitoing. Function
+ * to be called from governor in response to DEVFREQ_GOV_RESUME
+ * event or when polling interval is set to non-zero.
+ */
+void devfreq_monitor_resume(struct devfreq *devfreq)
+{
+       mutex_lock(&devfreq->lock);
+       if (!devfreq->stop_polling)
+               goto out;
+
+       if (!delayed_work_pending(&devfreq->work) &&
+                       devfreq->profile->polling_ms)
+               queue_delayed_work(devfreq_wq, &devfreq->work,
+                       msecs_to_jiffies(devfreq->profile->polling_ms));
+       devfreq->stop_polling = false;
+
+out:
+       mutex_unlock(&devfreq->lock);
+}
+EXPORT_SYMBOL(devfreq_monitor_resume);
+
+/**
+ * devfreq_interval_update() - Update device devfreq monitoring interval
+ * @devfreq:    the devfreq instance.
+ * @delay:      new polling interval to be set.
+ *
+ * Helper function to set new load monitoring polling interval. Function
+ * to be called from governor in response to DEVFREQ_GOV_INTERVAL event.
+ */
+void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay)
+{
+       unsigned int cur_delay = devfreq->profile->polling_ms;
+       unsigned int new_delay = *delay;
+
+       mutex_lock(&devfreq->lock);
+       devfreq->profile->polling_ms = new_delay;
+
+       if (devfreq->stop_polling)
+               goto out;
+
+       /* if new delay is zero, stop polling */
+       if (!new_delay) {
+               mutex_unlock(&devfreq->lock);
+               cancel_delayed_work_sync(&devfreq->work);
+               return;
+       }
+
+       /* if current delay is zero, start polling with new delay */
+       if (!cur_delay) {
+               queue_delayed_work(devfreq_wq, &devfreq->work,
+                       msecs_to_jiffies(devfreq->profile->polling_ms));
+               goto out;
+       }
+
+       /* if current delay is greater than new delay, restart polling */
+       if (cur_delay > new_delay) {
+               mutex_unlock(&devfreq->lock);
+               cancel_delayed_work_sync(&devfreq->work);
+               mutex_lock(&devfreq->lock);
+               if (!devfreq->stop_polling)
+                       queue_delayed_work(devfreq_wq, &devfreq->work,
+                             msecs_to_jiffies(devfreq->profile->polling_ms));
+       }
+out:
+       mutex_unlock(&devfreq->lock);
+}
+EXPORT_SYMBOL(devfreq_interval_update);
 
 /**
  * devfreq_notifier_call() - Notify that the device frequency requirements
  *                        has been changed out of devfreq framework.
- * @nb         the notifier_block (supposed to be devfreq->nb)
- * @type       not used
- * @devp       not used
+ * @nb:                the notifier_block (supposed to be devfreq->nb)
+ * @type:      not used
+ * @devp:      not used
  *
  * Called by a notifier that uses devfreq->nb.
  */
@@ -143,59 +372,34 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
 }
 
 /**
- * _remove_devfreq() - Remove devfreq from the device.
+ * _remove_devfreq() - Remove devfreq from the list and release its resources.
  * @devfreq:   the devfreq struct
  * @skip:      skip calling device_unregister().
- *
- * Note that the caller should lock devfreq->lock before calling
- * this. _remove_devfreq() will unlock it and free devfreq
- * internally. devfreq_list_lock should be locked by the caller
- * as well (not relased at return)
- *
- * Lock usage:
- * devfreq->lock: locked before call.
- *               unlocked at return (and freed)
- * devfreq_list_lock: locked before call.
- *                   kept locked at return.
- *                   if devfreq is centrally polled.
- *
- * Freed memory:
- * devfreq
  */
 static void _remove_devfreq(struct devfreq *devfreq, bool skip)
 {
-       if (!mutex_is_locked(&devfreq->lock)) {
-               WARN(true, "devfreq->lock must be locked by the caller.\n");
-               return;
-       }
-       if (!devfreq->governor->no_central_polling &&
-           !mutex_is_locked(&devfreq_list_lock)) {
-               WARN(true, "devfreq_list_lock must be locked by the caller.\n");
+       mutex_lock(&devfreq_list_lock);
+       if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
+               mutex_unlock(&devfreq_list_lock);
+               dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n");
                return;
        }
+       list_del(&devfreq->node);
+       mutex_unlock(&devfreq_list_lock);
 
-       if (devfreq->being_removed)
-               return;
-
-       devfreq->being_removed = true;
+       if (devfreq->governor)
+               devfreq->governor->event_handler(devfreq,
+                                                DEVFREQ_GOV_STOP, NULL);
 
        if (devfreq->profile->exit)
                devfreq->profile->exit(devfreq->dev.parent);
 
-       if (devfreq->governor->exit)
-               devfreq->governor->exit(devfreq);
-
        if (!skip && get_device(&devfreq->dev)) {
                device_unregister(&devfreq->dev);
                put_device(&devfreq->dev);
        }
 
-       if (!devfreq->governor->no_central_polling)
-               list_del(&devfreq->node);
-
-       mutex_unlock(&devfreq->lock);
        mutex_destroy(&devfreq->lock);
-
        kfree(devfreq);
 }
 
@@ -210,163 +414,39 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
 static void devfreq_dev_release(struct device *dev)
 {
        struct devfreq *devfreq = to_devfreq(dev);
-       bool central_polling = !devfreq->governor->no_central_polling;
-
-       /*
-        * If devfreq_dev_release() was called by device_unregister() of
-        * _remove_devfreq(), we cannot mutex_lock(&devfreq->lock) and
-        * being_removed is already set. This also partially checks the case
-        * where devfreq_dev_release() is called from a thread other than
-        * the one called _remove_devfreq(); however, this case is
-        * dealt completely with another following being_removed check.
-        *
-        * Because being_removed is never being
-        * unset, we do not need to worry about race conditions on
-        * being_removed.
-        */
-       if (devfreq->being_removed)
-               return;
-
-       if (central_polling)
-               mutex_lock(&devfreq_list_lock);
-
-       mutex_lock(&devfreq->lock);
 
-       /*
-        * Check being_removed flag again for the case where
-        * devfreq_dev_release() was called in a thread other than the one
-        * possibly called _remove_devfreq().
-        */
-       if (devfreq->being_removed) {
-               mutex_unlock(&devfreq->lock);
-               goto out;
-       }
-
-       /* devfreq->lock is unlocked and removed in _removed_devfreq() */
        _remove_devfreq(devfreq, true);
-
-out:
-       if (central_polling)
-               mutex_unlock(&devfreq_list_lock);
-}
-
-/**
- * devfreq_monitor() - Periodically poll devfreq objects.
- * @work: the work struct used to run devfreq_monitor periodically.
- *
- */
-static void devfreq_monitor(struct work_struct *work)
-{
-       static unsigned long last_polled_at;
-       struct devfreq *devfreq, *tmp;
-       int error;
-       unsigned long jiffies_passed;
-       unsigned long next_jiffies = ULONG_MAX, now = jiffies;
-       struct device *dev;
-
-       /* Initially last_polled_at = 0, polling every device at bootup */
-       jiffies_passed = now - last_polled_at;
-       last_polled_at = now;
-       if (jiffies_passed == 0)
-               jiffies_passed = 1;
-
-       mutex_lock(&devfreq_list_lock);
-       list_for_each_entry_safe(devfreq, tmp, &devfreq_list, node) {
-               mutex_lock(&devfreq->lock);
-               dev = devfreq->dev.parent;
-
-               /* Do not remove tmp for a while */
-               wait_remove_device = tmp;
-
-               if (devfreq->governor->no_central_polling ||
-                   devfreq->next_polling == 0) {
-                       mutex_unlock(&devfreq->lock);
-                       continue;
-               }
-               mutex_unlock(&devfreq_list_lock);
-
-               /*
-                * Reduce more next_polling if devfreq_wq took an extra
-                * delay. (i.e., CPU has been idled.)
-                */
-               if (devfreq->next_polling <= jiffies_passed) {
-                       error = update_devfreq(devfreq);
-
-                       /* Remove a devfreq with an error. */
-                       if (error && error != -EAGAIN) {
-
-                               dev_err(dev, "Due to update_devfreq error(%d), devfreq(%s) is removed from the device\n",
-                                       error, devfreq->governor->name);
-
-                               /*
-                                * Unlock devfreq before locking the list
-                                * in order to avoid deadlock with
-                                * find_device_devfreq or others
-                                */
-                               mutex_unlock(&devfreq->lock);
-                               mutex_lock(&devfreq_list_lock);
-                               /* Check if devfreq is already removed */
-                               if (IS_ERR(find_device_devfreq(dev)))
-                                       continue;
-                               mutex_lock(&devfreq->lock);
-                               /* This unlocks devfreq->lock and free it */
-                               _remove_devfreq(devfreq, false);
-                               continue;
-                       }
-                       devfreq->next_polling = devfreq->polling_jiffies;
-               } else {
-                       devfreq->next_polling -= jiffies_passed;
-               }
-
-               if (devfreq->next_polling)
-                       next_jiffies = (next_jiffies > devfreq->next_polling) ?
-                                       devfreq->next_polling : next_jiffies;
-
-               mutex_unlock(&devfreq->lock);
-               mutex_lock(&devfreq_list_lock);
-       }
-       wait_remove_device = NULL;
-       mutex_unlock(&devfreq_list_lock);
-
-       if (next_jiffies > 0 && next_jiffies < ULONG_MAX) {
-               polling = true;
-               queue_delayed_work(devfreq_wq, &devfreq_work, next_jiffies);
-       } else {
-               polling = false;
-       }
 }
 
 /**
  * devfreq_add_device() - Add devfreq feature to the device
  * @dev:       the device to add devfreq feature.
  * @profile:   device-specific profile to run devfreq.
- * @governor the policy to choose frequency.
+ * @governor_name:     name of the policy to choose frequency.
  * @data:      private data for the governor. The devfreq framework does not
  *             touch this value.
  */
 struct devfreq *devfreq_add_device(struct device *dev,
                                   struct devfreq_dev_profile *profile,
-                                  const struct devfreq_governor *governor,
+                                  const char *governor_name,
                                   void *data)
 {
        struct devfreq *devfreq;
+       struct devfreq_governor *governor;
        int err = 0;
 
-       if (!dev || !profile || !governor) {
+       if (!dev || !profile || !governor_name) {
                dev_err(dev, "%s: Invalid parameters.\n", __func__);
                return ERR_PTR(-EINVAL);
        }
 
-
-       if (!governor->no_central_polling) {
-               mutex_lock(&devfreq_list_lock);
-               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__);
-                       err = -EINVAL;
-                       goto err_out;
-               }
+       mutex_lock(&devfreq_list_lock);
+       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__);
+               err = -EINVAL;
+               goto err_out;
        }
 
        devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
@@ -383,91 +463,315 @@ struct devfreq *devfreq_add_device(struct device *dev,
        devfreq->dev.class = devfreq_class;
        devfreq->dev.release = devfreq_dev_release;
        devfreq->profile = profile;
-       devfreq->governor = governor;
+       strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
        devfreq->previous_freq = profile->initial_freq;
        devfreq->data = data;
-       devfreq->next_polling = devfreq->polling_jiffies
-                             = msecs_to_jiffies(devfreq->profile->polling_ms);
        devfreq->nb.notifier_call = devfreq_notifier_call;
 
+       devfreq->trans_table =  devm_kzalloc(dev, sizeof(unsigned int) *
+                                               devfreq->profile->max_state *
+                                               devfreq->profile->max_state,
+                                               GFP_KERNEL);
+       devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) *
+                                               devfreq->profile->max_state,
+                                               GFP_KERNEL);
+       devfreq->last_stat_updated = jiffies;
+
        dev_set_name(&devfreq->dev, dev_name(dev));
        err = device_register(&devfreq->dev);
        if (err) {
                put_device(&devfreq->dev);
+               mutex_unlock(&devfreq->lock);
                goto err_dev;
        }
 
-       if (governor->init)
-               err = governor->init(devfreq);
-       if (err)
-               goto err_init;
-
        mutex_unlock(&devfreq->lock);
 
-       if (governor->no_central_polling)
-               goto out;
-
        mutex_lock(&devfreq_list_lock);
-
        list_add(&devfreq->node, &devfreq_list);
 
-       if (devfreq_wq && devfreq->next_polling && !polling) {
-               polling = true;
-               queue_delayed_work(devfreq_wq, &devfreq_work,
-                                  devfreq->next_polling);
-       }
+       governor = find_devfreq_governor(devfreq->governor_name);
+       if (!IS_ERR(governor))
+               devfreq->governor = governor;
+       if (devfreq->governor)
+               err = devfreq->governor->event_handler(devfreq,
+                                       DEVFREQ_GOV_START, NULL);
        mutex_unlock(&devfreq_list_lock);
-out:
+       if (err) {
+               dev_err(dev, "%s: Unable to start governor for the device\n",
+                       __func__);
+               goto err_init;
+       }
+
        return devfreq;
 
 err_init:
+       list_del(&devfreq->node);
        device_unregister(&devfreq->dev);
 err_dev:
-       mutex_unlock(&devfreq->lock);
        kfree(devfreq);
 err_out:
        return ERR_PTR(err);
 }
+EXPORT_SYMBOL(devfreq_add_device);
 
 /**
  * devfreq_remove_device() - Remove devfreq feature from a device.
- * @devfreq    the devfreq instance to be removed
+ * @devfreq:   the devfreq instance to be removed
  */
 int devfreq_remove_device(struct devfreq *devfreq)
 {
-       bool central_polling;
+       if (!devfreq)
+               return -EINVAL;
+
+       _remove_devfreq(devfreq, false);
 
+       return 0;
+}
+EXPORT_SYMBOL(devfreq_remove_device);
+
+/**
+ * devfreq_suspend_device() - Suspend devfreq of a device.
+ * @devfreq: the devfreq instance to be suspended
+ */
+int devfreq_suspend_device(struct devfreq *devfreq)
+{
        if (!devfreq)
                return -EINVAL;
 
-       central_polling = !devfreq->governor->no_central_polling;
+       if (!devfreq->governor)
+               return 0;
+
+       return devfreq->governor->event_handler(devfreq,
+                               DEVFREQ_GOV_SUSPEND, NULL);
+}
+EXPORT_SYMBOL(devfreq_suspend_device);
+
+/**
+ * devfreq_resume_device() - Resume devfreq of a device.
+ * @devfreq: the devfreq instance to be resumed
+ */
+int devfreq_resume_device(struct devfreq *devfreq)
+{
+       if (!devfreq)
+               return -EINVAL;
+
+       if (!devfreq->governor)
+               return 0;
+
+       return devfreq->governor->event_handler(devfreq,
+                               DEVFREQ_GOV_RESUME, NULL);
+}
+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;
+       struct devfreq *devfreq;
+       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;
+       }
 
-       if (central_polling) {
-               mutex_lock(&devfreq_list_lock);
-               while (wait_remove_device == devfreq) {
-                       mutex_unlock(&devfreq_list_lock);
-                       schedule();
-                       mutex_lock(&devfreq_list_lock);
+       list_add(&governor->node, &devfreq_governor_list);
+
+       list_for_each_entry(devfreq, &devfreq_list, node) {
+               int ret = 0;
+               struct device *dev = devfreq->dev.parent;
+
+               if (!strncmp(devfreq->governor_name, governor->name,
+                            DEVFREQ_NAME_LEN)) {
+                       /* The following should never occur */
+                       if (devfreq->governor) {
+                               dev_warn(dev,
+                                        "%s: Governor %s already present\n",
+                                        __func__, devfreq->governor->name);
+                               ret = devfreq->governor->event_handler(devfreq,
+                                                       DEVFREQ_GOV_STOP, NULL);
+                               if (ret) {
+                                       dev_warn(dev,
+                                                "%s: Governor %s stop = %d\n",
+                                                __func__,
+                                                devfreq->governor->name, ret);
+                               }
+                               /* Fall through */
+                       }
+                       devfreq->governor = governor;
+                       ret = devfreq->governor->event_handler(devfreq,
+                                               DEVFREQ_GOV_START, NULL);
+                       if (ret) {
+                               dev_warn(dev, "%s: Governor %s start=%d\n",
+                                        __func__, devfreq->governor->name,
+                                        ret);
+                       }
                }
        }
 
-       mutex_lock(&devfreq->lock);
-       _remove_devfreq(devfreq, false); /* it unlocks devfreq->lock */
+err_out:
+       mutex_unlock(&devfreq_list_lock);
 
-       if (central_polling)
-               mutex_unlock(&devfreq_list_lock);
+       return err;
+}
+EXPORT_SYMBOL(devfreq_add_governor);
 
-       return 0;
+/**
+ * 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;
+       struct devfreq *devfreq;
+       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__,
+                      governor->name);
+               err = PTR_ERR(g);
+               goto err_out;
+       }
+       list_for_each_entry(devfreq, &devfreq_list, node) {
+               int ret;
+               struct device *dev = devfreq->dev.parent;
+
+               if (!strncmp(devfreq->governor_name, governor->name,
+                            DEVFREQ_NAME_LEN)) {
+                       /* we should have a devfreq governor! */
+                       if (!devfreq->governor) {
+                               dev_warn(dev, "%s: Governor %s NOT present\n",
+                                        __func__, governor->name);
+                               continue;
+                               /* Fall through */
+                       }
+                       ret = devfreq->governor->event_handler(devfreq,
+                                               DEVFREQ_GOV_STOP, NULL);
+                       if (ret) {
+                               dev_warn(dev, "%s: Governor %s stop=%d\n",
+                                        __func__, devfreq->governor->name,
+                                        ret);
+                       }
+                       devfreq->governor = NULL;
+               }
+       }
+
+       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)
 {
+       if (!to_devfreq(dev)->governor)
+               return -EINVAL;
+
        return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name);
 }
 
+static ssize_t store_governor(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct devfreq *df = to_devfreq(dev);
+       int ret;
+       char str_governor[DEVFREQ_NAME_LEN + 1];
+       struct devfreq_governor *governor;
+
+       ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
+       if (ret != 1)
+               return -EINVAL;
+
+       mutex_lock(&devfreq_list_lock);
+       governor = find_devfreq_governor(str_governor);
+       if (IS_ERR(governor)) {
+               ret = PTR_ERR(governor);
+               goto out;
+       }
+       if (df->governor == governor)
+               goto out;
+
+       if (df->governor) {
+               ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
+               if (ret) {
+                       dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
+                                __func__, df->governor->name, ret);
+                       goto out;
+               }
+       }
+       df->governor = governor;
+       strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
+       ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
+       if (ret)
+               dev_warn(dev, "%s: Governor %s not started(%d)\n",
+                        __func__, df->governor->name, ret);
+out:
+       mutex_unlock(&devfreq_list_lock);
+
+       if (!ret)
+               ret = count;
+       return ret;
+}
+static ssize_t show_available_governors(struct device *d,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct devfreq_governor *tmp_governor;
+       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);
+       mutex_unlock(&devfreq_list_lock);
+
+       /* Truncate the trailing space */
+       if (count)
+               count--;
+
+       count += sprintf(&buf[count], "\n");
+
+       return count;
+}
+
 static ssize_t show_freq(struct device *dev,
                         struct device_attribute *attr, char *buf)
+{
+       unsigned long freq;
+       struct devfreq *devfreq = to_devfreq(dev);
+
+       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", devfreq->previous_freq);
+}
+
+static ssize_t show_target_freq(struct device *dev,
+                       struct device_attribute *attr, char *buf)
 {
        return sprintf(buf, "%lu\n", to_devfreq(dev)->previous_freq);
 }
@@ -486,39 +790,19 @@ static ssize_t store_polling_interval(struct device *dev,
        unsigned int value;
        int ret;
 
+       if (!df->governor)
+               return -EINVAL;
+
        ret = sscanf(buf, "%u", &value);
        if (ret != 1)
-               goto out;
-
-       mutex_lock(&df->lock);
-       df->profile->polling_ms = value;
-       df->next_polling = df->polling_jiffies
-                        = msecs_to_jiffies(value);
-       mutex_unlock(&df->lock);
+               return -EINVAL;
 
+       df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
        ret = count;
 
-       if (df->governor->no_central_polling)
-               goto out;
-
-       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);
-out:
        return ret;
 }
 
-static ssize_t show_central_polling(struct device *dev,
-                                   struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n",
-                      !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)
 {
@@ -529,7 +813,7 @@ static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr,
 
        ret = sscanf(buf, "%lu", &value);
        if (ret != 1)
-               goto out;
+               return -EINVAL;
 
        mutex_lock(&df->lock);
        max = df->max_freq;
@@ -543,7 +827,6 @@ static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr,
        ret = count;
 unlock:
        mutex_unlock(&df->lock);
-out:
        return ret;
 }
 
@@ -563,7 +846,7 @@ static ssize_t store_max_freq(struct device *dev, struct device_attribute *attr,
 
        ret = sscanf(buf, "%lu", &value);
        if (ret != 1)
-               goto out;
+               return -EINVAL;
 
        mutex_lock(&df->lock);
        min = df->min_freq;
@@ -577,7 +860,6 @@ static ssize_t store_max_freq(struct device *dev, struct device_attribute *attr,
        ret = count;
 unlock:
        mutex_unlock(&df->lock);
-out:
        return ret;
 }
 
@@ -587,34 +869,92 @@ static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr,
        return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq);
 }
 
+static ssize_t show_available_freqs(struct device *d,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct devfreq *df = to_devfreq(d);
+       struct device *dev = df->dev.parent;
+       struct opp *opp;
+       ssize_t count = 0;
+       unsigned long freq = 0;
+
+       rcu_read_lock();
+       do {
+               opp = opp_find_freq_ceil(dev, &freq);
+               if (IS_ERR(opp))
+                       break;
+
+               count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
+                                  "%lu ", freq);
+               freq++;
+       } while (1);
+       rcu_read_unlock();
+
+       /* Truncate the trailing space */
+       if (count)
+               count--;
+
+       count += sprintf(&buf[count], "\n");
+
+       return count;
+}
+
+static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr,
+                               char *buf)
+{
+       struct devfreq *devfreq = to_devfreq(dev);
+       ssize_t len;
+       int i, j, err;
+       unsigned int max_state = devfreq->profile->max_state;
+
+       err = devfreq_update_status(devfreq, devfreq->previous_freq);
+       if (err)
+               return 0;
+
+       len = sprintf(buf, "   From  :   To\n");
+       len += sprintf(buf + len, "         :");
+       for (i = 0; i < max_state; i++)
+               len += sprintf(buf + len, "%8u",
+                               devfreq->profile->freq_table[i]);
+
+       len += sprintf(buf + len, "   time(ms)\n");
+
+       for (i = 0; i < max_state; i++) {
+               if (devfreq->profile->freq_table[i]
+                                       == devfreq->previous_freq) {
+                       len += sprintf(buf + len, "*");
+               } else {
+                       len += sprintf(buf + len, " ");
+               }
+               len += sprintf(buf + len, "%8u:",
+                               devfreq->profile->freq_table[i]);
+               for (j = 0; j < max_state; j++)
+                       len += sprintf(buf + len, "%8u",
+                               devfreq->trans_table[(i * max_state) + j]);
+               len += sprintf(buf + len, "%10u\n",
+                       jiffies_to_msecs(devfreq->time_in_state[i]));
+       }
+
+       len += sprintf(buf + len, "Total transition : %u\n",
+                                       devfreq->total_trans);
+       return len;
+}
+
 static struct device_attribute devfreq_attrs[] = {
-       __ATTR(governor, S_IRUGO, show_governor, NULL),
+       __ATTR(governor, S_IRUGO | S_IWUSR, show_governor, store_governor),
+       __ATTR(available_governors, S_IRUGO, show_available_governors, NULL),
        __ATTR(cur_freq, S_IRUGO, show_freq, NULL),
-       __ATTR(central_polling, S_IRUGO, show_central_polling, NULL),
+       __ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL),
+       __ATTR(target_freq, S_IRUGO, show_target_freq, 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),
+       __ATTR(trans_stat, S_IRUGO, show_trans_table, NULL),
        { },
 };
 
-/**
- * devfreq_start_polling() - Initialize data structure for devfreq framework and
- *                        start polling registered devfreq devices.
- */
-static int __init devfreq_start_polling(void)
-{
-       mutex_lock(&devfreq_list_lock);
-       polling = false;
-       devfreq_wq = create_freezable_workqueue("devfreq_wq");
-       INIT_DEFERRABLE_WORK(&devfreq_work, devfreq_monitor);
-       mutex_unlock(&devfreq_list_lock);
-
-       devfreq_monitor(&devfreq_work.work);
-       return 0;
-}
-late_initcall(devfreq_start_polling);
-
 static int __init devfreq_init(void)
 {
        devfreq_class = class_create(THIS_MODULE, "devfreq");
@@ -622,7 +962,15 @@ static int __init devfreq_init(void)
                pr_err("%s: couldn't create class\n", __FILE__);
                return PTR_ERR(devfreq_class);
        }
+
+       devfreq_wq = create_freezable_workqueue("devfreq_wq");
+       if (IS_ERR(devfreq_wq)) {
+               class_destroy(devfreq_class);
+               pr_err("%s: couldn't create workqueue\n", __FILE__);
+               return PTR_ERR(devfreq_wq);
+       }
        devfreq_class->dev_attrs = devfreq_attrs;
+
        return 0;
 }
 subsys_initcall(devfreq_init);
@@ -630,6 +978,7 @@ subsys_initcall(devfreq_init);
 static void __exit devfreq_exit(void)
 {
        class_destroy(devfreq_class);
+       destroy_workqueue(devfreq_wq);
 }
 module_exit(devfreq_exit);
 
@@ -641,9 +990,9 @@ module_exit(devfreq_exit);
 /**
  * devfreq_recommended_opp() - Helper function to get proper OPP for the
  *                          freq value given to target callback.
- * @dev                The devfreq user device. (parent of devfreq)
- * @freq       The frequency given to target function
- * @flags      Flags handed from devfreq framework.
+ * @dev:       The devfreq user device. (parent of devfreq)
+ * @freq:      The frequency given to target function
+ * @flags:     Flags handed from devfreq framework.
  *
  */
 struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
@@ -674,35 +1023,49 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
  * devfreq_register_opp_notifier() - Helper function to get devfreq notified
  *                                for any changes in the OPP availability
  *                                changes
- * @dev                The devfreq user device. (parent of devfreq)
- * @devfreq    The devfreq object.
+ * @dev:       The devfreq user device. (parent of devfreq)
+ * @devfreq:   The devfreq object.
  */
 int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
 {
-       struct srcu_notifier_head *nh = opp_get_notifier(dev);
+       struct srcu_notifier_head *nh;
+       int ret = 0;
 
+       rcu_read_lock();
+       nh = opp_get_notifier(dev);
        if (IS_ERR(nh))
-               return PTR_ERR(nh);
-       return srcu_notifier_chain_register(nh, &devfreq->nb);
+               ret = PTR_ERR(nh);
+       rcu_read_unlock();
+       if (!ret)
+               ret = srcu_notifier_chain_register(nh, &devfreq->nb);
+
+       return ret;
 }
 
 /**
  * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
  *                                  notified for any changes in the OPP
  *                                  availability changes anymore.
- * @dev                The devfreq user device. (parent of devfreq)
- * @devfreq    The devfreq object.
+ * @dev:       The devfreq user device. (parent of devfreq)
+ * @devfreq:   The devfreq object.
  *
  * At exit() callback of devfreq_dev_profile, this must be included if
  * devfreq_recommended_opp is used.
  */
 int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
 {
-       struct srcu_notifier_head *nh = opp_get_notifier(dev);
+       struct srcu_notifier_head *nh;
+       int ret = 0;
 
+       rcu_read_lock();
+       nh = opp_get_notifier(dev);
        if (IS_ERR(nh))
-               return PTR_ERR(nh);
-       return srcu_notifier_chain_unregister(nh, &devfreq->nb);
+               ret = PTR_ERR(nh);
+       rcu_read_unlock();
+       if (!ret)
+               ret = srcu_notifier_chain_unregister(nh, &devfreq->nb);
+
+       return ret;
 }
 
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
index 88ddc77a9bb175a393888821b46c30c0a0f27f80..741837208716e293d06bbe9cfa94fac4051303d4 100644 (file)
@@ -987,7 +987,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        int err = 0;
 
-       data = kzalloc(sizeof(struct busfreq_data), GFP_KERNEL);
+       data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL);
        if (data == NULL) {
                dev_err(dev, "Cannot allocate memory.\n");
                return -ENOMEM;
@@ -1012,31 +1012,26 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev)
                err = -EINVAL;
        }
        if (err)
-               goto err_regulator;
+               return err;
 
-       data->vdd_int = regulator_get(dev, "vdd_int");
+       data->vdd_int = devm_regulator_get(dev, "vdd_int");
        if (IS_ERR(data->vdd_int)) {
                dev_err(dev, "Cannot get the regulator \"vdd_int\"\n");
-               err = PTR_ERR(data->vdd_int);
-               goto err_regulator;
+               return PTR_ERR(data->vdd_int);
        }
        if (data->type == TYPE_BUSF_EXYNOS4x12) {
-               data->vdd_mif = regulator_get(dev, "vdd_mif");
+               data->vdd_mif = devm_regulator_get(dev, "vdd_mif");
                if (IS_ERR(data->vdd_mif)) {
                        dev_err(dev, "Cannot get the regulator \"vdd_mif\"\n");
-                       err = PTR_ERR(data->vdd_mif);
-                       regulator_put(data->vdd_int);
-                       goto err_regulator;
-
+                       return PTR_ERR(data->vdd_mif);
                }
        }
 
        opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq);
        if (IS_ERR(opp)) {
                dev_err(dev, "Invalid initial frequency %lu kHz.\n",
-                      exynos4_devfreq_profile.initial_freq);
-               err = PTR_ERR(opp);
-               goto err_opp_add;
+                       exynos4_devfreq_profile.initial_freq);
+               return PTR_ERR(opp);
        }
        data->curr_opp = opp;
 
@@ -1045,30 +1040,20 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev)
        busfreq_mon_reset(data);
 
        data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile,
-                                          &devfreq_simple_ondemand, NULL);
-       if (IS_ERR(data->devfreq)) {
-               err = PTR_ERR(data->devfreq);
-               goto err_opp_add;
-       }
+                                          "simple_ondemand", NULL);
+       if (IS_ERR(data->devfreq))
+               return PTR_ERR(data->devfreq);
 
        devfreq_register_opp_notifier(dev, data->devfreq);
 
        err = register_pm_notifier(&data->pm_notifier);
        if (err) {
                dev_err(dev, "Failed to setup pm notifier\n");
-               goto err_devfreq_add;
+               devfreq_remove_device(data->devfreq);
+               return err;
        }
 
        return 0;
-err_devfreq_add:
-       devfreq_remove_device(data->devfreq);
-err_opp_add:
-       if (data->vdd_mif)
-               regulator_put(data->vdd_mif);
-       regulator_put(data->vdd_int);
-err_regulator:
-       kfree(data);
-       return err;
 }
 
 static __devexit int exynos4_busfreq_remove(struct platform_device *pdev)
@@ -1077,10 +1062,6 @@ static __devexit int exynos4_busfreq_remove(struct platform_device *pdev)
 
        unregister_pm_notifier(&data->pm_notifier);
        devfreq_remove_device(data->devfreq);
-       regulator_put(data->vdd_int);
-       if (data->vdd_mif)
-               regulator_put(data->vdd_mif);
-       kfree(data);
 
        return 0;
 }
index ea7f13c58dedf332c809dcc53351a196d1baa5fa..fad7d63219786387e191ea4130bf5a0b7b37001b 100644 (file)
 
 #define to_devfreq(DEV)        container_of((DEV), struct devfreq, dev)
 
+/* Devfreq events */
+#define DEVFREQ_GOV_START                      0x1
+#define DEVFREQ_GOV_STOP                       0x2
+#define DEVFREQ_GOV_INTERVAL                   0x3
+#define DEVFREQ_GOV_SUSPEND                    0x4
+#define DEVFREQ_GOV_RESUME                     0x5
+
 /* Caution: devfreq->lock must be locked before calling update_devfreq */
 extern int update_devfreq(struct devfreq *devfreq);
 
+extern void devfreq_monitor_start(struct devfreq *devfreq);
+extern void devfreq_monitor_stop(struct devfreq *devfreq);
+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 af75ddd4f158b5de78f9d5bc44ec2f9c579e78a5..c72f942f30a85a198b89514703b485d9f790867b 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/devfreq.h>
+#include <linux/module.h>
 #include "governor.h"
 
 static int devfreq_performance_func(struct devfreq *df,
@@ -26,14 +27,41 @@ static int devfreq_performance_func(struct devfreq *df,
        return 0;
 }
 
-static int performance_init(struct devfreq *devfreq)
+static int devfreq_performance_handler(struct devfreq *devfreq,
+                               unsigned int event, void *data)
 {
-       return update_devfreq(devfreq);
+       int ret = 0;
+
+       if (event == DEVFREQ_GOV_START) {
+               mutex_lock(&devfreq->lock);
+               ret = update_devfreq(devfreq);
+               mutex_unlock(&devfreq->lock);
+       }
+
+       return ret;
 }
 
-const struct devfreq_governor devfreq_performance = {
+static struct devfreq_governor devfreq_performance = {
        .name = "performance",
-       .init = performance_init,
        .get_target_freq = devfreq_performance_func,
-       .no_central_polling = true,
+       .event_handler = devfreq_performance_handler,
 };
+
+static int __init devfreq_performance_init(void)
+{
+       return devfreq_add_governor(&devfreq_performance);
+}
+subsys_initcall(devfreq_performance_init);
+
+static void __exit devfreq_performance_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_performance);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_performance_exit);
+MODULE_LICENSE("GPL");
index fec0cdbd24773fd531b5c300bfb4afb5111d9545..0c6bed567e6d4a12249f9891f06ed97411e118bc 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/devfreq.h>
+#include <linux/module.h>
 #include "governor.h"
 
 static int devfreq_powersave_func(struct devfreq *df,
@@ -23,14 +24,41 @@ static int devfreq_powersave_func(struct devfreq *df,
        return 0;
 }
 
-static int powersave_init(struct devfreq *devfreq)
+static int devfreq_powersave_handler(struct devfreq *devfreq,
+                               unsigned int event, void *data)
 {
-       return update_devfreq(devfreq);
+       int ret = 0;
+
+       if (event == DEVFREQ_GOV_START) {
+               mutex_lock(&devfreq->lock);
+               ret = update_devfreq(devfreq);
+               mutex_unlock(&devfreq->lock);
+       }
+
+       return ret;
 }
 
-const struct devfreq_governor devfreq_powersave = {
+static struct devfreq_governor devfreq_powersave = {
        .name = "powersave",
-       .init = powersave_init,
        .get_target_freq = devfreq_powersave_func,
-       .no_central_polling = true,
+       .event_handler = devfreq_powersave_handler,
 };
+
+static int __init devfreq_powersave_init(void)
+{
+       return devfreq_add_governor(&devfreq_powersave);
+}
+subsys_initcall(devfreq_powersave_init);
+
+static void __exit devfreq_powersave_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_powersave);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_powersave_exit);
+MODULE_LICENSE("GPL");
index a2e3eae790119ef7e01527a94b4bf44b20589a07..0720ba84ca9277df895168a788cae9d3d41bc40b 100644 (file)
  */
 
 #include <linux/errno.h>
+#include <linux/module.h>
 #include <linux/devfreq.h>
 #include <linux/math64.h>
+#include "governor.h"
 
 /* Default constants for DevFreq-Simple-Ondemand (DFSO) */
 #define DFSO_UPTHRESHOLD       (90)
@@ -88,7 +90,58 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
        return 0;
 }
 
-const struct devfreq_governor devfreq_simple_ondemand = {
+static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
+                               unsigned int event, void *data)
+{
+       switch (event) {
+       case DEVFREQ_GOV_START:
+               devfreq_monitor_start(devfreq);
+               break;
+
+       case DEVFREQ_GOV_STOP:
+               devfreq_monitor_stop(devfreq);
+               break;
+
+       case DEVFREQ_GOV_INTERVAL:
+               devfreq_interval_update(devfreq, (unsigned int *)data);
+               break;
+
+       case DEVFREQ_GOV_SUSPEND:
+               devfreq_monitor_suspend(devfreq);
+               break;
+
+       case DEVFREQ_GOV_RESUME:
+               devfreq_monitor_resume(devfreq);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static struct devfreq_governor devfreq_simple_ondemand = {
        .name = "simple_ondemand",
        .get_target_freq = devfreq_simple_ondemand_func,
+       .event_handler = devfreq_simple_ondemand_handler,
 };
+
+static int __init devfreq_simple_ondemand_init(void)
+{
+       return devfreq_add_governor(&devfreq_simple_ondemand);
+}
+subsys_initcall(devfreq_simple_ondemand_init);
+
+static void __exit devfreq_simple_ondemand_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_simple_ondemand);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_simple_ondemand_exit);
+MODULE_LICENSE("GPL");
index 0681246fc89dc32e53900929abd2c2df4329e0ab..35de6e83c1febedd99f8c155e374b5d4f8bb5202 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/devfreq.h>
 #include <linux/pm.h>
 #include <linux/mutex.h>
+#include <linux/module.h>
 #include "governor.h"
 
 struct userspace_data {
@@ -116,10 +117,46 @@ static void userspace_exit(struct devfreq *devfreq)
        devfreq->data = NULL;
 }
 
-const struct devfreq_governor devfreq_userspace = {
+static int devfreq_userspace_handler(struct devfreq *devfreq,
+                       unsigned int event, void *data)
+{
+       int ret = 0;
+
+       switch (event) {
+       case DEVFREQ_GOV_START:
+               ret = userspace_init(devfreq);
+               break;
+       case DEVFREQ_GOV_STOP:
+               userspace_exit(devfreq);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static struct devfreq_governor devfreq_userspace = {
        .name = "userspace",
        .get_target_freq = devfreq_userspace_func,
-       .init = userspace_init,
-       .exit = userspace_exit,
-       .no_central_polling = true,
+       .event_handler = devfreq_userspace_handler,
 };
+
+static int __init devfreq_userspace_init(void)
+{
+       return devfreq_add_governor(&devfreq_userspace);
+}
+subsys_initcall(devfreq_userspace_init);
+
+static void __exit devfreq_userspace_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_userspace);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_userspace_exit);
+MODULE_LICENSE("GPL");
index 281c72a3b9d50d19161c0f6182d38f67991cce8f..e83ef39b3bea7359475d328aeed2e81d6b8926af 100644 (file)
@@ -25,12 +25,12 @@ struct devfreq;
  * struct devfreq_dev_status - Data given from devfreq user device to
  *                          governors. Represents the performance
  *                          statistics.
- * @total_time         The total time represented by this instance of
+ * @total_time:                The total time represented by this instance of
  *                     devfreq_dev_status
- * @busy_time          The time that the device was working among the
+ * @busy_time:         The time that the device was working among the
  *                     total_time.
- * @current_frequency  The operating frequency.
- * @private_data       An entry not specified by the devfreq framework.
+ * @current_frequency: The operating frequency.
+ * @private_data:      An entry not specified by the devfreq framework.
  *                     A device and a specific governor may have their
  *                     own protocol with private_data. However, because
  *                     this is governor-specific, a governor using this
@@ -54,23 +54,27 @@ struct devfreq_dev_status {
 
 /**
  * struct devfreq_dev_profile - Devfreq's user device profile
- * @initial_freq       The operating frequency when devfreq_add_device() is
+ * @initial_freq:      The operating frequency when devfreq_add_device() is
  *                     called.
- * @polling_ms         The polling interval in ms. 0 disables polling.
- * @target             The device should set its operating frequency at
+ * @polling_ms:                The polling interval in ms. 0 disables polling.
+ * @target:            The device should set its operating frequency at
  *                     freq or lowest-upper-than-freq value. If freq is
  *                     higher than any operable frequency, set maximum.
  *                     Before returning, target function should set
  *                     freq at the current frequency.
  *                     The "flags" parameter's possible values are
  *                     explained above with "DEVFREQ_FLAG_*" macros.
- * @get_dev_status     The device should provide the current performance
+ * @get_dev_status:    The device should provide the current performance
  *                     status to devfreq, which is used by governors.
- * @exit               An optional callback that is called when devfreq
+ * @get_cur_freq:      The device should provide the current frequency
+ *                     at which it is operating.
+ * @exit:              An optional callback that is called when devfreq
  *                     is removing the devfreq object due to error or
  *                     from devfreq_remove_device() call. If the user
  *                     has registered devfreq->nb at a notifier-head,
  *                     this is the time to unregister it.
+ * @freq_table:        Optional list of frequencies to support statistics.
+ * @max_state: The size of freq_table.
  */
 struct devfreq_dev_profile {
        unsigned long initial_freq;
@@ -79,63 +83,63 @@ struct devfreq_dev_profile {
        int (*target)(struct device *dev, unsigned long *freq, u32 flags);
        int (*get_dev_status)(struct device *dev,
                              struct devfreq_dev_status *stat);
+       int (*get_cur_freq)(struct device *dev, unsigned long *freq);
        void (*exit)(struct device *dev);
+
+       unsigned int *freq_table;
+       unsigned int max_state;
 };
 
 /**
  * struct devfreq_governor - Devfreq policy governor
- * @name               Governor's name
- * @get_target_freq    Returns desired operating frequency for the device.
+ * @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
  *                     devfreq_dev_profile.get_dev_status() to get the
  *                     status of the device (load = busy_time / total_time).
  *                     If no_central_polling is set, this callback is called
  *                     only with update_devfreq() notified by OPP.
- * @init               Called when the devfreq is being attached to a device
- * @exit               Called when the devfreq is being removed from a
- *                     device. Governor should stop any internal routines
- *                     before return because related data may be
- *                     freed after exit().
- * @no_central_polling Do not use devfreq's central polling mechanism.
- *                     When this is set, devfreq will not call
- *                     get_target_freq with devfreq_monitor(). However,
- *                     devfreq will call get_target_freq with
- *                     devfreq_update() notified by OPP framework.
+ * @event_handler:      Callback for devfreq core framework to notify events
+ *                      to governors. Events include per device governor
+ *                      init and exit, opp changes out of devfreq, suspend
+ *                      and resume of per device devfreq during device idle.
  *
  * 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 (*init)(struct devfreq *this);
-       void (*exit)(struct devfreq *this);
-       const bool no_central_polling;
+       int (*event_handler)(struct devfreq *devfreq,
+                               unsigned int event, void *data);
 };
 
 /**
  * struct devfreq - Device devfreq structure
- * @node       list node - contains the devices with devfreq that have been
+ * @node:      list node - contains the devices with devfreq that have been
  *             registered.
- * @lock       a mutex to protect accessing devfreq.
- * @dev                device registered by devfreq class. dev.parent is the device
+ * @lock:      a mutex to protect accessing devfreq.
+ * @dev:       device registered by devfreq class. dev.parent is the device
  *             using devfreq.
- * @profile    device-specific devfreq profile
- * @governor   method how to choose frequency based on the usage.
- * @nb         notifier block used to notify devfreq object that it should
+ * @profile:   device-specific devfreq profile
+ * @governor:  method how to choose frequency based on the usage.
+ * @governor_name:     devfreq governor name for use with this devfreq
+ * @nb:                notifier block used to notify devfreq object that it should
  *             reevaluate operable frequencies. Devfreq users may use
  *             devfreq.nb to the corresponding register notifier call chain.
- * @polling_jiffies    interval in jiffies.
- * @previous_freq      previously configured frequency value.
- * @next_polling       the number of remaining jiffies to poll with
- *                     "devfreq_monitor" executions to reevaluate
- *                     frequency/voltage of the device. Set by
- *                     profile's polling_ms interval.
- * @data       Private data of the governor. The devfreq framework does not
+ * @work:      delayed work for load monitoring.
+ * @previous_freq:     previously configured frequency value.
+ * @data:      Private data of the governor. The devfreq framework does not
  *             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)
+ * @min_freq:  Limit minimum frequency requested by user (0: none)
+ * @max_freq:  Limit maximum frequency requested by user (0: none)
+ * @stop_polling:       devfreq polling status of a device.
+ * @total_trans:       Number of devfreq transitions
+ * @trans_table:       Statistics of devfreq transitions
+ * @time_in_state:     Statistics of devfreq states
+ * @last_stat_updated: The last time stat updated
  *
  * This structure stores the devfreq information for a give device.
  *
@@ -152,26 +156,33 @@ struct devfreq {
        struct device dev;
        struct devfreq_dev_profile *profile;
        const struct devfreq_governor *governor;
+       char governor_name[DEVFREQ_NAME_LEN];
        struct notifier_block nb;
+       struct delayed_work work;
 
-       unsigned long polling_jiffies;
        unsigned long previous_freq;
-       unsigned int next_polling;
 
        void *data; /* private data for governors */
 
-       bool being_removed;
-
        unsigned long min_freq;
        unsigned long max_freq;
+       bool stop_polling;
+
+       /* information for device freqeuncy transition */
+       unsigned int total_trans;
+       unsigned int *trans_table;
+       unsigned long *time_in_state;
+       unsigned long last_stat_updated;
 };
 
 #if defined(CONFIG_PM_DEVFREQ)
 extern struct devfreq *devfreq_add_device(struct device *dev,
                                  struct devfreq_dev_profile *profile,
-                                 const struct devfreq_governor *governor,
+                                 const char *governor_name,
                                  void *data);
 extern int devfreq_remove_device(struct devfreq *devfreq);
+extern int devfreq_suspend_device(struct devfreq *devfreq);
+extern int devfreq_resume_device(struct devfreq *devfreq);
 
 /* Helper functions for devfreq user device driver with OPP. */
 extern struct opp *devfreq_recommended_opp(struct device *dev,
@@ -181,23 +192,13 @@ extern int devfreq_register_opp_notifier(struct device *dev,
 extern int devfreq_unregister_opp_notifier(struct device *dev,
                                           struct devfreq *devfreq);
 
-#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE
-extern const struct devfreq_governor devfreq_powersave;
-#endif
-#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE
-extern const struct devfreq_governor devfreq_performance;
-#endif
-#ifdef CONFIG_DEVFREQ_GOV_USERSPACE
-extern const struct devfreq_governor devfreq_userspace;
-#endif
-#ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND
-extern const struct devfreq_governor devfreq_simple_ondemand;
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
 /**
  * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq
  *     and devfreq_add_device
- * @ upthreshold       If the load is over this value, the frequency jumps.
+ * @upthreshold:       If the load is over this value, the frequency jumps.
  *                     Specify 0 to use the default. Valid value = 0 to 100.
- * @ downdifferential  If the load is under upthreshold - downdifferential,
+ * @downdifferential:  If the load is under upthreshold - downdifferential,
  *                     the governor may consider slowing the frequency down.
  *                     Specify 0 to use the default. Valid value = 0 to 100.
  *                     downdifferential < upthreshold must hold.
@@ -214,7 +215,7 @@ struct devfreq_simple_ondemand_data {
 #else /* !CONFIG_PM_DEVFREQ */
 static struct devfreq *devfreq_add_device(struct device *dev,
                                          struct devfreq_dev_profile *profile,
-                                         struct devfreq_governor *governor,
+                                         const char *governor_name,
                                          void *data)
 {
        return NULL;
@@ -225,6 +226,16 @@ static int devfreq_remove_device(struct devfreq *devfreq)
        return 0;
 }
 
+static int devfreq_suspend_device(struct devfreq *devfreq)
+{
+       return 0;
+}
+
+static int devfreq_resume_device(struct devfreq *devfreq)
+{
+       return 0;
+}
+
 static struct opp *devfreq_recommended_opp(struct device *dev,
                                           unsigned long *freq, u32 flags)
 {
@@ -243,11 +254,6 @@ static int devfreq_unregister_opp_notifier(struct device *dev,
        return -EINVAL;
 }
 
-#define devfreq_powersave      NULL
-#define devfreq_performance    NULL
-#define devfreq_userspace      NULL
-#define devfreq_simple_ondemand        NULL
-
 #endif /* CONFIG_PM_DEVFREQ */
 
 #endif /* __LINUX_DEVFREQ_H__ */