[COMMON] devfreq: Update to devfreq core code.
authorChungwoo Park <cww.park@samsung.com>
Wed, 23 May 2018 00:46:03 +0000 (09:46 +0900)
committerChungwoo Park <cww.park@samsung.com>
Wed, 23 May 2018 10:52:15 +0000 (19:52 +0900)
This patch updated devfreq core related code.

Change-Id: I74c069783c8f87779107cddb35b2f984d359627e
Signed-off-by: Chungwoo Park <cww.park@samsung.com>
drivers/devfreq/devfreq.c

index 202476fbbc4c0256bf7793a7f560f8f686081350..520d0128695a419df369ebbda139fdf18f9b566d 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/pm_opp.h>
+#include <linux/pm_qos.h>
 #include <linux/devfreq.h>
 #include <linux/workqueue.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
 #include "governor.h"
 
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ
+#include <soc/samsung/exynos-devfreq.h>
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+#include <soc/samsung/exynos-dm.h>
+#endif
+
+#endif
+
 static struct class *devfreq_class;
 
 /*
@@ -130,7 +140,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
  */
 int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 {
-       int lev, prev_lev, ret = 0;
+       int lev, prev_lev = 0, ret = 0;
        unsigned long cur_time;
 
        cur_time = jiffies;
@@ -154,7 +164,13 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
                goto out;
        }
 
-       if (lev != prev_lev) {
+       if (freq != devfreq->previous_freq) {
+               prev_lev = devfreq_get_freq_level(devfreq,
+                                               devfreq->previous_freq);
+               if (prev_lev && prev_lev < 0) {
+                       pr_err("DEVFREQ: invalid index to update status\n");
+                       return -EINVAL;
+               }
                devfreq->trans_table[(prev_lev *
                                devfreq->profile->max_state) + lev]++;
                devfreq->total_trans++;
@@ -192,6 +208,66 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
        return ERR_PTR(-ENODEV);
 }
 
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+static int devfreq_frequency_scaler(int dm_type, void *devdata,
+                               u32 target_freq, unsigned int relation)
+{
+       struct device *dev;
+       struct devfreq *devfreq;
+       unsigned long freq = target_freq;
+       u32 flags = 0;
+       int err = 0;
+
+       dev = find_exynos_devfreq_device(devdata);
+       if (IS_ERR(dev)) {
+               pr_err("%s: No such devfreq device for dm_type(%d)\n", __func__, dm_type);
+               err = -ENODEV;
+               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: No such devfreq for the device\n", __func__);
+               err = -ENODEV;
+               goto err_out;
+       }
+
+       /*
+        * Adjust the freuqency with user freq and QoS.
+        *
+        * List from the highest proiority
+        * max_freq (probably called by thermal when it's too hot)
+        * min_freq
+        */
+
+       if (devfreq->min_freq && freq < devfreq->min_freq) {
+               freq = devfreq->min_freq;
+               flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
+       }
+       if (devfreq->max_freq && freq > devfreq->max_freq) {
+               freq = devfreq->max_freq;
+               flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
+       }
+
+       err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
+       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;
+
+err_out:
+       return err;
+}
+#endif
+
+#if !defined(CONFIG_EXYNOS_DVFS_MANAGER) || !defined(CONFIG_ARM_EXYNOS_DEVFREQ)
 static int devfreq_notify_transition(struct devfreq *devfreq,
                struct devfreq_freqs *freqs, unsigned int state)
 {
@@ -214,6 +290,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq,
 
        return 0;
 }
+#endif
 
 /* Load monitoring helper functions for governors use */
 
@@ -226,10 +303,19 @@ static int devfreq_notify_transition(struct devfreq *devfreq,
  */
 int update_devfreq(struct devfreq *devfreq)
 {
-       struct devfreq_freqs freqs;
-       unsigned long freq, cur_freq;
+       unsigned long freq;
        int err = 0;
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       int dm_type;
+       unsigned long pm_qos_max;
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE)
+       struct devfreq_simple_interactive_data *gov_data = devfreq->data;
+#endif
+#else
        u32 flags = 0;
+       unsigned long cur_freq;
+       struct devfreq_freqs freqs;
+#endif
 
        if (!mutex_is_locked(&devfreq->lock)) {
                WARN(true, "devfreq->lock must be locked by the caller.\n");
@@ -244,6 +330,24 @@ int update_devfreq(struct devfreq *devfreq)
        if (err)
                return err;
 
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       err = find_exynos_devfreq_dm_type(devfreq->dev.parent, &dm_type);
+       if (err)
+               return -EINVAL;
+
+       pm_qos_max = devfreq->max_freq;
+
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE)
+       if (!strcmp(devfreq->governor->name, "interactive") && gov_data->pm_qos_class_max)
+               pm_qos_max = (unsigned long)pm_qos_request(gov_data->pm_qos_class_max);
+#endif
+       if (devfreq->str_freq)
+               policy_update_call_to_DM(dm_type, devfreq->str_freq,
+                                        devfreq->str_freq);
+       else
+               policy_update_call_to_DM(dm_type, freq, pm_qos_max);
+       DM_CALL(dm_type, &freq);
+#else
        /*
         * Adjust the frequency with user freq and QoS.
         *
@@ -286,6 +390,7 @@ int update_devfreq(struct devfreq *devfreq)
                                "Couldn't update frequency transition information.\n");
 
        devfreq->previous_freq = freq;
+#endif
        return err;
 }
 EXPORT_SYMBOL(update_devfreq);
@@ -303,11 +408,15 @@ static void devfreq_monitor(struct work_struct *work)
 
        mutex_lock(&devfreq->lock);
        err = update_devfreq(devfreq);
-       if (err)
+       if (err && err != -EAGAIN)
                dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err);
-
+#ifdef CONFIG_SCHED_HMP
+       mod_delayed_work_on(0, devfreq_wq, &devfreq->work,
+                               msecs_to_jiffies(devfreq->profile->polling_ms));
+#else
        queue_delayed_work(devfreq_wq, &devfreq->work,
                                msecs_to_jiffies(devfreq->profile->polling_ms));
+#endif
        mutex_unlock(&devfreq->lock);
 }
 
@@ -322,7 +431,7 @@ static void devfreq_monitor(struct work_struct *work)
  */
 void devfreq_monitor_start(struct devfreq *devfreq)
 {
-       INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
+       INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor);
        if (devfreq->profile->polling_ms)
                queue_delayed_work(devfreq_wq, &devfreq->work,
                        msecs_to_jiffies(devfreq->profile->polling_ms));
@@ -517,8 +626,10 @@ 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 defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       int dm_type;
+#endif
 
        if (!dev || !profile || !governor_name) {
                dev_err(dev, "%s: Invalid parameters.\n", __func__);
@@ -559,8 +670,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
                mutex_lock(&devfreq->lock);
        }
 
-       dev_set_name(&devfreq->dev, "devfreq%d",
-                               atomic_inc_return(&devfreq_no));
+       dev_set_name(&devfreq->dev, "%s", dev_name(dev));
        err = device_register(&devfreq->dev);
        if (err) {
                mutex_unlock(&devfreq->lock);
@@ -582,6 +692,16 @@ struct devfreq *devfreq_add_device(struct device *dev,
 
        mutex_unlock(&devfreq->lock);
 
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       err = find_exynos_devfreq_dm_type(dev, &dm_type);
+       if (err)
+               goto err_dm_type;
+
+       err = register_exynos_dm_freq_scaler(dm_type, devfreq_frequency_scaler);
+       if (err)
+               goto err_dm_scaler;
+#endif
+
        mutex_lock(&devfreq_list_lock);
        list_add(&devfreq->node, &devfreq_list);
 
@@ -604,11 +724,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
        mutex_unlock(&devfreq_list_lock);
 
        return devfreq;
-
 err_init:
        list_del(&devfreq->node);
        mutex_unlock(&devfreq_list_lock);
-
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       unregister_exynos_dm_freq_scaler(dm_type);
+err_dm_scaler:
+err_dm_type:
+#endif
        device_unregister(&devfreq->dev);
 err_dev:
        if (devfreq)
@@ -626,9 +749,20 @@ EXPORT_SYMBOL(devfreq_add_device);
  */
 int devfreq_remove_device(struct devfreq *devfreq)
 {
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       int dm_type;
+       int err = 0;
+#endif
        if (!devfreq)
                return -EINVAL;
 
+#if defined(CONFIG_EXYNOS_DVFS_MANAGER) && defined(CONFIG_ARM_EXYNOS_DEVFREQ)
+       err = find_exynos_devfreq_dm_type(devfreq->dev.parent, &dm_type);
+       if (err)
+               return err;
+
+       unregister_exynos_dm_freq_scaler(dm_type);
+#endif
        device_unregister(&devfreq->dev);
 
        return 0;
@@ -1055,60 +1189,6 @@ static ssize_t polling_interval_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(polling_interval);
 
-static ssize_t min_freq_store(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)
-               return -EINVAL;
-
-       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);
-       return ret;
-}
-
-static ssize_t max_freq_store(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)
-               return -EINVAL;
-
-       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);
-       return ret;
-}
-
 #define show_one(name)                                         \
 static ssize_t name##_show                                     \
 (struct device *dev, struct device_attribute *attr, char *buf) \
@@ -1118,8 +1198,8 @@ static ssize_t name##_show                                        \
 show_one(min_freq);
 show_one(max_freq);
 
-static DEVICE_ATTR_RW(min_freq);
-static DEVICE_ATTR_RW(max_freq);
+static DEVICE_ATTR_RO(min_freq);
+static DEVICE_ATTR_RO(max_freq);
 
 static ssize_t available_frequencies_show(struct device *d,
                                          struct device_attribute *attr,
@@ -1196,6 +1276,28 @@ static ssize_t trans_stat_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(trans_stat);
 
+static ssize_t time_in_state_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct devfreq *devfreq = to_devfreq(dev);
+       ssize_t len = 0;
+       int i, err;
+       unsigned int max_state = devfreq->profile->max_state;
+
+       err = devfreq_update_status(devfreq, devfreq->previous_freq);
+       if (err)
+               return 0;
+
+       for (i = 0; i < max_state; i++) {
+               len += sprintf(buf + len, "%8lu",
+                               devfreq->profile->freq_table[i]);
+               len += sprintf(buf + len, "%10u\n",
+                       jiffies_to_msecs(devfreq->time_in_state[i]));
+       }
+       return len;
+}
+static DEVICE_ATTR_RO(time_in_state);
+
 static struct attribute *devfreq_attrs[] = {
        &dev_attr_governor.attr,
        &dev_attr_available_governors.attr,
@@ -1206,6 +1308,7 @@ static struct attribute *devfreq_attrs[] = {
        &dev_attr_min_freq.attr,
        &dev_attr_max_freq.attr,
        &dev_attr_trans_stat.attr,
+       &dev_attr_time_in_state.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(devfreq);