ipq806x: 5.15: backport devfreq new cpufreq based PASSIVE governor
authorChristian Marangi <ansuelsmth@gmail.com>
Thu, 16 Jun 2022 20:16:04 +0000 (22:16 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Tue, 11 Oct 2022 19:28:42 +0000 (21:28 +0200)
Backport devfreq new cpufreq based PASSIVE governor needed for devfreq
based fab and cache scaling.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/ipq806x/patches-5.15/111-v5.19-01-PM-devfreq-Export-devfreq_get_freq_range-symbol-with.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/111-v5.19-02-PM-devfreq-Add-cpu-based-scaling-support-to-passive-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/111-v5.19-03-PM-devfreq-passive-Reduce-duplicate-code-when-passiv.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/111-v5.19-04-PM-devfreq-passive-Keep-cpufreq_policy-for-possible-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/111-v5.19-05-PM-devfreq-passive-Return-non-error-when-not-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/112-v5.19-PM-devfreq-Fix-kernel-warning-with-cpufreq-passive-r.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/113-v5.19-01-PM-devfreq-Fix-cpufreq-passive-unregister-errorin.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/113-v5.19-02-PM-devfreq-Fix-kernel-panic-with-cpu-based-scaling-t.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/113-v5.19-03-PM-devfreq-Rework-freq_table-to-be-local-to-devfr.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/113-v5.19-04-PM-devfreq-Mute-warning-on-governor-PROBE_DEFER.patch [new file with mode: 0644]

diff --git a/target/linux/ipq806x/patches-5.15/111-v5.19-01-PM-devfreq-Export-devfreq_get_freq_range-symbol-with.patch b/target/linux/ipq806x/patches-5.15/111-v5.19-01-PM-devfreq-Export-devfreq_get_freq_range-symbol-with.patch
new file mode 100644 (file)
index 0000000..0442105
--- /dev/null
@@ -0,0 +1,113 @@
+From 713472e53e6e53c985e283782b0fd76b8ecfd47e Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi@samsung.com>
+Date: Mon, 1 Mar 2021 02:07:29 +0900
+Subject: [PATCH 1/5] PM / devfreq: Export devfreq_get_freq_range symbol within
+ devfreq
+
+In order to get frequency range within devfreq governors,
+export devfreq_get_freq_range symbol within devfreq.
+
+Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Tested-by: Johnson Wang <johnson.wang@mediatek.com>
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+---
+ drivers/devfreq/devfreq.c  | 20 ++++++++++++--------
+ drivers/devfreq/governor.h |  2 ++
+ 2 files changed, 14 insertions(+), 8 deletions(-)
+
+--- a/drivers/devfreq/devfreq.c
++++ b/drivers/devfreq/devfreq.c
+@@ -112,16 +112,16 @@ static unsigned long find_available_max_
+ }
+ /**
+- * get_freq_range() - Get the current freq range
++ * devfreq_get_freq_range() - Get the current freq range
+  * @devfreq:  the devfreq instance
+  * @min_freq: the min frequency
+  * @max_freq: the max frequency
+  *
+  * This takes into consideration all constraints.
+  */
+-static void get_freq_range(struct devfreq *devfreq,
+-                         unsigned long *min_freq,
+-                         unsigned long *max_freq)
++void devfreq_get_freq_range(struct devfreq *devfreq,
++                          unsigned long *min_freq,
++                          unsigned long *max_freq)
+ {
+       unsigned long *freq_table = devfreq->profile->freq_table;
+       s32 qos_min_freq, qos_max_freq;
+@@ -158,6 +158,7 @@ static void get_freq_range(struct devfre
+       if (*min_freq > *max_freq)
+               *min_freq = *max_freq;
+ }
++EXPORT_SYMBOL(devfreq_get_freq_range);
+ /**
+  * devfreq_get_freq_level() - Lookup freq_table for the frequency
+@@ -418,7 +419,7 @@ int devfreq_update_target(struct devfreq
+       err = devfreq->governor->get_target_freq(devfreq, &freq);
+       if (err)
+               return err;
+-      get_freq_range(devfreq, &min_freq, &max_freq);
++      devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
+       if (freq < min_freq) {
+               freq = min_freq;
+@@ -785,6 +786,7 @@ struct devfreq *devfreq_add_device(struc
+ {
+       struct devfreq *devfreq;
+       struct devfreq_governor *governor;
++      unsigned long min_freq, max_freq;
+       int err = 0;
+       if (!dev || !profile || !governor_name) {
+@@ -849,6 +851,8 @@ struct devfreq *devfreq_add_device(struc
+               goto err_dev;
+       }
++      devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
++
+       devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
+       devfreq->opp_table = dev_pm_opp_get_opp_table(dev);
+       if (IS_ERR(devfreq->opp_table))
+@@ -1561,7 +1565,7 @@ static ssize_t min_freq_show(struct devi
+       unsigned long min_freq, max_freq;
+       mutex_lock(&df->lock);
+-      get_freq_range(df, &min_freq, &max_freq);
++      devfreq_get_freq_range(df, &min_freq, &max_freq);
+       mutex_unlock(&df->lock);
+       return sprintf(buf, "%lu\n", min_freq);
+@@ -1615,7 +1619,7 @@ static ssize_t max_freq_show(struct devi
+       unsigned long min_freq, max_freq;
+       mutex_lock(&df->lock);
+-      get_freq_range(df, &min_freq, &max_freq);
++      devfreq_get_freq_range(df, &min_freq, &max_freq);
+       mutex_unlock(&df->lock);
+       return sprintf(buf, "%lu\n", max_freq);
+@@ -1929,7 +1933,7 @@ static int devfreq_summary_show(struct s
+               mutex_lock(&devfreq->lock);
+               cur_freq = devfreq->previous_freq;
+-              get_freq_range(devfreq, &min_freq, &max_freq);
++              devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
+               timer = devfreq->profile->timer;
+               if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
+--- a/drivers/devfreq/governor.h
++++ b/drivers/devfreq/governor.h
+@@ -86,6 +86,8 @@ int devfreq_remove_governor(struct devfr
+ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
+ int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);
++void devfreq_get_freq_range(struct devfreq *devfreq, unsigned long *min_freq,
++                          unsigned long *max_freq);
+ static inline int devfreq_update_stats(struct devfreq *df)
+ {
diff --git a/target/linux/ipq806x/patches-5.15/111-v5.19-02-PM-devfreq-Add-cpu-based-scaling-support-to-passive-.patch b/target/linux/ipq806x/patches-5.15/111-v5.19-02-PM-devfreq-Add-cpu-based-scaling-support-to-passive-.patch
new file mode 100644 (file)
index 0000000..52f1200
--- /dev/null
@@ -0,0 +1,461 @@
+From a03dacb0316f74400846aaf144d6c73f4217ca08 Mon Sep 17 00:00:00 2001
+From: Saravana Kannan <skannan@codeaurora.org>
+Date: Tue, 2 Mar 2021 15:58:21 +0900
+Subject: [PATCH 2/5] PM / devfreq: Add cpu based scaling support to passive
+ governor
+
+Many CPU architectures have caches that can scale independent of the
+CPUs. Frequency scaling of the caches is necessary to make sure that the
+cache is not a performance bottleneck that leads to poor performance and
+power. The same idea applies for RAM/DDR.
+
+To achieve this, this patch adds support for cpu based scaling to the
+passive governor. This is accomplished by taking the current frequency
+of each CPU frequency domain and then adjust the frequency of the cache
+(or any devfreq device) based on the frequency of the CPUs. It listens
+to CPU frequency transition notifiers to keep itself up to date on the
+current CPU frequency.
+
+To decide the frequency of the device, the governor does one of the
+following:
+* Derives the optimal devfreq device opp from required-opps property of
+  the parent cpu opp_table.
+
+* Scales the device frequency in proportion to the CPU frequency. So, if
+  the CPUs are running at their max frequency, the device runs at its
+  max frequency. If the CPUs are running at their min frequency, the
+  device runs at its min frequency. It is interpolated for frequencies
+  in between.
+
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Tested-by: Johnson Wang <johnson.wang@mediatek.com>
+Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
+[Sibi: Integrated cpu-freqmap governor into passive_governor]
+Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
+[Chanwoo: Fix conflict with latest code and cleanup code]
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+---
+ drivers/devfreq/governor.h         |  22 +++
+ drivers/devfreq/governor_passive.c | 298 +++++++++++++++++++++++++++--
+ include/linux/devfreq.h            |  17 +-
+ 3 files changed, 323 insertions(+), 14 deletions(-)
+
+--- a/drivers/devfreq/governor.h
++++ b/drivers/devfreq/governor.h
+@@ -48,6 +48,28 @@
+ #define DEVFREQ_GOV_ATTR_TIMER                                BIT(1)
+ /**
++ * struct devfreq_cpu_data - Hold the per-cpu data
++ * @dev:      reference to cpu device.
++ * @first_cpu:        the cpumask of the first cpu of a policy.
++ * @opp_table:        reference to cpu opp table.
++ * @cur_freq: the current frequency of the cpu.
++ * @min_freq: the min frequency of the cpu.
++ * @max_freq: the max frequency of the cpu.
++ *
++ * This structure stores the required cpu_data of a cpu.
++ * This is auto-populated by the governor.
++ */
++struct devfreq_cpu_data {
++      struct device *dev;
++      unsigned int first_cpu;
++
++      struct opp_table *opp_table;
++      unsigned int cur_freq;
++      unsigned int min_freq;
++      unsigned int max_freq;
++};
++
++/**
+  * struct devfreq_governor - Devfreq policy governor
+  * @node:             list node - contains registered devfreq governors
+  * @name:             Governor's name
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -8,11 +8,85 @@
+  */
+ #include <linux/module.h>
++#include <linux/cpu.h>
++#include <linux/cpufreq.h>
++#include <linux/cpumask.h>
++#include <linux/slab.h>
+ #include <linux/device.h>
+ #include <linux/devfreq.h>
+ #include "governor.h"
+-static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
++#define HZ_PER_KHZ    1000
++
++static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
++                                              struct opp_table *p_opp_table,
++                                              struct opp_table *opp_table,
++                                              unsigned long *freq)
++{
++      struct dev_pm_opp *opp = NULL, *p_opp = NULL;
++      unsigned long target_freq;
++
++      if (!p_dev || !p_opp_table || !opp_table || !freq)
++              return 0;
++
++      p_opp = devfreq_recommended_opp(p_dev, freq, 0);
++      if (IS_ERR(p_opp))
++              return 0;
++
++      opp = dev_pm_opp_xlate_required_opp(p_opp_table, opp_table, p_opp);
++      dev_pm_opp_put(p_opp);
++
++      if (IS_ERR(opp))
++              return 0;
++
++      target_freq = dev_pm_opp_get_freq(opp);
++      dev_pm_opp_put(opp);
++
++      return target_freq;
++}
++
++static int get_target_freq_with_cpufreq(struct devfreq *devfreq,
++                                      unsigned long *target_freq)
++{
++      struct devfreq_passive_data *p_data =
++                              (struct devfreq_passive_data *)devfreq->data;
++      struct devfreq_cpu_data *parent_cpu_data;
++      unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
++      unsigned long dev_min, dev_max;
++      unsigned long freq = 0;
++
++      for_each_online_cpu(cpu) {
++              parent_cpu_data = p_data->parent_cpu_data[cpu];
++              if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu)
++                      continue;
++
++              /* Get target freq via required opps */
++              cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
++              freq = get_target_freq_by_required_opp(parent_cpu_data->dev,
++                                      parent_cpu_data->opp_table,
++                                      devfreq->opp_table, &cpu_cur);
++              if (freq) {
++                      *target_freq = max(freq, *target_freq);
++                      continue;
++              }
++
++              /* Use interpolation if required opps is not available */
++              devfreq_get_freq_range(devfreq, &dev_min, &dev_max);
++
++              cpu_min = parent_cpu_data->min_freq;
++              cpu_max = parent_cpu_data->max_freq;
++              cpu_cur = parent_cpu_data->cur_freq;
++
++              cpu_percent = ((cpu_cur - cpu_min) * 100) / (cpu_max - cpu_min);
++              freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
++
++              *target_freq = max(freq, *target_freq);
++      }
++
++      return 0;
++}
++
++static int get_target_freq_with_devfreq(struct devfreq *devfreq,
+                                       unsigned long *freq)
+ {
+       struct devfreq_passive_data *p_data
+@@ -99,6 +173,181 @@ no_required_opp:
+       return 0;
+ }
++static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
++                                         unsigned long *freq)
++{
++      struct devfreq_passive_data *p_data =
++                              (struct devfreq_passive_data *)devfreq->data;
++      int ret;
++
++      if (!p_data)
++              return -EINVAL;
++
++      /*
++       * If the devfreq device with passive governor has the specific method
++       * to determine the next frequency, should use the get_target_freq()
++       * of struct devfreq_passive_data.
++       */
++      if (p_data->get_target_freq)
++              return p_data->get_target_freq(devfreq, freq);
++
++      switch (p_data->parent_type) {
++      case DEVFREQ_PARENT_DEV:
++              ret = get_target_freq_with_devfreq(devfreq, freq);
++              break;
++      case CPUFREQ_PARENT_DEV:
++              ret = get_target_freq_with_cpufreq(devfreq, freq);
++              break;
++      default:
++              ret = -EINVAL;
++              dev_err(&devfreq->dev, "Invalid parent type\n");
++              break;
++      }
++
++      return ret;
++}
++
++static int cpufreq_passive_notifier_call(struct notifier_block *nb,
++                                       unsigned long event, void *ptr)
++{
++      struct devfreq_passive_data *p_data =
++                      container_of(nb, struct devfreq_passive_data, nb);
++      struct devfreq *devfreq = (struct devfreq *)p_data->this;
++      struct devfreq_cpu_data *parent_cpu_data;
++      struct cpufreq_freqs *freqs = ptr;
++      unsigned int cur_freq;
++      int ret;
++
++      if (event != CPUFREQ_POSTCHANGE || !freqs ||
++              !p_data->parent_cpu_data[freqs->policy->cpu])
++              return 0;
++
++      parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
++      if (parent_cpu_data->cur_freq == freqs->new)
++              return 0;
++
++      cur_freq = parent_cpu_data->cur_freq;
++      parent_cpu_data->cur_freq = freqs->new;
++
++      mutex_lock(&devfreq->lock);
++      ret = devfreq_update_target(devfreq, freqs->new);
++      mutex_unlock(&devfreq->lock);
++      if (ret) {
++              parent_cpu_data->cur_freq = cur_freq;
++              dev_err(&devfreq->dev, "failed to update the frequency.\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq)
++{
++      struct devfreq_passive_data *p_data
++                      = (struct devfreq_passive_data *)devfreq->data;
++      struct devfreq_cpu_data *parent_cpu_data;
++      int cpu, ret;
++
++      if (p_data->nb.notifier_call) {
++              ret = cpufreq_unregister_notifier(&p_data->nb,
++                                      CPUFREQ_TRANSITION_NOTIFIER);
++              if (ret < 0)
++                      return ret;
++      }
++
++      for_each_possible_cpu(cpu) {
++              parent_cpu_data = p_data->parent_cpu_data[cpu];
++              if (!parent_cpu_data)
++                      continue;
++
++              if (parent_cpu_data->opp_table)
++                      dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
++              kfree(parent_cpu_data);
++      }
++
++      return 0;
++}
++
++static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
++{
++      struct devfreq_passive_data *p_data
++                      = (struct devfreq_passive_data *)devfreq->data;
++      struct device *dev = devfreq->dev.parent;
++      struct opp_table *opp_table = NULL;
++      struct devfreq_cpu_data *parent_cpu_data;
++      struct cpufreq_policy *policy;
++      struct device *cpu_dev;
++      unsigned int cpu;
++      int ret;
++
++      p_data->nb.notifier_call = cpufreq_passive_notifier_call;
++      ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
++      if (ret) {
++              dev_err(dev, "failed to register cpufreq notifier\n");
++              p_data->nb.notifier_call = NULL;
++              goto err;
++      }
++
++      for_each_possible_cpu(cpu) {
++              if (p_data->parent_cpu_data[cpu])
++                      continue;
++
++              policy = cpufreq_cpu_get(cpu);
++              if (!policy) {
++                      ret = -EPROBE_DEFER;
++                      goto err;
++              }
++
++              parent_cpu_data = kzalloc(sizeof(*parent_cpu_data),
++                                              GFP_KERNEL);
++              if (!parent_cpu_data) {
++                      ret = -ENOMEM;
++                      goto err_put_policy;
++              }
++
++              cpu_dev = get_cpu_device(cpu);
++              if (!cpu_dev) {
++                      dev_err(dev, "failed to get cpu device\n");
++                      ret = -ENODEV;
++                      goto err_free_cpu_data;
++              }
++
++              opp_table = dev_pm_opp_get_opp_table(cpu_dev);
++              if (IS_ERR(opp_table)) {
++                      dev_err(dev, "failed to get opp_table of cpu%d\n", cpu);
++                      ret = PTR_ERR(opp_table);
++                      goto err_free_cpu_data;
++              }
++
++              parent_cpu_data->dev = cpu_dev;
++              parent_cpu_data->opp_table = opp_table;
++              parent_cpu_data->first_cpu = cpumask_first(policy->related_cpus);
++              parent_cpu_data->cur_freq = policy->cur;
++              parent_cpu_data->min_freq = policy->cpuinfo.min_freq;
++              parent_cpu_data->max_freq = policy->cpuinfo.max_freq;
++
++              p_data->parent_cpu_data[cpu] = parent_cpu_data;
++              cpufreq_cpu_put(policy);
++      }
++
++      mutex_lock(&devfreq->lock);
++      ret = devfreq_update_target(devfreq, 0L);
++      mutex_unlock(&devfreq->lock);
++      if (ret)
++              dev_err(dev, "failed to update the frequency\n");
++
++      return ret;
++
++err_free_cpu_data:
++      kfree(parent_cpu_data);
++err_put_policy:
++      cpufreq_cpu_put(policy);
++err:
++      WARN_ON(cpufreq_passive_unregister_notifier(devfreq));
++
++      return ret;
++}
++
+ static int devfreq_passive_notifier_call(struct notifier_block *nb,
+                               unsigned long event, void *ptr)
+ {
+@@ -131,30 +380,55 @@ static int devfreq_passive_notifier_call
+       return NOTIFY_DONE;
+ }
+-static int devfreq_passive_event_handler(struct devfreq *devfreq,
+-                              unsigned int event, void *data)
++static int devfreq_passive_unregister_notifier(struct devfreq *devfreq)
++{
++      struct devfreq_passive_data *p_data
++                      = (struct devfreq_passive_data *)devfreq->data;
++      struct devfreq *parent = (struct devfreq *)p_data->parent;
++      struct notifier_block *nb = &p_data->nb;
++
++      return devfreq_unregister_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER);
++}
++
++static int devfreq_passive_register_notifier(struct devfreq *devfreq)
+ {
+       struct devfreq_passive_data *p_data
+                       = (struct devfreq_passive_data *)devfreq->data;
+       struct devfreq *parent = (struct devfreq *)p_data->parent;
+       struct notifier_block *nb = &p_data->nb;
+-      int ret = 0;
+       if (!parent)
+               return -EPROBE_DEFER;
++      nb->notifier_call = devfreq_passive_notifier_call;
++      return devfreq_register_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER);
++}
++
++static int devfreq_passive_event_handler(struct devfreq *devfreq,
++                              unsigned int event, void *data)
++{
++      struct devfreq_passive_data *p_data
++                      = (struct devfreq_passive_data *)devfreq->data;
++      int ret = -EINVAL;
++
++      if (!p_data)
++              return -EINVAL;
++
++      if (!p_data->this)
++              p_data->this = devfreq;
++
+       switch (event) {
+       case DEVFREQ_GOV_START:
+-              if (!p_data->this)
+-                      p_data->this = devfreq;
+-
+-              nb->notifier_call = devfreq_passive_notifier_call;
+-              ret = devfreq_register_notifier(parent, nb,
+-                                      DEVFREQ_TRANSITION_NOTIFIER);
++              if (p_data->parent_type == DEVFREQ_PARENT_DEV)
++                      ret = devfreq_passive_register_notifier(devfreq);
++              else if (p_data->parent_type == CPUFREQ_PARENT_DEV)
++                      ret = cpufreq_passive_register_notifier(devfreq);
+               break;
+       case DEVFREQ_GOV_STOP:
+-              WARN_ON(devfreq_unregister_notifier(parent, nb,
+-                                      DEVFREQ_TRANSITION_NOTIFIER));
++              if (p_data->parent_type == DEVFREQ_PARENT_DEV)
++                      WARN_ON(devfreq_passive_unregister_notifier(devfreq));
++              else if (p_data->parent_type == CPUFREQ_PARENT_DEV)
++                      WARN_ON(cpufreq_passive_unregister_notifier(devfreq));
+               break;
+       default:
+               break;
+--- a/include/linux/devfreq.h
++++ b/include/linux/devfreq.h
+@@ -38,6 +38,7 @@ enum devfreq_timer {
+ struct devfreq;
+ struct devfreq_governor;
++struct devfreq_cpu_data;
+ struct thermal_cooling_device;
+ /**
+@@ -288,6 +289,11 @@ struct devfreq_simple_ondemand_data {
+ #endif
+ #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
++enum devfreq_parent_dev_type {
++      DEVFREQ_PARENT_DEV,
++      CPUFREQ_PARENT_DEV,
++};
++
+ /**
+  * struct devfreq_passive_data - ``void *data`` fed to struct devfreq
+  *    and devfreq_add_device
+@@ -299,8 +305,11 @@ struct devfreq_simple_ondemand_data {
+  *                    using governors except for passive governor.
+  *                    If the devfreq device has the specific method to decide
+  *                    the next frequency, should use this callback.
+- * @this:     the devfreq instance of own device.
+- * @nb:               the notifier block for DEVFREQ_TRANSITION_NOTIFIER list
++ * @parent_type:      the parent type of the device.
++ * @this:             the devfreq instance of own device.
++ * @nb:                       the notifier block for DEVFREQ_TRANSITION_NOTIFIER or
++ *                    CPUFREQ_TRANSITION_NOTIFIER list.
++ * @parent_cpu_data:  the state min/max/current frequency of all online cpu's.
+  *
+  * The devfreq_passive_data have to set the devfreq instance of parent
+  * device with governors except for the passive governor. But, don't need to
+@@ -314,9 +323,13 @@ struct devfreq_passive_data {
+       /* Optional callback to decide the next frequency of passvice device */
+       int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
++      /* Should set the type of parent device */
++      enum devfreq_parent_dev_type parent_type;
++
+       /* For passive governor's internal use. Don't need to set them */
+       struct devfreq *this;
+       struct notifier_block nb;
++      struct devfreq_cpu_data *parent_cpu_data[NR_CPUS];
+ };
+ #endif
diff --git a/target/linux/ipq806x/patches-5.15/111-v5.19-03-PM-devfreq-passive-Reduce-duplicate-code-when-passiv.patch b/target/linux/ipq806x/patches-5.15/111-v5.19-03-PM-devfreq-passive-Reduce-duplicate-code-when-passiv.patch
new file mode 100644 (file)
index 0000000..cb10bd1
--- /dev/null
@@ -0,0 +1,110 @@
+From 05723e71234b60a1a47313ea1a889797ec648f1c Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi@samsung.com>
+Date: Tue, 2 Mar 2021 17:22:50 +0900
+Subject: [PATCH 3/5] PM / devfreq: passive: Reduce duplicate code when
+ passive_devfreq case
+
+In order to keep the consistent coding style between passive_devfreq
+and passive_cpufreq, use common code for handling required opp property.
+Also remove the unneed conditional statement and unify the comment
+of both passive_devfreq and passive_cpufreq when getting the target frequency.
+
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Tested-by: Johnson Wang <johnson.wang@mediatek.com>
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+---
+ drivers/devfreq/governor_passive.c | 66 ++++--------------------------
+ 1 file changed, 8 insertions(+), 58 deletions(-)
+
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -93,65 +93,16 @@ static int get_target_freq_with_devfreq(
+                       = (struct devfreq_passive_data *)devfreq->data;
+       struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
+       unsigned long child_freq = ULONG_MAX;
+-      struct dev_pm_opp *opp, *p_opp;
+       int i, count;
+-      /*
+-       * If the devfreq device with passive governor has the specific method
+-       * to determine the next frequency, should use the get_target_freq()
+-       * of struct devfreq_passive_data.
+-       */
+-      if (p_data->get_target_freq)
+-              return p_data->get_target_freq(devfreq, freq);
+-
+-      /*
+-       * If the parent and passive devfreq device uses the OPP table,
+-       * get the next frequency by using the OPP table.
+-       */
+-
+-      /*
+-       * - parent devfreq device uses the governors except for passive.
+-       * - passive devfreq device uses the passive governor.
+-       *
+-       * Each devfreq has the OPP table. After deciding the new frequency
+-       * from the governor of parent devfreq device, the passive governor
+-       * need to get the index of new frequency on OPP table of parent
+-       * device. And then the index is used for getting the suitable
+-       * new frequency for passive devfreq device.
+-       */
+-      if (!devfreq->profile || !devfreq->profile->freq_table
+-              || devfreq->profile->max_state <= 0)
+-              return -EINVAL;
+-
+-      /*
+-       * The passive governor have to get the correct frequency from OPP
+-       * list of parent device. Because in this case, *freq is temporary
+-       * value which is decided by ondemand governor.
+-       */
+-      if (devfreq->opp_table && parent_devfreq->opp_table) {
+-              p_opp = devfreq_recommended_opp(parent_devfreq->dev.parent,
+-                                              freq, 0);
+-              if (IS_ERR(p_opp))
+-                      return PTR_ERR(p_opp);
+-
+-              opp = dev_pm_opp_xlate_required_opp(parent_devfreq->opp_table,
+-                                                  devfreq->opp_table, p_opp);
+-              dev_pm_opp_put(p_opp);
+-
+-              if (IS_ERR(opp))
+-                      goto no_required_opp;
+-
+-              *freq = dev_pm_opp_get_freq(opp);
+-              dev_pm_opp_put(opp);
+-
+-              return 0;
+-      }
++      /* Get target freq via required opps */
++      child_freq = get_target_freq_by_required_opp(parent_devfreq->dev.parent,
++                                              parent_devfreq->opp_table,
++                                              devfreq->opp_table, freq);
++      if (child_freq)
++              goto out;
+-no_required_opp:
+-      /*
+-       * Get the OPP table's index of decided frequency by governor
+-       * of parent device.
+-       */
++      /* Use interpolation if required opps is not available */
+       for (i = 0; i < parent_devfreq->profile->max_state; i++)
+               if (parent_devfreq->profile->freq_table[i] == *freq)
+                       break;
+@@ -159,7 +110,6 @@ no_required_opp:
+       if (i == parent_devfreq->profile->max_state)
+               return -EINVAL;
+-      /* Get the suitable frequency by using index of parent device. */
+       if (i < devfreq->profile->max_state) {
+               child_freq = devfreq->profile->freq_table[i];
+       } else {
+@@ -167,7 +117,7 @@ no_required_opp:
+               child_freq = devfreq->profile->freq_table[count - 1];
+       }
+-      /* Return the suitable frequency for passive device. */
++out:
+       *freq = child_freq;
+       return 0;
diff --git a/target/linux/ipq806x/patches-5.15/111-v5.19-04-PM-devfreq-passive-Keep-cpufreq_policy-for-possible-.patch b/target/linux/ipq806x/patches-5.15/111-v5.19-04-PM-devfreq-passive-Keep-cpufreq_policy-for-possible-.patch
new file mode 100644 (file)
index 0000000..12de8af
--- /dev/null
@@ -0,0 +1,232 @@
+From 26984d9d581e5049bd75091d2e789b9cc3ea12e0 Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi@samsung.com>
+Date: Wed, 27 Apr 2022 03:49:19 +0900
+Subject: [PATCH 4/5] PM / devfreq: passive: Keep cpufreq_policy for possible
+ cpus
+
+The passive governor requires the cpu data to get the next target frequency
+of devfreq device if depending on cpu. In order to reduce the unnecessary
+memory data, keep cpufreq_policy data for possible cpus instead of NR_CPU.
+
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Tested-by: Johnson Wang <johnson.wang@mediatek.com>
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+---
+ drivers/devfreq/governor.h         |  3 ++
+ drivers/devfreq/governor_passive.c | 75 +++++++++++++++++++++++-------
+ include/linux/devfreq.h            |  4 +-
+ 3 files changed, 64 insertions(+), 18 deletions(-)
+
+--- a/drivers/devfreq/governor.h
++++ b/drivers/devfreq/governor.h
+@@ -49,6 +49,7 @@
+ /**
+  * struct devfreq_cpu_data - Hold the per-cpu data
++ * @node:     list node
+  * @dev:      reference to cpu device.
+  * @first_cpu:        the cpumask of the first cpu of a policy.
+  * @opp_table:        reference to cpu opp table.
+@@ -60,6 +61,8 @@
+  * This is auto-populated by the governor.
+  */
+ struct devfreq_cpu_data {
++      struct list_head node;
++
+       struct device *dev;
+       unsigned int first_cpu;
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0-only
++      // SPDX-License-Identifier: GPL-2.0-only
+ /*
+  * linux/drivers/devfreq/governor_passive.c
+  *
+@@ -18,6 +18,22 @@
+ #define HZ_PER_KHZ    1000
++static struct devfreq_cpu_data *
++get_parent_cpu_data(struct devfreq_passive_data *p_data,
++                  struct cpufreq_policy *policy)
++{
++      struct devfreq_cpu_data *parent_cpu_data;
++
++      if (!p_data || !policy)
++              return NULL;
++
++      list_for_each_entry(parent_cpu_data, &p_data->cpu_data_list, node)
++              if (parent_cpu_data->first_cpu == cpumask_first(policy->related_cpus))
++                      return parent_cpu_data;
++
++      return NULL;
++}
++
+ static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
+                                               struct opp_table *p_opp_table,
+                                               struct opp_table *opp_table,
+@@ -51,14 +67,24 @@ static int get_target_freq_with_cpufreq(
+       struct devfreq_passive_data *p_data =
+                               (struct devfreq_passive_data *)devfreq->data;
+       struct devfreq_cpu_data *parent_cpu_data;
++      struct cpufreq_policy *policy;
+       unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
+       unsigned long dev_min, dev_max;
+       unsigned long freq = 0;
++      int ret = 0;
+       for_each_online_cpu(cpu) {
+-              parent_cpu_data = p_data->parent_cpu_data[cpu];
+-              if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu)
++              policy = cpufreq_cpu_get(cpu);
++              if (!policy) {
++                      ret = -EINVAL;
++                      continue;
++              }
++
++              parent_cpu_data = get_parent_cpu_data(p_data, policy);
++              if (!parent_cpu_data) {
++                      cpufreq_cpu_put(policy);
+                       continue;
++              }
+               /* Get target freq via required opps */
+               cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
+@@ -67,6 +93,7 @@ static int get_target_freq_with_cpufreq(
+                                       devfreq->opp_table, &cpu_cur);
+               if (freq) {
+                       *target_freq = max(freq, *target_freq);
++                      cpufreq_cpu_put(policy);
+                       continue;
+               }
+@@ -81,9 +108,10 @@ static int get_target_freq_with_cpufreq(
+               freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
+               *target_freq = max(freq, *target_freq);
++              cpufreq_cpu_put(policy);
+       }
+-      return 0;
++      return ret;
+ }
+ static int get_target_freq_with_devfreq(struct devfreq *devfreq,
+@@ -168,12 +196,11 @@ static int cpufreq_passive_notifier_call
+       unsigned int cur_freq;
+       int ret;
+-      if (event != CPUFREQ_POSTCHANGE || !freqs ||
+-              !p_data->parent_cpu_data[freqs->policy->cpu])
++      if (event != CPUFREQ_POSTCHANGE || !freqs)
+               return 0;
+-      parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
+-      if (parent_cpu_data->cur_freq == freqs->new)
++      parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy);
++      if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new)
+               return 0;
+       cur_freq = parent_cpu_data->cur_freq;
+@@ -196,7 +223,7 @@ static int cpufreq_passive_unregister_no
+       struct devfreq_passive_data *p_data
+                       = (struct devfreq_passive_data *)devfreq->data;
+       struct devfreq_cpu_data *parent_cpu_data;
+-      int cpu, ret;
++      int cpu, ret = 0;
+       if (p_data->nb.notifier_call) {
+               ret = cpufreq_unregister_notifier(&p_data->nb,
+@@ -206,16 +233,26 @@ static int cpufreq_passive_unregister_no
+       }
+       for_each_possible_cpu(cpu) {
+-              parent_cpu_data = p_data->parent_cpu_data[cpu];
+-              if (!parent_cpu_data)
++              struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
++              if (!policy) {
++                      ret = -EINVAL;
++                      continue;
++              }
++
++              parent_cpu_data = get_parent_cpu_data(p_data, policy);
++              if (!parent_cpu_data) {
++                      cpufreq_cpu_put(policy);
+                       continue;
++              }
++              list_del(&parent_cpu_data->node);
+               if (parent_cpu_data->opp_table)
+                       dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
+               kfree(parent_cpu_data);
++              cpufreq_cpu_put(policy);
+       }
+-      return 0;
++      return ret;
+ }
+ static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
+@@ -230,6 +267,9 @@ static int cpufreq_passive_register_noti
+       unsigned int cpu;
+       int ret;
++      p_data->cpu_data_list
++              = (struct list_head)LIST_HEAD_INIT(p_data->cpu_data_list);
++
+       p_data->nb.notifier_call = cpufreq_passive_notifier_call;
+       ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
+       if (ret) {
+@@ -239,15 +279,18 @@ static int cpufreq_passive_register_noti
+       }
+       for_each_possible_cpu(cpu) {
+-              if (p_data->parent_cpu_data[cpu])
+-                      continue;
+-
+               policy = cpufreq_cpu_get(cpu);
+               if (!policy) {
+                       ret = -EPROBE_DEFER;
+                       goto err;
+               }
++              parent_cpu_data = get_parent_cpu_data(p_data, policy);
++              if (parent_cpu_data) {
++                      cpufreq_cpu_put(policy);
++                      continue;
++              }
++
+               parent_cpu_data = kzalloc(sizeof(*parent_cpu_data),
+                                               GFP_KERNEL);
+               if (!parent_cpu_data) {
+@@ -276,7 +319,7 @@ static int cpufreq_passive_register_noti
+               parent_cpu_data->min_freq = policy->cpuinfo.min_freq;
+               parent_cpu_data->max_freq = policy->cpuinfo.max_freq;
+-              p_data->parent_cpu_data[cpu] = parent_cpu_data;
++              list_add_tail(&parent_cpu_data->node, &p_data->cpu_data_list);
+               cpufreq_cpu_put(policy);
+       }
+--- a/include/linux/devfreq.h
++++ b/include/linux/devfreq.h
+@@ -309,7 +309,7 @@ enum devfreq_parent_dev_type {
+  * @this:             the devfreq instance of own device.
+  * @nb:                       the notifier block for DEVFREQ_TRANSITION_NOTIFIER or
+  *                    CPUFREQ_TRANSITION_NOTIFIER list.
+- * @parent_cpu_data:  the state min/max/current frequency of all online cpu's.
++ * @cpu_data_list:    the list of cpu frequency data for all cpufreq_policy.
+  *
+  * The devfreq_passive_data have to set the devfreq instance of parent
+  * device with governors except for the passive governor. But, don't need to
+@@ -329,7 +329,7 @@ struct devfreq_passive_data {
+       /* For passive governor's internal use. Don't need to set them */
+       struct devfreq *this;
+       struct notifier_block nb;
+-      struct devfreq_cpu_data *parent_cpu_data[NR_CPUS];
++      struct list_head cpu_data_list;
+ };
+ #endif
diff --git a/target/linux/ipq806x/patches-5.15/111-v5.19-05-PM-devfreq-passive-Return-non-error-when-not-support.patch b/target/linux/ipq806x/patches-5.15/111-v5.19-05-PM-devfreq-passive-Return-non-error-when-not-support.patch
new file mode 100644 (file)
index 0000000..69160fd
--- /dev/null
@@ -0,0 +1,31 @@
+From 42d2607d91c4ec37ea1970899c2d614824f3014b Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi@samsung.com>
+Date: Thu, 19 May 2022 10:07:53 +0900
+Subject: [PATCH 5/5] PM / devfreq: passive: Return non-error when
+ not-supported event is required
+
+Each devfreq governor specifies the supported governor event
+such as GOV_START and GOV_STOP. When not-supported event is required,
+just return non-error. But, commit ce9a0d88d97a ("PM / devfreq: Add
+cpu based scaling support to passive governor") returned the error
+value. So that return non-error value when not-supported event is required.
+
+Fixes: ce9a0d88d97a ("PM / devfreq: Add cpu based scaling support to passive governor")
+Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+---
+ drivers/devfreq/governor_passive.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -402,7 +402,7 @@ static int devfreq_passive_event_handler
+ {
+       struct devfreq_passive_data *p_data
+                       = (struct devfreq_passive_data *)devfreq->data;
+-      int ret = -EINVAL;
++      int ret = 0;
+       if (!p_data)
+               return -EINVAL;
diff --git a/target/linux/ipq806x/patches-5.15/112-v5.19-PM-devfreq-Fix-kernel-warning-with-cpufreq-passive-r.patch b/target/linux/ipq806x/patches-5.15/112-v5.19-PM-devfreq-Fix-kernel-warning-with-cpufreq-passive-r.patch
new file mode 100644 (file)
index 0000000..3d2bb2d
--- /dev/null
@@ -0,0 +1,31 @@
+From 82c66d2bbbeda9e493487e7413769087a0b46250 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 20 Jun 2022 00:29:39 +0200
+Subject: [PATCH 1/1] PM / devfreq: Fix kernel warning with cpufreq passive
+ register fail
+
+Remove cpufreq_passive_unregister_notifier from
+cpufreq_passive_register_notifier in case of error as devfreq core
+already call unregister on GOV_START fail.
+
+This fix the kernel always printing a WARN on governor PROBE_DEFER as
+cpufreq_passive_unregister_notifier is called two times and return
+error on the second call as the cpufreq is already unregistered.
+
+Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+---
+ drivers/devfreq/governor_passive.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -336,7 +336,6 @@ err_free_cpu_data:
+ err_put_policy:
+       cpufreq_cpu_put(policy);
+ err:
+-      WARN_ON(cpufreq_passive_unregister_notifier(devfreq));
+       return ret;
+ }
diff --git a/target/linux/ipq806x/patches-5.15/113-v5.19-01-PM-devfreq-Fix-cpufreq-passive-unregister-errorin.patch b/target/linux/ipq806x/patches-5.15/113-v5.19-01-PM-devfreq-Fix-cpufreq-passive-unregister-errorin.patch
new file mode 100644 (file)
index 0000000..0883424
--- /dev/null
@@ -0,0 +1,85 @@
+From 8953603eb5447be52f6fc3d8fcae1b3ce9899189 Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+Date: Mon, 6 Jun 2022 11:58:49 +0200
+Subject: [PATCH v4 1/4] PM / devfreq: Fix cpufreq passive unregister erroring
+ on PROBE_DEFER
+
+With the passive governor, the cpu based scaling can PROBE_DEFER due to
+the fact that CPU policy are not ready.
+The cpufreq passive unregister notifier is called both from the
+GOV_START errors and for the GOV_STOP and assume the notifier is
+successfully registred every time. With GOV_START failing it's wrong to
+loop over each possible CPU since the register path has failed for
+some CPU policy not ready. Change the logic and unregister the notifer
+based on the current allocated parent_cpu_data list to correctly handle
+errors and the governor unregister path.
+
+Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
+Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+---
+ drivers/devfreq/governor_passive.c | 39 +++++++++++++-----------------
+ 1 file changed, 17 insertions(+), 22 deletions(-)
+
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -34,6 +34,20 @@ get_parent_cpu_data(struct devfreq_passi
+       return NULL;
+ }
++static void delete_parent_cpu_data(struct devfreq_passive_data *p_data)
++{
++      struct devfreq_cpu_data *parent_cpu_data, *tmp;
++
++      list_for_each_entry_safe(parent_cpu_data, tmp, &p_data->cpu_data_list, node) {
++              list_del(&parent_cpu_data->node);
++
++              if (parent_cpu_data->opp_table)
++                      dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
++
++              kfree(parent_cpu_data);
++      }
++}
++
+ static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
+                                               struct opp_table *p_opp_table,
+                                               struct opp_table *opp_table,
+@@ -222,8 +236,7 @@ static int cpufreq_passive_unregister_no
+ {
+       struct devfreq_passive_data *p_data
+                       = (struct devfreq_passive_data *)devfreq->data;
+-      struct devfreq_cpu_data *parent_cpu_data;
+-      int cpu, ret = 0;
++      int ret;
+       if (p_data->nb.notifier_call) {
+               ret = cpufreq_unregister_notifier(&p_data->nb,
+@@ -232,27 +245,9 @@ static int cpufreq_passive_unregister_no
+                       return ret;
+       }
+-      for_each_possible_cpu(cpu) {
+-              struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+-              if (!policy) {
+-                      ret = -EINVAL;
+-                      continue;
+-              }
+-
+-              parent_cpu_data = get_parent_cpu_data(p_data, policy);
+-              if (!parent_cpu_data) {
+-                      cpufreq_cpu_put(policy);
+-                      continue;
+-              }
+-
+-              list_del(&parent_cpu_data->node);
+-              if (parent_cpu_data->opp_table)
+-                      dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
+-              kfree(parent_cpu_data);
+-              cpufreq_cpu_put(policy);
+-      }
++      delete_parent_cpu_data(p_data);
+-      return ret;
++      return 0;
+ }
+ static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
diff --git a/target/linux/ipq806x/patches-5.15/113-v5.19-02-PM-devfreq-Fix-kernel-panic-with-cpu-based-scaling-t.patch b/target/linux/ipq806x/patches-5.15/113-v5.19-02-PM-devfreq-Fix-kernel-panic-with-cpu-based-scaling-t.patch
new file mode 100644 (file)
index 0000000..84cb67b
--- /dev/null
@@ -0,0 +1,39 @@
+From 57e00b40033a376de3f3cf0bb9bf7590d2dd679d Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+Date: Tue, 14 Jun 2022 13:06:59 +0200
+Subject: [PATCH 1/1] PM / devfreq: Fix kernel panic with cpu based scaling to
+ passive gov
+
+The cpufreq passive register notifier can PROBE_DEFER and the devfreq
+struct is freed and then reallocaed on probe retry.
+The current logic assume that the code can't PROBE_DEFER so the devfreq
+struct in the this variable in devfreq_passive_data is assumed to be
+(if already set) always correct.
+This cause kernel panic as the code try to access the wrong address.
+To correctly handle this, update the this variable in
+devfreq_passive_data to the devfreq reallocated struct.
+
+Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
+Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
+---
+ drivers/devfreq/governor_passive.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
+index 72c67979ebe1..091a69e1f487 100644
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -407,8 +407,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
+       if (!p_data)
+               return -EINVAL;
+-      if (!p_data->this)
+-              p_data->this = devfreq;
++      p_data->this = devfreq;
+       switch (event) {
+       case DEVFREQ_GOV_START:
+-- 
+2.37.2
+
diff --git a/target/linux/ipq806x/patches-5.15/113-v5.19-03-PM-devfreq-Rework-freq_table-to-be-local-to-devfr.patch b/target/linux/ipq806x/patches-5.15/113-v5.19-03-PM-devfreq-Rework-freq_table-to-be-local-to-devfr.patch
new file mode 100644 (file)
index 0000000..0813140
--- /dev/null
@@ -0,0 +1,269 @@
+From 46d05776a1a5dd8eb479e868f5ff4f4b97d68238 Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+Date: Mon, 6 Jun 2022 12:39:19 +0200
+Subject: [PATCH v4 3/4] PM / devfreq: Rework freq_table to be local to devfreq
+ struct
+
+Currently we reference the freq_table to the profile defined one and we
+make changes on it. Devfreq never supported PROBE_DEFER before the cpu
+based scaling support to the passive governor and assumed that a devfreq
+device could only had error and be done with it.
+Now that a device can PROBE_DEFER a rework to the freq_table logic is
+required.
+
+If a device PROBE_DEFER on the GOV_START, the freq_table is already set
+in the device profile struct and its init is skipped. This is due to the
+fact that it's common for devs to declare this kind of struct static.
+This cause the devfreq logic to find a freq table declared (freq_table
+not NULL) with random data and poiting to the old addrs freed by devm.
+
+This problem CAN be solved by devs by clearing the freq_table in their
+profile struct on driver exit path but it should not be trusted and it
+looks to use a flawed logic.
+
+A better solution is to move the freq_table and max_state to the
+devfreq struct and never change the profile struct.
+This permit to correctly handle PROBE_DEFER since the devfreq struct is
+reallocated and contains new values.
+Also the profile struct should only be used to init the driver and should
+not be used by the devfreq to write the freq_table if it's not provided
+by the driver.
+
+Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
+Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+---
+ drivers/devfreq/devfreq.c          | 71 ++++++++++++++----------------
+ drivers/devfreq/governor_passive.c | 14 +++---
+ include/linux/devfreq.h            |  4 ++
+ 3 files changed, 45 insertions(+), 44 deletions(-)
+
+--- a/drivers/devfreq/devfreq.c
++++ b/drivers/devfreq/devfreq.c
+@@ -123,7 +123,7 @@ void devfreq_get_freq_range(struct devfr
+                           unsigned long *min_freq,
+                           unsigned long *max_freq)
+ {
+-      unsigned long *freq_table = devfreq->profile->freq_table;
++      unsigned long *freq_table = devfreq->freq_table;
+       s32 qos_min_freq, qos_max_freq;
+       lockdep_assert_held(&devfreq->lock);
+@@ -133,11 +133,11 @@ void devfreq_get_freq_range(struct devfr
+        * The devfreq drivers can initialize this in either ascending or
+        * descending order and devfreq core supports both.
+        */
+-      if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) {
++      if (freq_table[0] < freq_table[devfreq->max_state - 1]) {
+               *min_freq = freq_table[0];
+-              *max_freq = freq_table[devfreq->profile->max_state - 1];
++              *max_freq = freq_table[devfreq->max_state - 1];
+       } else {
+-              *min_freq = freq_table[devfreq->profile->max_state - 1];
++              *min_freq = freq_table[devfreq->max_state - 1];
+               *max_freq = freq_table[0];
+       }
+@@ -169,8 +169,8 @@ static int devfreq_get_freq_level(struct
+ {
+       int lev;
+-      for (lev = 0; lev < devfreq->profile->max_state; lev++)
+-              if (freq == devfreq->profile->freq_table[lev])
++      for (lev = 0; lev < devfreq->max_state; lev++)
++              if (freq == devfreq->freq_table[lev])
+                       return lev;
+       return -EINVAL;
+@@ -178,7 +178,6 @@ static int devfreq_get_freq_level(struct
+ static int set_freq_table(struct devfreq *devfreq)
+ {
+-      struct devfreq_dev_profile *profile = devfreq->profile;
+       struct dev_pm_opp *opp;
+       unsigned long freq;
+       int i, count;
+@@ -188,25 +187,22 @@ static int set_freq_table(struct devfreq
+       if (count <= 0)
+               return -EINVAL;
+-      profile->max_state = count;
+-      profile->freq_table = devm_kcalloc(devfreq->dev.parent,
+-                                      profile->max_state,
+-                                      sizeof(*profile->freq_table),
+-                                      GFP_KERNEL);
+-      if (!profile->freq_table) {
+-              profile->max_state = 0;
++      devfreq->max_state = count;
++      devfreq->freq_table = devm_kcalloc(devfreq->dev.parent,
++                                         devfreq->max_state,
++                                         sizeof(*devfreq->freq_table),
++                                         GFP_KERNEL);
++      if (!devfreq->freq_table)
+               return -ENOMEM;
+-      }
+-      for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
++      for (i = 0, freq = 0; i < devfreq->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;
++                      devm_kfree(devfreq->dev.parent, devfreq->freq_table);
+                       return PTR_ERR(opp);
+               }
+               dev_pm_opp_put(opp);
+-              profile->freq_table[i] = freq;
++              devfreq->freq_table[i] = freq;
+       }
+       return 0;
+@@ -246,7 +242,7 @@ int devfreq_update_status(struct devfreq
+       if (lev != prev_lev) {
+               devfreq->stats.trans_table[
+-                      (prev_lev * devfreq->profile->max_state) + lev]++;
++                      (prev_lev * devfreq->max_state) + lev]++;
+               devfreq->stats.total_trans++;
+       }
+@@ -835,6 +831,9 @@ struct devfreq *devfreq_add_device(struc
+               if (err < 0)
+                       goto err_dev;
+               mutex_lock(&devfreq->lock);
++      } else {
++              devfreq->freq_table = devfreq->profile->freq_table;
++              devfreq->max_state = devfreq->profile->max_state;
+       }
+       devfreq->scaling_min_freq = find_available_min_freq(devfreq);
+@@ -870,8 +869,8 @@ struct devfreq *devfreq_add_device(struc
+       devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev,
+                       array3_size(sizeof(unsigned int),
+-                                  devfreq->profile->max_state,
+-                                  devfreq->profile->max_state),
++                                  devfreq->max_state,
++                                  devfreq->max_state),
+                       GFP_KERNEL);
+       if (!devfreq->stats.trans_table) {
+               mutex_unlock(&devfreq->lock);
+@@ -880,7 +879,7 @@ struct devfreq *devfreq_add_device(struc
+       }
+       devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev,
+-                      devfreq->profile->max_state,
++                      devfreq->max_state,
+                       sizeof(*devfreq->stats.time_in_state),
+                       GFP_KERNEL);
+       if (!devfreq->stats.time_in_state) {
+@@ -1639,9 +1638,9 @@ static ssize_t available_frequencies_sho
+       mutex_lock(&df->lock);
+-      for (i = 0; i < df->profile->max_state; i++)
++      for (i = 0; i < df->max_state; i++)
+               count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
+-                              "%lu ", df->profile->freq_table[i]);
++                              "%lu ", df->freq_table[i]);
+       mutex_unlock(&df->lock);
+       /* Truncate the trailing space */
+@@ -1664,7 +1663,7 @@ static ssize_t trans_stat_show(struct de
+       if (!df->profile)
+               return -EINVAL;
+-      max_state = df->profile->max_state;
++      max_state = df->max_state;
+       if (max_state == 0)
+               return sprintf(buf, "Not Supported.\n");
+@@ -1681,19 +1680,17 @@ static ssize_t trans_stat_show(struct de
+       len += sprintf(buf + len, "           :");
+       for (i = 0; i < max_state; i++)
+               len += sprintf(buf + len, "%10lu",
+-                              df->profile->freq_table[i]);
++                              df->freq_table[i]);
+       len += sprintf(buf + len, "   time(ms)\n");
+       for (i = 0; i < max_state; i++) {
+-              if (df->profile->freq_table[i]
+-                                      == df->previous_freq) {
++              if (df->freq_table[i] == df->previous_freq)
+                       len += sprintf(buf + len, "*");
+-              } else {
++              else
+                       len += sprintf(buf + len, " ");
+-              }
+-              len += sprintf(buf + len, "%10lu:",
+-                              df->profile->freq_table[i]);
++
++              len += sprintf(buf + len, "%10lu:", df->freq_table[i]);
+               for (j = 0; j < max_state; j++)
+                       len += sprintf(buf + len, "%10u",
+                               df->stats.trans_table[(i * max_state) + j]);
+@@ -1717,7 +1714,7 @@ static ssize_t trans_stat_store(struct d
+       if (!df->profile)
+               return -EINVAL;
+-      if (df->profile->max_state == 0)
++      if (df->max_state == 0)
+               return count;
+       err = kstrtoint(buf, 10, &value);
+@@ -1725,11 +1722,11 @@ static ssize_t trans_stat_store(struct d
+               return -EINVAL;
+       mutex_lock(&df->lock);
+-      memset(df->stats.time_in_state, 0, (df->profile->max_state *
++      memset(df->stats.time_in_state, 0, (df->max_state *
+                                       sizeof(*df->stats.time_in_state)));
+       memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int),
+-                                      df->profile->max_state,
+-                                      df->profile->max_state));
++                                      df->max_state,
++                                      df->max_state));
+       df->stats.total_trans = 0;
+       df->stats.last_update = get_jiffies_64();
+       mutex_unlock(&df->lock);
+--- a/drivers/devfreq/governor_passive.c
++++ b/drivers/devfreq/governor_passive.c
+@@ -145,18 +145,18 @@ static int get_target_freq_with_devfreq(
+               goto out;
+       /* Use interpolation if required opps is not available */
+-      for (i = 0; i < parent_devfreq->profile->max_state; i++)
+-              if (parent_devfreq->profile->freq_table[i] == *freq)
++      for (i = 0; i < parent_devfreq->max_state; i++)
++              if (parent_devfreq->freq_table[i] == *freq)
+                       break;
+-      if (i == parent_devfreq->profile->max_state)
++      if (i == parent_devfreq->max_state)
+               return -EINVAL;
+-      if (i < devfreq->profile->max_state) {
+-              child_freq = devfreq->profile->freq_table[i];
++      if (i < devfreq->max_state) {
++              child_freq = devfreq->freq_table[i];
+       } else {
+-              count = devfreq->profile->max_state;
+-              child_freq = devfreq->profile->freq_table[count - 1];
++              count = devfreq->max_state;
++              child_freq = devfreq->freq_table[count - 1];
+       }
+ out:
+--- a/include/linux/devfreq.h
++++ b/include/linux/devfreq.h
+@@ -185,6 +185,10 @@ struct devfreq {
+       struct notifier_block nb;
+       struct delayed_work work;
++      /* devfreq local freq_table */
++      unsigned long *freq_table;
++      unsigned int max_state;
++
+       unsigned long previous_freq;
+       struct devfreq_dev_status last_status;
diff --git a/target/linux/ipq806x/patches-5.15/113-v5.19-04-PM-devfreq-Mute-warning-on-governor-PROBE_DEFER.patch b/target/linux/ipq806x/patches-5.15/113-v5.19-04-PM-devfreq-Mute-warning-on-governor-PROBE_DEFER.patch
new file mode 100644 (file)
index 0000000..cfdef8d
--- /dev/null
@@ -0,0 +1,28 @@
+From eee9f767c41b03a2744d4b0f0c1a144e4ff41e78 Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+Date: Mon, 6 Jun 2022 13:01:02 +0200
+Subject: [PATCH v4 4/4] PM / devfreq: Mute warning on governor PROBE_DEFER
+
+Don't print warning when a governor PROBE_DEFER as it's not a real
+GOV_START fail.
+
+Fixes: a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor")
+Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+---
+ drivers/devfreq/devfreq.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/devfreq/devfreq.c
++++ b/drivers/devfreq/devfreq.c
+@@ -931,8 +931,9 @@ struct devfreq *devfreq_add_device(struc
+       err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,
+                                               NULL);
+       if (err) {
+-              dev_err(dev, "%s: Unable to start governor for the device\n",
+-                      __func__);
++              dev_err_probe(dev, err,
++                            "%s: Unable to start governor for the device\n",
++                            __func__);
+               goto err_init;
+       }
+       create_sysfs_files(devfreq, devfreq->governor);