Skip to content

Commit

Permalink
cpufreq: qcom: Add support for krait based socs
Browse files Browse the repository at this point in the history
In Certain QCOM SoCs like ipq8064, apq8064, msm8960, msm8974
that has KRAIT processors the voltage/current value of each OPP
varies based on the silicon variant in use.

The required OPP related data is determined based on
the efuse value. This is similar to the existing code for
kryo cores. So adding support for krait cores here.

Signed-off-by: Sricharan R <sricharan@codeaurora.org>
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
  • Loading branch information
Ansuel Smith authored and Viresh Kumar committed Mar 20, 2020
1 parent 74a189e commit a8811ec
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 18 deletions.
3 changes: 2 additions & 1 deletion Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ In 'cpu' nodes:

In 'operating-points-v2' table:
- compatible: Should be
- 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.
- 'operating-points-v2-kryo-cpu' for apq8096, msm8996, msm8974,
apq8064, ipq8064, msm8960 and ipq8074.

Optional properties:
--------------------
Expand Down
2 changes: 1 addition & 1 deletion drivers/cpufreq/Kconfig.arm
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ config ARM_OMAP2PLUS_CPUFREQ

config ARM_QCOM_CPUFREQ_NVMEM
tristate "Qualcomm nvmem based CPUFreq"
depends on ARM64
depends on ARCH_QCOM
depends on QCOM_QFPROM
depends on QCOM_SMEM
select PM_OPP
Expand Down
5 changes: 5 additions & 0 deletions drivers/cpufreq/cpufreq-dt-platdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "ti,dra7", },
{ .compatible = "ti,omap3", },

{ .compatible = "qcom,ipq8064", },
{ .compatible = "qcom,apq8064", },
{ .compatible = "qcom,msm8974", },
{ .compatible = "qcom,msm8960", },

{ }
};

Expand Down
191 changes: 175 additions & 16 deletions drivers/cpufreq/qcom-cpufreq-nvmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,99 @@ struct qcom_cpufreq_drv;
struct qcom_cpufreq_match_data {
int (*get_version)(struct device *cpu_dev,
struct nvmem_cell *speedbin_nvmem,
char **pvs_name,
struct qcom_cpufreq_drv *drv);
const char **genpd_names;
};

struct qcom_cpufreq_drv {
struct opp_table **opp_tables;
struct opp_table **names_opp_tables;
struct opp_table **hw_opp_tables;
struct opp_table **genpd_opp_tables;
u32 versions;
const struct qcom_cpufreq_match_data *data;
};

static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;

static void get_krait_bin_format_a(struct device *cpu_dev,
int *speed, int *pvs, int *pvs_ver,
struct nvmem_cell *pvs_nvmem, u8 *buf)
{
u32 pte_efuse;

pte_efuse = *((u32 *)buf);

*speed = pte_efuse & 0xf;
if (*speed == 0xf)
*speed = (pte_efuse >> 4) & 0xf;

if (*speed == 0xf) {
*speed = 0;
dev_warn(cpu_dev, "Speed bin: Defaulting to %d\n", *speed);
} else {
dev_dbg(cpu_dev, "Speed bin: %d\n", *speed);
}

*pvs = (pte_efuse >> 10) & 0x7;
if (*pvs == 0x7)
*pvs = (pte_efuse >> 13) & 0x7;

if (*pvs == 0x7) {
*pvs = 0;
dev_warn(cpu_dev, "PVS bin: Defaulting to %d\n", *pvs);
} else {
dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
}
}

static void get_krait_bin_format_b(struct device *cpu_dev,
int *speed, int *pvs, int *pvs_ver,
struct nvmem_cell *pvs_nvmem, u8 *buf)
{
u32 pte_efuse, redundant_sel;

pte_efuse = *((u32 *)buf);
redundant_sel = (pte_efuse >> 24) & 0x7;

*pvs_ver = (pte_efuse >> 4) & 0x3;

switch (redundant_sel) {
case 1:
*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
*speed = (pte_efuse >> 27) & 0xf;
break;
case 2:
*pvs = (pte_efuse >> 27) & 0xf;
*speed = pte_efuse & 0x7;
break;
default:
/* 4 bits of PVS are in efuse register bits 31, 8-6. */
*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
*speed = pte_efuse & 0x7;
}

/* Check SPEED_BIN_BLOW_STATUS */
if (pte_efuse & BIT(3)) {
dev_dbg(cpu_dev, "Speed bin: %d\n", *speed);
} else {
dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!\n");
*speed = 0;
}

/* Check PVS_BLOW_STATUS */
pte_efuse = *(((u32 *)buf) + 4);
pte_efuse &= BIT(21);
if (pte_efuse) {
dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
} else {
dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!\n");
*pvs = 0;
}

dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver);
}

static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
{
size_t len;
Expand Down Expand Up @@ -93,11 +173,13 @@ static enum _msm8996_version qcom_cpufreq_get_msm_id(void)

static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
struct nvmem_cell *speedbin_nvmem,
char **pvs_name,
struct qcom_cpufreq_drv *drv)
{
size_t len;
u8 *speedbin;
enum _msm8996_version msm8996_version;
*pvs_name = NULL;

msm8996_version = qcom_cpufreq_get_msm_id();
if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
Expand Down Expand Up @@ -125,10 +207,51 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
return 0;
}

static int qcom_cpufreq_krait_name_version(struct device *cpu_dev,
struct nvmem_cell *speedbin_nvmem,
char **pvs_name,
struct qcom_cpufreq_drv *drv)
{
int speed = 0, pvs = 0, pvs_ver = 0;
u8 *speedbin;
size_t len;

speedbin = nvmem_cell_read(speedbin_nvmem, &len);

if (IS_ERR(speedbin))
return PTR_ERR(speedbin);

switch (len) {
case 4:
get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver,
speedbin_nvmem, speedbin);
break;
case 8:
get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver,
speedbin_nvmem, speedbin);
break;
default:
dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n");
return -ENODEV;
}

snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d",
speed, pvs, pvs_ver);

drv->versions = (1 << speed);

kfree(speedbin);
return 0;
}

static const struct qcom_cpufreq_match_data match_data_kryo = {
.get_version = qcom_cpufreq_kryo_name_version,
};

static const struct qcom_cpufreq_match_data match_data_krait = {
.get_version = qcom_cpufreq_krait_name_version,
};

static const char *qcs404_genpd_names[] = { "cpr", NULL };

static const struct qcom_cpufreq_match_data match_data_qcs404 = {
Expand All @@ -141,6 +264,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
struct nvmem_cell *speedbin_nvmem;
struct device_node *np;
struct device *cpu_dev;
char *pvs_name = "speedXX-pvsXX-vXX";
unsigned cpu;
const struct of_device_id *match;
int ret;
Expand All @@ -153,7 +277,7 @@ 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-qcom-cpu");
if (!ret) {
of_node_put(np);
return -ENOENT;
Expand Down Expand Up @@ -181,7 +305,8 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
goto free_drv;
}

ret = drv->data->get_version(cpu_dev, speedbin_nvmem, drv);
ret = drv->data->get_version(cpu_dev,
speedbin_nvmem, &pvs_name, drv);
if (ret) {
nvmem_cell_put(speedbin_nvmem);
goto free_drv;
Expand All @@ -190,12 +315,20 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
}
of_node_put(np);

drv->opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables),
drv->names_opp_tables = kcalloc(num_possible_cpus(),
sizeof(*drv->names_opp_tables),
GFP_KERNEL);
if (!drv->opp_tables) {
if (!drv->names_opp_tables) {
ret = -ENOMEM;
goto free_drv;
}
drv->hw_opp_tables = kcalloc(num_possible_cpus(),
sizeof(*drv->hw_opp_tables),
GFP_KERNEL);
if (!drv->hw_opp_tables) {
ret = -ENOMEM;
goto free_opp_names;
}

drv->genpd_opp_tables = kcalloc(num_possible_cpus(),
sizeof(*drv->genpd_opp_tables),
Expand All @@ -213,11 +346,23 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
}

if (drv->data->get_version) {
drv->opp_tables[cpu] =
dev_pm_opp_set_supported_hw(cpu_dev,
&drv->versions, 1);
if (IS_ERR(drv->opp_tables[cpu])) {
ret = PTR_ERR(drv->opp_tables[cpu]);

if (pvs_name) {
drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name(
cpu_dev,
pvs_name);
if (IS_ERR(drv->names_opp_tables[cpu])) {
ret = PTR_ERR(drv->names_opp_tables[cpu]);
dev_err(cpu_dev, "Failed to add OPP name %s\n",
pvs_name);
goto free_opp;
}
}

drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw(
cpu_dev, &drv->versions, 1);
if (IS_ERR(drv->hw_opp_tables[cpu])) {
ret = PTR_ERR(drv->hw_opp_tables[cpu]);
dev_err(cpu_dev,
"Failed to set supported hardware\n");
goto free_genpd_opp;
Expand Down Expand Up @@ -259,11 +404,18 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
kfree(drv->genpd_opp_tables);
free_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(drv->opp_tables[cpu]))
if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu]))
break;
dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
}
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu]))
break;
dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
}
kfree(drv->opp_tables);
kfree(drv->hw_opp_tables);
free_opp_names:
kfree(drv->names_opp_tables);
free_drv:
kfree(drv);

Expand All @@ -278,13 +430,16 @@ static int qcom_cpufreq_remove(struct platform_device *pdev)
platform_device_unregister(cpufreq_dt_pdev);

for_each_possible_cpu(cpu) {
if (drv->opp_tables[cpu])
dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);
if (drv->names_opp_tables[cpu])
dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
if (drv->hw_opp_tables[cpu])
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
if (drv->genpd_opp_tables[cpu])
dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
}

kfree(drv->opp_tables);
kfree(drv->names_opp_tables);
kfree(drv->hw_opp_tables);
kfree(drv->genpd_opp_tables);
kfree(drv);

Expand All @@ -303,6 +458,10 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
{ .compatible = "qcom,apq8096", .data = &match_data_kryo },
{ .compatible = "qcom,msm8996", .data = &match_data_kryo },
{ .compatible = "qcom,qcs404", .data = &match_data_qcs404 },
{ .compatible = "qcom,ipq8064", .data = &match_data_krait },
{ .compatible = "qcom,apq8064", .data = &match_data_krait },
{ .compatible = "qcom,msm8974", .data = &match_data_krait },
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
{},
};

Expand Down

0 comments on commit a8811ec

Please sign in to comment.