diff options
Diffstat (limited to 'drivers')
44 files changed, 534 insertions, 336 deletions
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index b2f05d27167e..37f1cdf46d29 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -1011,22 +1011,20 @@ static int __init acpi_cpufreq_probe(struct platform_device *pdev) return ret; } -static int acpi_cpufreq_remove(struct platform_device *pdev) +static void acpi_cpufreq_remove(struct platform_device *pdev) { pr_debug("%s\n", __func__); cpufreq_unregister_driver(&acpi_cpufreq_driver); free_acpi_perf_data(); - - return 0; } static struct platform_driver acpi_cpufreq_platdrv = { .driver = { .name = "acpi-cpufreq", }, - .remove = acpi_cpufreq_remove, + .remove_new = acpi_cpufreq_remove, }; static int __init acpi_cpufreq_init(void) diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index 7f3fe2048981..f04ae67dda37 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -64,27 +64,9 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { static bool get_shared_mem(void) { bool result = false; - char path[] = "/sys/module/amd_pstate/parameters/shared_mem"; - char buf[5] = {0}; - struct file *filp = NULL; - loff_t pos = 0; - ssize_t ret; - - if (!boot_cpu_has(X86_FEATURE_CPPC)) { - filp = filp_open(path, O_RDONLY, 0); - if (IS_ERR(filp)) - pr_err("%s unable to open %s file!\n", __func__, path); - else { - ret = kernel_read(filp, &buf, sizeof(buf), &pos); - if (ret < 0) - pr_err("%s read %s file fail ret=%ld!\n", - __func__, path, (long)ret); - filp_close(filp, NULL); - } - if ('Y' == *buf) - result = true; - } + if (!boot_cpu_has(X86_FEATURE_CPPC)) + result = true; return result; } @@ -145,8 +127,6 @@ static void amd_pstate_ut_check_perf(u32 index) struct cpufreq_policy *policy = NULL; struct amd_cpudata *cpudata = NULL; - highest_perf = amd_get_highest_perf(); - for_each_possible_cpu(cpu) { policy = cpufreq_cpu_get(cpu); if (!policy) @@ -158,9 +138,10 @@ static void amd_pstate_ut_check_perf(u32 index) if (ret) { amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL; pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret); - return; + goto skip_test; } + highest_perf = cppc_perf.highest_perf; nominal_perf = cppc_perf.nominal_perf; lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf; lowest_perf = cppc_perf.lowest_perf; @@ -169,9 +150,10 @@ static void amd_pstate_ut_check_perf(u32 index) if (ret) { amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL; pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret); - return; + goto skip_test; } + highest_perf = AMD_CPPC_HIGHEST_PERF(cap1); nominal_perf = AMD_CPPC_NOMINAL_PERF(cap1); lowest_nonlinear_perf = AMD_CPPC_LOWNONLIN_PERF(cap1); lowest_perf = AMD_CPPC_LOWEST_PERF(cap1); @@ -187,7 +169,7 @@ static void amd_pstate_ut_check_perf(u32 index) nominal_perf, cpudata->nominal_perf, lowest_nonlinear_perf, cpudata->lowest_nonlinear_perf, lowest_perf, cpudata->lowest_perf); - return; + goto skip_test; } if (!((highest_perf >= nominal_perf) && @@ -198,11 +180,15 @@ static void amd_pstate_ut_check_perf(u32 index) pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n", __func__, cpu, highest_perf, nominal_perf, lowest_nonlinear_perf, lowest_perf); - return; + goto skip_test; } + cpufreq_cpu_put(policy); } amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS; + return; +skip_test: + cpufreq_cpu_put(policy); } /* @@ -230,14 +216,14 @@ static void amd_pstate_ut_check_freq(u32 index) pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n", __func__, cpu, cpudata->max_freq, cpudata->nominal_freq, cpudata->lowest_nonlinear_freq, cpudata->min_freq); - return; + goto skip_test; } if (cpudata->min_freq != policy->min) { amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL; pr_err("%s cpu%d cpudata_min_freq=%d policy_min=%d, they should be equal!\n", __func__, cpu, cpudata->min_freq, policy->min); - return; + goto skip_test; } if (cpudata->boost_supported) { @@ -249,16 +235,20 @@ static void amd_pstate_ut_check_freq(u32 index) pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n", __func__, cpu, policy->max, cpudata->max_freq, cpudata->nominal_freq); - return; + goto skip_test; } } else { amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL; pr_err("%s cpu%d must support boost!\n", __func__, cpu); - return; + goto skip_test; } + cpufreq_cpu_put(policy); } amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS; + return; +skip_test: + cpufreq_cpu_put(policy); } static int __init amd_pstate_ut_init(void) diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index b74289a95a17..bea41ccabf1f 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -14,10 +14,8 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mfd/syscon.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/regmap.h> diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index ffea6402189d..35fb3a559ea9 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -434,7 +434,11 @@ brcm_avs_get_freq_table(struct device *dev, struct private_data *priv) if (ret) return ERR_PTR(ret); - table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1, sizeof(*table), + /* + * We allocate space for the 5 different P-STATES AVS, + * plus extra space for a terminating element. + */ + table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1 + 1, sizeof(*table), GFP_KERNEL); if (!table) return ERR_PTR(-ENOMEM); @@ -749,13 +753,11 @@ static int brcm_avs_cpufreq_probe(struct platform_device *pdev) return ret; } -static int brcm_avs_cpufreq_remove(struct platform_device *pdev) +static void brcm_avs_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&brcm_avs_driver); brcm_avs_prepare_uninit(pdev); - - return 0; } static const struct of_device_id brcm_avs_cpufreq_match[] = { @@ -770,7 +772,7 @@ static struct platform_driver brcm_avs_cpufreq_platdrv = { .of_match_table = brcm_avs_cpufreq_match, }, .probe = brcm_avs_cpufreq_probe, - .remove = brcm_avs_cpufreq_remove, + .remove_new = brcm_avs_cpufreq_remove, }; module_platform_driver(brcm_avs_cpufreq_platdrv); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 022e3555407c..fe08ca419b3d 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -249,15 +249,19 @@ static void __init cppc_freq_invariance_init(void) return; kworker_fie = kthread_create_worker(0, "cppc_fie"); - if (IS_ERR(kworker_fie)) + if (IS_ERR(kworker_fie)) { + pr_warn("%s: failed to create kworker_fie: %ld\n", __func__, + PTR_ERR(kworker_fie)); + fie_disabled = FIE_DISABLED; return; + } ret = sched_setattr_nocheck(kworker_fie->task, &attr); if (ret) { pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__, ret); kthread_destroy_worker(kworker_fie); - return; + fie_disabled = FIE_DISABLED; } } @@ -267,7 +271,6 @@ static void cppc_freq_invariance_exit(void) return; kthread_destroy_worker(kworker_fie); - kworker_fie = NULL; } #else @@ -849,13 +852,13 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); if (ret) - return ret; + return 0; udelay(2); /* 2usec delay between sampling */ ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); if (ret) - return ret; + return 0; delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, &fb_ctrs_t1); diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index e2b20080de3a..fb2875ce1fdd 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -143,14 +143,19 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,apq8096", }, { .compatible = "qcom,msm8996", }, + { .compatible = "qcom,msm8998", }, + { .compatible = "qcom,qcm2290", }, { .compatible = "qcom,qcs404", }, + { .compatible = "qcom,qdu1000", }, { .compatible = "qcom,sa8155p" }, { .compatible = "qcom,sa8540p" }, + { .compatible = "qcom,sa8775p" }, { .compatible = "qcom,sc7180", }, { .compatible = "qcom,sc7280", }, { .compatible = "qcom,sc8180x", }, { .compatible = "qcom,sc8280xp", }, { .compatible = "qcom,sdm845", }, + { .compatible = "qcom,sdx75", }, { .compatible = "qcom,sm6115", }, { .compatible = "qcom,sm6350", }, { .compatible = "qcom,sm6375", }, @@ -158,6 +163,8 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,sm8150", }, { .compatible = "qcom,sm8250", }, { .compatible = "qcom,sm8350", }, + { .compatible = "qcom,sm8450", }, + { .compatible = "qcom,sm8550", }, { .compatible = "st,stih407", }, { .compatible = "st,stih410", }, diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 4aec4b2a5225..8bd6e5e8f121 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -349,11 +349,10 @@ err: return ret; } -static int dt_cpufreq_remove(struct platform_device *pdev) +static void dt_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&dt_cpufreq_driver); dt_cpufreq_release(); - return 0; } static struct platform_driver dt_cpufreq_platdrv = { @@ -361,7 +360,7 @@ static struct platform_driver dt_cpufreq_platdrv = { .name = "cpufreq-dt", }, .probe = dt_cpufreq_probe, - .remove = dt_cpufreq_remove, + .remove_new = dt_cpufreq_remove, }; module_platform_driver(dt_cpufreq_platdrv); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 50bbc969ffe5..a757f90aa9d6 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1234,16 +1234,16 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) ret = freq_qos_add_notifier(&policy->constraints, FREQ_QOS_MIN, &policy->nb_min); if (ret) { - dev_err(dev, "Failed to register MIN QoS notifier: %d (%*pbl)\n", - ret, cpumask_pr_args(policy->cpus)); + dev_err(dev, "Failed to register MIN QoS notifier: %d (CPU%u)\n", + ret, cpu); goto err_kobj_remove; } ret = freq_qos_add_notifier(&policy->constraints, FREQ_QOS_MAX, &policy->nb_max); if (ret) { - dev_err(dev, "Failed to register MAX QoS notifier: %d (%*pbl)\n", - ret, cpumask_pr_args(policy->cpus)); + dev_err(dev, "Failed to register MAX QoS notifier: %d (CPU%u)\n", + ret, cpu); goto err_min_qos_notifier; } diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 55c7ffd37d1c..a33df3c66c88 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -243,7 +243,8 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy) /* Find valid-unique entries */ cpufreq_for_each_valid_entry(pos, policy->freq_table) - if (freq_table_get_index(stats, pos->frequency) == -1) + if (policy->freq_table_sorted != CPUFREQ_TABLE_UNSORTED || + freq_table_get_index(stats, pos->frequency) == -1) stats->freq_table[i++] = pos->frequency; stats->state_num = i; diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index ebb3a8102681..7d2754411d8c 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -131,7 +131,7 @@ static int __init davinci_cpufreq_probe(struct platform_device *pdev) return cpufreq_register_driver(&davinci_driver); } -static int __exit davinci_cpufreq_remove(struct platform_device *pdev) +static void __exit davinci_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&davinci_driver); @@ -139,15 +139,13 @@ static int __exit davinci_cpufreq_remove(struct platform_device *pdev) if (cpufreq.asyncclk) clk_put(cpufreq.asyncclk); - - return 0; } static struct platform_driver davinci_cpufreq_driver = { .driver = { .name = "cpufreq-davinci", }, - .remove = __exit_p(davinci_cpufreq_remove), + .remove_new = __exit_p(davinci_cpufreq_remove), }; int __init davinci_cpufreq_init(void) diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index 535867a7dfdd..577bb9e2f112 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -172,20 +172,18 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) return 0; } -static int imx_cpufreq_dt_remove(struct platform_device *pdev) +static void imx_cpufreq_dt_remove(struct platform_device *pdev) { platform_device_unregister(cpufreq_dt_pdev); if (!of_machine_is_compatible("fsl,imx7ulp")) dev_pm_opp_put_supported_hw(cpufreq_opp_token); else clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks); - - return 0; } static struct platform_driver imx_cpufreq_dt_driver = { .probe = imx_cpufreq_dt_probe, - .remove = imx_cpufreq_dt_remove, + .remove_new = imx_cpufreq_dt_remove, .driver = { .name = "imx-cpufreq-dt", }, diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 9fb1501033bb..494d044b9e72 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -519,7 +519,7 @@ put_node: return ret; } -static int imx6q_cpufreq_remove(struct platform_device *pdev) +static void imx6q_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&imx6q_cpufreq_driver); dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); @@ -530,8 +530,6 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev) regulator_put(soc_reg); clk_bulk_put(num_clks, clks); - - return 0; } static struct platform_driver imx6q_cpufreq_platdrv = { @@ -539,7 +537,7 @@ static struct platform_driver imx6q_cpufreq_platdrv = { .name = "imx6q-cpufreq", }, .probe = imx6q_cpufreq_probe, - .remove = imx6q_cpufreq_remove, + .remove_new = imx6q_cpufreq_remove, }; module_platform_driver(imx6q_cpufreq_platdrv); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 8ca2bce4341a..dc50c9fb488d 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2609,6 +2609,11 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) intel_pstate_clear_update_util_hook(policy->cpu); intel_pstate_hwp_set(policy->cpu); } + /* + * policy->cur is never updated with the intel_pstate driver, but it + * is used as a stale frequency value. So, keep it within limits. + */ + policy->cur = policy->min; mutex_unlock(&intel_pstate_limits_lock); diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index 95588101efbd..fd20b986d1f2 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -178,20 +178,18 @@ out_node: return err; } -static int kirkwood_cpufreq_remove(struct platform_device *pdev) +static void kirkwood_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&kirkwood_cpufreq_driver); clk_disable_unprepare(priv.powersave_clk); clk_disable_unprepare(priv.ddr_clk); clk_disable_unprepare(priv.cpu_clk); - - return 0; } static struct platform_driver kirkwood_cpufreq_platform_driver = { .probe = kirkwood_cpufreq_probe, - .remove = kirkwood_cpufreq_remove, + .remove_new = kirkwood_cpufreq_remove, .driver = { .name = "kirkwood-cpufreq", }, diff --git a/drivers/cpufreq/mediatek-cpufreq-hw.c b/drivers/cpufreq/mediatek-cpufreq-hw.c index b22f5cc8a463..d46afb3c0092 100644 --- a/drivers/cpufreq/mediatek-cpufreq-hw.c +++ b/drivers/cpufreq/mediatek-cpufreq-hw.c @@ -10,8 +10,9 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_address.h> +#include <linux/of.h> #include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/slab.h> #define LUT_MAX_ENTRIES 32U @@ -315,11 +316,9 @@ static int mtk_cpufreq_hw_driver_probe(struct platform_device *pdev) return ret; } -static int mtk_cpufreq_hw_driver_remove(struct platform_device *pdev) +static void mtk_cpufreq_hw_driver_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&cpufreq_mtk_hw_driver); - - return 0; } static const struct of_device_id mtk_cpufreq_hw_match[] = { @@ -330,7 +329,7 @@ MODULE_DEVICE_TABLE(of, mtk_cpufreq_hw_match); static struct platform_driver mtk_cpufreq_hw_driver = { .probe = mtk_cpufreq_hw_driver_probe, - .remove = mtk_cpufreq_hw_driver_remove, + .remove_new = mtk_cpufreq_hw_driver_remove, .driver = { .name = "mtk-cpufreq-hw", .of_match_table = mtk_cpufreq_hw_match, diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index fef68cb2b38f..a0a61919bc4c 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -313,8 +313,6 @@ out: return ret; } -#define DYNAMIC_POWER "dynamic-power-coefficient" - static int mtk_cpufreq_opp_notifier(struct notifier_block *nb, unsigned long event, void *data) { diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 81649a1969b6..895690856665 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -182,11 +182,9 @@ static int omap_cpufreq_probe(struct platform_device *pdev) return cpufreq_register_driver(&omap_driver); } -static int omap_cpufreq_remove(struct platform_device *pdev) +static void omap_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&omap_driver); - - return 0; } static struct platform_driver omap_cpufreq_platdrv = { @@ -194,7 +192,7 @@ static struct platform_driver omap_cpufreq_platdrv = { .name = "omap-cpufreq", }, .probe = omap_cpufreq_probe, - .remove = omap_cpufreq_remove, + .remove_new = omap_cpufreq_remove, }; module_platform_driver(omap_cpufreq_platdrv); diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index 73efbcf5513b..84fe37def0f1 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -608,22 +608,20 @@ static int __init pcc_cpufreq_probe(struct platform_device *pdev) return ret; } -static int pcc_cpufreq_remove(struct platform_device *pdev) +static void pcc_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&pcc_cpufreq_driver); pcc_clear_mapping(); free_percpu(pcc_cpu_info); - - return 0; } static struct platform_driver pcc_cpufreq_platdrv = { .driver = { .name = "pcc-cpufreq", }, - .remove = pcc_cpufreq_remove, + .remove_new = pcc_cpufreq_remove, }; static int __init pcc_cpufreq_init(void) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index d289036beff2..b10f7a1b77f1 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1101,7 +1101,8 @@ static int powernowk8_cpu_exit(struct cpufreq_policy *pol) kfree(data->powernow_table); kfree(data); - for_each_cpu(cpu, pol->cpus) + /* pol->cpus will be empty here, use related_cpus instead. */ + for_each_cpu(cpu, pol->related_cpus) per_cpu(powernow_data, cpu) = NULL; return 0; diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index e3313ce63b38..88afc49941b7 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -9,7 +9,7 @@ #include <linux/cpufreq.h> #include <linux/module.h> -#include <linux/of_platform.h> +#include <linux/of.h> #include <asm/machdep.h> #include <asm/cell-regs.h> diff --git a/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c b/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c index 4fba3637b115..6f0c32592416 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c @@ -11,7 +11,6 @@ #include <linux/types.h> #include <linux/timer.h> #include <linux/init.h> -#include <linux/of_platform.h> #include <linux/pm_qos.h> #include <linux/slab.h> diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index f2830371d25f..70b0f21968a0 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -28,7 +28,7 @@ #define GT_IRQ_STATUS BIT(2) -#define MAX_FREQ_DOMAINS 3 +#define MAX_FREQ_DOMAINS 4 struct qcom_cpufreq_soc_data { u32 reg_enable; @@ -730,16 +730,14 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) return ret; } -static int qcom_cpufreq_hw_driver_remove(struct platform_device *pdev) +static void qcom_cpufreq_hw_driver_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&cpufreq_qcom_hw_driver); - - return 0; } static struct platform_driver qcom_cpufreq_hw_driver = { .probe = qcom_cpufreq_hw_driver_probe, - .remove = qcom_cpufreq_hw_driver_remove, + .remove_new = qcom_cpufreq_hw_driver_remove, .driver = { .name = "qcom-cpufreq-hw", .of_match_table = qcom_cpufreq_hw_match, diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index a88b6fe5db50..84d7033e5efe 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -22,7 +22,6 @@ #include <linux/module.h> #include <linux/nvmem-consumer.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_opp.h> @@ -334,7 +333,7 @@ free_drv: return ret; } -static int qcom_cpufreq_remove(struct platform_device *pdev) +static void qcom_cpufreq_remove(struct platform_device *pdev) { struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev); unsigned int cpu; @@ -346,13 +345,11 @@ static int qcom_cpufreq_remove(struct platform_device *pdev) kfree(drv->opp_tokens); kfree(drv); - - return 0; } static struct platform_driver qcom_cpufreq_driver = { .probe = qcom_cpufreq_probe, - .remove = qcom_cpufreq_remove, + .remove_new = qcom_cpufreq_remove, .driver = { .name = "qcom-cpufreq-nvmem", }, diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 573b417e1483..0aecaecbb0e6 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -288,11 +288,9 @@ static int qoriq_cpufreq_probe(struct platform_device *pdev) return 0; } -static int qoriq_cpufreq_remove(struct platform_device *pdev) +static void qoriq_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&qoriq_cpufreq_driver); - - return 0; } static struct platform_driver qoriq_cpufreq_platform_driver = { @@ -300,7 +298,7 @@ static struct platform_driver qoriq_cpufreq_platform_driver = { .name = "qoriq-cpufreq", }, .probe = qoriq_cpufreq_probe, - .remove = qoriq_cpufreq_remove, + .remove_new = qoriq_cpufreq_remove, }; module_platform_driver(qoriq_cpufreq_platform_driver); diff --git a/drivers/cpufreq/raspberrypi-cpufreq.c b/drivers/cpufreq/raspberrypi-cpufreq.c index 2bc7d9734272..e0705cc9a57d 100644 --- a/drivers/cpufreq/raspberrypi-cpufreq.c +++ b/drivers/cpufreq/raspberrypi-cpufreq.c @@ -65,7 +65,7 @@ remove_opp: return ret; } -static int raspberrypi_cpufreq_remove(struct platform_device *pdev) +static void raspberrypi_cpufreq_remove(struct platform_device *pdev) { struct device *cpu_dev; @@ -74,8 +74,6 @@ static int raspberrypi_cpufreq_remove(struct platform_device *pdev) dev_pm_opp_remove_all_dynamic(cpu_dev); platform_device_unregister(cpufreq_dt); - - return 0; } /* @@ -87,7 +85,7 @@ static struct platform_driver raspberrypi_cpufreq_driver = { .name = "raspberrypi-cpufreq", }, .probe = raspberrypi_cpufreq_probe, - .remove = raspberrypi_cpufreq_remove, + .remove_new = raspberrypi_cpufreq_remove, }; module_platform_driver(raspberrypi_cpufreq_driver); diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index fd2c16821d54..d33be56983ed 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -14,7 +14,7 @@ #include <linux/cpumask.h> #include <linux/export.h> #include <linux/module.h> -#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/scpi_protocol.h> #include <linux/slab.h> @@ -208,11 +208,10 @@ static int scpi_cpufreq_probe(struct platform_device *pdev) return ret; } -static int scpi_cpufreq_remove(struct platform_device *pdev) +static void scpi_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&scpi_cpufreq_driver); scpi_ops = NULL; - return 0; } static struct platform_driver scpi_cpufreq_platdrv = { @@ -220,7 +219,7 @@ static struct platform_driver scpi_cpufreq_platdrv = { .name = "scpi-cpufreq", }, .probe = scpi_cpufreq_probe, - .remove = scpi_cpufreq_remove, + .remove_new = scpi_cpufreq_remove, }; module_platform_driver(scpi_cpufreq_platdrv); diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c index 1a63aeea8711..9c542e723a15 100644 --- a/drivers/cpufreq/sti-cpufreq.c +++ b/drivers/cpufreq/sti-cpufreq.c @@ -13,7 +13,7 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/regmap.h> diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c index 4321d7bbe769..32a9c88f8ff6 100644 --- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c +++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c @@ -137,7 +137,7 @@ free_opp: return ret; } -static int sun50i_cpufreq_nvmem_remove(struct platform_device *pdev) +static void sun50i_cpufreq_nvmem_remove(struct platform_device *pdev) { int *opp_tokens = platform_get_drvdata(pdev); unsigned int cpu; @@ -148,13 +148,11 @@ static int sun50i_cpufreq_nvmem_remove(struct platform_device *pdev) dev_pm_opp_put_prop_name(opp_tokens[cpu]); kfree(opp_tokens); - - return 0; } static struct platform_driver sun50i_cpufreq_driver = { .probe = sun50i_cpufreq_nvmem_probe, - .remove = sun50i_cpufreq_nvmem_remove, + .remove_new = sun50i_cpufreq_nvmem_remove, .driver = { .name = "sun50i-cpufreq-nvmem", }, diff --git a/drivers/cpufreq/tegra186-cpufreq.c b/drivers/cpufreq/tegra186-cpufreq.c index f98f53bf1011..7b8fcfa55038 100644 --- a/drivers/cpufreq/tegra186-cpufreq.c +++ b/drivers/cpufreq/tegra186-cpufreq.c @@ -259,11 +259,9 @@ put_bpmp: return err; } -static int tegra186_cpufreq_remove(struct platform_device *pdev) +static void tegra186_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&tegra186_cpufreq_driver); - - return 0; } static const struct of_device_id tegra186_cpufreq_of_match[] = { @@ -278,7 +276,7 @@ static struct platform_driver tegra186_cpufreq_platform_driver = { .of_match_table = tegra186_cpufreq_of_match, }, .probe = tegra186_cpufreq_probe, - .remove = tegra186_cpufreq_remove, + .remove_new = tegra186_cpufreq_remove, }; module_platform_driver(tegra186_cpufreq_platform_driver); diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c index 36dad5ea5947..88ef5e57ccd0 100644 --- a/drivers/cpufreq/tegra194-cpufreq.c +++ b/drivers/cpufreq/tegra194-cpufreq.c @@ -508,6 +508,32 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy) return 0; } +static int tegra194_cpufreq_online(struct cpufreq_policy *policy) +{ + /* We did light-weight tear down earlier, nothing to do here */ + return 0; +} + +static int tegra194_cpufreq_offline(struct cpufreq_policy *policy) +{ + /* + * Preserve policy->driver_data and don't free resources on light-weight + * tear down. + */ + + return 0; +} + +static int tegra194_cpufreq_exit(struct cpufreq_policy *policy) +{ + struct device *cpu_dev = get_cpu_device(policy->cpu); + + dev_pm_opp_remove_all_dynamic(cpu_dev); + dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); + + return 0; +} + static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) { @@ -535,6 +561,9 @@ static struct cpufreq_driver tegra194_cpufreq_driver = { .target_index = tegra194_cpufreq_set_target, .get = tegra194_get_speed, .init = tegra194_cpufreq_init, + .exit = tegra194_cpufreq_exit, + .online = tegra194_cpufreq_online, + .offline = tegra194_cpufreq_offline, .attr = cpufreq_generic_attr, }; @@ -708,12 +737,10 @@ put_bpmp: return err; } -static int tegra194_cpufreq_remove(struct platform_device *pdev) +static void tegra194_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&tegra194_cpufreq_driver); tegra194_cpufreq_free_resources(); - - return 0; } static const struct of_device_id tegra194_cpufreq_of_match[] = { @@ -730,7 +757,7 @@ static struct platform_driver tegra194_ccplex_driver = { .of_match_table = tegra194_cpufreq_of_match, }, .probe = tegra194_cpufreq_probe, - .remove = tegra194_cpufreq_remove, + .remove_new = tegra194_cpufreq_remove, }; module_platform_driver(tegra194_ccplex_driver); diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index d5cd2fd25cad..3c37d7899660 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/drivers/cpufreq/vexpress-spc-cpufreq.c b/drivers/cpufreq/vexpress-spc-cpufreq.c index d295f405c4bb..9ac4ea50b874 100644 --- a/drivers/cpufreq/vexpress-spc-cpufreq.c +++ b/drivers/cpufreq/vexpress-spc-cpufreq.c @@ -18,7 +18,6 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/slab.h> @@ -552,7 +551,7 @@ static int ve_spc_cpufreq_probe(struct platform_device *pdev) return ret; } -static int ve_spc_cpufreq_remove(struct platform_device *pdev) +static void ve_spc_cpufreq_remove(struct platform_device *pdev) { bL_switcher_get_enabled(); __bLs_unregister_notifier(); @@ -560,7 +559,6 @@ static int ve_spc_cpufreq_remove(struct platform_device *pdev) bL_switcher_put_enabled(); pr_info("%s: Un-registered platform driver: %s\n", __func__, ve_spc_cpufreq_driver.name); - return 0; } static struct platform_driver ve_spc_cpufreq_platdrv = { @@ -568,7 +566,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = { .name = "vexpress-spc-cpufreq", }, .probe = ve_spc_cpufreq_probe, - .remove = ve_spc_cpufreq_remove, + .remove_new = ve_spc_cpufreq_remove, }; module_platform_driver(ve_spc_cpufreq_platdrv); diff --git a/drivers/cpuidle/governors/gov.h b/drivers/cpuidle/governors/gov.h new file mode 100644 index 000000000000..99e067d9668c --- /dev/null +++ b/drivers/cpuidle/governors/gov.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Common definitions for cpuidle governors. */ + +#ifndef __CPUIDLE_GOVERNOR_H +#define __CPUIDLE_GOVERNOR_H + +/* + * Idle state target residency threshold used for deciding whether or not to + * check the time till the closest expected timer event. + */ +#define RESIDENCY_THRESHOLD_NS (15 * NSEC_PER_USEC) + +#endif /* __CPUIDLE_GOVERNOR_H */ diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index c4922684f305..b96e3da0fedd 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -19,6 +19,8 @@ #include <linux/sched/stat.h> #include <linux/math64.h> +#include "gov.h" + #define BUCKETS 12 #define INTERVAL_SHIFT 3 #define INTERVALS (1UL << INTERVAL_SHIFT) @@ -166,8 +168,7 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); * of points is below a threshold. If it is... then use the * average of these 8 points as the estimated value. */ -static unsigned int get_typical_interval(struct menu_device *data, - unsigned int predicted_us) +static unsigned int get_typical_interval(struct menu_device *data) { int i, divisor; unsigned int min, max, thresh, avg; @@ -195,11 +196,7 @@ again: } } - /* - * If the result of the computation is going to be discarded anyway, - * avoid the computation altogether. - */ - if (min >= predicted_us) + if (!max) return UINT_MAX; if (divisor == INTERVALS) @@ -267,7 +264,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, { struct menu_device *data = this_cpu_ptr(&menu_devices); s64 latency_req = cpuidle_governor_latency_req(dev->cpu); - unsigned int predicted_us; u64 predicted_ns; u64 interactivity_req; unsigned int nr_iowaiters; @@ -279,16 +275,41 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, data->needs_update = 0; } - /* determine the expected residency time, round up */ - delta = tick_nohz_get_sleep_length(&delta_tick); - if (unlikely(delta < 0)) { - delta = 0; - delta_tick = 0; - } - data->next_timer_ns = delta; - nr_iowaiters = nr_iowait_cpu(dev->cpu); - data->bucket = which_bucket(data->next_timer_ns, nr_iowaiters); + + /* Find the shortest expected idle interval. */ + predicted_ns = get_typical_interval(data) * NSEC_PER_USEC; + if (predicted_ns > RESIDENCY_THRESHOLD_NS) { + unsigned int timer_us; + + /* Determine the time till the closest timer. */ + delta = tick_nohz_get_sleep_length(&delta_tick); + if (unlikely(delta < 0)) { + delta = 0; + delta_tick = 0; + } + + data->next_timer_ns = delta; + data->bucket = which_bucket(data->next_timer_ns, nr_iowaiters); + + /* Round up the result for half microseconds. */ + timer_us = div_u64((RESOLUTION * DECAY * NSEC_PER_USEC) / 2 + + data->next_timer_ns * + data->correction_factor[data->bucket], + RESOLUTION * DECAY * NSEC_PER_USEC); + /* Use the lowest expected idle interval to pick the idle state. */ + predicted_ns = min((u64)timer_us * NSEC_PER_USEC, predicted_ns); + } else { + /* + * Because the next timer event is not going to be determined + * in this case, assume that without the tick the closest timer + * will be in distant future and that the closest tick will occur + * after 1/2 of the tick period. + */ + data->next_timer_ns = KTIME_MAX; + delta_tick = TICK_NSEC / 2; + data->bucket = which_bucket(KTIME_MAX, nr_iowaiters); + } if (unlikely(drv->state_count <= 1 || latency_req == 0) || ((data->next_timer_ns < drv->states[1].target_residency_ns || @@ -303,16 +324,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, return 0; } - /* Round up the result for half microseconds. */ - predicted_us = div_u64(data->next_timer_ns * - data->correction_factor[data->bucket] + - (RESOLUTION * DECAY * NSEC_PER_USEC) / 2, - RESOLUTION * DECAY * NSEC_PER_USEC); - /* Use the lowest expected idle interval to pick the idle state. */ - predicted_ns = (u64)min(predicted_us, - get_typical_interval(data, predicted_us)) * - NSEC_PER_USEC; - if (tick_nohz_tick_stopped()) { /* * If the tick is already stopped, the cost of possible short diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c index 987fc5f3997d..7244f71c59c5 100644 --- a/drivers/cpuidle/governors/teo.c +++ b/drivers/cpuidle/governors/teo.c @@ -140,6 +140,8 @@ #include <linux/sched/topology.h> #include <linux/tick.h> +#include "gov.h" + /* * The number of bits to shift the CPU's capacity by in order to determine * the utilized threshold. @@ -152,7 +154,6 @@ */ #define UTIL_THRESHOLD_SHIFT 6 - /* * The PULSE value is added to metrics when they grow and the DECAY_SHIFT value * is used for decreasing metrics on a regular basis. @@ -186,8 +187,8 @@ struct teo_bin { * @total: Grand total of the "intercepts" and "hits" metrics for all bins. * @next_recent_idx: Index of the next @recent_idx entry to update. * @recent_idx: Indices of bins corresponding to recent "intercepts". + * @tick_hits: Number of "hits" after TICK_NSEC. * @util_threshold: Threshold above which the CPU is considered utilized - * @utilized: Whether the last sleep on the CPU happened while utilized */ struct teo_cpu { s64 time_span_ns; @@ -196,8 +197,8 @@ struct teo_cpu { unsigned int total; int next_recent_idx; int recent_idx[NR_RECENT]; + unsigned int tick_hits; unsigned long util_threshold; - bool utilized; }; static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); @@ -228,6 +229,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); int i, idx_timer = 0, idx_duration = 0; + s64 target_residency_ns; u64 measured_ns; if (cpu_data->time_span_ns >= cpu_data->sleep_length_ns) { @@ -268,7 +270,6 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) * fall into. */ for (i = 0; i < drv->state_count; i++) { - s64 target_residency_ns = drv->states[i].target_residency_ns; struct teo_bin *bin = &cpu_data->state_bins[i]; bin->hits -= bin->hits >> DECAY_SHIFT; @@ -276,6 +277,8 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) cpu_data->total += bin->hits + bin->intercepts; + target_residency_ns = drv->states[i].target_residency_ns; + if (target_residency_ns <= cpu_data->sleep_length_ns) { idx_timer = i; if (target_residency_ns <= measured_ns) @@ -291,6 +294,26 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) cpu_data->state_bins[cpu_data->recent_idx[i]].recent--; /* + * If the deepest state's target residency is below the tick length, + * make a record of it to help teo_select() decide whether or not + * to stop the tick. This effectively adds an extra hits-only bin + * beyond the last state-related one. + */ + if (target_residency_ns < TICK_NSEC) { + cpu_data->tick_hits -= cpu_data->tick_hits >> DECAY_SHIFT; + + cpu_data->total += cpu_data->tick_hits; + + if (TICK_NSEC <= cpu_data->sleep_length_ns) { + idx_timer = drv->state_count; + if (TICK_NSEC <= measured_ns) { + cpu_data->tick_hits += PULSE; + goto end; + } + } + } + + /* * If the measured idle duration falls into the same bin as the sleep * length, this is a "hit", so update the "hits" metric for that bin. * Otherwise, update the "intercepts" metric for the bin fallen into by @@ -305,18 +328,14 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) cpu_data->recent_idx[i] = idx_duration; } +end: cpu_data->total += PULSE; } -static bool teo_time_ok(u64 interval_ns) +static bool teo_state_ok(int i, struct cpuidle_driver *drv) { - return !tick_nohz_tick_stopped() || interval_ns >= TICK_NSEC; -} - -static s64 teo_middle_of_bin(int idx, struct cpuidle_driver *drv) -{ - return (drv->states[idx].target_residency_ns + - drv->states[idx+1].target_residency_ns) / 2; + return !tick_nohz_tick_stopped() || + drv->states[i].target_residency_ns >= TICK_NSEC; } /** @@ -356,6 +375,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, { struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); s64 latency_req = cpuidle_governor_latency_req(dev->cpu); + ktime_t delta_tick = TICK_NSEC / 2; + unsigned int tick_intercept_sum = 0; unsigned int idx_intercept_sum = 0; unsigned int intercept_sum = 0; unsigned int idx_recent_sum = 0; @@ -365,7 +386,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, int constraint_idx = 0; int idx0 = 0, idx = -1; bool alt_intercepts, alt_recent; - ktime_t delta_tick; + bool cpu_utilized; s64 duration_ns; int i; @@ -375,44 +396,48 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, } cpu_data->time_span_ns = local_clock(); - - duration_ns = tick_nohz_get_sleep_length(&delta_tick); - cpu_data->sleep_length_ns = duration_ns; + /* + * Set the expected sleep length to infinity in case of an early + * return. + */ + cpu_data->sleep_length_ns = KTIME_MAX; /* Check if there is any choice in the first place. */ if (drv->state_count < 2) { idx = 0; - goto end; + goto out_tick; } - if (!dev->states_usage[0].disable) { + + if (!dev->states_usage[0].disable) idx = 0; - if (drv->states[1].target_residency_ns > duration_ns) - goto end; - } - cpu_data->utilized = teo_cpu_is_utilized(dev->cpu, cpu_data); + cpu_utilized = teo_cpu_is_utilized(dev->cpu, cpu_data); /* * If the CPU is being utilized over the threshold and there are only 2 * states to choose from, the metrics need not be considered, so choose * the shallowest non-polling state and exit. */ - if (drv->state_count < 3 && cpu_data->utilized) { - for (i = 0; i < drv->state_count; ++i) { - if (!dev->states_usage[i].disable && - !(drv->states[i].flags & CPUIDLE_FLAG_POLLING)) { - idx = i; - goto end; - } + if (drv->state_count < 3 && cpu_utilized) { + /* + * If state 0 is enabled and it is not a polling one, select it + * right away unless the scheduler tick has been stopped, in + * which case care needs to be taken to leave the CPU in a deep + * enough state in case it is not woken up any time soon after + * all. If state 1 is disabled, though, state 0 must be used + * anyway. + */ + if ((!idx && !(drv->states[0].flags & CPUIDLE_FLAG_POLLING) && + teo_state_ok(0, drv)) || dev->states_usage[1].disable) { + idx = 0; + goto out_tick; } + /* Assume that state 1 is not a polling one and use it. */ + idx = 1; + duration_ns = drv->states[1].target_residency_ns; + goto end; } - /* - * Find the deepest idle state whose target residency does not exceed - * the current sleep length and the deepest idle state not deeper than - * the former whose exit latency does not exceed the current latency - * constraint. Compute the sums of metrics for early wakeup pattern - * detection. - */ + /* Compute the sums of metrics for early wakeup pattern detection. */ for (i = 1; i < drv->state_count; i++) { struct teo_bin *prev_bin = &cpu_data->state_bins[i-1]; struct cpuidle_state *s = &drv->states[i]; @@ -428,19 +453,15 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, if (dev->states_usage[i].disable) continue; - if (idx < 0) { - idx = i; /* first enabled state */ - idx0 = i; - } - - if (s->target_residency_ns > duration_ns) - break; + if (idx < 0) + idx0 = i; /* first enabled state */ idx = i; if (s->exit_latency_ns <= latency_req) constraint_idx = i; + /* Save the sums for the current state. */ idx_intercept_sum = intercept_sum; idx_hit_sum = hit_sum; idx_recent_sum = recent_sum; @@ -449,11 +470,21 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, /* Avoid unnecessary overhead. */ if (idx < 0) { idx = 0; /* No states enabled, must use 0. */ - goto end; - } else if (idx == idx0) { + goto out_tick; + } + + if (idx == idx0) { + /* + * Only one idle state is enabled, so use it, but do not + * allow the tick to be stopped it is shallow enough. + */ + duration_ns = drv->states[idx].target_residency_ns; goto end; } + tick_intercept_sum = intercept_sum + + cpu_data->state_bins[drv->state_count-1].intercepts; + /* * If the sum of the intercepts metric for all of the idle states * shallower than the current candidate one (idx) is greater than the @@ -461,13 +492,11 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * all of the deeper states, or the sum of the numbers of recent * intercepts over all of the states shallower than the candidate one * is greater than a half of the number of recent events taken into - * account, the CPU is likely to wake up early, so find an alternative - * idle state to select. + * account, a shallower idle state is likely to be a better choice. */ alt_intercepts = 2 * idx_intercept_sum > cpu_data->total - idx_hit_sum; alt_recent = idx_recent_sum > NR_RECENT / 2; if (alt_recent || alt_intercepts) { - s64 first_suitable_span_ns = duration_ns; int first_suitable_idx = idx; /* @@ -476,44 +505,39 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * cases (both with respect to intercepts overall and with * respect to the recent intercepts only) in the past. * - * Take the possible latency constraint and duration limitation - * present if the tick has been stopped already into account. + * Take the possible duration limitation present if the tick + * has been stopped already into account. */ intercept_sum = 0; recent_sum = 0; for (i = idx - 1; i >= 0; i--) { struct teo_bin *bin = &cpu_data->state_bins[i]; - s64 span_ns; intercept_sum += bin->intercepts; recent_sum += bin->recent; - span_ns = teo_middle_of_bin(i, drv); - if ((!alt_recent || 2 * recent_sum > idx_recent_sum) && (!alt_intercepts || 2 * intercept_sum > idx_intercept_sum)) { - if (teo_time_ok(span_ns) && - !dev->states_usage[i].disable) { + /* + * Use the current state unless it is too + * shallow or disabled, in which case take the + * first enabled state that is deep enough. + */ + if (teo_state_ok(i, drv) && + !dev->states_usage[i].disable) idx = i; - duration_ns = span_ns; - } else { - /* - * The current state is too shallow or - * disabled, so take the first enabled - * deeper state with suitable time span. - */ + else idx = first_suitable_idx; - duration_ns = first_suitable_span_ns; - } + break; } if (dev->states_usage[i].disable) continue; - if (!teo_time_ok(span_ns)) { + if (!teo_state_ok(i, drv)) { /* * The current state is too shallow, but if an * alternative candidate state has been found, @@ -525,7 +549,6 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, break; } - first_suitable_span_ns = span_ns; first_suitable_idx = i; } } @@ -539,31 +562,75 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, /* * If the CPU is being utilized over the threshold, choose a shallower - * non-polling state to improve latency + * non-polling state to improve latency, unless the scheduler tick has + * been stopped already and the shallower state's target residency is + * not sufficiently large. */ - if (cpu_data->utilized) - idx = teo_find_shallower_state(drv, dev, idx, duration_ns, true); + if (cpu_utilized) { + i = teo_find_shallower_state(drv, dev, idx, KTIME_MAX, true); + if (teo_state_ok(i, drv)) + idx = i; + } -end: /* - * Don't stop the tick if the selected state is a polling one or if the - * expected idle duration is shorter than the tick period length. + * Skip the timers check if state 0 is the current candidate one, + * because an immediate non-timer wakeup is expected in that case. */ - if (((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) || - duration_ns < TICK_NSEC) && !tick_nohz_tick_stopped()) { - *stop_tick = false; + if (!idx) + goto out_tick; - /* - * The tick is not going to be stopped, so if the target - * residency of the state to be returned is not within the time - * till the closest timer including the tick, try to correct - * that. - */ - if (idx > idx0 && - drv->states[idx].target_residency_ns > delta_tick) - idx = teo_find_shallower_state(drv, dev, idx, delta_tick, false); + /* + * If state 0 is a polling one, check if the target residency of + * the current candidate state is low enough and skip the timers + * check in that case too. + */ + if ((drv->states[0].flags & CPUIDLE_FLAG_POLLING) && + drv->states[idx].target_residency_ns < RESIDENCY_THRESHOLD_NS) + goto out_tick; + + duration_ns = tick_nohz_get_sleep_length(&delta_tick); + cpu_data->sleep_length_ns = duration_ns; + + /* + * If the closest expected timer is before the terget residency of the + * candidate state, a shallower one needs to be found. + */ + if (drv->states[idx].target_residency_ns > duration_ns) { + i = teo_find_shallower_state(drv, dev, idx, duration_ns, false); + if (teo_state_ok(i, drv)) + idx = i; } + /* + * If the selected state's target residency is below the tick length + * and intercepts occurring before the tick length are the majority of + * total wakeup events, do not stop the tick. + */ + if (drv->states[idx].target_residency_ns < TICK_NSEC && + tick_intercept_sum > cpu_data->total / 2 + cpu_data->total / 8) + duration_ns = TICK_NSEC / 2; + +end: + /* + * Allow the tick to be stopped unless the selected state is a polling + * one or the expected idle duration is shorter than the tick period + * length. + */ + if ((!(drv->states[idx].flags & CPUIDLE_FLAG_POLLING) && + duration_ns >= TICK_NSEC) || tick_nohz_tick_stopped()) + return idx; + + /* + * The tick is not going to be stopped, so if the target residency of + * the state to be returned is not within the time till the closest + * timer including the tick, try to correct that. + */ + if (idx > idx0 && + drv->states[idx].target_residency_ns > delta_tick) + idx = teo_find_shallower_state(drv, dev, idx, delta_tick, false); + +out_tick: + *stop_tick = false; return idx; } diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index e36cbb920ec8..474d81831ad3 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -472,10 +472,11 @@ static void devfreq_monitor(struct work_struct *work) * devfreq_monitor_start() - Start load monitoring of devfreq instance * @devfreq: the devfreq instance. * - * Helper function for starting devfreq device load monitoring. By - * default delayed work based monitoring is supported. Function - * to be called from governor in response to DEVFREQ_GOV_START - * event when device is added to devfreq framework. + * Helper function for starting devfreq device load monitoring. By default, + * deferrable timer is used for load monitoring. But the users can change this + * behavior using the "timer" type in devfreq_dev_profile. This function will be + * called by devfreq governor in response to the DEVFREQ_GOV_START event + * generated while adding a device to the devfreq framework. */ void devfreq_monitor_start(struct devfreq *devfreq) { @@ -763,6 +764,7 @@ static void devfreq_dev_release(struct device *dev) dev_pm_opp_put_opp_table(devfreq->opp_table); mutex_destroy(&devfreq->lock); + srcu_cleanup_notifier_head(&devfreq->transition_notifier_list); kfree(devfreq); } diff --git a/drivers/devfreq/imx-bus.c b/drivers/devfreq/imx-bus.c index a727067980fb..86850b7dea09 100644 --- a/drivers/devfreq/imx-bus.c +++ b/drivers/devfreq/imx-bus.c @@ -7,7 +7,7 @@ #include <linux/devfreq.h> #include <linux/device.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pm_opp.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/drivers/devfreq/imx8m-ddrc.c b/drivers/devfreq/imx8m-ddrc.c index 16636973eb10..e1348490c8aa 100644 --- a/drivers/devfreq/imx8m-ddrc.c +++ b/drivers/devfreq/imx8m-ddrc.c @@ -3,9 +3,9 @@ * Copyright 2019 NXP */ +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/device.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/devfreq.h> #include <linux/pm_opp.h> diff --git a/drivers/devfreq/mtk-cci-devfreq.c b/drivers/devfreq/mtk-cci-devfreq.c index 6354622eda65..83a73f0ccd80 100644 --- a/drivers/devfreq/mtk-cci-devfreq.c +++ b/drivers/devfreq/mtk-cci-devfreq.c @@ -8,7 +8,6 @@ #include <linux/minmax.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/regulator/consumer.h> diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c index 503376b894b6..4a4f0106ab9d 100644 --- a/drivers/devfreq/tegra30-devfreq.c +++ b/drivers/devfreq/tegra30-devfreq.c @@ -13,7 +13,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/reset.h> diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 3f46e499d615..919cc53bc02e 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -177,25 +177,24 @@ unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp) EXPORT_SYMBOL_GPL(dev_pm_opp_get_power); /** - * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp - * @opp: opp for which frequency has to be returned for + * dev_pm_opp_get_freq_indexed() - Gets the frequency corresponding to an + * available opp with specified index + * @opp: opp for which frequency has to be returned for + * @index: index of the frequency within the required opp * - * Return: frequency in hertz corresponding to the opp, else - * return 0 + * Return: frequency in hertz corresponding to the opp with specified index, + * else return 0 */ -unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) +unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index) { - if (IS_ERR_OR_NULL(opp)) { + if (IS_ERR_OR_NULL(opp) || index >= opp->opp_table->clk_count) { pr_err("%s: Invalid parameters\n", __func__); return 0; } - if (!assert_single_clk(opp->opp_table)) - return 0; - - return opp->rates[0]; + return opp->rates[index]; } -EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); +EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed); /** * dev_pm_opp_get_level() - Gets the level corresponding to an available opp @@ -227,20 +226,18 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, unsigned int index) { - struct opp_table *opp_table = opp->opp_table; - if (IS_ERR_OR_NULL(opp) || !opp->available || - index >= opp_table->required_opp_count) { + index >= opp->opp_table->required_opp_count) { pr_err("%s: Invalid parameters\n", __func__); return 0; } /* required-opps not fully initialized yet */ - if (lazy_linking_pending(opp_table)) + if (lazy_linking_pending(opp->opp_table)) return 0; /* The required OPP table must belong to a genpd */ - if (unlikely(!opp_table->required_opp_tables[index]->is_genpd)) { + if (unlikely(!opp->opp_table->required_opp_tables[index]->is_genpd)) { pr_err("%s: Performance state is only valid for genpds.\n", __func__); return 0; } @@ -450,7 +447,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); /* Helpers to read keys */ static unsigned long _read_freq(struct dev_pm_opp *opp, int index) { - return opp->rates[0]; + return opp->rates[index]; } static unsigned long _read_level(struct dev_pm_opp *opp, int index) @@ -626,6 +623,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); +/** + * dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the + * clock corresponding to the index + * @dev: Device for which we do this operation + * @freq: frequency to search for + * @index: Clock index + * @available: true/false - match for available opp + * + * Search for the matching exact OPP for the clock corresponding to the + * specified index from a starting freq for a device. + * + * Return: matching *opp , else returns ERR_PTR in case of error and should be + * handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp * +dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, + u32 index, bool available) +{ + return _find_key_exact(dev, freq, index, available, _read_freq, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact_indexed); + static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { @@ -659,6 +684,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); /** + * dev_pm_opp_find_freq_ceil_indexed() - Search for a rounded ceil freq for the + * clock corresponding to the index + * @dev: Device for which we do this operation + * @freq: Start frequency + * @index: Clock index + * + * Search for the matching ceil *available* OPP for the clock corresponding to + * the specified index from a starting freq for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp * +dev_pm_opp_find_freq_ceil_indexed(struct device *dev, unsigned long *freq, + u32 index) +{ + return _find_key_ceil(dev, freq, index, true, _read_freq, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_indexed); + +/** * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq * @dev: device for which we do this operation * @freq: Start frequency @@ -684,6 +737,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); /** + * dev_pm_opp_find_freq_floor_indexed() - Search for a rounded floor freq for the + * clock corresponding to the index + * @dev: Device for which we do this operation + * @freq: Start frequency + * @index: Clock index + * + * Search for the matching floor *available* OPP for the clock corresponding to + * the specified index from a starting freq for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp * +dev_pm_opp_find_freq_floor_indexed(struct device *dev, unsigned long *freq, + u32 index) +{ + return _find_key_floor(dev, freq, index, true, _read_freq, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor_indexed); + +/** * dev_pm_opp_find_level_exact() - search for an exact level * @dev: device for which we do this operation * @level: level to search for @@ -2379,7 +2460,7 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, virt_dev = dev_pm_domain_attach_by_name(dev, *name); if (IS_ERR_OR_NULL(virt_dev)) { - ret = PTR_ERR(virt_dev) ? : -ENODEV; + ret = virt_dev ? PTR_ERR(virt_dev) : -ENODEV; dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret); goto err; } diff --git a/drivers/opp/cpu.c b/drivers/opp/cpu.c index 3c3506021501..12c429b407ca 100644 --- a/drivers/opp/cpu.c +++ b/drivers/opp/cpu.c @@ -24,7 +24,7 @@ /** * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device * @dev: device for which we do this operation - * @table: Cpufreq table returned back to caller + * @opp_table: Cpufreq table returned back to caller * * Generate a cpufreq table for a provided device- this assumes that the * opp table is already initialized and ready for usage. @@ -89,7 +89,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); /** * dev_pm_opp_free_cpufreq_table() - free the cpufreq table * @dev: device for which we do this operation - * @table: table to free + * @opp_table: table to free * * Free up the table allocated by dev_pm_opp_init_cpufreq_table */ diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c index 5231f6d52ae3..a081f177e702 100644 --- a/drivers/powercap/arm_scmi_powercap.c +++ b/drivers/powercap/arm_scmi_powercap.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/powercap.h> #include <linux/scmi_protocol.h> +#include <linux/slab.h> #define to_scmi_powercap_zone(z) \ container_of(z, struct scmi_powercap_zone, zone) @@ -19,6 +20,8 @@ static const struct scmi_powercap_proto_ops *powercap_ops; struct scmi_powercap_zone { + bool registered; + bool invalid; unsigned int height; struct device *dev; struct scmi_protocol_handle *ph; @@ -32,6 +35,7 @@ struct scmi_powercap_root { unsigned int num_zones; struct scmi_powercap_zone *spzones; struct list_head *registered_zones; + struct list_head scmi_zones; }; static struct powercap_control_type *scmi_top_pcntrl; @@ -271,12 +275,6 @@ static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr) } } -static inline bool -scmi_powercap_is_zone_registered(struct scmi_powercap_zone *spz) -{ - return !list_empty(&spz->node); -} - static inline unsigned int scmi_powercap_get_zone_height(struct scmi_powercap_zone *spz) { @@ -295,11 +293,46 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz) return &spz->spzones[spz->info->parent_id]; } +static int scmi_powercap_register_zone(struct scmi_powercap_root *pr, + struct scmi_powercap_zone *spz, + struct scmi_powercap_zone *parent) +{ + int ret = 0; + struct powercap_zone *z; + + if (spz->invalid) { + list_del(&spz->node); + return -EINVAL; + } + + z = powercap_register_zone(&spz->zone, scmi_top_pcntrl, spz->info->name, + parent ? &parent->zone : NULL, + &zone_ops, 1, &constraint_ops); + if (!IS_ERR(z)) { + spz->height = scmi_powercap_get_zone_height(spz); + spz->registered = true; + list_move(&spz->node, &pr->registered_zones[spz->height]); + dev_dbg(spz->dev, "Registered node %s - parent %s - height:%d\n", + spz->info->name, parent ? parent->info->name : "ROOT", + spz->height); + } else { + list_del(&spz->node); + ret = PTR_ERR(z); + dev_err(spz->dev, + "Error registering node:%s - parent:%s - h:%d - ret:%d\n", + spz->info->name, + parent ? parent->info->name : "ROOT", + spz->height, ret); + } + + return ret; +} + /** - * scmi_powercap_register_zone - Register an SCMI powercap zone recursively + * scmi_zones_register- Register SCMI powercap zones starting from parent zones * + * @dev: A reference to the SCMI device * @pr: A reference to the root powercap zones descriptors - * @spz: A reference to the SCMI powercap zone to register * * When registering SCMI powercap zones with the powercap framework we should * take care to always register zones starting from the root ones and to @@ -309,10 +342,10 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz) * zones provided by the SCMI platform firmware is built to comply with such * requirement. * - * This function, given an SCMI powercap zone to register, takes care to walk - * the SCMI powercap zones tree up to the root looking recursively for - * unregistered parent zones before registering the provided zone; at the same - * time each registered zone height in such a tree is accounted for and each + * This function, given the set of SCMI powercap zones to register, takes care + * to walk the SCMI powercap zones trees up to the root registering any + * unregistered parent zone before registering the child zones; at the same + * time each registered-zone height in such a tree is accounted for and each * zone, once registered, is stored in the @registered_zones array that is * indexed by zone height: this way will be trivial, at unregister time, to walk * the @registered_zones array backward and unregister all the zones starting @@ -330,57 +363,55 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz) * * Return: 0 on Success */ -static int scmi_powercap_register_zone(struct scmi_powercap_root *pr, - struct scmi_powercap_zone *spz) +static int scmi_zones_register(struct device *dev, + struct scmi_powercap_root *pr) { int ret = 0; - struct scmi_powercap_zone *parent; - - if (!spz->info) - return ret; + unsigned int sp = 0, reg_zones = 0; + struct scmi_powercap_zone *spz, **zones_stack; - parent = scmi_powercap_get_parent_zone(spz); - if (parent && !scmi_powercap_is_zone_registered(parent)) { - /* - * Bail out if a parent domain was marked as unsupported: - * only domains participating as leaves can be skipped. - */ - if (!parent->info) - return -ENODEV; + zones_stack = kcalloc(pr->num_zones, sizeof(spz), GFP_KERNEL); + if (!zones_stack) + return -ENOMEM; - ret = scmi_powercap_register_zone(pr, parent); - if (ret) - return ret; - } + spz = list_first_entry_or_null(&pr->scmi_zones, + struct scmi_powercap_zone, node); + while (spz) { + struct scmi_powercap_zone *parent; - if (!scmi_powercap_is_zone_registered(spz)) { - struct powercap_zone *z; - - z = powercap_register_zone(&spz->zone, - scmi_top_pcntrl, - spz->info->name, - parent ? &parent->zone : NULL, - &zone_ops, 1, &constraint_ops); - if (!IS_ERR(z)) { - spz->height = scmi_powercap_get_zone_height(spz); - list_add(&spz->node, - &pr->registered_zones[spz->height]); - dev_dbg(spz->dev, - "Registered node %s - parent %s - height:%d\n", - spz->info->name, - parent ? parent->info->name : "ROOT", - spz->height); - ret = 0; + parent = scmi_powercap_get_parent_zone(spz); + if (parent && !parent->registered) { + zones_stack[sp++] = spz; + spz = parent; } else { - ret = PTR_ERR(z); - dev_err(spz->dev, - "Error registering node:%s - parent:%s - h:%d - ret:%d\n", - spz->info->name, - parent ? parent->info->name : "ROOT", - spz->height, ret); + ret = scmi_powercap_register_zone(pr, spz, parent); + if (!ret) { + reg_zones++; + } else if (sp) { + /* Failed to register a non-leaf zone. + * Bail-out. + */ + dev_err(dev, + "Failed to register non-leaf zone - ret:%d\n", + ret); + scmi_powercap_unregister_all_zones(pr); + reg_zones = 0; + goto out; + } + /* Pick next zone to process */ + if (sp) + spz = zones_stack[--sp]; + else + spz = list_first_entry_or_null(&pr->scmi_zones, + struct scmi_powercap_zone, + node); } } +out: + kfree(zones_stack); + dev_info(dev, "Registered %d SCMI Powercap domains !\n", reg_zones); + return ret; } @@ -424,6 +455,8 @@ static int scmi_powercap_probe(struct scmi_device *sdev) if (!pr->registered_zones) return -ENOMEM; + INIT_LIST_HEAD(&pr->scmi_zones); + for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) { /* * Powercap domains are validate by the protocol layer, i.e. @@ -438,6 +471,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev) INIT_LIST_HEAD(&spz->node); INIT_LIST_HEAD(&pr->registered_zones[i]); + list_add_tail(&spz->node, &pr->scmi_zones); /* * Forcibly skip powercap domains using an abstract scale. * Note that only leaves domains can be skipped, so this could @@ -448,7 +482,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev) dev_warn(dev, "Abstract power scale not supported. Skip %s.\n", spz->info->name); - spz->info = NULL; + spz->invalid = true; continue; } } @@ -457,21 +491,12 @@ static int scmi_powercap_probe(struct scmi_device *sdev) * Scan array of retrieved SCMI powercap domains and register them * recursively starting from the root domains. */ - for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) { - ret = scmi_powercap_register_zone(pr, spz); - if (ret) { - dev_err(dev, - "Failed to register powercap zone %s - ret:%d\n", - spz->info->name, ret); - scmi_powercap_unregister_all_zones(pr); - return ret; - } - } + ret = scmi_zones_register(dev, pr); + if (ret) + return ret; dev_set_drvdata(dev, pr); - dev_info(dev, "Registered %d SCMI Powercap domains !\n", pr->num_zones); - return ret; } diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index 72a8d336f980..5c2e6d5eea2a 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1485,7 +1485,7 @@ static int rapl_detect_domains(struct rapl_package *rp) } pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name); - rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain), + rp->domains = kcalloc(rp->nr_domains, sizeof(struct rapl_domain), GFP_KERNEL); if (!rp->domains) return -ENOMEM; |