diff mbox series

[v2] cpufreq: qcom: Add support for krait based socs

Message ID 20200313175213.8654-1-ansuelsmth@gmail.com (mailing list archive)
State New, archived
Delegated to: viresh kumar
Headers show
Series [v2] cpufreq: qcom: Add support for krait based socs | expand

Commit Message

Ansuel Smith March 13, 2020, 5:52 p.m. UTC
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>
---
v2:
* Use switch when possible 
* Change free_opp1 to free_opp_names
* Check if nvmem_cell_read fails

 .../bindings/opp/qcom-nvmem-cpufreq.txt       |   3 +-
 drivers/cpufreq/Kconfig.arm                   |   2 +-
 drivers/cpufreq/cpufreq-dt-platdev.c          |   5 +
 drivers/cpufreq/qcom-cpufreq-nvmem.c          | 191 ++++++++++++++++--
 4 files changed, 183 insertions(+), 18 deletions(-)

Comments

Ilia Lin March 13, 2020, 7:34 p.m. UTC | #1
Reviewed-by: Ilia Lin <ilia.lin@kernel.org>

On Fri, Mar 13, 2020 at 7:52 PM Ansuel Smith <ansuelsmth@gmail.com> wrote:
>
> 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>
> ---
> v2:
> * Use switch when possible
> * Change free_opp1 to free_opp_names
> * Check if nvmem_cell_read fails
>
>  .../bindings/opp/qcom-nvmem-cpufreq.txt       |   3 +-
>  drivers/cpufreq/Kconfig.arm                   |   2 +-
>  drivers/cpufreq/cpufreq-dt-platdev.c          |   5 +
>  drivers/cpufreq/qcom-cpufreq-nvmem.c          | 191 ++++++++++++++++--
>  4 files changed, 183 insertions(+), 18 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
> index 4751029b9b74..64f07417ecfb 100644
> --- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
> +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
> @@ -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:
>  --------------------
> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> index 3858d86cf409..15c1a1231516 100644
> --- a/drivers/cpufreq/Kconfig.arm
> +++ b/drivers/cpufreq/Kconfig.arm
> @@ -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
> diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
> index f2ae9cd455c1..cb9db16bea61 100644
> --- a/drivers/cpufreq/cpufreq-dt-platdev.c
> +++ b/drivers/cpufreq/cpufreq-dt-platdev.c
> @@ -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", },
> +
>         { }
>  };
>
> diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c
> index f0d2d5035413..a1b8238872a2 100644
> --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
> +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
> @@ -49,12 +49,14 @@ 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;
> @@ -62,6 +64,84 @@ struct qcom_cpufreq_drv {
>
>  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;
> @@ -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) {
> @@ -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 = {
> @@ -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;
> @@ -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;
> @@ -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;
> @@ -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),
> @@ -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;
> @@ -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);
>
> @@ -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);
>
> @@ -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 },
>         {},
>  };
>
> --
> 2.25.0
>
Viresh Kumar March 20, 2020, 4:30 a.m. UTC | #2
On 13-03-20, 21:34, Ilia Lin wrote:
> Reviewed-by: Ilia Lin <ilia.lin@kernel.org>

Applied. Thanks.
Loic Poulain April 17, 2020, 2:34 p.m. UTC | #3
Hi Viresh, Ansuel,



On Fri, 13 Mar 2020 at 18:52, Ansuel Smith <ansuelsmth@gmail.com> wrote:
>
> 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>
> ---

> --- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
> +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
> @@ -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.

> @@ -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");

This breaks backward compatibility and is not aligned with the
qcom-nvmem-cpufreq compatible binding.

Regards,
Loic
diff mbox series

Patch

diff --git a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
index 4751029b9b74..64f07417ecfb 100644
--- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
+++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
@@ -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:
 --------------------
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 3858d86cf409..15c1a1231516 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -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
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index f2ae9cd455c1..cb9db16bea61 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -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", },
+
 	{ }
 };
 
diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c
index f0d2d5035413..a1b8238872a2 100644
--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
@@ -49,12 +49,14 @@  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;
@@ -62,6 +64,84 @@  struct qcom_cpufreq_drv {
 
 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;
@@ -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) {
@@ -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 = {
@@ -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;
@@ -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;
@@ -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;
@@ -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),
@@ -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;
@@ -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);
 
@@ -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);
 
@@ -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 },
 	{},
 };