[openwrt/openwrt] ipq806x: 5.15: backport devfreq new cpufreq based PASSIVE governor

LEDE Commits lede-commits at lists.infradead.org
Tue Oct 11 12:29:06 PDT 2022


ansuel pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/3a911b8c2d97293f04288775599843ab5cb77045

commit 3a911b8c2d97293f04288775599843ab5cb77045
Author: Christian Marangi <ansuelsmth at gmail.com>
AuthorDate: Thu Jun 16 22:16:04 2022 +0200

    ipq806x: 5.15: backport devfreq new cpufreq based PASSIVE governor
    
    Backport devfreq new cpufreq based PASSIVE governor needed for devfreq
    based fab and cache scaling.
    
    Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 ...Export-devfreq_get_freq_range-symbol-with.patch | 113 +++++
 ...Add-cpu-based-scaling-support-to-passive-.patch | 461 +++++++++++++++++++++
 ...passive-Reduce-duplicate-code-when-passiv.patch | 110 +++++
 ...passive-Keep-cpufreq_policy-for-possible-.patch | 232 +++++++++++
 ...passive-Return-non-error-when-not-support.patch |  31 ++
 ...Fix-kernel-warning-with-cpufreq-passive-r.patch |  31 ++
 ...eq-Fix-cpufreq-passive-unregister-errorin.patch |  85 ++++
 ...Fix-kernel-panic-with-cpu-based-scaling-t.patch |  39 ++
 ...eq-Rework-freq_table-to-be-local-to-devfr.patch | 269 ++++++++++++
 ...freq-Mute-warning-on-governor-PROBE_DEFER.patch |  28 ++
 10 files changed, 1399 insertions(+)

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
index 0000000000..0442105d87
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/111-v5.19-01-PM-devfreq-Export-devfreq_get_freq_range-symbol-with.patch
@@ -0,0 +1,113 @@
+From 713472e53e6e53c985e283782b0fd76b8ecfd47e Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi at 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 at chromium.org>
+Tested-by: Chen-Yu Tsai <wenst at chromium.org>
+Tested-by: Johnson Wang <johnson.wang at mediatek.com>
+Signed-off-by: Chanwoo Choi <cw00.choi at 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
index 0000000000..52f1200c31
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/111-v5.19-02-PM-devfreq-Add-cpu-based-scaling-support-to-passive-.patch
@@ -0,0 +1,461 @@
+From a03dacb0316f74400846aaf144d6c73f4217ca08 Mon Sep 17 00:00:00 2001
+From: Saravana Kannan <skannan at 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 at chromium.org>
+Tested-by: Johnson Wang <johnson.wang at mediatek.com>
+Signed-off-by: Saravana Kannan <skannan at codeaurora.org>
+[Sibi: Integrated cpu-freqmap governor into passive_governor]
+Signed-off-by: Sibi Sankar <sibis at codeaurora.org>
+[Chanwoo: Fix conflict with latest code and cleanup code]
+Signed-off-by: Chanwoo Choi <cw00.choi at 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
index 0000000000..cb10bd1b62
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/111-v5.19-03-PM-devfreq-passive-Reduce-duplicate-code-when-passiv.patch
@@ -0,0 +1,110 @@
+From 05723e71234b60a1a47313ea1a889797ec648f1c Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi at 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 at chromium.org>
+Tested-by: Johnson Wang <johnson.wang at mediatek.com>
+Signed-off-by: Chanwoo Choi <cw00.choi at 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
index 0000000000..12de8af22e
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/111-v5.19-04-PM-devfreq-passive-Keep-cpufreq_policy-for-possible-.patch
@@ -0,0 +1,232 @@
+From 26984d9d581e5049bd75091d2e789b9cc3ea12e0 Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi at 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 at chromium.org>
+Tested-by: Johnson Wang <johnson.wang at mediatek.com>
+Signed-off-by: Chanwoo Choi <cw00.choi at 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
index 0000000000..69160fd77a
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/111-v5.19-05-PM-devfreq-passive-Return-non-error-when-not-support.patch
@@ -0,0 +1,31 @@
+From 42d2607d91c4ec37ea1970899c2d614824f3014b Mon Sep 17 00:00:00 2001
+From: Chanwoo Choi <cw00.choi at 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 at samsung.com>
+Signed-off-by: Chanwoo Choi <cw00.choi at samsung.com>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki at 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
index 0000000000..3d2bb2de05
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/112-v5.19-PM-devfreq-Fix-kernel-warning-with-cpufreq-passive-r.patch
@@ -0,0 +1,31 @@
+From 82c66d2bbbeda9e493487e7413769087a0b46250 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at 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 at gmail.com>
+Signed-off-by: Chanwoo Choi <cw00.choi at 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
index 0000000000..0883424548
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/113-v5.19-01-PM-devfreq-Fix-cpufreq-passive-unregister-errorin.patch
@@ -0,0 +1,85 @@
+From 8953603eb5447be52f6fc3d8fcae1b3ce9899189 Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth at 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 at 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
index 0000000000..84cb67bc99
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/113-v5.19-02-PM-devfreq-Fix-kernel-panic-with-cpu-based-scaling-t.patch
@@ -0,0 +1,39 @@
+From 57e00b40033a376de3f3cf0bb9bf7590d2dd679d Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth at 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 at gmail.com>
+Signed-off-by: Chanwoo Choi <cw00.choi at 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
index 0000000000..0813140eca
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/113-v5.19-03-PM-devfreq-Rework-freq_table-to-be-local-to-devfr.patch
@@ -0,0 +1,269 @@
+From 46d05776a1a5dd8eb479e868f5ff4f4b97d68238 Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth at 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 at 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
index 0000000000..cfdef8d6c5
--- /dev/null
+++ b/target/linux/ipq806x/patches-5.15/113-v5.19-04-PM-devfreq-Mute-warning-on-governor-PROBE_DEFER.patch
@@ -0,0 +1,28 @@
+From eee9f767c41b03a2744d4b0f0c1a144e4ff41e78 Mon Sep 17 00:00:00 2001
+From: Christian 'Ansuel' Marangi <ansuelsmth at 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 at 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);




More information about the lede-commits mailing list