mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'cpufreq-arm-updates-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull CPUFreq updates for 6.19 from Viresh Kumar: "- tegra186: Add OPP / bandwidth support for Tegra186 (Aaron Kling). - Minor improvements to various cpufreq drivers (Christian Marangi, Hal Feng, Jie Zhan, Marco Crivellari, Miaoqian Lin, and Shuhao Fu)." * tag 'cpufreq-arm-updates-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: cpufreq: qcom-nvmem: fix compilation warning for qcom_cpufreq_ipq806x_match_list cpufreq: tegra194: add WQ_PERCPU to alloc_workqueue users cpufreq: qcom-nvmem: add compatible fallback for ipq806x for no SMEM cpufreq: CPPC: Don't warn if FIE init fails to read counters cpufreq: nforce2: fix reference count leak in nforce2 cpufreq: tegra186: add OPP support and set bandwidth cpufreq: dt-platdev: Add JH7110S SOC to the allowlist cpufreq: s5pv210: fix refcount leak
This commit is contained in:
@@ -142,16 +142,15 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
||||
init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
|
||||
|
||||
ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
|
||||
if (ret) {
|
||||
pr_warn("%s: failed to read perf counters for cpu:%d: %d\n",
|
||||
__func__, cpu, ret);
|
||||
|
||||
/*
|
||||
* Don't abort if the CPU was offline while the driver
|
||||
* was getting registered.
|
||||
*/
|
||||
if (cpu_online(cpu))
|
||||
return;
|
||||
/*
|
||||
* Don't abort as the CPU was offline while the driver was
|
||||
* getting registered.
|
||||
*/
|
||||
if (ret && cpu_online(cpu)) {
|
||||
pr_debug("%s: failed to read perf counters for cpu:%d: %d\n",
|
||||
__func__, cpu, ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ static const struct of_device_id allowlist[] __initconst = {
|
||||
{ .compatible = "st-ericsson,u9540", },
|
||||
|
||||
{ .compatible = "starfive,jh7110", },
|
||||
{ .compatible = "starfive,jh7110s", },
|
||||
|
||||
{ .compatible = "ti,omap2", },
|
||||
{ .compatible = "ti,omap4", },
|
||||
|
||||
@@ -145,6 +145,8 @@ static unsigned int nforce2_fsb_read(int bootfsb)
|
||||
pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
|
||||
fsb /= 1000000;
|
||||
|
||||
pci_dev_put(nforce2_sub5);
|
||||
|
||||
/* Check if PLL register is already set */
|
||||
pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
|
||||
|
||||
@@ -426,6 +428,7 @@ static int __init nforce2_init(void)
|
||||
static void __exit nforce2_exit(void)
|
||||
{
|
||||
cpufreq_unregister_driver(&nforce2_driver);
|
||||
pci_dev_put(nforce2_dev);
|
||||
}
|
||||
|
||||
module_init(nforce2_init);
|
||||
|
||||
@@ -256,13 +256,22 @@ len_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_cpufreq_ipq806x_match_list[] __maybe_unused = {
|
||||
{ .compatible = "qcom,ipq8062", .data = (const void *)QCOM_ID_IPQ8062 },
|
||||
{ .compatible = "qcom,ipq8064", .data = (const void *)QCOM_ID_IPQ8064 },
|
||||
{ .compatible = "qcom,ipq8065", .data = (const void *)QCOM_ID_IPQ8065 },
|
||||
{ .compatible = "qcom,ipq8066", .data = (const void *)QCOM_ID_IPQ8066 },
|
||||
{ .compatible = "qcom,ipq8068", .data = (const void *)QCOM_ID_IPQ8068 },
|
||||
{ .compatible = "qcom,ipq8069", .data = (const void *)QCOM_ID_IPQ8069 },
|
||||
};
|
||||
|
||||
static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
|
||||
struct nvmem_cell *speedbin_nvmem,
|
||||
char **pvs_name,
|
||||
struct qcom_cpufreq_drv *drv)
|
||||
{
|
||||
int msm_id = -1, ret = 0;
|
||||
int speed = 0, pvs = 0;
|
||||
int msm_id, ret = 0;
|
||||
u8 *speedbin;
|
||||
size_t len;
|
||||
|
||||
@@ -279,8 +288,30 @@ static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
|
||||
get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin);
|
||||
|
||||
ret = qcom_smem_get_soc_id(&msm_id);
|
||||
if (ret)
|
||||
if (ret == -ENODEV) {
|
||||
const struct of_device_id *match;
|
||||
struct device_node *root;
|
||||
|
||||
root = of_find_node_by_path("/");
|
||||
if (!root) {
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Fallback to compatible match with no SMEM initialized */
|
||||
match = of_match_node(qcom_cpufreq_ipq806x_match_list, root);
|
||||
of_node_put(root);
|
||||
if (!match) {
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* We found a matching device, get the msm_id from the data entry */
|
||||
msm_id = (int)(uintptr_t)match->data;
|
||||
ret = 0;
|
||||
} else if (ret) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (msm_id) {
|
||||
case QCOM_ID_IPQ8062:
|
||||
|
||||
@@ -518,7 +518,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
|
||||
|
||||
if (policy->cpu != 0) {
|
||||
ret = -EINVAL;
|
||||
goto out_dmc1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -530,7 +530,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
|
||||
if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
|
||||
pr_err("CPUFreq doesn't support this memory type\n");
|
||||
ret = -EINVAL;
|
||||
goto out_dmc1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find current refresh counter and frequency each DMC */
|
||||
@@ -544,6 +544,8 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
|
||||
cpufreq_generic_init(policy, s5pv210_freq_table, 40000);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
clk_put(dmc1_clk);
|
||||
out_dmc1:
|
||||
clk_put(dmc0_clk);
|
||||
out_dmc0:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/bpmp-abi.h>
|
||||
@@ -58,7 +59,7 @@ static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
|
||||
};
|
||||
|
||||
struct tegra186_cpufreq_cluster {
|
||||
struct cpufreq_frequency_table *table;
|
||||
struct cpufreq_frequency_table *bpmp_lut;
|
||||
u32 ref_clk_khz;
|
||||
u32 div;
|
||||
};
|
||||
@@ -66,16 +67,119 @@ struct tegra186_cpufreq_cluster {
|
||||
struct tegra186_cpufreq_data {
|
||||
void __iomem *regs;
|
||||
const struct tegra186_cpufreq_cpu *cpus;
|
||||
bool icc_dram_bw_scaling;
|
||||
struct tegra186_cpufreq_cluster clusters[];
|
||||
};
|
||||
|
||||
static int tegra_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
dev = get_cpu_device(policy->cpu);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
struct dev_pm_opp *opp __free(put_opp) =
|
||||
dev_pm_opp_find_freq_exact(dev, freq_khz * HZ_PER_KHZ, true);
|
||||
if (IS_ERR(opp))
|
||||
return PTR_ERR(opp);
|
||||
|
||||
ret = dev_pm_opp_set_opp(dev, opp);
|
||||
if (ret)
|
||||
data->icc_dram_bw_scaling = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
|
||||
struct cpufreq_frequency_table *bpmp_lut,
|
||||
struct cpufreq_frequency_table **opp_table)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct cpufreq_frequency_table *freq_table = NULL;
|
||||
struct cpufreq_frequency_table *pos;
|
||||
struct device *cpu_dev;
|
||||
unsigned long rate;
|
||||
int ret, max_opps;
|
||||
int j = 0;
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
pr_err("%s: failed to get cpu%d device\n", __func__, policy->cpu);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Initialize OPP table mentioned in operating-points-v2 property in DT */
|
||||
ret = dev_pm_opp_of_add_table_indexed(cpu_dev, 0);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "Invalid or empty opp table in device tree\n");
|
||||
data->icc_dram_bw_scaling = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
max_opps = dev_pm_opp_get_opp_count(cpu_dev);
|
||||
if (max_opps <= 0) {
|
||||
dev_err(cpu_dev, "Failed to add OPPs\n");
|
||||
return max_opps;
|
||||
}
|
||||
|
||||
/* Disable all opps and cross-validate against LUT later */
|
||||
for (rate = 0; ; rate++) {
|
||||
struct dev_pm_opp *opp __free(put_opp) =
|
||||
dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
|
||||
if (IS_ERR(opp))
|
||||
break;
|
||||
|
||||
dev_pm_opp_disable(cpu_dev, rate);
|
||||
}
|
||||
|
||||
freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
|
||||
if (!freq_table)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
|
||||
* Enable only those DT OPP's which are present in LUT also.
|
||||
*/
|
||||
cpufreq_for_each_valid_entry(pos, bpmp_lut) {
|
||||
struct dev_pm_opp *opp __free(put_opp) =
|
||||
dev_pm_opp_find_freq_exact(cpu_dev, pos->frequency * HZ_PER_KHZ, false);
|
||||
if (IS_ERR(opp))
|
||||
continue;
|
||||
|
||||
ret = dev_pm_opp_enable(cpu_dev, pos->frequency * HZ_PER_KHZ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
freq_table[j].driver_data = pos->driver_data;
|
||||
freq_table[j].frequency = pos->frequency;
|
||||
j++;
|
||||
}
|
||||
|
||||
freq_table[j].driver_data = pos->driver_data;
|
||||
freq_table[j].frequency = CPUFREQ_TABLE_END;
|
||||
|
||||
*opp_table = &freq_table[0];
|
||||
|
||||
dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
|
||||
|
||||
/* Prime interconnect data */
|
||||
tegra_cpufreq_set_bw(policy, freq_table[j - 1].frequency);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct cpufreq_frequency_table *bpmp_lut;
|
||||
u32 cpu;
|
||||
int ret;
|
||||
|
||||
policy->freq_table = data->clusters[cluster].table;
|
||||
policy->cpuinfo.transition_latency = 300 * 1000;
|
||||
policy->driver_data = NULL;
|
||||
|
||||
@@ -85,6 +189,20 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
|
||||
cpumask_set_cpu(cpu, policy->cpus);
|
||||
}
|
||||
|
||||
bpmp_lut = data->clusters[cluster].bpmp_lut;
|
||||
|
||||
if (data->icc_dram_bw_scaling) {
|
||||
ret = tegra_cpufreq_init_cpufreq_table(policy, bpmp_lut, &freq_table);
|
||||
if (!ret) {
|
||||
policy->freq_table = freq_table;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
data->icc_dram_bw_scaling = false;
|
||||
policy->freq_table = bpmp_lut;
|
||||
pr_info("OPP tables missing from DT, EMC frequency scaling disabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,6 +220,10 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
writel(edvd_val, data->regs + edvd_offset);
|
||||
}
|
||||
|
||||
if (data->icc_dram_bw_scaling)
|
||||
tegra_cpufreq_set_bw(policy, tbl->frequency);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -134,7 +256,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
|
||||
.init = tegra186_cpufreq_init,
|
||||
};
|
||||
|
||||
static struct cpufreq_frequency_table *init_vhint_table(
|
||||
static struct cpufreq_frequency_table *tegra_cpufreq_bpmp_read_lut(
|
||||
struct platform_device *pdev, struct tegra_bpmp *bpmp,
|
||||
struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id,
|
||||
int *num_rates)
|
||||
@@ -229,6 +351,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data;
|
||||
struct tegra_bpmp *bpmp;
|
||||
struct device *cpu_dev;
|
||||
unsigned int i = 0, err, edvd_offset;
|
||||
int num_rates = 0;
|
||||
u32 edvd_val, cpu;
|
||||
@@ -254,9 +377,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
|
||||
cluster->table = init_vhint_table(pdev, bpmp, cluster, i, &num_rates);
|
||||
if (IS_ERR(cluster->table)) {
|
||||
err = PTR_ERR(cluster->table);
|
||||
cluster->bpmp_lut = tegra_cpufreq_bpmp_read_lut(pdev, bpmp, cluster, i, &num_rates);
|
||||
if (IS_ERR(cluster->bpmp_lut)) {
|
||||
err = PTR_ERR(cluster->bpmp_lut);
|
||||
goto put_bpmp;
|
||||
} else if (!num_rates) {
|
||||
err = -EINVAL;
|
||||
@@ -265,7 +388,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
||||
|
||||
for (cpu = 0; cpu < ARRAY_SIZE(tegra186_cpus); cpu++) {
|
||||
if (data->cpus[cpu].bpmp_cluster_id == i) {
|
||||
edvd_val = cluster->table[num_rates - 1].driver_data;
|
||||
edvd_val = cluster->bpmp_lut[num_rates - 1].driver_data;
|
||||
edvd_offset = data->cpus[cpu].edvd_offset;
|
||||
writel(edvd_val, data->regs + edvd_offset);
|
||||
}
|
||||
@@ -274,6 +397,19 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
||||
|
||||
tegra186_cpufreq_driver.driver_data = data;
|
||||
|
||||
/* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
|
||||
cpu_dev = get_cpu_device(0);
|
||||
if (!cpu_dev) {
|
||||
err = -EPROBE_DEFER;
|
||||
goto put_bpmp;
|
||||
}
|
||||
|
||||
if (dev_pm_opp_of_get_opp_desc_node(cpu_dev)) {
|
||||
err = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
|
||||
if (!err)
|
||||
data->icc_dram_bw_scaling = true;
|
||||
}
|
||||
|
||||
err = cpufreq_register_driver(&tegra186_cpufreq_driver);
|
||||
|
||||
put_bpmp:
|
||||
|
||||
@@ -750,7 +750,8 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(bpmp))
|
||||
return PTR_ERR(bpmp);
|
||||
|
||||
read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1);
|
||||
read_counters_wq = alloc_workqueue("read_counters_wq",
|
||||
__WQ_LEGACY | WQ_PERCPU, 1);
|
||||
if (!read_counters_wq) {
|
||||
dev_err(&pdev->dev, "fail to create_workqueue\n");
|
||||
err = -EINVAL;
|
||||
|
||||
Reference in New Issue
Block a user