ipq806x: add patch to support krait cpu scaling driver
authorAnsuel Smith <ansuelsmth@gmail.com>
Sun, 15 Dec 2019 01:30:48 +0000 (02:30 +0100)
committerPetr Štetiar <ynezz@true.cz>
Thu, 26 Dec 2019 07:31:42 +0000 (08:31 +0100)
This patch has been proposed but never actually merged to
mainline. It was accepted but never re proposed by the
creator.
Rework it, fix kernel panic cause by double kfree.

Tested-by: Stefan Lippers-Hollmann <s.l-h@gmx.de> [nbg6817/ipq8065]
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
target/linux/ipq806x/patches-4.19/0038-qcom-cpufreq-nvmem-Add-support-for-krait-based-socs.patch [new file with mode: 0644]

diff --git a/target/linux/ipq806x/patches-4.19/0038-qcom-cpufreq-nvmem-Add-support-for-krait-based-socs.patch b/target/linux/ipq806x/patches-4.19/0038-qcom-cpufreq-nvmem-Add-support-for-krait-based-socs.patch
new file mode 100644 (file)
index 0000000..13e8ead
--- /dev/null
@@ -0,0 +1,351 @@
+Subject: [PATCH v12 14/14] cpufreq: qcom: Add support for krait based socs
+Date: Tue, 14 Aug 2018 17:42:33 +0530
+Message-Id: <1534248753-2440-15-git-send-email-sricharan@codeaurora.org>
+X-Mailer: git-send-email 1.9.1
+In-Reply-To: <1534248753-2440-1-git-send-email-sricharan@codeaurora.org>
+References: <1534248753-2440-1-git-send-email-sricharan@codeaurora.org>
+Sender: linux-kernel-owner@vger.kernel.org
+Precedence: bulk
+List-ID: <linux-kernel.vger.kernel.org>
+X-Mailing-List: linux-kernel@vger.kernel.org
+
+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>
+---
+ .../devicetree/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               | 151 +++++++++++++++++++--
+ 4 files changed, 149 insertions(+), 12 deletions(-)
+
+# diff --git a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
+# index 6dcdfcd..7bc0f1a 100644
+# --- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
+# +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
+# @@ -19,7 +19,8 @@ In 'cpus' nodes:
+#  In 'operating-points-v2' table:
+#  - compatible: Should be
+# -    - 'operating-points-v2-qcom-cpu' for apq8096 and msm8996.
+# +    - 'operating-points-v2-qcom-cpu' for apq8096, msm8996, msm8974,
+# +                                         apq8064, msm8960 and ipq8074.
+#  - nvmem-cells: A phandle pointing to a nvmem-cells node representing the
+#              efuse registers that has information about the
+#              speedbin that is used to select the right frequency/voltage
+diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
+index 13fbd97..497ae89 100644
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -126,7 +126,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 fe14c57..917cdc2 100644
+--- a/drivers/cpufreq/cpufreq-dt-platdev.c
++++ b/drivers/cpufreq/cpufreq-dt-platdev.c
+@@ -128,6 +128,11 @@
+       { .compatible = "ti,am43", },
+       { .compatible = "ti,dra7", },
++      { .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 0ad8e5b..5f2add0 100644
+--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
+@@ -48,17 +48,92 @@
+ 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);
+ };
+ struct qcom_cpufreq_drv {
+-      struct opp_table **opp_tables;
++      struct opp_table **opp_tables1;
++      struct opp_table **opp_tables2;
+       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(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;
++              pr_warn("Speed bin: Defaulting to %d\n", *speed);
++      } else {
++              pr_info("Speed bin: %d\n", *speed);
++      }
++
++      *pvs = (pte_efuse >> 10) & 0x7;
++      if (*pvs == 0x7)
++              *pvs = (pte_efuse >> 13) & 0x7;
++
++      if (*pvs == 0x7) {
++              *pvs = 0;
++              pr_warn("PVS bin: Defaulting to %d\n", *pvs);
++      } else {
++              pr_info("PVS bin: %d\n", *pvs);
++      }
++}
++
++static void get_krait_bin_format_b(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;
++      *speed = pte_efuse & 0x7;
++
++      /* 4 bits of PVS are in efuse register bits 31, 8-6. */
++      *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
++      *pvs_ver = (pte_efuse >> 4) & 0x3;
++
++      switch (redundant_sel) {
++      case 1:
++              *speed = (pte_efuse >> 27) & 0xf;
++              break;
++      case 2:
++              *pvs = (pte_efuse >> 27) & 0xf;
++              break;
++      }
++
++      /* Check SPEED_BIN_BLOW_STATUS */
++      if (pte_efuse & BIT(3)) {
++              pr_info("Speed bin: %d\n", *speed);
++      } else {
++              pr_warn("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) {
++              pr_info("PVS bin: %d\n", *pvs);
++      } else {
++              pr_warn("PVS bin not set. Defaulting to 0!\n");
++              *pvs = 0;
++      }
++
++      pr_info("PVS version: %d\n", *pvs_ver);
++}
++
+ static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
+ {
+       size_t len;
+@@ -90,11 +165,13 @@
+ 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) {
+@@ -122,16 +199,51 @@
+       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 (len == 4) {
++              get_krait_bin_format_a(&speed, &pvs, &pvs_ver,
++                                     speedbin_nvmem, speedbin);
++      } else if (len == 8) {
++              get_krait_bin_format_b(&speed, &pvs, &pvs_ver,
++                                     speedbin_nvmem, speedbin);
++      } else {
++              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 int qcom_cpufreq_probe(struct platform_device *pdev)
+ {
+       struct qcom_cpufreq_drv *drv;
+       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;
+@@ -144,7 +256,7 @@
+       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;
+@@ -172,7 +284,7 @@
+                       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;
+@@ -181,12 +293,18 @@
+       }
+       of_node_put(np);
+-      drv->opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables),
++      drv->opp_tables1 = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables1),
+                                 GFP_KERNEL);
+-      if (!drv->opp_tables) {
++      if (!drv->opp_tables1) {
+               ret = -ENOMEM;
+               goto free_drv;
+       }
++      drv->opp_tables2 = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables2),
++                                GFP_KERNEL);
++      if (!drv->opp_tables2) {
++              ret = -ENOMEM;
++              goto free_opp1;
++      }
+       for_each_possible_cpu(cpu) {
+               cpu_dev = get_cpu_device(cpu);
+@@ -196,11 +314,22 @@
+               }
+               if (drv->data->get_version) {
+-                      drv->opp_tables[cpu] =
+-                              dev_pm_opp_set_supported_hw(cpu_dev,
++
++                      if (pvs_name) {
++                              drv->opp_tables1[cpu] = dev_pm_opp_set_prop_name(cpu_dev,
++                                                                   pvs_name);
++                              if (IS_ERR(drv->opp_tables1[cpu])) {
++                                      ret = PTR_ERR(drv->opp_tables1[cpu]);
++                                      dev_err(cpu_dev, "Failed to add OPP name %s\n",
++                                              pvs_name);
++                                      goto free_opp;
++                              }
++                      }
++
++                      drv->opp_tables2[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 (IS_ERR(drv->opp_tables2[cpu])) {
++                              ret = PTR_ERR(drv->opp_tables2[cpu]);
+                               dev_err(cpu_dev,
+                                       "Failed to set supported hardware\n");
+                               goto free_opp;
+@@ -220,11 +349,18 @@
+ free_opp:
+       for_each_possible_cpu(cpu) {
+-              if (IS_ERR_OR_NULL(drv->opp_tables[cpu]))
++              if (IS_ERR_OR_NULL(drv->opp_tables1[cpu]))
+                       break;
+-              dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);
++              dev_pm_opp_put_prop_name(drv->opp_tables1[cpu]);
+       }
+-      kfree(drv->opp_tables);
++      for_each_possible_cpu(cpu) {
++              if (IS_ERR_OR_NULL(drv->opp_tables2[cpu]))
++                      break;
++              dev_pm_opp_put_supported_hw(drv->opp_tables2[cpu]);
++      }
++      kfree(drv->opp_tables2);
++free_opp1:
++      kfree(drv->opp_tables1);
+ free_drv:
+       kfree(drv);
+@@ -239,10 +375,14 @@
+       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->opp_tables1[cpu])
++                      dev_pm_opp_put_prop_name(drv->opp_tables1[cpu]);
++      for_each_possible_cpu(cpu)
++              if (drv->opp_tables2[cpu])
++                      dev_pm_opp_put_supported_hw(drv->opp_tables2[cpu]);
+-      kfree(drv->opp_tables);
++      kfree(drv->opp_tables1);
++      kfree(drv->opp_tables2);
+       kfree(drv);
+       return 0;
+@@ -259,6 +399,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,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 },
+       {},
+ };