ARM cpufreq updates for 6.7
- Add support for several Qualcomm SoC versions and other similar changes (Christian Marangi, Dmitry Baryshkov, Luca Weiss, Neil Armstrong, Richard Acayan, Robert Marko, Rohit Agarwal, Stephan Gerhold and Varadarajan Narayanan). - Cleanups to the tegra cpufreq driver (Sumit Gupta). - Use of_property_read_reg() to parse "reg" in pmac32 driver (Rob Herring). - Add support for TI's am62p5 Soc (Bryan Brattlof). - Make ARM_BRCMSTB_AVS_CPUFREQ depends on !ARM_SCMI_CPUFREQ (Florian Fainelli). - Update Kconfig to mention i.MX7 as well (Alexander Stein). -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEx73Crsp7f6M6scA70rkcPK6BEhwFAmU47hsACgkQ0rkcPK6B EhxAhxAAitJXDgRgeeKin6JUKClW8vJXgCZLcwDMIn8ehQExvxggmq9D/jP5sxHQ wQ0P5JSGG+slrBFCey15Qz/NYpF72o70htTXdV4pmcurYNWe1dKCL/bWcgY0vdQh muEd4vU0gKC4IRBE0T4dOZw3svUA++PRvXa2WStGgQ9Cqiil2mvpZxaJANbjCnFA QerAmcgbnvHmeEmNU40h34hoUeQL86rs2YZLGqWh3PE/igM587emFUXFC+YXh/j2 7awET+ITuTlgcadoYtDgfz7j0jHYHDni0DpDr4Ous6i0djPKBuyS/bgEVru+9UHj WgBBAWKrW/aQ2RsgdMAZL8LRnC+dciw0cqa6q7b+PcVSUYdfFETalGZtC1VOLdO3 BLS6xTqtGRL8gZNxMA3LMGMBe3N1tJTONJ9x89S+3nhI56PK2I0uLcm9ZcLU+oyC 8IihLv6YM9gUNozrR2EkQOq+gqfPi2bC7ZQpyNlbJ2TPxELoA91r8N2wA71BwM+K f2iiwL8NDL/cqgWVEe/4j88Xy+DzOSKOYfncMEfupeiMlaT8yi/+SyxUOsVh2mYj 3kkgS2iDdUzAJnxGPh2R4fpaaYRajgGlwl/agwLcg3EbvO44giT7CWmrjhweDTI4 m45FzRbB8O0a3QzjqXyGUnj37ZOAye0z2d6XITITNbZHaedpZjQ= =DwUn -----END PGP SIGNATURE----- Merge tag 'cpufreq-arm-updates-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm Merge ARM cpufreq updates for 6.7 from Viresh Kumar: "- Add support for several Qualcomm SoC versions and other similar changes (Christian Marangi, Dmitry Baryshkov, Luca Weiss, Neil Armstrong, Richard Acayan, Robert Marko, Rohit Agarwal, Stephan Gerhold and Varadarajan Narayanan). - Cleanups to the tegra cpufreq driver (Sumit Gupta). - Use of_property_read_reg() to parse "reg" in pmac32 driver (Rob Herring). - Add support for TI's am62p5 Soc (Bryan Brattlof). - Make ARM_BRCMSTB_AVS_CPUFREQ depends on !ARM_SCMI_CPUFREQ (Florian Fainelli). - Update Kconfig to mention i.MX7 as well (Alexander Stein)." * tag 'cpufreq-arm-updates-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: (25 commits) dt-bindings: cpufreq: qcom-hw: document SM8650 CPUFREQ Hardware cpufreq: arm: Kconfig: Add i.MX7 to supported SoC for ARM_IMX_CPUFREQ_DT cpufreq: qcom-nvmem: add support for IPQ8064 cpufreq: qcom-nvmem: also accept operating-points-v2-krait-cpu cpufreq: qcom-nvmem: drop pvs_ver for format a fuses dt-bindings: cpufreq: qcom-cpufreq-nvmem: Document krait-cpu cpufreq: qcom-nvmem: add support for IPQ6018 dt-bindings: cpufreq: qcom-cpufreq-nvmem: document IPQ6018 cpufreq: qcom-nvmem: Add MSM8909 cpufreq: qcom-nvmem: Simplify driver data allocation dt-bindings: cpufreq: cpufreq-qcom-hw: Add SDX75 compatible cpufreq: ARM_BRCMSTB_AVS_CPUFREQ cannot be used with ARM_SCMI_CPUFREQ cpufreq: ti-cpufreq: Add opp support for am62p5 SoCs cpufreq: dt-platdev: add am62p5 to blocklist cpufreq: tegra194: remove redundant AND with cpu_online_mask cpufreq: tegra194: use refclk delta based loop instead of udelay cpufreq: tegra194: save CPU data to avoid repeated SMP calls dt-bindings: cpufreq: cpufreq-qcom-hw: add SDM670 compatible dt-bindings: cpufreq: qcom-nvmem: Document MSM8909 cpufreq: dt: platdev: Add MSM8909 to blocklist ...
This commit is contained in:
commit
7c35584899
@ -23,6 +23,7 @@ properties:
|
||||
- enum:
|
||||
- qcom,qcm2290-cpufreq-hw
|
||||
- qcom,sc7180-cpufreq-hw
|
||||
- qcom,sdm670-cpufreq-hw
|
||||
- qcom,sdm845-cpufreq-hw
|
||||
- qcom,sm6115-cpufreq-hw
|
||||
- qcom,sm6350-cpufreq-hw
|
||||
@ -36,11 +37,13 @@ properties:
|
||||
- qcom,sa8775p-cpufreq-epss
|
||||
- qcom,sc7280-cpufreq-epss
|
||||
- qcom,sc8280xp-cpufreq-epss
|
||||
- qcom,sdx75-cpufreq-epss
|
||||
- qcom,sm6375-cpufreq-epss
|
||||
- qcom,sm8250-cpufreq-epss
|
||||
- qcom,sm8350-cpufreq-epss
|
||||
- qcom,sm8450-cpufreq-epss
|
||||
- qcom,sm8550-cpufreq-epss
|
||||
- qcom,sm8650-cpufreq-epss
|
||||
- const: qcom,cpufreq-epss
|
||||
|
||||
reg:
|
||||
@ -128,6 +131,7 @@ allOf:
|
||||
- qcom,qdu1000-cpufreq-epss
|
||||
- qcom,sc7180-cpufreq-hw
|
||||
- qcom,sc8280xp-cpufreq-epss
|
||||
- qcom,sdm670-cpufreq-hw
|
||||
- qcom,sdm845-cpufreq-hw
|
||||
- qcom,sm6115-cpufreq-hw
|
||||
- qcom,sm6350-cpufreq-hw
|
||||
|
@ -27,8 +27,12 @@ select:
|
||||
enum:
|
||||
- qcom,apq8064
|
||||
- qcom,apq8096
|
||||
- qcom,ipq5332
|
||||
- qcom,ipq6018
|
||||
- qcom,ipq8064
|
||||
- qcom,ipq8074
|
||||
- qcom,ipq9574
|
||||
- qcom,msm8909
|
||||
- qcom,msm8939
|
||||
- qcom,msm8960
|
||||
- qcom,msm8974
|
||||
@ -43,7 +47,9 @@ patternProperties:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: operating-points-v2-kryo-cpu
|
||||
enum:
|
||||
- operating-points-v2-krait-cpu
|
||||
- operating-points-v2-kryo-cpu
|
||||
then:
|
||||
$ref: /schemas/opp/opp-v2-kryo-cpu.yaml#
|
||||
|
||||
|
@ -90,7 +90,7 @@ config ARM_VEXPRESS_SPC_CPUFREQ
|
||||
|
||||
config ARM_BRCMSTB_AVS_CPUFREQ
|
||||
tristate "Broadcom STB AVS CPUfreq driver"
|
||||
depends on ARCH_BRCMSTB || COMPILE_TEST
|
||||
depends on (ARCH_BRCMSTB && !ARM_SCMI_CPUFREQ) || COMPILE_TEST
|
||||
default y
|
||||
help
|
||||
Some Broadcom STB SoCs use a co-processor running proprietary firmware
|
||||
@ -124,8 +124,8 @@ config ARM_IMX_CPUFREQ_DT
|
||||
tristate "Freescale i.MX8M cpufreq support"
|
||||
depends on ARCH_MXC && CPUFREQ_DT
|
||||
help
|
||||
This adds cpufreq driver support for Freescale i.MX8M series SoCs,
|
||||
based on cpufreq-dt.
|
||||
This adds cpufreq driver support for Freescale i.MX7/i.MX8M
|
||||
series SoCs, based on cpufreq-dt.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
|
@ -142,9 +142,11 @@ static const struct of_device_id blocklist[] __initconst = {
|
||||
{ .compatible = "nvidia,tegra234", },
|
||||
|
||||
{ .compatible = "qcom,apq8096", },
|
||||
{ .compatible = "qcom,msm8909", },
|
||||
{ .compatible = "qcom,msm8996", },
|
||||
{ .compatible = "qcom,msm8998", },
|
||||
{ .compatible = "qcom,qcm2290", },
|
||||
{ .compatible = "qcom,qcm6490", },
|
||||
{ .compatible = "qcom,qcs404", },
|
||||
{ .compatible = "qcom,qdu1000", },
|
||||
{ .compatible = "qcom,sa8155p" },
|
||||
@ -176,7 +178,9 @@ static const struct of_device_id blocklist[] __initconst = {
|
||||
{ .compatible = "ti,omap3", },
|
||||
{ .compatible = "ti,am625", },
|
||||
{ .compatible = "ti,am62a7", },
|
||||
{ .compatible = "ti,am62p5", },
|
||||
|
||||
{ .compatible = "qcom,ipq6018", },
|
||||
{ .compatible = "qcom,ipq8064", },
|
||||
{ .compatible = "qcom,apq8064", },
|
||||
{ .compatible = "qcom,msm8974", },
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/irq.h>
|
||||
@ -378,10 +379,9 @@ static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
|
||||
static u32 read_gpio(struct device_node *np)
|
||||
{
|
||||
const u32 *reg = of_get_property(np, "reg", NULL);
|
||||
u32 offset;
|
||||
u64 offset;
|
||||
|
||||
if (reg == NULL)
|
||||
if (of_property_read_reg(np, 0, &offset, NULL) < 0)
|
||||
return 0;
|
||||
/* That works for all keylargos but shall be fixed properly
|
||||
* some day... The problem is that it seems we can't rely
|
||||
@ -389,7 +389,6 @@ static u32 read_gpio(struct device_node *np)
|
||||
* relative to the base of KeyLargo or to the base of the
|
||||
* GPIO space, and the device-tree doesn't help.
|
||||
*/
|
||||
offset = *reg;
|
||||
if (offset < KEYLARGO_GPIO_LEVELS0)
|
||||
offset += KEYLARGO_GPIO_LEVELS0;
|
||||
return offset;
|
||||
|
@ -30,6 +30,14 @@
|
||||
|
||||
#include <dt-bindings/arm/qcom,ids.h>
|
||||
|
||||
enum ipq806x_versions {
|
||||
IPQ8062_VERSION = 0,
|
||||
IPQ8064_VERSION,
|
||||
IPQ8065_VERSION,
|
||||
};
|
||||
|
||||
#define IPQ6000_VERSION BIT(2)
|
||||
|
||||
struct qcom_cpufreq_drv;
|
||||
|
||||
struct qcom_cpufreq_match_data {
|
||||
@ -40,16 +48,38 @@ struct qcom_cpufreq_match_data {
|
||||
const char **genpd_names;
|
||||
};
|
||||
|
||||
struct qcom_cpufreq_drv_cpu {
|
||||
int opp_token;
|
||||
};
|
||||
|
||||
struct qcom_cpufreq_drv {
|
||||
int *opp_tokens;
|
||||
u32 versions;
|
||||
const struct qcom_cpufreq_match_data *data;
|
||||
struct qcom_cpufreq_drv_cpu cpus[];
|
||||
};
|
||||
|
||||
static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
|
||||
|
||||
static int qcom_cpufreq_simple_get_version(struct device *cpu_dev,
|
||||
struct nvmem_cell *speedbin_nvmem,
|
||||
char **pvs_name,
|
||||
struct qcom_cpufreq_drv *drv)
|
||||
{
|
||||
u8 *speedbin;
|
||||
|
||||
*pvs_name = NULL;
|
||||
speedbin = nvmem_cell_read(speedbin_nvmem, NULL);
|
||||
if (IS_ERR(speedbin))
|
||||
return PTR_ERR(speedbin);
|
||||
|
||||
dev_dbg(cpu_dev, "speedbin: %d\n", *speedbin);
|
||||
drv->versions = 1 << *speedbin;
|
||||
kfree(speedbin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_krait_bin_format_a(struct device *cpu_dev,
|
||||
int *speed, int *pvs, int *pvs_ver,
|
||||
int *speed, int *pvs,
|
||||
u8 *buf)
|
||||
{
|
||||
u32 pte_efuse;
|
||||
@ -180,8 +210,7 @@ static int qcom_cpufreq_krait_name_version(struct device *cpu_dev,
|
||||
|
||||
switch (len) {
|
||||
case 4:
|
||||
get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver,
|
||||
speedbin);
|
||||
get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin);
|
||||
break;
|
||||
case 8:
|
||||
get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver,
|
||||
@ -203,6 +232,114 @@ len_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 speed = 0, pvs = 0;
|
||||
int msm_id, ret = 0;
|
||||
u8 *speedbin;
|
||||
size_t len;
|
||||
|
||||
speedbin = nvmem_cell_read(speedbin_nvmem, &len);
|
||||
if (IS_ERR(speedbin))
|
||||
return PTR_ERR(speedbin);
|
||||
|
||||
if (len != 4) {
|
||||
dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n");
|
||||
ret = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin);
|
||||
|
||||
ret = qcom_smem_get_soc_id(&msm_id);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
switch (msm_id) {
|
||||
case QCOM_ID_IPQ8062:
|
||||
drv->versions = BIT(IPQ8062_VERSION);
|
||||
break;
|
||||
case QCOM_ID_IPQ8064:
|
||||
case QCOM_ID_IPQ8066:
|
||||
case QCOM_ID_IPQ8068:
|
||||
drv->versions = BIT(IPQ8064_VERSION);
|
||||
break;
|
||||
case QCOM_ID_IPQ8065:
|
||||
case QCOM_ID_IPQ8069:
|
||||
drv->versions = BIT(IPQ8065_VERSION);
|
||||
break;
|
||||
default:
|
||||
dev_err(cpu_dev,
|
||||
"SoC ID %u is not part of IPQ8064 family, limiting to 1.0GHz!\n",
|
||||
msm_id);
|
||||
drv->versions = BIT(IPQ8062_VERSION);
|
||||
break;
|
||||
}
|
||||
|
||||
/* IPQ8064 speed is never fused. Only pvs values are fused. */
|
||||
snprintf(*pvs_name, sizeof("speed0-pvsXX"), "speed0-pvs%d", pvs);
|
||||
|
||||
exit:
|
||||
kfree(speedbin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_cpufreq_ipq6018_name_version(struct device *cpu_dev,
|
||||
struct nvmem_cell *speedbin_nvmem,
|
||||
char **pvs_name,
|
||||
struct qcom_cpufreq_drv *drv)
|
||||
{
|
||||
u32 msm_id;
|
||||
int ret;
|
||||
u8 *speedbin;
|
||||
*pvs_name = NULL;
|
||||
|
||||
ret = qcom_smem_get_soc_id(&msm_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
speedbin = nvmem_cell_read(speedbin_nvmem, NULL);
|
||||
if (IS_ERR(speedbin))
|
||||
return PTR_ERR(speedbin);
|
||||
|
||||
switch (msm_id) {
|
||||
case QCOM_ID_IPQ6005:
|
||||
case QCOM_ID_IPQ6010:
|
||||
case QCOM_ID_IPQ6018:
|
||||
case QCOM_ID_IPQ6028:
|
||||
/* Fuse Value Freq BIT to set
|
||||
* ---------------------------------
|
||||
* 2’b0 No Limit BIT(0)
|
||||
* 2’b1 1.5 GHz BIT(1)
|
||||
*/
|
||||
drv->versions = 1 << (unsigned int)(*speedbin);
|
||||
break;
|
||||
case QCOM_ID_IPQ6000:
|
||||
/*
|
||||
* IPQ6018 family only has one bit to advertise the CPU
|
||||
* speed-bin, but that is not enough for IPQ6000 which
|
||||
* is only rated up to 1.2GHz.
|
||||
* So for IPQ6000 manually set BIT(2) based on SMEM ID.
|
||||
*/
|
||||
drv->versions = IPQ6000_VERSION;
|
||||
break;
|
||||
default:
|
||||
dev_err(cpu_dev,
|
||||
"SoC ID %u is not part of IPQ6018 family, limiting to 1.2GHz!\n",
|
||||
msm_id);
|
||||
drv->versions = IPQ6000_VERSION;
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(speedbin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *generic_genpd_names[] = { "perf", NULL };
|
||||
|
||||
static const struct qcom_cpufreq_match_data match_data_kryo = {
|
||||
.get_version = qcom_cpufreq_kryo_name_version,
|
||||
};
|
||||
@ -211,12 +348,25 @@ static const struct qcom_cpufreq_match_data match_data_krait = {
|
||||
.get_version = qcom_cpufreq_krait_name_version,
|
||||
};
|
||||
|
||||
static const struct qcom_cpufreq_match_data match_data_msm8909 = {
|
||||
.get_version = qcom_cpufreq_simple_get_version,
|
||||
.genpd_names = generic_genpd_names,
|
||||
};
|
||||
|
||||
static const char *qcs404_genpd_names[] = { "cpr", NULL };
|
||||
|
||||
static const struct qcom_cpufreq_match_data match_data_qcs404 = {
|
||||
.genpd_names = qcs404_genpd_names,
|
||||
};
|
||||
|
||||
static const struct qcom_cpufreq_match_data match_data_ipq6018 = {
|
||||
.get_version = qcom_cpufreq_ipq6018_name_version,
|
||||
};
|
||||
|
||||
static const struct qcom_cpufreq_match_data match_data_ipq8064 = {
|
||||
.get_version = qcom_cpufreq_ipq8064_name_version,
|
||||
};
|
||||
|
||||
static int qcom_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_cpufreq_drv *drv;
|
||||
@ -237,48 +387,39 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
|
||||
if (!np)
|
||||
return -ENOENT;
|
||||
|
||||
ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
|
||||
ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu") ||
|
||||
of_device_is_compatible(np, "operating-points-v2-krait-cpu");
|
||||
if (!ret) {
|
||||
of_node_put(np);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
drv = kzalloc(sizeof(*drv), GFP_KERNEL);
|
||||
drv = devm_kzalloc(&pdev->dev, struct_size(drv, cpus, num_possible_cpus()),
|
||||
GFP_KERNEL);
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
match = pdev->dev.platform_data;
|
||||
drv->data = match->data;
|
||||
if (!drv->data) {
|
||||
ret = -ENODEV;
|
||||
goto free_drv;
|
||||
}
|
||||
if (!drv->data)
|
||||
return -ENODEV;
|
||||
|
||||
if (drv->data->get_version) {
|
||||
speedbin_nvmem = of_nvmem_cell_get(np, NULL);
|
||||
if (IS_ERR(speedbin_nvmem)) {
|
||||
ret = dev_err_probe(cpu_dev, PTR_ERR(speedbin_nvmem),
|
||||
"Could not get nvmem cell\n");
|
||||
goto free_drv;
|
||||
}
|
||||
if (IS_ERR(speedbin_nvmem))
|
||||
return dev_err_probe(cpu_dev, PTR_ERR(speedbin_nvmem),
|
||||
"Could not get nvmem cell\n");
|
||||
|
||||
ret = drv->data->get_version(cpu_dev,
|
||||
speedbin_nvmem, &pvs_name, drv);
|
||||
if (ret) {
|
||||
nvmem_cell_put(speedbin_nvmem);
|
||||
goto free_drv;
|
||||
return ret;
|
||||
}
|
||||
nvmem_cell_put(speedbin_nvmem);
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
drv->opp_tokens = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tokens),
|
||||
GFP_KERNEL);
|
||||
if (!drv->opp_tokens) {
|
||||
ret = -ENOMEM;
|
||||
goto free_drv;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct dev_pm_opp_config config = {
|
||||
.supported_hw = NULL,
|
||||
@ -304,9 +445,9 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (config.supported_hw || config.genpd_names) {
|
||||
drv->opp_tokens[cpu] = dev_pm_opp_set_config(cpu_dev, &config);
|
||||
if (drv->opp_tokens[cpu] < 0) {
|
||||
ret = drv->opp_tokens[cpu];
|
||||
drv->cpus[cpu].opp_token = dev_pm_opp_set_config(cpu_dev, &config);
|
||||
if (drv->cpus[cpu].opp_token < 0) {
|
||||
ret = drv->cpus[cpu].opp_token;
|
||||
dev_err(cpu_dev, "Failed to set OPP config\n");
|
||||
goto free_opp;
|
||||
}
|
||||
@ -325,11 +466,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
|
||||
|
||||
free_opp:
|
||||
for_each_possible_cpu(cpu)
|
||||
dev_pm_opp_clear_config(drv->opp_tokens[cpu]);
|
||||
kfree(drv->opp_tokens);
|
||||
free_drv:
|
||||
kfree(drv);
|
||||
|
||||
dev_pm_opp_clear_config(drv->cpus[cpu].opp_token);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -341,10 +478,7 @@ static void qcom_cpufreq_remove(struct platform_device *pdev)
|
||||
platform_device_unregister(cpufreq_dt_pdev);
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
dev_pm_opp_clear_config(drv->opp_tokens[cpu]);
|
||||
|
||||
kfree(drv->opp_tokens);
|
||||
kfree(drv);
|
||||
dev_pm_opp_clear_config(drv->cpus[cpu].opp_token);
|
||||
}
|
||||
|
||||
static struct platform_driver qcom_cpufreq_driver = {
|
||||
@ -357,9 +491,11 @@ static struct platform_driver qcom_cpufreq_driver = {
|
||||
|
||||
static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
|
||||
{ .compatible = "qcom,apq8096", .data = &match_data_kryo },
|
||||
{ .compatible = "qcom,msm8909", .data = &match_data_msm8909 },
|
||||
{ .compatible = "qcom,msm8996", .data = &match_data_kryo },
|
||||
{ .compatible = "qcom,qcs404", .data = &match_data_qcs404 },
|
||||
{ .compatible = "qcom,ipq8064", .data = &match_data_krait },
|
||||
{ .compatible = "qcom,ipq6018", .data = &match_data_ipq6018 },
|
||||
{ .compatible = "qcom,ipq8064", .data = &match_data_ipq8064 },
|
||||
{ .compatible = "qcom,apq8064", .data = &match_data_krait },
|
||||
{ .compatible = "qcom,msm8974", .data = &match_data_krait },
|
||||
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -21,10 +20,11 @@
|
||||
|
||||
#define KHZ 1000
|
||||
#define REF_CLK_MHZ 408 /* 408 MHz */
|
||||
#define US_DELAY 500
|
||||
#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)
|
||||
#define MAX_CNT ~0U
|
||||
|
||||
#define MAX_DELTA_KHZ 115200
|
||||
|
||||
#define NDIV_MASK 0x1FF
|
||||
|
||||
#define CORE_OFFSET(cpu) (cpu * 8)
|
||||
@ -39,6 +39,12 @@
|
||||
/* cpufreq transisition latency */
|
||||
#define TEGRA_CPUFREQ_TRANSITION_LATENCY (300 * 1000) /* unit in nanoseconds */
|
||||
|
||||
struct tegra_cpu_data {
|
||||
u32 cpuid;
|
||||
u32 clusterid;
|
||||
void __iomem *freq_core_reg;
|
||||
};
|
||||
|
||||
struct tegra_cpu_ctr {
|
||||
u32 cpu;
|
||||
u32 coreclk_cnt, last_coreclk_cnt;
|
||||
@ -62,6 +68,7 @@ struct tegra_cpufreq_soc {
|
||||
int maxcpus_per_cluster;
|
||||
unsigned int num_clusters;
|
||||
phys_addr_t actmon_cntr_base;
|
||||
u32 refclk_delta_min;
|
||||
};
|
||||
|
||||
struct tegra194_cpufreq_data {
|
||||
@ -69,6 +76,7 @@ struct tegra194_cpufreq_data {
|
||||
struct cpufreq_frequency_table **bpmp_luts;
|
||||
const struct tegra_cpufreq_soc *soc;
|
||||
bool icc_dram_bw_scaling;
|
||||
struct tegra_cpu_data *cpu_data;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *read_counters_wq;
|
||||
@ -116,14 +124,8 @@ static void tegra234_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
|
||||
static int tegra234_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
|
||||
{
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
void __iomem *freq_core_reg;
|
||||
u64 mpidr_id;
|
||||
|
||||
/* use physical id to get address of per core frequency register */
|
||||
mpidr_id = (clusterid * data->soc->maxcpus_per_cluster) + cpuid;
|
||||
freq_core_reg = SCRATCH_FREQ_CORE_REG(data, mpidr_id);
|
||||
|
||||
*ndiv = readl(freq_core_reg) & NDIV_MASK;
|
||||
*ndiv = readl(data->cpu_data[cpu].freq_core_reg) & NDIV_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -131,19 +133,10 @@ static int tegra234_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
|
||||
static void tegra234_set_cpu_ndiv(struct cpufreq_policy *policy, u64 ndiv)
|
||||
{
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
void __iomem *freq_core_reg;
|
||||
u32 cpu, cpuid, clusterid;
|
||||
u64 mpidr_id;
|
||||
u32 cpu;
|
||||
|
||||
for_each_cpu_and(cpu, policy->cpus, cpu_online_mask) {
|
||||
data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
|
||||
|
||||
/* use physical id to get address of per core frequency register */
|
||||
mpidr_id = (clusterid * data->soc->maxcpus_per_cluster) + cpuid;
|
||||
freq_core_reg = SCRATCH_FREQ_CORE_REG(data, mpidr_id);
|
||||
|
||||
writel(ndiv, freq_core_reg);
|
||||
}
|
||||
for_each_cpu(cpu, policy->cpus)
|
||||
writel(ndiv, data->cpu_data[cpu].freq_core_reg);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -157,19 +150,35 @@ static void tegra234_read_counters(struct tegra_cpu_ctr *c)
|
||||
{
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
void __iomem *actmon_reg;
|
||||
u32 cpuid, clusterid;
|
||||
u32 delta_refcnt;
|
||||
int cnt = 0;
|
||||
u64 val;
|
||||
|
||||
data->soc->ops->get_cpu_cluster_id(c->cpu, &cpuid, &clusterid);
|
||||
actmon_reg = CORE_ACTMON_CNTR_REG(data, clusterid, cpuid);
|
||||
actmon_reg = CORE_ACTMON_CNTR_REG(data, data->cpu_data[c->cpu].clusterid,
|
||||
data->cpu_data[c->cpu].cpuid);
|
||||
|
||||
val = readq(actmon_reg);
|
||||
c->last_refclk_cnt = upper_32_bits(val);
|
||||
c->last_coreclk_cnt = lower_32_bits(val);
|
||||
udelay(US_DELAY);
|
||||
val = readq(actmon_reg);
|
||||
c->refclk_cnt = upper_32_bits(val);
|
||||
c->coreclk_cnt = lower_32_bits(val);
|
||||
|
||||
/*
|
||||
* The sampling window is based on the minimum number of reference
|
||||
* clock cycles which is known to give a stable value of CPU frequency.
|
||||
*/
|
||||
do {
|
||||
val = readq(actmon_reg);
|
||||
c->refclk_cnt = upper_32_bits(val);
|
||||
c->coreclk_cnt = lower_32_bits(val);
|
||||
if (c->refclk_cnt < c->last_refclk_cnt)
|
||||
delta_refcnt = c->refclk_cnt + (MAX_CNT - c->last_refclk_cnt);
|
||||
else
|
||||
delta_refcnt = c->refclk_cnt - c->last_refclk_cnt;
|
||||
if (++cnt >= 0xFFFF) {
|
||||
pr_warn("cpufreq: problem with refclk on cpu:%d, delta_refcnt:%u, cnt:%d\n",
|
||||
c->cpu, delta_refcnt, cnt);
|
||||
break;
|
||||
}
|
||||
} while (delta_refcnt < data->soc->refclk_delta_min);
|
||||
}
|
||||
|
||||
static struct tegra_cpufreq_ops tegra234_cpufreq_ops = {
|
||||
@ -184,6 +193,7 @@ static const struct tegra_cpufreq_soc tegra234_cpufreq_soc = {
|
||||
.actmon_cntr_base = 0x9000,
|
||||
.maxcpus_per_cluster = 4,
|
||||
.num_clusters = 3,
|
||||
.refclk_delta_min = 16000,
|
||||
};
|
||||
|
||||
static const struct tegra_cpufreq_soc tegra239_cpufreq_soc = {
|
||||
@ -191,6 +201,7 @@ static const struct tegra_cpufreq_soc tegra239_cpufreq_soc = {
|
||||
.actmon_cntr_base = 0x4000,
|
||||
.maxcpus_per_cluster = 8,
|
||||
.num_clusters = 1,
|
||||
.refclk_delta_min = 16000,
|
||||
};
|
||||
|
||||
static void tegra194_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
|
||||
@ -231,15 +242,33 @@ static inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_response
|
||||
|
||||
static void tegra194_read_counters(struct tegra_cpu_ctr *c)
|
||||
{
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
u32 delta_refcnt;
|
||||
int cnt = 0;
|
||||
u64 val;
|
||||
|
||||
val = read_freq_feedback();
|
||||
c->last_refclk_cnt = lower_32_bits(val);
|
||||
c->last_coreclk_cnt = upper_32_bits(val);
|
||||
udelay(US_DELAY);
|
||||
val = read_freq_feedback();
|
||||
c->refclk_cnt = lower_32_bits(val);
|
||||
c->coreclk_cnt = upper_32_bits(val);
|
||||
|
||||
/*
|
||||
* The sampling window is based on the minimum number of reference
|
||||
* clock cycles which is known to give a stable value of CPU frequency.
|
||||
*/
|
||||
do {
|
||||
val = read_freq_feedback();
|
||||
c->refclk_cnt = lower_32_bits(val);
|
||||
c->coreclk_cnt = upper_32_bits(val);
|
||||
if (c->refclk_cnt < c->last_refclk_cnt)
|
||||
delta_refcnt = c->refclk_cnt + (MAX_CNT - c->last_refclk_cnt);
|
||||
else
|
||||
delta_refcnt = c->refclk_cnt - c->last_refclk_cnt;
|
||||
if (++cnt >= 0xFFFF) {
|
||||
pr_warn("cpufreq: problem with refclk on cpu:%d, delta_refcnt:%u, cnt:%d\n",
|
||||
c->cpu, delta_refcnt, cnt);
|
||||
break;
|
||||
}
|
||||
} while (delta_refcnt < data->soc->refclk_delta_min);
|
||||
}
|
||||
|
||||
static void tegra_read_counters(struct work_struct *work)
|
||||
@ -297,9 +326,8 @@ static unsigned int tegra194_calculate_speed(u32 cpu)
|
||||
u32 rate_mhz;
|
||||
|
||||
/*
|
||||
* udelay() is required to reconstruct cpu frequency over an
|
||||
* observation window. Using workqueue to call udelay() with
|
||||
* interrupts enabled.
|
||||
* Reconstruct cpu frequency over an observation/sampling window.
|
||||
* Using workqueue to keep interrupts enabled during the interval.
|
||||
*/
|
||||
read_counters_work.c.cpu = cpu;
|
||||
INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters);
|
||||
@ -357,19 +385,17 @@ static void tegra194_set_cpu_ndiv(struct cpufreq_policy *policy, u64 ndiv)
|
||||
static unsigned int tegra194_get_speed(u32 cpu)
|
||||
{
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
u32 clusterid = data->cpu_data[cpu].clusterid;
|
||||
struct cpufreq_frequency_table *pos;
|
||||
u32 cpuid, clusterid;
|
||||
unsigned int rate;
|
||||
u64 ndiv;
|
||||
int ret;
|
||||
|
||||
data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
|
||||
|
||||
/* reconstruct actual cpu freq using counters */
|
||||
rate = tegra194_calculate_speed(cpu);
|
||||
|
||||
/* get last written ndiv value */
|
||||
ret = data->soc->ops->get_cpu_ndiv(cpu, cpuid, clusterid, &ndiv);
|
||||
ret = data->soc->ops->get_cpu_ndiv(cpu, data->cpu_data[cpu].cpuid, clusterid, &ndiv);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return rate;
|
||||
|
||||
@ -383,9 +409,9 @@ static unsigned int tegra194_get_speed(u32 cpu)
|
||||
if (pos->driver_data != ndiv)
|
||||
continue;
|
||||
|
||||
if (abs(pos->frequency - rate) > 115200) {
|
||||
pr_warn("cpufreq: cpu%d,cur:%u,set:%u,set ndiv:%llu\n",
|
||||
cpu, rate, pos->frequency, ndiv);
|
||||
if (abs(pos->frequency - rate) > MAX_DELTA_KHZ) {
|
||||
pr_warn("cpufreq: cpu%d,cur:%u,set:%u,delta:%d,set ndiv:%llu\n",
|
||||
cpu, rate, pos->frequency, abs(rate - pos->frequency), ndiv);
|
||||
} else {
|
||||
rate = pos->frequency;
|
||||
}
|
||||
@ -450,6 +476,8 @@ static int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
|
||||
if (IS_ERR(opp))
|
||||
continue;
|
||||
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
ret = dev_pm_opp_enable(cpu_dev, pos->frequency * KHZ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -473,13 +501,12 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
int maxcpus_per_cluster = data->soc->maxcpus_per_cluster;
|
||||
u32 clusterid = data->cpu_data[policy->cpu].clusterid;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct cpufreq_frequency_table *bpmp_lut;
|
||||
u32 start_cpu, cpu;
|
||||
u32 clusterid;
|
||||
int ret;
|
||||
|
||||
data->soc->ops->get_cpu_cluster_id(policy->cpu, NULL, &clusterid);
|
||||
if (clusterid >= data->soc->num_clusters || !data->bpmp_luts[clusterid])
|
||||
return -EINVAL;
|
||||
|
||||
@ -578,6 +605,7 @@ static const struct tegra_cpufreq_soc tegra194_cpufreq_soc = {
|
||||
.ops = &tegra194_cpufreq_ops,
|
||||
.maxcpus_per_cluster = 2,
|
||||
.num_clusters = 4,
|
||||
.refclk_delta_min = 16000,
|
||||
};
|
||||
|
||||
static void tegra194_cpufreq_free_resources(void)
|
||||
@ -657,6 +685,28 @@ tegra_cpufreq_bpmp_read_lut(struct platform_device *pdev, struct tegra_bpmp *bpm
|
||||
return freq_table;
|
||||
}
|
||||
|
||||
static int tegra194_cpufreq_store_physids(unsigned int cpu, struct tegra194_cpufreq_data *data)
|
||||
{
|
||||
int num_cpus = data->soc->maxcpus_per_cluster * data->soc->num_clusters;
|
||||
u32 cpuid, clusterid;
|
||||
u64 mpidr_id;
|
||||
|
||||
if (cpu > (num_cpus - 1)) {
|
||||
pr_err("cpufreq: wrong num of cpus or clusters in soc data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
|
||||
|
||||
mpidr_id = (clusterid * data->soc->maxcpus_per_cluster) + cpuid;
|
||||
|
||||
data->cpu_data[cpu].cpuid = cpuid;
|
||||
data->cpu_data[cpu].clusterid = clusterid;
|
||||
data->cpu_data[cpu].freq_core_reg = SCRATCH_FREQ_CORE_REG(data, mpidr_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra194_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct tegra_cpufreq_soc *soc;
|
||||
@ -664,6 +714,7 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
|
||||
struct tegra_bpmp *bpmp;
|
||||
struct device *cpu_dev;
|
||||
int err, i;
|
||||
u32 cpu;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
@ -671,7 +722,7 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
|
||||
|
||||
soc = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (soc->ops && soc->maxcpus_per_cluster && soc->num_clusters) {
|
||||
if (soc->ops && soc->maxcpus_per_cluster && soc->num_clusters && soc->refclk_delta_min) {
|
||||
data->soc = soc;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "soc data missing\n");
|
||||
@ -690,6 +741,12 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(data->regs);
|
||||
}
|
||||
|
||||
data->cpu_data = devm_kcalloc(&pdev->dev, data->soc->num_clusters *
|
||||
data->soc->maxcpus_per_cluster,
|
||||
sizeof(*data->cpu_data), GFP_KERNEL);
|
||||
if (!data->cpu_data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
bpmp = tegra_bpmp_get(&pdev->dev);
|
||||
@ -711,6 +768,12 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
err = tegra194_cpufreq_store_physids(cpu, data);
|
||||
if (err)
|
||||
goto err_free_res;
|
||||
}
|
||||
|
||||
tegra194_cpufreq_driver.driver_data = data;
|
||||
|
||||
/* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
|
||||
|
@ -338,6 +338,7 @@ static const struct of_device_id ti_cpufreq_of_match[] = {
|
||||
{ .compatible = "ti,omap36xx", .data = &omap36xx_soc_data, },
|
||||
{ .compatible = "ti,am625", .data = &am625_soc_data, },
|
||||
{ .compatible = "ti,am62a7", .data = &am625_soc_data, },
|
||||
{ .compatible = "ti,am62p5", .data = &am625_soc_data, },
|
||||
/* legacy */
|
||||
{ .compatible = "ti,omap3430", .data = &omap34xx_soc_data, },
|
||||
{ .compatible = "ti,omap3630", .data = &omap36xx_soc_data, },
|
||||
|
Loading…
Reference in New Issue
Block a user