ipq806x: 5.15: add patch for specific Krait WFI cpuidle ipq806x-experimental
authorChristian Marangi <ansuelsmth@gmail.com>
Tue, 27 Sep 2022 14:32:35 +0000 (16:32 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Mon, 10 Oct 2022 08:53:24 +0000 (10:53 +0200)
Add required patch to add support for specific Krait WFI cpuidle state.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/ipq806x/config-5.15
target/linux/ipq806x/patches-5.15/107-15-ARM-dts-qcom-add-multiple-missing-binding-for-cpu.patch
target/linux/ipq806x/patches-5.15/124-cpuidle-qcom_spm-Detach-state-machine-from-main-SPM-.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/125-01-soc-qcom-spm-Add-ipq8064-CPU-data.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/125-02-ARM-mach-qcom-Add-krait-CPU-specific-idle.patch [new file with mode: 0644]
target/linux/ipq806x/patches-5.15/125-03-cpuidle-qcom-spm-add-krait-specific-wfi.patch [new file with mode: 0644]

index b2b3e601beb25a4dd56cecb7a45408b1b8f58dbd..2dff59b41e5fb1dd5b8d9dc00150876db6df1ea8 100644 (file)
@@ -6,6 +6,7 @@ CONFIG_ARCH_32BIT_OFF_T=y
 CONFIG_ARCH_HIBERNATION_POSSIBLE=y
 # CONFIG_ARCH_IPQ40XX is not set
 CONFIG_ARCH_KEEP_MEMBLOCK=y
+CONFIG_ARCH_KRAIT_IDLE=y
 # CONFIG_ARCH_MDM9615 is not set
 CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
 CONFIG_ARCH_MSM8960=y
index cf27aaa08b63b43f2853fe9f5929c9bef4019536..ec6cf5b03b5279059a2b747a5a9da022f3a499ad 100644 (file)
@@ -27,7 +27,7 @@ Tested-by: Jonathan McDowell <noodles@earth.li>
 +                      cooling-min-state = <0>;
 +                      cooling-max-state = <10>;
 +                      #cooling-cells = <2>;
-+                      cpu-idle-states = <&CPU_SPC>;
++                      cpu-idle-states = <&CPU_WFI>;
                };
  
                cpu1: cpu@1 {
@@ -43,16 +43,16 @@ Tested-by: Jonathan McDowell <noodles@earth.li>
 +                      cooling-min-state = <0>;
 +                      cooling-max-state = <10>;
 +                      #cooling-cells = <2>;
-+                      cpu-idle-states = <&CPU_SPC>;
++                      cpu-idle-states = <&CPU_WFI>;
 +              };
 +
 +              idle-states {
-+                      CPU_SPC: spc {
-+                              compatible = "qcom,idle-state-spc";
-+                              status = "disabled";
-+                              entry-latency-us = <400>;
-+                              exit-latency-us = <900>;
-+                              min-residency-us = <3000>;
++                      CPU_WFI: wfi {
++                              compatible = "qcom,idle-state-wfi";
++                              idle-state-name = "Krait WFI";
++                              entry-latency-us = <0>;
++                              exit-latency-us = <0>;
++                              min-residency-us = <0>;
 +                      };
                };
  
diff --git a/target/linux/ipq806x/patches-5.15/124-cpuidle-qcom_spm-Detach-state-machine-from-main-SPM-.patch b/target/linux/ipq806x/patches-5.15/124-cpuidle-qcom_spm-Detach-state-machine-from-main-SPM-.patch
new file mode 100644 (file)
index 0000000..a23e559
--- /dev/null
@@ -0,0 +1,741 @@
+From 60f3692b5f0b3e1df3ccc07c28758379052d0ab6 Mon Sep 17 00:00:00 2001
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
+Date: Thu, 29 Jul 2021 17:56:05 +0200
+Subject: [PATCH 1/1] cpuidle: qcom_spm: Detach state machine from main SPM
+ handling
+
+In commit a871be6b8eee ("cpuidle: Convert Qualcomm SPM driver to a generic
+CPUidle driver") the SPM driver has been converted to a
+generic CPUidle driver: that was mainly made to simplify the
+driver and that was a great accomplishment;
+Though, at that time, this driver was only applicable to ARM 32-bit SoCs,
+lacking logic about the handling of newer generation SAW.
+
+In preparation for the enablement of SPM features on AArch64/ARM64,
+split the cpuidle-qcom-spm driver in two: the CPUIdle related
+state machine (currently used only on ARM SoCs) stays there, while
+the SPM communication handling lands back in soc/qcom/spm.c and
+also making sure to not discard the simplifications that were
+introduced in the aforementioned commit.
+
+Since now the "two drivers" are split, the SCM dependency in the
+main SPM handling is gone and for this reason it was also possible
+to move the SPM initialization early: this will also make sure that
+whenever the SAW CPUIdle driver is getting initialized, the SPM
+driver will be ready to do the job.
+
+Please note that the anticipation of the SPM initialization was
+also done to optimize the boot times on platforms that have their
+CPU/L2 idle states managed by other means (such as PSCI), while
+needing SAW initialization for other purposes, like AVS control.
+
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
+Reviewed-by: Stephan Gerhold <stephan@gerhold.net>
+Tested-by: Stephan Gerhold <stephan@gerhold.net>
+Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
+Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
+Link: https://lore.kernel.org/r/20210729155609.608159-2-angelogioacchino.delregno@somainline.org
+---
+ drivers/cpuidle/Kconfig.arm        |   1 +
+ drivers/cpuidle/cpuidle-qcom-spm.c | 318 ++++++-----------------------
+ drivers/soc/qcom/Kconfig           |   9 +
+ drivers/soc/qcom/Makefile          |   1 +
+ drivers/soc/qcom/spm.c             | 216 ++++++++++++++++++++
+ include/soc/qcom/spm.h             |  41 ++++
+ 6 files changed, 336 insertions(+), 250 deletions(-)
+ create mode 100644 drivers/soc/qcom/spm.c
+ create mode 100644 include/soc/qcom/spm.h
+
+diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
+index 334f83e56120..8a02213c8391 100644
+--- a/drivers/cpuidle/Kconfig.arm
++++ b/drivers/cpuidle/Kconfig.arm
+@@ -112,6 +112,7 @@ config ARM_QCOM_SPM_CPUIDLE
+       select CPU_IDLE_MULTIPLE_DRIVERS
+       select DT_IDLE_STATES
+       select QCOM_SCM
++      select QCOM_SPM
+       help
+         Select this to enable cpuidle for Qualcomm processors.
+         The Subsystem Power Manager (SPM) controls low power modes for the
+diff --git a/drivers/cpuidle/cpuidle-qcom-spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c
+index c0e7971da2da..01e77913a414 100644
+--- a/drivers/cpuidle/cpuidle-qcom-spm.c
++++ b/drivers/cpuidle/cpuidle-qcom-spm.c
+@@ -18,158 +18,18 @@
+ #include <linux/cpuidle.h>
+ #include <linux/cpu_pm.h>
+ #include <linux/qcom_scm.h>
++#include <soc/qcom/spm.h>
+ #include <asm/proc-fns.h>
+ #include <asm/suspend.h>
+ #include "dt_idle_states.h"
+-#define MAX_PMIC_DATA         2
+-#define MAX_SEQ_DATA          64
+-#define SPM_CTL_INDEX         0x7f
+-#define SPM_CTL_INDEX_SHIFT   4
+-#define SPM_CTL_EN            BIT(0)
+-
+-enum pm_sleep_mode {
+-      PM_SLEEP_MODE_STBY,
+-      PM_SLEEP_MODE_RET,
+-      PM_SLEEP_MODE_SPC,
+-      PM_SLEEP_MODE_PC,
+-      PM_SLEEP_MODE_NR,
+-};
+-
+-enum spm_reg {
+-      SPM_REG_CFG,
+-      SPM_REG_SPM_CTL,
+-      SPM_REG_DLY,
+-      SPM_REG_PMIC_DLY,
+-      SPM_REG_PMIC_DATA_0,
+-      SPM_REG_PMIC_DATA_1,
+-      SPM_REG_VCTL,
+-      SPM_REG_SEQ_ENTRY,
+-      SPM_REG_SPM_STS,
+-      SPM_REG_PMIC_STS,
+-      SPM_REG_NR,
+-};
+-
+-struct spm_reg_data {
+-      const u8 *reg_offset;
+-      u32 spm_cfg;
+-      u32 spm_dly;
+-      u32 pmic_dly;
+-      u32 pmic_data[MAX_PMIC_DATA];
+-      u8 seq[MAX_SEQ_DATA];
+-      u8 start_index[PM_SLEEP_MODE_NR];
+-};
+-
+-struct spm_driver_data {
++struct cpuidle_qcom_spm_data {
+       struct cpuidle_driver cpuidle_driver;
+-      void __iomem *reg_base;
+-      const struct spm_reg_data *reg_data;
+-};
+-
+-static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = {
+-      [SPM_REG_CFG]           = 0x08,
+-      [SPM_REG_SPM_CTL]       = 0x30,
+-      [SPM_REG_DLY]           = 0x34,
+-      [SPM_REG_SEQ_ENTRY]     = 0x80,
+-};
+-
+-/* SPM register data for 8974, 8084 */
+-static const struct spm_reg_data spm_reg_8974_8084_cpu  = {
+-      .reg_offset = spm_reg_offset_v2_1,
+-      .spm_cfg = 0x1,
+-      .spm_dly = 0x3C102800,
+-      .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
+-              0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
+-              0x0F },
+-      .start_index[PM_SLEEP_MODE_STBY] = 0,
+-      .start_index[PM_SLEEP_MODE_SPC] = 3,
++      struct spm_driver_data *spm;
+ };
+-/* SPM register data for 8226 */
+-static const struct spm_reg_data spm_reg_8226_cpu  = {
+-      .reg_offset = spm_reg_offset_v2_1,
+-      .spm_cfg = 0x0,
+-      .spm_dly = 0x3C102800,
+-      .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
+-              0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
+-              0x80, 0x10, 0x26, 0x30, 0x0F },
+-      .start_index[PM_SLEEP_MODE_STBY] = 0,
+-      .start_index[PM_SLEEP_MODE_SPC] = 5,
+-};
+-
+-static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = {
+-      [SPM_REG_CFG]           = 0x08,
+-      [SPM_REG_SPM_CTL]       = 0x20,
+-      [SPM_REG_PMIC_DLY]      = 0x24,
+-      [SPM_REG_PMIC_DATA_0]   = 0x28,
+-      [SPM_REG_PMIC_DATA_1]   = 0x2C,
+-      [SPM_REG_SEQ_ENTRY]     = 0x80,
+-};
+-
+-/* SPM register data for 8064 */
+-static const struct spm_reg_data spm_reg_8064_cpu = {
+-      .reg_offset = spm_reg_offset_v1_1,
+-      .spm_cfg = 0x1F,
+-      .pmic_dly = 0x02020004,
+-      .pmic_data[0] = 0x0084009C,
+-      .pmic_data[1] = 0x00A4001C,
+-      .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
+-              0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
+-      .start_index[PM_SLEEP_MODE_STBY] = 0,
+-      .start_index[PM_SLEEP_MODE_SPC] = 2,
+-};
+-
+-static inline void spm_register_write(struct spm_driver_data *drv,
+-                                      enum spm_reg reg, u32 val)
+-{
+-      if (drv->reg_data->reg_offset[reg])
+-              writel_relaxed(val, drv->reg_base +
+-                              drv->reg_data->reg_offset[reg]);
+-}
+-
+-/* Ensure a guaranteed write, before return */
+-static inline void spm_register_write_sync(struct spm_driver_data *drv,
+-                                      enum spm_reg reg, u32 val)
+-{
+-      u32 ret;
+-
+-      if (!drv->reg_data->reg_offset[reg])
+-              return;
+-
+-      do {
+-              writel_relaxed(val, drv->reg_base +
+-                              drv->reg_data->reg_offset[reg]);
+-              ret = readl_relaxed(drv->reg_base +
+-                              drv->reg_data->reg_offset[reg]);
+-              if (ret == val)
+-                      break;
+-              cpu_relax();
+-      } while (1);
+-}
+-
+-static inline u32 spm_register_read(struct spm_driver_data *drv,
+-                                      enum spm_reg reg)
+-{
+-      return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
+-}
+-
+-static void spm_set_low_power_mode(struct spm_driver_data *drv,
+-                                      enum pm_sleep_mode mode)
+-{
+-      u32 start_index;
+-      u32 ctl_val;
+-
+-      start_index = drv->reg_data->start_index[mode];
+-
+-      ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
+-      ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
+-      ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
+-      ctl_val |= SPM_CTL_EN;
+-      spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
+-}
+-
+ static int qcom_pm_collapse(unsigned long int unused)
+ {
+       qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON);
+@@ -201,10 +61,10 @@ static int qcom_cpu_spc(struct spm_driver_data *drv)
+ static int spm_enter_idle_state(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int idx)
+ {
+-      struct spm_driver_data *data = container_of(drv, struct spm_driver_data,
+-                                                  cpuidle_driver);
++      struct cpuidle_qcom_spm_data *data = container_of(drv, struct cpuidle_qcom_spm_data,
++                                                        cpuidle_driver);
+-      return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data);
++      return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data->spm);
+ }
+ static struct cpuidle_driver qcom_spm_idle_driver = {
+@@ -225,134 +85,92 @@ static const struct of_device_id qcom_idle_state_match[] = {
+       { },
+ };
+-static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu)
++static int spm_cpuidle_register(struct device *cpuidle_dev, int cpu)
+ {
++      struct platform_device *pdev = NULL;
++      struct device_node *cpu_node, *saw_node;
++      struct cpuidle_qcom_spm_data *data = NULL;
+       int ret;
+-      memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv));
+-      drv->cpumask = (struct cpumask *)cpumask_of(cpu);
++      cpu_node = of_cpu_device_node_get(cpu);
++      if (!cpu_node)
++              return -ENODEV;
+-      /* Parse idle states from device tree */
+-      ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1);
+-      if (ret <= 0)
+-              return ret ? : -ENODEV;
++      saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
++      if (!saw_node)
++              return -ENODEV;
+-      /* We have atleast one power down mode */
+-      return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask);
+-}
++      pdev = of_find_device_by_node(saw_node);
++      of_node_put(saw_node);
++      of_node_put(cpu_node);
++      if (!pdev)
++              return -ENODEV;
+-static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
+-              int *spm_cpu)
+-{
+-      struct spm_driver_data *drv = NULL;
+-      struct device_node *cpu_node, *saw_node;
+-      int cpu;
+-      bool found = 0;
++      data = devm_kzalloc(cpuidle_dev, sizeof(*data), GFP_KERNEL);
++      if (!data)
++              return -ENOMEM;
+-      for_each_possible_cpu(cpu) {
+-              cpu_node = of_cpu_device_node_get(cpu);
+-              if (!cpu_node)
+-                      continue;
+-              saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
+-              found = (saw_node == pdev->dev.of_node);
+-              of_node_put(saw_node);
+-              of_node_put(cpu_node);
+-              if (found)
+-                      break;
+-      }
++      data->spm = dev_get_drvdata(&pdev->dev);
++      if (!data->spm)
++              return -EINVAL;
+-      if (found) {
+-              drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+-              if (drv)
+-                      *spm_cpu = cpu;
+-      }
++      data->cpuidle_driver = qcom_spm_idle_driver;
++      data->cpuidle_driver.cpumask = (struct cpumask *)cpumask_of(cpu);
+-      return drv;
+-}
++      ret = dt_init_idle_driver(&data->cpuidle_driver,
++                                qcom_idle_state_match, 1);
++      if (ret <= 0)
++              return ret ? : -ENODEV;
+-static const struct of_device_id spm_match_table[] = {
+-      { .compatible = "qcom,msm8226-saw2-v2.1-cpu",
+-        .data = &spm_reg_8226_cpu },
+-      { .compatible = "qcom,msm8974-saw2-v2.1-cpu",
+-        .data = &spm_reg_8974_8084_cpu },
+-      { .compatible = "qcom,apq8084-saw2-v2.1-cpu",
+-        .data = &spm_reg_8974_8084_cpu },
+-      { .compatible = "qcom,apq8064-saw2-v1.1-cpu",
+-        .data = &spm_reg_8064_cpu },
+-      { },
+-};
++      ret = qcom_scm_set_warm_boot_addr(cpu_resume_arm, cpumask_of(cpu));
++      if (ret)
++              return ret;
++
++      return cpuidle_register(&data->cpuidle_driver, NULL);
++}
+-static int spm_dev_probe(struct platform_device *pdev)
++static int spm_cpuidle_drv_probe(struct platform_device *pdev)
+ {
+-      struct spm_driver_data *drv;
+-      struct resource *res;
+-      const struct of_device_id *match_id;
+-      void __iomem *addr;
+       int cpu, ret;
+       if (!qcom_scm_is_available())
+               return -EPROBE_DEFER;
+-      drv = spm_get_drv(pdev, &cpu);
+-      if (!drv)
+-              return -EINVAL;
+-      platform_set_drvdata(pdev, drv);
++      for_each_possible_cpu(cpu) {
++              ret = spm_cpuidle_register(&pdev->dev, cpu);
++              if (ret && ret != -ENODEV) {
++                      dev_err(&pdev->dev,
++                              "Cannot register for CPU%d: %d\n", cpu, ret);
++              }
++      }
+-      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+-      drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
+-      if (IS_ERR(drv->reg_base))
+-              return PTR_ERR(drv->reg_base);
++      return 0;
++}
+-      match_id = of_match_node(spm_match_table, pdev->dev.of_node);
+-      if (!match_id)
+-              return -ENODEV;
++static struct platform_driver spm_cpuidle_driver = {
++      .probe = spm_cpuidle_drv_probe,
++      .driver = {
++              .name = "qcom-spm-cpuidle",
++              .suppress_bind_attrs = true,
++      },
++};
+-      drv->reg_data = match_id->data;
++static int __init qcom_spm_cpuidle_init(void)
++{
++      struct platform_device *pdev;
++      int ret;
+-      ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu);
++      ret = platform_driver_register(&spm_cpuidle_driver);
+       if (ret)
+               return ret;
+-      /* Write the SPM sequences first.. */
+-      addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
+-      __iowrite32_copy(addr, drv->reg_data->seq,
+-                      ARRAY_SIZE(drv->reg_data->seq) / 4);
+-
+-      /*
+-       * ..and then the control registers.
+-       * On some SoC if the control registers are written first and if the
+-       * CPU was held in reset, the reset signal could trigger the SPM state
+-       * machine, before the sequences are completely written.
+-       */
+-      spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
+-      spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
+-      spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
+-      spm_register_write(drv, SPM_REG_PMIC_DATA_0,
+-                              drv->reg_data->pmic_data[0]);
+-      spm_register_write(drv, SPM_REG_PMIC_DATA_1,
+-                              drv->reg_data->pmic_data[1]);
+-
+-      /* Set up Standby as the default low power mode */
+-      spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
+-
+-      return cpuidle_register(&drv->cpuidle_driver, NULL);
+-}
+-
+-static int spm_dev_remove(struct platform_device *pdev)
+-{
+-      struct spm_driver_data *drv = platform_get_drvdata(pdev);
++      pdev = platform_device_register_simple("qcom-spm-cpuidle",
++                                             -1, NULL, 0);
++      if (IS_ERR(pdev)) {
++              platform_driver_unregister(&spm_cpuidle_driver);
++              return PTR_ERR(pdev);
++      }
+-      cpuidle_unregister(&drv->cpuidle_driver);
+       return 0;
+ }
+-
+-static struct platform_driver spm_driver = {
+-      .probe = spm_dev_probe,
+-      .remove = spm_dev_remove,
+-      .driver = {
+-              .name = "saw",
+-              .of_match_table = spm_match_table,
+-      },
+-};
+-
+-builtin_platform_driver(spm_driver);
++device_initcall(qcom_spm_cpuidle_init);
+diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
+index 79b568f82a1c..fe3c486ae32d 100644
+--- a/drivers/soc/qcom/Kconfig
++++ b/drivers/soc/qcom/Kconfig
+@@ -190,6 +190,15 @@ config QCOM_SOCINFO
+        Say yes here to support the Qualcomm socinfo driver, providing
+        information about the SoC to user space.
++config QCOM_SPM
++      tristate "Qualcomm Subsystem Power Manager (SPM)"
++      depends on ARCH_QCOM
++      select QCOM_SCM
++      help
++        Enable the support for the Qualcomm Subsystem Power Manager, used
++        to manage cores, L2 low power modes and to configure the internal
++        Adaptive Voltage Scaler parameters, where supported.
++
+ config QCOM_WCNSS_CTRL
+       tristate "Qualcomm WCNSS control driver"
+       depends on ARCH_QCOM || COMPILE_TEST
+diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
+index ad675a6593d0..24514c722832 100644
+--- a/drivers/soc/qcom/Makefile
++++ b/drivers/soc/qcom/Makefile
+@@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
+ obj-$(CONFIG_QCOM_SMP2P)      += smp2p.o
+ obj-$(CONFIG_QCOM_SMSM)       += smsm.o
+ obj-$(CONFIG_QCOM_SOCINFO)    += socinfo.o
++obj-$(CONFIG_QCOM_SPM)                += spm.o
+ obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
+ obj-$(CONFIG_QCOM_APR) += apr.o
+ obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
+diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
+new file mode 100644
+index 000000000000..ef20607877de
+--- /dev/null
++++ b/drivers/soc/qcom/spm.c
+@@ -0,0 +1,216 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2014,2015, Linaro Ltd.
++ *
++ * SAW power controller driver
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/err.h>
++#include <linux/platform_device.h>
++#include <soc/qcom/spm.h>
++
++#define SPM_CTL_INDEX         0x7f
++#define SPM_CTL_INDEX_SHIFT   4
++#define SPM_CTL_EN            BIT(0)
++
++enum spm_reg {
++      SPM_REG_CFG,
++      SPM_REG_SPM_CTL,
++      SPM_REG_DLY,
++      SPM_REG_PMIC_DLY,
++      SPM_REG_PMIC_DATA_0,
++      SPM_REG_PMIC_DATA_1,
++      SPM_REG_VCTL,
++      SPM_REG_SEQ_ENTRY,
++      SPM_REG_SPM_STS,
++      SPM_REG_PMIC_STS,
++      SPM_REG_NR,
++};
++
++static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = {
++      [SPM_REG_CFG]           = 0x08,
++      [SPM_REG_SPM_CTL]       = 0x30,
++      [SPM_REG_DLY]           = 0x34,
++      [SPM_REG_SEQ_ENTRY]     = 0x80,
++};
++
++/* SPM register data for 8974, 8084 */
++static const struct spm_reg_data spm_reg_8974_8084_cpu  = {
++      .reg_offset = spm_reg_offset_v2_1,
++      .spm_cfg = 0x1,
++      .spm_dly = 0x3C102800,
++      .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
++              0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
++              0x0F },
++      .start_index[PM_SLEEP_MODE_STBY] = 0,
++      .start_index[PM_SLEEP_MODE_SPC] = 3,
++};
++
++/* SPM register data for 8226 */
++static const struct spm_reg_data spm_reg_8226_cpu  = {
++      .reg_offset = spm_reg_offset_v2_1,
++      .spm_cfg = 0x0,
++      .spm_dly = 0x3C102800,
++      .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
++              0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
++              0x80, 0x10, 0x26, 0x30, 0x0F },
++      .start_index[PM_SLEEP_MODE_STBY] = 0,
++      .start_index[PM_SLEEP_MODE_SPC] = 5,
++};
++
++static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = {
++      [SPM_REG_CFG]           = 0x08,
++      [SPM_REG_SPM_CTL]       = 0x20,
++      [SPM_REG_PMIC_DLY]      = 0x24,
++      [SPM_REG_PMIC_DATA_0]   = 0x28,
++      [SPM_REG_PMIC_DATA_1]   = 0x2C,
++      [SPM_REG_SEQ_ENTRY]     = 0x80,
++};
++
++/* SPM register data for 8064 */
++static const struct spm_reg_data spm_reg_8064_cpu = {
++      .reg_offset = spm_reg_offset_v1_1,
++      .spm_cfg = 0x1F,
++      .pmic_dly = 0x02020004,
++      .pmic_data[0] = 0x0084009C,
++      .pmic_data[1] = 0x00A4001C,
++      .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
++              0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
++      .start_index[PM_SLEEP_MODE_STBY] = 0,
++      .start_index[PM_SLEEP_MODE_SPC] = 2,
++};
++
++static inline void spm_register_write(struct spm_driver_data *drv,
++                                      enum spm_reg reg, u32 val)
++{
++      if (drv->reg_data->reg_offset[reg])
++              writel_relaxed(val, drv->reg_base +
++                              drv->reg_data->reg_offset[reg]);
++}
++
++/* Ensure a guaranteed write, before return */
++static inline void spm_register_write_sync(struct spm_driver_data *drv,
++                                      enum spm_reg reg, u32 val)
++{
++      u32 ret;
++
++      if (!drv->reg_data->reg_offset[reg])
++              return;
++
++      do {
++              writel_relaxed(val, drv->reg_base +
++                              drv->reg_data->reg_offset[reg]);
++              ret = readl_relaxed(drv->reg_base +
++                              drv->reg_data->reg_offset[reg]);
++              if (ret == val)
++                      break;
++              cpu_relax();
++      } while (1);
++}
++
++static inline u32 spm_register_read(struct spm_driver_data *drv,
++                                  enum spm_reg reg)
++{
++      return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
++}
++
++void spm_set_low_power_mode(struct spm_driver_data *drv,
++                          enum pm_sleep_mode mode)
++{
++      u32 start_index;
++      u32 ctl_val;
++
++      start_index = drv->reg_data->start_index[mode];
++
++      ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
++      ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
++      ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
++      ctl_val |= SPM_CTL_EN;
++      spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
++}
++
++static const struct of_device_id spm_match_table[] = {
++      { .compatible = "qcom,msm8226-saw2-v2.1-cpu",
++        .data = &spm_reg_8226_cpu },
++      { .compatible = "qcom,msm8974-saw2-v2.1-cpu",
++        .data = &spm_reg_8974_8084_cpu },
++      { .compatible = "qcom,apq8084-saw2-v2.1-cpu",
++        .data = &spm_reg_8974_8084_cpu },
++      { .compatible = "qcom,apq8064-saw2-v1.1-cpu",
++        .data = &spm_reg_8064_cpu },
++      { },
++};
++MODULE_DEVICE_TABLE(of, spm_match_table);
++
++static int spm_dev_probe(struct platform_device *pdev)
++{
++      const struct of_device_id *match_id;
++      struct spm_driver_data *drv;
++      struct resource *res;
++      void __iomem *addr;
++
++      drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
++      if (!drv)
++              return -ENOMEM;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(drv->reg_base))
++              return PTR_ERR(drv->reg_base);
++
++      match_id = of_match_node(spm_match_table, pdev->dev.of_node);
++      if (!match_id)
++              return -ENODEV;
++
++      drv->reg_data = match_id->data;
++      platform_set_drvdata(pdev, drv);
++
++      /* Write the SPM sequences first.. */
++      addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
++      __iowrite32_copy(addr, drv->reg_data->seq,
++                      ARRAY_SIZE(drv->reg_data->seq) / 4);
++
++      /*
++       * ..and then the control registers.
++       * On some SoC if the control registers are written first and if the
++       * CPU was held in reset, the reset signal could trigger the SPM state
++       * machine, before the sequences are completely written.
++       */
++      spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
++      spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
++      spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
++      spm_register_write(drv, SPM_REG_PMIC_DATA_0,
++                              drv->reg_data->pmic_data[0]);
++      spm_register_write(drv, SPM_REG_PMIC_DATA_1,
++                              drv->reg_data->pmic_data[1]);
++
++      /* Set up Standby as the default low power mode */
++      spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
++
++      return 0;
++}
++
++static struct platform_driver spm_driver = {
++      .probe = spm_dev_probe,
++      .driver = {
++              .name = "qcom_spm",
++              .of_match_table = spm_match_table,
++      },
++};
++
++static int __init qcom_spm_init(void)
++{
++      return platform_driver_register(&spm_driver);
++}
++arch_initcall(qcom_spm_init);
++
++MODULE_LICENSE("GPL v2");
+diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
+new file mode 100644
+index 000000000000..4c7e5ac2583d
+--- /dev/null
++++ b/include/soc/qcom/spm.h
+@@ -0,0 +1,41 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2014,2015, Linaro Ltd.
++ */
++
++#ifndef __SPM_H__
++#define __SPM_H__
++
++#include <linux/cpuidle.h>
++
++#define MAX_PMIC_DATA         2
++#define MAX_SEQ_DATA          64
++
++enum pm_sleep_mode {
++      PM_SLEEP_MODE_STBY,
++      PM_SLEEP_MODE_RET,
++      PM_SLEEP_MODE_SPC,
++      PM_SLEEP_MODE_PC,
++      PM_SLEEP_MODE_NR,
++};
++
++struct spm_reg_data {
++      const u8 *reg_offset;
++      u32 spm_cfg;
++      u32 spm_dly;
++      u32 pmic_dly;
++      u32 pmic_data[MAX_PMIC_DATA];
++      u8 seq[MAX_SEQ_DATA];
++      u8 start_index[PM_SLEEP_MODE_NR];
++};
++
++struct spm_driver_data {
++      void __iomem *reg_base;
++      const struct spm_reg_data *reg_data;
++};
++
++void spm_set_low_power_mode(struct spm_driver_data *drv,
++                          enum pm_sleep_mode mode);
++
++#endif /* __SPM_H__ */
+-- 
+2.37.2
+
diff --git a/target/linux/ipq806x/patches-5.15/125-01-soc-qcom-spm-Add-ipq8064-CPU-data.patch b/target/linux/ipq806x/patches-5.15/125-01-soc-qcom-spm-Add-ipq8064-CPU-data.patch
new file mode 100644 (file)
index 0000000..30771fb
--- /dev/null
@@ -0,0 +1,63 @@
+From 923ab10f11a493f458d8ac069d76da6d56e7b194 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 19 Sep 2022 14:36:21 +0200
+Subject: [PATCH 1/3] soc: qcom: spm: Add ipq8064 CPU data
+
+Add ipq8064 CPU data for boot and non-boot CPU. Load only the WFI
+sequence as some SoC seems to be broken with SPC sequence and seems to
+be not supported on this platform.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/soc/qcom/spm.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
+index 484b42b7454e..d822ea6dee38 100644
+--- a/drivers/soc/qcom/spm.c
++++ b/drivers/soc/qcom/spm.c
+@@ -151,6 +151,30 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
+       .start_index[PM_SLEEP_MODE_SPC] = 2,
+ };
++/* SPM register data for ipq8064 boot cpu */
++static const struct spm_reg_data spm_reg_ipq8064_boot_cpu = {
++      .reg_offset = spm_reg_offset_v1_1,
++      .spm_cfg = 0x1F,
++      .pmic_dly = 0x03020004,
++      .pmic_data[0] = 0x0084009C,
++      .pmic_data[1] = 0x00A4001C,
++      .seq = { 0x03, 0x0F,
++      },
++      .start_index[PM_SLEEP_MODE_STBY] = 0,
++};
++
++/* SPM register data for ipq8064 non boot cpu */
++static const struct spm_reg_data spm_reg_ipq8064_nonboot_cpu = {
++      .reg_offset = spm_reg_offset_v1_1,
++      .spm_cfg = 0x1F,
++      .pmic_dly = 0x02020004,
++      .pmic_data[0] = 0x0060009C,
++      .pmic_data[1] = 0x0000001C,
++      .seq = { 0x03, 0x0F,
++      },
++      .start_index[PM_SLEEP_MODE_STBY] = 0,
++};
++
+ static inline void spm_register_write(struct spm_driver_data *drv,
+                                       enum spm_reg reg, u32 val)
+ {
+@@ -221,6 +245,10 @@ static const struct of_device_id spm_match_table[] = {
+         .data = &spm_reg_8974_8084_cpu },
+       { .compatible = "qcom,apq8064-saw2-v1.1-cpu",
+         .data = &spm_reg_8064_cpu },
++      { .compatible = "qcom,ipq8064-saw2-v1.1-boot-cpu",
++        .data = &spm_reg_ipq8064_boot_cpu },
++      { .compatible = "qcom,ipq8064-saw2-v1.1-nonboot-cpu",
++        .data = &spm_reg_ipq8064_nonboot_cpu },
+       { },
+ };
+ MODULE_DEVICE_TABLE(of, spm_match_table);
+-- 
+2.37.2
+
diff --git a/target/linux/ipq806x/patches-5.15/125-02-ARM-mach-qcom-Add-krait-CPU-specific-idle.patch b/target/linux/ipq806x/patches-5.15/125-02-ARM-mach-qcom-Add-krait-CPU-specific-idle.patch
new file mode 100644 (file)
index 0000000..2d60ad1
--- /dev/null
@@ -0,0 +1,98 @@
+From 50523bace0af72bf488c26359bc0d7e054c3f1be Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 19 Sep 2022 14:49:02 +0200
+Subject: [PATCH 2/3] ARM: mach-qcom: Add krait CPU specific idle
+
+Add krait CPU specific idle that does extra save while in WFI.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ arch/arm/include/asm/mach/krait-idle.h | 11 +++++++++
+ arch/arm/mach-qcom/Kconfig             |  3 +++
+ arch/arm/mach-qcom/Makefile            |  1 +
+ arch/arm/mach-qcom/krait-idle.S        | 34 ++++++++++++++++++++++++++
+ 4 files changed, 49 insertions(+)
+ create mode 100644 arch/arm/include/asm/mach/krait-idle.h
+ create mode 100644 arch/arm/mach-qcom/krait-idle.S
+
+diff --git a/arch/arm/include/asm/mach/krait-idle.h b/arch/arm/include/asm/mach/krait-idle.h
+new file mode 100644
+index 000000000000..c8da6b3fb792
+--- /dev/null
++++ b/arch/arm/include/asm/mach/krait-idle.h
+@@ -0,0 +1,11 @@
++#ifndef __ASSEMBLY__
++
++#ifdef ARCH_KRAIT_IDLE
++extern void arm_krait_idle(void);
++#else
++static void arm_krait_idle(void) {
++      cpu_do_idle();
++}
++#endif
++
++#endif /* __ASSEMBLY__ */
+\ No newline at end of file
+diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
+index 12a812e61c16..ae26bca49b39 100644
+--- a/arch/arm/mach-qcom/Kconfig
++++ b/arch/arm/mach-qcom/Kconfig
+@@ -46,4 +46,7 @@ config ARCH_MDM9615
+       bool "Enable support for MDM9615"
+       select CLKSRC_QCOM
++config ARCH_KRAIT_IDLE
++      bool "Enable Krait Idle specific WFI"
++
+ endif
+diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
+index b839201c9802..fc828b60def0 100644
+--- a/arch/arm/mach-qcom/Makefile
++++ b/arch/arm/mach-qcom/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ obj-$(CONFIG_SMP)     += platsmp.o
++obj-$(CONFIG_ARCH_KRAIT_IDLE) += krait-idle.o
+diff --git a/arch/arm/mach-qcom/krait-idle.S b/arch/arm/mach-qcom/krait-idle.S
+new file mode 100644
+index 000000000000..005b5d511e69
+--- /dev/null
++++ b/arch/arm/mach-qcom/krait-idle.S
+@@ -0,0 +1,34 @@
++#include <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/mach/krait-idle.h>
++
++ENTRY(arm_krait_idle)
++      mrc     p15, 0, r0, c0, c0, 0
++      bic     r1, r0, #0xff
++      movw    r2, #0x0400
++      movt    r2, #0x511F
++      movw    r3, #0x0600
++      movt    r3, #0x510F
++      cmp     r2, r1
++      cmpne   r3, r1
++      bne     go_wfi
++
++      mrs     r0, cpsr
++      cpsid   if
++
++      mrc     p15, 7, r1, c15, c0, 5
++      bic     r2, r1, #0x20000
++      mcr     p15, 7, r2, c15, c0, 5
++      isb
++
++go_wfi:
++      wfi
++      bne     wfi_done
++      mcr     p15, 7, r1, c15, c0, 5
++      isb
++      msr     cpsr_c, r0
++
++wfi_done:
++      bx      lr
++ENDPROC(arm_krait_idle)
++
+-- 
+2.37.2
+
diff --git a/target/linux/ipq806x/patches-5.15/125-03-cpuidle-qcom-spm-add-krait-specific-wfi.patch b/target/linux/ipq806x/patches-5.15/125-03-cpuidle-qcom-spm-add-krait-specific-wfi.patch
new file mode 100644 (file)
index 0000000..04722d9
--- /dev/null
@@ -0,0 +1,82 @@
+From 0863141455934f78a6781c2867e550070eaff69c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 19 Sep 2022 14:56:35 +0200
+Subject: [PATCH 3/3] cpuidle: qcom-spm: add krait specific wfi
+
+Add krait specific wfi cpuidle that use the specific krait idle code.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/cpuidle/cpuidle-qcom-spm.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/cpuidle/cpuidle-qcom-spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c
+index beedf22cbe78..8468d34dd33e 100644
+--- a/drivers/cpuidle/cpuidle-qcom-spm.c
++++ b/drivers/cpuidle/cpuidle-qcom-spm.c
+@@ -25,6 +25,8 @@
+ #include "dt_idle_states.h"
++#include <asm/mach/krait-idle.h>
++
+ struct cpuidle_qcom_spm_data {
+       struct cpuidle_driver cpuidle_driver;
+       struct spm_driver_data *spm;
+@@ -58,7 +60,7 @@ static int qcom_cpu_spc(struct spm_driver_data *drv)
+       return ret;
+ }
+-static int spm_enter_idle_state(struct cpuidle_device *dev,
++static int spm_enter_idle_state_spc(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int idx)
+ {
+       struct cpuidle_qcom_spm_data *data = container_of(drv, struct cpuidle_qcom_spm_data,
+@@ -67,21 +69,27 @@ static int spm_enter_idle_state(struct cpuidle_device *dev,
+       return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data->spm);
+ }
++static int spm_enter_idle_state_wfi(struct cpuidle_device *dev,
++                              struct cpuidle_driver *drv, int idx)
++{
++      arm_krait_idle();
++      return idx;
++}
++
+ static struct cpuidle_driver qcom_spm_idle_driver = {
+       .name = "qcom_spm",
+       .owner = THIS_MODULE,
+       .states[0] = {
+-              .enter                  = spm_enter_idle_state,
+-              .exit_latency           = 1,
+-              .target_residency       = 1,
+-              .power_usage            = UINT_MAX,
+-              .name                   = "WFI",
+-              .desc                   = "ARM WFI",
+-      }
++              .enter                  = spm_enter_idle_state_wfi,
++      },
++      .states[1] = {
++              .enter                  = spm_enter_idle_state_spc,
++      },
+ };
+ static const struct of_device_id qcom_idle_state_match[] = {
+-      { .compatible = "qcom,idle-state-spc", .data = spm_enter_idle_state },
++      { .compatible = "qcom,idle-state-wfi", .data = &spm_enter_idle_state_wfi },
++      { .compatible = "qcom,idle-state-spc", .data = &spm_enter_idle_state_spc },
+       { },
+ };
+@@ -118,7 +126,7 @@ static int spm_cpuidle_register(struct device *cpuidle_dev, int cpu)
+       data->cpuidle_driver.cpumask = (struct cpumask *)cpumask_of(cpu);
+       ret = dt_init_idle_driver(&data->cpuidle_driver,
+-                                qcom_idle_state_match, 1);
++                                qcom_idle_state_match, 0);
+       if (ret <= 0)
+               return ret ? : -ENODEV;
+-- 
+2.37.2
+