From 3a911b8c2d97293f04288775599843ab5cb77045 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 16 Jun 2022 22:16:04 +0200 Subject: 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 --- ...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(+) create mode 100644 target/linux/ipq806x/patches-5.15/111-v5.19-01-PM-devfreq-Export-devfreq_get_freq_range-symbol-with.patch create mode 100644 target/linux/ipq806x/patches-5.15/111-v5.19-02-PM-devfreq-Add-cpu-based-scaling-support-to-passive-.patch create mode 100644 target/linux/ipq806x/patches-5.15/111-v5.19-03-PM-devfreq-passive-Reduce-duplicate-code-when-passiv.patch create mode 100644 target/linux/ipq806x/patches-5.15/111-v5.19-04-PM-devfreq-passive-Keep-cpufreq_policy-for-possible-.patch create mode 100644 target/linux/ipq806x/patches-5.15/111-v5.19-05-PM-devfreq-passive-Return-non-error-when-not-support.patch create mode 100644 target/linux/ipq806x/patches-5.15/112-v5.19-PM-devfreq-Fix-kernel-warning-with-cpufreq-passive-r.patch create mode 100644 target/linux/ipq806x/patches-5.15/113-v5.19-01-PM-devfreq-Fix-cpufreq-passive-unregister-errorin.patch create mode 100644 target/linux/ipq806x/patches-5.15/113-v5.19-02-PM-devfreq-Fix-kernel-panic-with-cpu-based-scaling-t.patch create mode 100644 target/linux/ipq806x/patches-5.15/113-v5.19-03-PM-devfreq-Rework-freq_table-to-be-local-to-devfr.patch create mode 100644 target/linux/ipq806x/patches-5.15/113-v5.19-04-PM-devfreq-Mute-warning-on-governor-PROBE_DEFER.patch (limited to 'target/linux/ipq806x') 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 +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 +Tested-by: Chen-Yu Tsai +Tested-by: Johnson Wang +Signed-off-by: Chanwoo Choi +--- + 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 +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 +Tested-by: Johnson Wang +Signed-off-by: Saravana Kannan +[Sibi: Integrated cpu-freqmap governor into passive_governor] +Signed-off-by: Sibi Sankar +[Chanwoo: Fix conflict with latest code and cleanup code] +Signed-off-by: Chanwoo Choi +--- + 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 ++#include ++#include ++#include ++#include + #include + #include + #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 +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 +Tested-by: Johnson Wang +Signed-off-by: Chanwoo Choi +--- + 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 +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 +Tested-by: Johnson Wang +Signed-off-by: Chanwoo Choi +--- + 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 +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 +Signed-off-by: Chanwoo Choi +Signed-off-by: Rafael J. Wysocki +--- + 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 +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 +Signed-off-by: Chanwoo Choi +--- + 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 +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 +--- + 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 +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 +Signed-off-by: Chanwoo Choi +--- + 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 +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 +--- + 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 +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 +--- + 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); -- cgit v1.2.3