From c27180b4c7bbfb0ba030d22e831f3bf13ced70bc Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Tue, 1 Feb 2022 11:14:55 -0800 Subject: [PATCH 1/4] cpufreq: qcom-hw: Initialize qcom_cpufreq_counter spinlock Initialize qcom_cpufreq_counter spinlock to avoid uninitilized access or panic when driver tries to get lock during cycle_counter framework call. Change-Id: If56cf3030a2d468c0493a5946c778908fd015221 Signed-off-by: Vivek Aknurwar --- drivers/cpufreq/qcom-cpufreq-hw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 93035db9a5e7..6830b35868b8 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -705,7 +705,7 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) { struct device *cpu_dev; struct clk *clk; - int ret; + int ret, cpu; clk = clk_get(&pdev->dev, "xo"); if (IS_ERR(clk)) @@ -732,6 +732,9 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) if (ret) return ret; + for_each_possible_cpu(cpu) + spin_lock_init(&qcom_cpufreq_counter[cpu].lock); + ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver); if (ret) dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n"); From b83f18ed9a838a6de7cba86472223d3911603d5c Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Thu, 3 Feb 2022 12:26:33 -0800 Subject: [PATCH 2/4] cpufreq: qcom-hw: Keep driver_data for cycle_counter API use driver_data is freed between cpu_freq framework cpufreq_offline and cpufreq_online callbacks. cycle counter api can be invoked by irq inbetween cpu online/init call which can lead to panic if driver_date is released on last exit callback from framework. Hence for now keep driver_data, once allocated in init call. Change-Id: I892fcad460b8bbe38d7240b2a43105d5fcb11639 Signed-off-by: Vivek Aknurwar --- drivers/cpufreq/qcom-cpufreq-hw.c | 61 ++++++++++++++++--------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 6830b35868b8..0ea6f9ba12a0 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -570,33 +570,39 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) index = args.args[0]; - res = platform_get_resource(pdev, IORESOURCE_MEM, index); - if (!res) { - dev_err(dev, "failed to get mem resource %d\n", index); - return -ENODEV; - } + data = policy->driver_data; - if (!request_mem_region(res->start, resource_size(res), res->name)) { - dev_err(dev, "failed to request resource %pR\n", res); - return -EBUSY; - } - - base = ioremap(res->start, resource_size(res)); - if (!base) { - dev_err(dev, "failed to map resource %pR\n", res); - ret = -ENOMEM; - goto release_region; - } - - data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { - ret = -ENOMEM; - goto unmap_base; + res = platform_get_resource(pdev, IORESOURCE_MEM, index); + if (!res) { + dev_err(dev, "failed to get mem resource %d\n", index); + return -ENODEV; + } + + if (!devm_request_mem_region(dev, res->start, resource_size(res), res->name)) { + dev_err(dev, "failed to request resource %pR\n", res); + return -EBUSY; + } + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) { + dev_err(dev, "failed to map resource %pR\n", res); + ret = -ENOMEM; + goto release_region; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto unmap_base; + } + + data->soc_data = of_device_get_match_data(&pdev->dev); + data->base = base; + data->res = res; } - data->soc_data = of_device_get_match_data(&pdev->dev); - data->base = base; - data->res = res; + base = data->base; /* HW should be in enabled state to proceed */ if (!(readl_relaxed(base + data->soc_data->reg_enable) & 0x1)) { @@ -644,6 +650,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) return 0; error: kfree(data); + policy->driver_data = NULL; unmap_base: iounmap(base); release_region: @@ -654,17 +661,11 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) { struct device *cpu_dev = get_cpu_device(policy->cpu); - struct qcom_cpufreq_data *data = policy->driver_data; - struct resource *res = data->res; - void __iomem *base = data->base; - qcom_cpufreq_hw_lmh_exit(data); + qcom_cpufreq_hw_lmh_exit(policy->driver_data); dev_pm_opp_remove_all_dynamic(cpu_dev); dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); kfree(policy->freq_table); - kfree(data); - iounmap(base); - release_mem_region(res->start, resource_size(res)); return 0; } From 6d360fbe4b4197fdbb293059368ef5f61e1c4481 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Tue, 1 Mar 2022 10:46:00 -0800 Subject: [PATCH 3/4] cpufreq: qcom-hw: Add device NULL check in notify/polling callback If cpu is hotplugged then policy->cpus is cleared in cpufreq_offline which will results in device being NULL on last CPU hotplugged out. Also since cpu is hotplugged out, polling callback can safely be ignored in this case. Change-Id: Ic2d29fe2bdd7c6aa1e952d700d11e321e69f6acb Signed-off-by: Vivek Aknurwar --- drivers/cpufreq/qcom-cpufreq-hw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 0ea6f9ba12a0..98a160ec0c3f 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -361,6 +361,9 @@ static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data) unsigned long freq_hz, throttled_freq; struct dev_pm_opp *opp; + if (!dev) + return; + /* * Get the h/w throttled frequency, normalize it using the * registered opp table and use it to calculate thermal pressure. From 078592290436d0650155bc5f260f52f3af92c991 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 9 Mar 2022 19:08:59 -0800 Subject: [PATCH 4/4] cpufreq: qcom-hw: remove thermal pressure on cpu hotplugged/offline When cpu is hotplugged out, cpu stack is torn down and lmh mitigation also stops. Hence on lmh exit remove thermal pressure. This will ensure that scheduler requests higher frequency when cpu is back online again, since it's likely the CPU has cooled down in the meantime. And if it hasn't, then lmh IRQ will fire again and we'll reapply pressure. Change-Id: I0c17e8eb3b54f4c0661c730015c19b476e322770 Signed-off-by: Vivek Aknurwar Signed-off-by: Mike Tipton --- drivers/cpufreq/qcom-cpufreq-hw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 98a160ec0c3f..3d077c644624 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -531,6 +531,8 @@ static int qcom_cpufreq_hw_cpu_offline(struct cpufreq_policy *policy) cancel_delayed_work_sync(&data->throttle_work); irq_set_affinity_hint(data->throttle_irq, NULL); + arch_update_thermal_pressure(policy->related_cpus, U32_MAX); + return 0; }