All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/5] QCOM 8074 cpuidle driver
@ 2014-09-23 23:51 ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm, Lina Iyer

Changes since v5:
[ https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10559.html ]
- Merge spm-devices.c and spm.c into one file and one patch
	- Simplify implementation of the driver.
	- Update documentation mapping the DT properties with corresponding
	  SPM register information.
- Removed scm-boot changes for quad core warmboot, its has been pulled in.

Changes since v4:
[ https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10327.html ]
- Update to the v8 of ARM generic idle states patches
- Use platform device model for cpuidle-qcom
- Clean up msm-pm.c to remove unnecessary include files and functions
- Update commit text and documentation for all idle states
- Remove scm-boot relocate patch from this series, submitted earlier
[ https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10518.html ]

Changes since v3:
[ https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10288.html ]
- Fix CONFIG_QCOM_PM Kconfig as bool
- More clean ups in spm.c and spm-devices.c
	- Removed and re-organized data structures to make initialization simple
	- Remove export of sequence flush functions
	- Updated commit text
	- Comments for use of barriers.
- Rebase on top of 3.17-rc1

Changes since v2:
[ https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10148.html ]
- Prune all the drivers to support basic WFI and power down cpuidle
  functionality. Remove debug code.
- Integrate KConfig changes into the drivers' patches.
- Use Lorenzo's ARM idle-states patches as the basis for reading cpuidle
  c-states from DT.
  [ http://marc.info/?l=linux-pm&m=140794514812383&w=2 ]
- Incorporate review comments
- Rebase on top of 3.16

Changes since v1/RFC:
[ https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10065.html ]
- Remove hotplug from the patch series. Will submit it separately.
- Fix SPM drivers per the review comments
- Modify patch sequence to compile SPM drivers independent of msm-pm, so as to
  allow wfi() calls to use SPM even without SoC interface driver.

8074 like any ARM SoC can do architectural clock gating, that helps save on
power, but not enough of leakage power.  Leakage power of the SoC can be
further reduced by turning off power to the core. To aid this, every core (cpu
and L2) is accompanied by a Sub-system Power Manager (SPM), that can be
configured to indicate the low power mode, the core would be put into and the
SPM programs the peripheral h/w accordingly to enter low power and turn off the
power rail to the core.

The idle invocation hierarchy - 

	CPUIDLE
	|
	cpuidle-qcom.c [CPUIdle driver]
	|
	------>	msm-pm.c [SoC Interface layer for QCOM chipsets]
		|
		------> spm.c [SPM driver]
		|
		------> scm-boot.c [SCM interface layer]		
			|
------------------------|--------------------------
(EL)			Secure Monitor Code
			|
			|
			wfi(); 
------------------------|--------------------------
(HW)			[CPU] {clock gate}
			|
			-----> [SPM] {statemachine}
			

The patchset does the following -

- Introduce the SPM driver to control power to the core. The SPM h/w IP works
in conjunction with the Krait CPU/L2. When the core executes WFI instruction,
the core is clockgated and the SPM state machine takes over and powers the core
down. An interrupt from GIC, resumes the SPM state machine which brings the cpu
out of the low power mode.

- Add the device tree configuration for each of the SPM nodes. There is one for
each cpu. There is one for each cpu.

- Introduce the SoC driver interface layer to configure SPM per the core's idle
 state. To power down the cpu core, the SPM h/w needs to be set up correctly
to power down the core, when the core executes WFI. Linux is expected to call
into Secure Monitor to power down the core. At reset, the core will start in
seure mode and will be returned back to Linux. 

- Add CPUIDLE driver for QCOM cpus. The cpuidle driver uses the SoC interface
layer to configure the SPM to allow Krait to be powered down. The cpuidle driver
is based on ARM idle-state framework for cpuidle drivers.

- Provide device configuration for 8074 SoC. Current support is for WFI and
standalone power collapse, which powers only the core independent of the
other cores and caches.

Thanks,
Lina

Lina Iyer (5):
  qcom: spm: Add Subsystem Power Manager driver
  arm: dts: qcom: Add SPM device bindings for 8974
  qcom: msm-pm: Add cpu low power mode functions
  qcom: cpuidle: Add cpuidle driver for QCOM cpus
  arm: dts: qcom: Add idle states device nodes for 8974

 .../bindings/arm/msm/qcom,idle-state.txt           |  72 ++++
 Documentation/devicetree/bindings/arm/msm/spm.txt  |  43 +++
 arch/arm/boot/dts/qcom-msm8974-pm.dtsi             |  69 ++++
 arch/arm/boot/dts/qcom-msm8974.dtsi                |  30 +-
 drivers/cpuidle/Kconfig.arm                        |   7 +
 drivers/cpuidle/Makefile                           |   1 +
 drivers/cpuidle/cpuidle-qcom.c                     |  89 +++++
 drivers/soc/qcom/Kconfig                           |   8 +
 drivers/soc/qcom/Makefile                          |   1 +
 drivers/soc/qcom/msm-pm.c                          | 106 ++++++
 drivers/soc/qcom/spm.c                             | 388 +++++++++++++++++++++
 include/soc/qcom/pm.h                              |  31 ++
 include/soc/qcom/spm.h                             |  38 ++
 13 files changed, 879 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
 create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
 create mode 100644 drivers/cpuidle/cpuidle-qcom.c
 create mode 100644 drivers/soc/qcom/msm-pm.c
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 include/soc/qcom/pm.h
 create mode 100644 include/soc/qcom/spm.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 0/5] QCOM 8074 cpuidle driver
@ 2014-09-23 23:51 ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

Changes since v5:
[ https://www.mail-archive.com/linux-arm-msm at vger.kernel.org/msg10559.html ]
- Merge spm-devices.c and spm.c into one file and one patch
	- Simplify implementation of the driver.
	- Update documentation mapping the DT properties with corresponding
	  SPM register information.
- Removed scm-boot changes for quad core warmboot, its has been pulled in.

Changes since v4:
[ https://www.mail-archive.com/linux-arm-msm at vger.kernel.org/msg10327.html ]
- Update to the v8 of ARM generic idle states patches
- Use platform device model for cpuidle-qcom
- Clean up msm-pm.c to remove unnecessary include files and functions
- Update commit text and documentation for all idle states
- Remove scm-boot relocate patch from this series, submitted earlier
[ https://www.mail-archive.com/linux-arm-msm at vger.kernel.org/msg10518.html ]

Changes since v3:
[ https://www.mail-archive.com/linux-arm-msm at vger.kernel.org/msg10288.html ]
- Fix CONFIG_QCOM_PM Kconfig as bool
- More clean ups in spm.c and spm-devices.c
	- Removed and re-organized data structures to make initialization simple
	- Remove export of sequence flush functions
	- Updated commit text
	- Comments for use of barriers.
- Rebase on top of 3.17-rc1

Changes since v2:
[ https://www.mail-archive.com/linux-arm-msm at vger.kernel.org/msg10148.html ]
- Prune all the drivers to support basic WFI and power down cpuidle
  functionality. Remove debug code.
- Integrate KConfig changes into the drivers' patches.
- Use Lorenzo's ARM idle-states patches as the basis for reading cpuidle
  c-states from DT.
  [ http://marc.info/?l=linux-pm&m=140794514812383&w=2 ]
- Incorporate review comments
- Rebase on top of 3.16

Changes since v1/RFC:
[ https://www.mail-archive.com/linux-arm-msm at vger.kernel.org/msg10065.html ]
- Remove hotplug from the patch series. Will submit it separately.
- Fix SPM drivers per the review comments
- Modify patch sequence to compile SPM drivers independent of msm-pm, so as to
  allow wfi() calls to use SPM even without SoC interface driver.

8074 like any ARM SoC can do architectural clock gating, that helps save on
power, but not enough of leakage power.  Leakage power of the SoC can be
further reduced by turning off power to the core. To aid this, every core (cpu
and L2) is accompanied by a Sub-system Power Manager (SPM), that can be
configured to indicate the low power mode, the core would be put into and the
SPM programs the peripheral h/w accordingly to enter low power and turn off the
power rail to the core.

The idle invocation hierarchy - 

	CPUIDLE
	|
	cpuidle-qcom.c [CPUIdle driver]
	|
	------>	msm-pm.c [SoC Interface layer for QCOM chipsets]
		|
		------> spm.c [SPM driver]
		|
		------> scm-boot.c [SCM interface layer]		
			|
------------------------|--------------------------
(EL)			Secure Monitor Code
			|
			|
			wfi(); 
------------------------|--------------------------
(HW)			[CPU] {clock gate}
			|
			-----> [SPM] {statemachine}
			

The patchset does the following -

- Introduce the SPM driver to control power to the core. The SPM h/w IP works
in conjunction with the Krait CPU/L2. When the core executes WFI instruction,
the core is clockgated and the SPM state machine takes over and powers the core
down. An interrupt from GIC, resumes the SPM state machine which brings the cpu
out of the low power mode.

- Add the device tree configuration for each of the SPM nodes. There is one for
each cpu. There is one for each cpu.

- Introduce the SoC driver interface layer to configure SPM per the core's idle
 state. To power down the cpu core, the SPM h/w needs to be set up correctly
to power down the core, when the core executes WFI. Linux is expected to call
into Secure Monitor to power down the core. At reset, the core will start in
seure mode and will be returned back to Linux. 

- Add CPUIDLE driver for QCOM cpus. The cpuidle driver uses the SoC interface
layer to configure the SPM to allow Krait to be powered down. The cpuidle driver
is based on ARM idle-state framework for cpuidle drivers.

- Provide device configuration for 8074 SoC. Current support is for WFI and
standalone power collapse, which powers only the core independent of the
other cores and caches.

Thanks,
Lina

Lina Iyer (5):
  qcom: spm: Add Subsystem Power Manager driver
  arm: dts: qcom: Add SPM device bindings for 8974
  qcom: msm-pm: Add cpu low power mode functions
  qcom: cpuidle: Add cpuidle driver for QCOM cpus
  arm: dts: qcom: Add idle states device nodes for 8974

 .../bindings/arm/msm/qcom,idle-state.txt           |  72 ++++
 Documentation/devicetree/bindings/arm/msm/spm.txt  |  43 +++
 arch/arm/boot/dts/qcom-msm8974-pm.dtsi             |  69 ++++
 arch/arm/boot/dts/qcom-msm8974.dtsi                |  30 +-
 drivers/cpuidle/Kconfig.arm                        |   7 +
 drivers/cpuidle/Makefile                           |   1 +
 drivers/cpuidle/cpuidle-qcom.c                     |  89 +++++
 drivers/soc/qcom/Kconfig                           |   8 +
 drivers/soc/qcom/Makefile                          |   1 +
 drivers/soc/qcom/msm-pm.c                          | 106 ++++++
 drivers/soc/qcom/spm.c                             | 388 +++++++++++++++++++++
 include/soc/qcom/pm.h                              |  31 ++
 include/soc/qcom/spm.h                             |  38 ++
 13 files changed, 879 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
 create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
 create mode 100644 drivers/cpuidle/cpuidle-qcom.c
 create mode 100644 drivers/soc/qcom/msm-pm.c
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 include/soc/qcom/pm.h
 create mode 100644 include/soc/qcom/spm.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-23 23:51 ` Lina Iyer
@ 2014-09-23 23:51   ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm, Lina Iyer

Based on work by many authors, available at codeaurora.org

SPM is a hardware block that controls the peripheral logic surrounding
the application cores (cpu/l$). When the core executes WFI instruction,
the SPM takes over the putting the core in low power state as
configured. The wake up for the SPM is an interrupt at the GIC, which
then completes the rest of low power mode sequence and brings the core
out of low power mode.

The SPM has a set of control registers that configure the SPMs
individually based on the type of the core and the runtime conditions.
SPM is a finite state machine block to which a sequence is provided and
it interprets the bytes  and executes them in sequence. Each low power
mode that the core can enter into is provided to the SPM as a sequence.

Configure the SPM to set the core (cpu or L2) into its low power mode,
the index of the first command in the sequence is set in the SPM_CTL
register. When the core executes ARM wfi instruction, it triggers the
SPM state machine to start executing from that index. The SPM state
machine waits until the interrupt occurs and starts executing the rest
of the sequence until it hits the end of the sequence. The end of the
sequence jumps the core out of its low power mode.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
[lina: simplify the driver for initial submission, clean up and update
commit text]
---
 Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
 drivers/soc/qcom/Kconfig                          |   8 +
 drivers/soc/qcom/Makefile                         |   1 +
 drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
 include/soc/qcom/spm.h                            |  38 +++
 5 files changed, 478 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 include/soc/qcom/spm.h

diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
new file mode 100644
index 0000000..2ff2454
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
@@ -0,0 +1,43 @@
+* Subsystem Power Manager (SPM)
+
+Qualcomm Snapdragons have SPM hardware blocks to control the Application
+Processor Sub-System power. These SPM blocks run individual state machine
+to determine what the core (L2 or Krait/Scorpion) would do when the WFI
+instruction is executed by the core.
+
+The devicetree representation of the SPM block should be:
+
+Required properties
+
+- compatible: Must be -
+		 "qcom,spm-v2.1"
+- reg: The physical address and the size of the SPM's memory mapped registers
+- qcom,cpu: phandle for the CPU that the SPM block is attached to.
+	This field is required on only for SPMs that control the CPU.
+- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
+	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
+- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
+	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
+- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
+	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
+
+Optional properties
+
+- qcom,saw2-spm-cmd-wfi: The WFI command sequence
+- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
+
+Example:
+	spm@f9089000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,cpu = <&CPU0>;
+		qcom,saw2-clk-div = <0x1>;
+		qcom,saw2-delays = <0x20000400>;
+		qcom,saw2-enable = <0x1>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
+				a0 b0 03 68 70 3b 92 a0 b0
+				82 2b 50 10 30 02 22 30 0f];
+	};
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 7dcd554..cd249c4 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -11,3 +11,11 @@ config QCOM_GSBI
 
 config QCOM_SCM
 	bool
+
+config QCOM_PM
+	bool "Qualcomm Power Management"
+	depends on PM && ARCH_QCOM
+	help
+	  QCOM Platform specific power driver to manage cores and L2 low power
+	  modes. It interface with various system drivers to put the cores in
+	  low power modes.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 70d52ed..20b329f 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
+obj-$(CONFIG_QCOM_PM)	+=	spm.o
 CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
 obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
new file mode 100644
index 0000000..1fa6a96
--- /dev/null
+++ b/drivers/soc/qcom/spm.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <soc/qcom/spm.h>
+
+#define NUM_SEQ_ENTRY 32
+#define SPM_CTL_ENABLE BIT(0)
+
+enum {
+	MSM_SPM_REG_SAW2_CFG,
+	MSM_SPM_REG_SAW2_AVS_CTL,
+	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
+	MSM_SPM_REG_SAW2_SPM_CTL,
+	MSM_SPM_REG_SAW2_PMIC_DLY,
+	MSM_SPM_REG_SAW2_AVS_LIMIT,
+	MSM_SPM_REG_SAW2_AVS_DLY,
+	MSM_SPM_REG_SAW2_SPM_DLY,
+	MSM_SPM_REG_SAW2_PMIC_DATA_0,
+	MSM_SPM_REG_SAW2_PMIC_DATA_1,
+	MSM_SPM_REG_SAW2_PMIC_DATA_2,
+	MSM_SPM_REG_SAW2_PMIC_DATA_3,
+	MSM_SPM_REG_SAW2_PMIC_DATA_4,
+	MSM_SPM_REG_SAW2_PMIC_DATA_5,
+	MSM_SPM_REG_SAW2_PMIC_DATA_6,
+	MSM_SPM_REG_SAW2_PMIC_DATA_7,
+	MSM_SPM_REG_SAW2_RST,
+
+	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
+
+	MSM_SPM_REG_SAW2_ID,
+	MSM_SPM_REG_SAW2_SECURE,
+	MSM_SPM_REG_SAW2_STS0,
+	MSM_SPM_REG_SAW2_STS1,
+	MSM_SPM_REG_SAW2_STS2,
+	MSM_SPM_REG_SAW2_VCTL,
+	MSM_SPM_REG_SAW2_SEQ_ENTRY,
+	MSM_SPM_REG_SAW2_SPM_STS,
+	MSM_SPM_REG_SAW2_AVS_STS,
+	MSM_SPM_REG_SAW2_PMIC_STS,
+	MSM_SPM_REG_SAW2_VERSION,
+
+	MSM_SPM_REG_NR,
+};
+
+static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
+	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
+	[MSM_SPM_REG_SAW2_ID]			= 0x04,
+	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
+	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
+	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
+	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
+	[MSM_SPM_REG_SAW2_RST]			= 0x18,
+	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
+	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
+	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
+	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
+	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
+	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
+	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
+	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
+	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
+};
+
+struct spm_of {
+	char *key;
+	u32 id;
+};
+
+struct msm_spm_mode {
+	u32 mode;
+	u32 start_addr;
+};
+
+struct msm_spm_driver_data {
+	void __iomem *reg_base_addr;
+	u32 *reg_offsets;
+	struct msm_spm_mode *modes;
+	u32 num_modes;
+};
+
+struct msm_spm_device {
+	bool initialized;
+	struct msm_spm_driver_data drv;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
+
+static const struct of_device_id msm_spm_match_table[] __initconst;
+
+static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
+						u32 mode)
+{
+	int i;
+	u32 start_addr = 0;
+	u32 ctl_val;
+
+	for (i = 0; i < drv->num_modes; i++) {
+		if (drv->modes[i].mode == mode) {
+			start_addr = drv->modes[i].start_addr;
+			break;
+		}
+	}
+
+	if (i == drv->num_modes)
+		return -EINVAL;
+
+	/* Update bits 10:4 in the SPM CTL register */
+	ctl_val = readl_relaxed(drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+	start_addr &= 0x7F;
+	start_addr <<= 4;
+	ctl_val &= 0xFFFFF80F;
+	ctl_val |= start_addr;
+	writel_relaxed(ctl_val, drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+	/* Ensure we have written the start address */
+	wmb();
+
+	return 0;
+}
+
+static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
+					bool enable)
+{
+	u32 value = enable ? 0x01 : 0x00;
+	u32 ctl_val;
+
+	ctl_val = readl_relaxed(drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+
+	/* Update SPM_CTL to enable/disable the SPM */
+	if ((ctl_val & SPM_CTL_ENABLE) != value) {
+		/* Clear the existing value and update */
+		ctl_val &= ~0x1;
+		ctl_val |= value;
+		writel_relaxed(ctl_val, drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+
+		/* Ensure we have enabled/disabled before returning */
+		wmb();
+	}
+
+	return 0;
+}
+
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ */
+int msm_spm_set_low_power_mode(u32 mode)
+{
+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
+	int ret = -EINVAL;
+
+	if (!dev->initialized)
+		return -ENXIO;
+
+	if (mode == MSM_SPM_MODE_DISABLED)
+		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
+	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
+		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
+
+static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
+{
+	u32 cmd_w;
+	u32 offset_w = *offset / 4;
+	u8 last_cmd;
+
+	while (1) {
+		int i;
+
+		cmd_w = 0;
+		last_cmd = 0;
+		cmd_w = reg_seq_entry[offset_w];
+
+		for (i = (*offset % 4); i < 4; i++) {
+			last_cmd = *(cmd++);
+			cmd_w |=  last_cmd << (i * 8);
+			(*offset)++;
+			if (last_cmd == 0x0f)
+				break;
+		}
+
+		reg_seq_entry[offset_w++] = cmd_w;
+		if (last_cmd == 0x0f)
+			break;
+	}
+}
+
+static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
+			struct platform_device *pdev)
+{
+	int i;
+	u8 *cmd;
+	void *addr;
+	u32 val;
+	u32 count = 0;
+	int offset = 0;
+	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
+	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
+	struct msm_spm_driver_data *drv = &spm_dev->drv;
+
+	/* SPM sleep sequences */
+	struct spm_of mode_of_data[] = {
+		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
+		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
+	};
+
+	/**
+	 * Compose the u32 array based on the individual bytes of the SPM
+	 * sequence for each low power mode that we read from the DT.
+	 * The sequences are appended if there is space available in the
+	 * u32 after the end of the previous sequence.
+	 */
+
+	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
+		cmd = (u8 *)of_get_property(pdev->dev.of_node,
+						mode_of_data[i].key, &val);
+		if (!cmd)
+			continue;
+		/* The last in the sequence should be 0x0F */
+		if (cmd[val - 1] != 0x0F)
+			continue;
+		modes[count].mode = mode_of_data[i].id;
+		modes[count].start_addr = offset;
+		append_seq_data(&sequences[0], cmd, &offset);
+		count++;
+	}
+
+	/* Write the idle state sequences to SPM */
+	drv->modes = devm_kcalloc(&pdev->dev, count,
+					sizeof(modes[0]), GFP_KERNEL);
+	if (!drv->modes)
+		return -ENOMEM;
+
+	drv->num_modes = count;
+	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
+
+	/* Flush the integer array */
+	addr = drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
+	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
+		writel_relaxed(sequences[i], addr);
+
+	/* Ensure we flush the writes */
+	wmb();
+
+	return 0;
+}
+
+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
+{
+	struct msm_spm_device *dev = NULL;
+	struct device_node *cpu_node;
+	u32 cpu;
+
+	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
+	if (cpu_node) {
+		for_each_possible_cpu(cpu) {
+			if (of_get_cpu_node(cpu, NULL) == cpu_node)
+				dev = &per_cpu(msm_cpu_spm_device, cpu);
+		}
+	}
+
+	return dev;
+}
+
+static int msm_spm_dev_probe(struct platform_device *pdev)
+{
+	int ret;
+	int i;
+	u32 val;
+	struct msm_spm_device *spm_dev;
+	struct resource *res;
+	const struct of_device_id *match_id;
+
+	/* SPM Configuration registers */
+	struct spm_of spm_of_data[] = {
+		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
+		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
+		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
+	};
+
+	 /* Get the right SPM device */
+	spm_dev = msm_spm_get_device(pdev);
+	if (IS_ERR_OR_NULL(spm_dev))
+		return -EINVAL;
+
+	/* Get the SPM start address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto fail;
+	}
+	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!spm_dev->drv.reg_base_addr) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
+	if (!match_id)
+		return -ENODEV;
+
+	/* Use the register offsets for the SPM version in use */
+	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
+	if (!spm_dev->drv.reg_offsets)
+		return -EFAULT;
+
+	/* Read the SPM idle state sequences */
+	ret = msm_spm_seq_init(spm_dev, pdev);
+	if (ret)
+		return ret;
+
+	/* Read the SPM register values */
+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+		ret = of_property_read_u32(pdev->dev.of_node,
+					spm_of_data[i].key, &val);
+		if (ret)
+			continue;
+		writel_relaxed(val, spm_dev->drv.reg_base_addr +
+				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
+	}
+
+	/* Flush all writes */
+	wmb();
+
+	spm_dev->initialized = true;
+	return ret;
+fail:
+	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
+	return ret;
+}
+
+static const struct of_device_id msm_spm_match_table[] __initconst = {
+	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
+	{ },
+};
+
+
+static struct platform_driver msm_spm_device_driver = {
+	.probe = msm_spm_dev_probe,
+	.driver = {
+		.name = "spm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_spm_match_table,
+	},
+};
+
+static int __init msm_spm_device_init(void)
+{
+	return platform_driver_register(&msm_spm_device_driver);
+}
+device_initcall(msm_spm_device_init);
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
new file mode 100644
index 0000000..29686ef
--- /dev/null
+++ b/include/soc/qcom/spm.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCOM_SPM_H
+#define __QCOM_SPM_H
+
+enum {
+	MSM_SPM_MODE_DISABLED,
+	MSM_SPM_MODE_CLOCK_GATING,
+	MSM_SPM_MODE_RETENTION,
+	MSM_SPM_MODE_GDHS,
+	MSM_SPM_MODE_POWER_COLLAPSE,
+	MSM_SPM_MODE_NR
+};
+
+struct msm_spm_device;
+
+#if defined(CONFIG_QCOM_PM)
+
+int msm_spm_set_low_power_mode(u32 mode);
+
+#else
+
+static inline int msm_spm_set_low_power_mode(u32 mode)
+{ return -ENOSYS; }
+
+#endif  /* CONFIG_QCOM_PM */
+
+#endif  /* __QCOM_SPM_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-23 23:51   ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

Based on work by many authors, available at codeaurora.org

SPM is a hardware block that controls the peripheral logic surrounding
the application cores (cpu/l$). When the core executes WFI instruction,
the SPM takes over the putting the core in low power state as
configured. The wake up for the SPM is an interrupt at the GIC, which
then completes the rest of low power mode sequence and brings the core
out of low power mode.

The SPM has a set of control registers that configure the SPMs
individually based on the type of the core and the runtime conditions.
SPM is a finite state machine block to which a sequence is provided and
it interprets the bytes  and executes them in sequence. Each low power
mode that the core can enter into is provided to the SPM as a sequence.

Configure the SPM to set the core (cpu or L2) into its low power mode,
the index of the first command in the sequence is set in the SPM_CTL
register. When the core executes ARM wfi instruction, it triggers the
SPM state machine to start executing from that index. The SPM state
machine waits until the interrupt occurs and starts executing the rest
of the sequence until it hits the end of the sequence. The end of the
sequence jumps the core out of its low power mode.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
[lina: simplify the driver for initial submission, clean up and update
commit text]
---
 Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
 drivers/soc/qcom/Kconfig                          |   8 +
 drivers/soc/qcom/Makefile                         |   1 +
 drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
 include/soc/qcom/spm.h                            |  38 +++
 5 files changed, 478 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 include/soc/qcom/spm.h

diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
new file mode 100644
index 0000000..2ff2454
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
@@ -0,0 +1,43 @@
+* Subsystem Power Manager (SPM)
+
+Qualcomm Snapdragons have SPM hardware blocks to control the Application
+Processor Sub-System power. These SPM blocks run individual state machine
+to determine what the core (L2 or Krait/Scorpion) would do when the WFI
+instruction is executed by the core.
+
+The devicetree representation of the SPM block should be:
+
+Required properties
+
+- compatible: Must be -
+		 "qcom,spm-v2.1"
+- reg: The physical address and the size of the SPM's memory mapped registers
+- qcom,cpu: phandle for the CPU that the SPM block is attached to.
+	This field is required on only for SPMs that control the CPU.
+- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
+	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
+- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
+	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
+- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
+	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
+
+Optional properties
+
+- qcom,saw2-spm-cmd-wfi: The WFI command sequence
+- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
+
+Example:
+	spm at f9089000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,cpu = <&CPU0>;
+		qcom,saw2-clk-div = <0x1>;
+		qcom,saw2-delays = <0x20000400>;
+		qcom,saw2-enable = <0x1>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
+				a0 b0 03 68 70 3b 92 a0 b0
+				82 2b 50 10 30 02 22 30 0f];
+	};
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 7dcd554..cd249c4 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -11,3 +11,11 @@ config QCOM_GSBI
 
 config QCOM_SCM
 	bool
+
+config QCOM_PM
+	bool "Qualcomm Power Management"
+	depends on PM && ARCH_QCOM
+	help
+	  QCOM Platform specific power driver to manage cores and L2 low power
+	  modes. It interface with various system drivers to put the cores in
+	  low power modes.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 70d52ed..20b329f 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
+obj-$(CONFIG_QCOM_PM)	+=	spm.o
 CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
 obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
new file mode 100644
index 0000000..1fa6a96
--- /dev/null
+++ b/drivers/soc/qcom/spm.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <soc/qcom/spm.h>
+
+#define NUM_SEQ_ENTRY 32
+#define SPM_CTL_ENABLE BIT(0)
+
+enum {
+	MSM_SPM_REG_SAW2_CFG,
+	MSM_SPM_REG_SAW2_AVS_CTL,
+	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
+	MSM_SPM_REG_SAW2_SPM_CTL,
+	MSM_SPM_REG_SAW2_PMIC_DLY,
+	MSM_SPM_REG_SAW2_AVS_LIMIT,
+	MSM_SPM_REG_SAW2_AVS_DLY,
+	MSM_SPM_REG_SAW2_SPM_DLY,
+	MSM_SPM_REG_SAW2_PMIC_DATA_0,
+	MSM_SPM_REG_SAW2_PMIC_DATA_1,
+	MSM_SPM_REG_SAW2_PMIC_DATA_2,
+	MSM_SPM_REG_SAW2_PMIC_DATA_3,
+	MSM_SPM_REG_SAW2_PMIC_DATA_4,
+	MSM_SPM_REG_SAW2_PMIC_DATA_5,
+	MSM_SPM_REG_SAW2_PMIC_DATA_6,
+	MSM_SPM_REG_SAW2_PMIC_DATA_7,
+	MSM_SPM_REG_SAW2_RST,
+
+	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
+
+	MSM_SPM_REG_SAW2_ID,
+	MSM_SPM_REG_SAW2_SECURE,
+	MSM_SPM_REG_SAW2_STS0,
+	MSM_SPM_REG_SAW2_STS1,
+	MSM_SPM_REG_SAW2_STS2,
+	MSM_SPM_REG_SAW2_VCTL,
+	MSM_SPM_REG_SAW2_SEQ_ENTRY,
+	MSM_SPM_REG_SAW2_SPM_STS,
+	MSM_SPM_REG_SAW2_AVS_STS,
+	MSM_SPM_REG_SAW2_PMIC_STS,
+	MSM_SPM_REG_SAW2_VERSION,
+
+	MSM_SPM_REG_NR,
+};
+
+static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
+	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
+	[MSM_SPM_REG_SAW2_ID]			= 0x04,
+	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
+	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
+	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
+	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
+	[MSM_SPM_REG_SAW2_RST]			= 0x18,
+	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
+	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
+	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
+	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
+	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
+	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
+	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
+	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
+	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
+	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
+};
+
+struct spm_of {
+	char *key;
+	u32 id;
+};
+
+struct msm_spm_mode {
+	u32 mode;
+	u32 start_addr;
+};
+
+struct msm_spm_driver_data {
+	void __iomem *reg_base_addr;
+	u32 *reg_offsets;
+	struct msm_spm_mode *modes;
+	u32 num_modes;
+};
+
+struct msm_spm_device {
+	bool initialized;
+	struct msm_spm_driver_data drv;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
+
+static const struct of_device_id msm_spm_match_table[] __initconst;
+
+static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
+						u32 mode)
+{
+	int i;
+	u32 start_addr = 0;
+	u32 ctl_val;
+
+	for (i = 0; i < drv->num_modes; i++) {
+		if (drv->modes[i].mode == mode) {
+			start_addr = drv->modes[i].start_addr;
+			break;
+		}
+	}
+
+	if (i == drv->num_modes)
+		return -EINVAL;
+
+	/* Update bits 10:4 in the SPM CTL register */
+	ctl_val = readl_relaxed(drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+	start_addr &= 0x7F;
+	start_addr <<= 4;
+	ctl_val &= 0xFFFFF80F;
+	ctl_val |= start_addr;
+	writel_relaxed(ctl_val, drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+	/* Ensure we have written the start address */
+	wmb();
+
+	return 0;
+}
+
+static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
+					bool enable)
+{
+	u32 value = enable ? 0x01 : 0x00;
+	u32 ctl_val;
+
+	ctl_val = readl_relaxed(drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+
+	/* Update SPM_CTL to enable/disable the SPM */
+	if ((ctl_val & SPM_CTL_ENABLE) != value) {
+		/* Clear the existing value and update */
+		ctl_val &= ~0x1;
+		ctl_val |= value;
+		writel_relaxed(ctl_val, drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
+
+		/* Ensure we have enabled/disabled before returning */
+		wmb();
+	}
+
+	return 0;
+}
+
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ */
+int msm_spm_set_low_power_mode(u32 mode)
+{
+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
+	int ret = -EINVAL;
+
+	if (!dev->initialized)
+		return -ENXIO;
+
+	if (mode == MSM_SPM_MODE_DISABLED)
+		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
+	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
+		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
+
+static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
+{
+	u32 cmd_w;
+	u32 offset_w = *offset / 4;
+	u8 last_cmd;
+
+	while (1) {
+		int i;
+
+		cmd_w = 0;
+		last_cmd = 0;
+		cmd_w = reg_seq_entry[offset_w];
+
+		for (i = (*offset % 4); i < 4; i++) {
+			last_cmd = *(cmd++);
+			cmd_w |=  last_cmd << (i * 8);
+			(*offset)++;
+			if (last_cmd == 0x0f)
+				break;
+		}
+
+		reg_seq_entry[offset_w++] = cmd_w;
+		if (last_cmd == 0x0f)
+			break;
+	}
+}
+
+static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
+			struct platform_device *pdev)
+{
+	int i;
+	u8 *cmd;
+	void *addr;
+	u32 val;
+	u32 count = 0;
+	int offset = 0;
+	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
+	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
+	struct msm_spm_driver_data *drv = &spm_dev->drv;
+
+	/* SPM sleep sequences */
+	struct spm_of mode_of_data[] = {
+		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
+		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
+	};
+
+	/**
+	 * Compose the u32 array based on the individual bytes of the SPM
+	 * sequence for each low power mode that we read from the DT.
+	 * The sequences are appended if there is space available in the
+	 * u32 after the end of the previous sequence.
+	 */
+
+	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
+		cmd = (u8 *)of_get_property(pdev->dev.of_node,
+						mode_of_data[i].key, &val);
+		if (!cmd)
+			continue;
+		/* The last in the sequence should be 0x0F */
+		if (cmd[val - 1] != 0x0F)
+			continue;
+		modes[count].mode = mode_of_data[i].id;
+		modes[count].start_addr = offset;
+		append_seq_data(&sequences[0], cmd, &offset);
+		count++;
+	}
+
+	/* Write the idle state sequences to SPM */
+	drv->modes = devm_kcalloc(&pdev->dev, count,
+					sizeof(modes[0]), GFP_KERNEL);
+	if (!drv->modes)
+		return -ENOMEM;
+
+	drv->num_modes = count;
+	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
+
+	/* Flush the integer array */
+	addr = drv->reg_base_addr +
+			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
+	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
+		writel_relaxed(sequences[i], addr);
+
+	/* Ensure we flush the writes */
+	wmb();
+
+	return 0;
+}
+
+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
+{
+	struct msm_spm_device *dev = NULL;
+	struct device_node *cpu_node;
+	u32 cpu;
+
+	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
+	if (cpu_node) {
+		for_each_possible_cpu(cpu) {
+			if (of_get_cpu_node(cpu, NULL) == cpu_node)
+				dev = &per_cpu(msm_cpu_spm_device, cpu);
+		}
+	}
+
+	return dev;
+}
+
+static int msm_spm_dev_probe(struct platform_device *pdev)
+{
+	int ret;
+	int i;
+	u32 val;
+	struct msm_spm_device *spm_dev;
+	struct resource *res;
+	const struct of_device_id *match_id;
+
+	/* SPM Configuration registers */
+	struct spm_of spm_of_data[] = {
+		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
+		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
+		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
+	};
+
+	 /* Get the right SPM device */
+	spm_dev = msm_spm_get_device(pdev);
+	if (IS_ERR_OR_NULL(spm_dev))
+		return -EINVAL;
+
+	/* Get the SPM start address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto fail;
+	}
+	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!spm_dev->drv.reg_base_addr) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
+	if (!match_id)
+		return -ENODEV;
+
+	/* Use the register offsets for the SPM version in use */
+	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
+	if (!spm_dev->drv.reg_offsets)
+		return -EFAULT;
+
+	/* Read the SPM idle state sequences */
+	ret = msm_spm_seq_init(spm_dev, pdev);
+	if (ret)
+		return ret;
+
+	/* Read the SPM register values */
+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+		ret = of_property_read_u32(pdev->dev.of_node,
+					spm_of_data[i].key, &val);
+		if (ret)
+			continue;
+		writel_relaxed(val, spm_dev->drv.reg_base_addr +
+				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
+	}
+
+	/* Flush all writes */
+	wmb();
+
+	spm_dev->initialized = true;
+	return ret;
+fail:
+	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
+	return ret;
+}
+
+static const struct of_device_id msm_spm_match_table[] __initconst = {
+	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
+	{ },
+};
+
+
+static struct platform_driver msm_spm_device_driver = {
+	.probe = msm_spm_dev_probe,
+	.driver = {
+		.name = "spm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_spm_match_table,
+	},
+};
+
+static int __init msm_spm_device_init(void)
+{
+	return platform_driver_register(&msm_spm_device_driver);
+}
+device_initcall(msm_spm_device_init);
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
new file mode 100644
index 0000000..29686ef
--- /dev/null
+++ b/include/soc/qcom/spm.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCOM_SPM_H
+#define __QCOM_SPM_H
+
+enum {
+	MSM_SPM_MODE_DISABLED,
+	MSM_SPM_MODE_CLOCK_GATING,
+	MSM_SPM_MODE_RETENTION,
+	MSM_SPM_MODE_GDHS,
+	MSM_SPM_MODE_POWER_COLLAPSE,
+	MSM_SPM_MODE_NR
+};
+
+struct msm_spm_device;
+
+#if defined(CONFIG_QCOM_PM)
+
+int msm_spm_set_low_power_mode(u32 mode);
+
+#else
+
+static inline int msm_spm_set_low_power_mode(u32 mode)
+{ return -ENOSYS; }
+
+#endif  /* CONFIG_QCOM_PM */
+
+#endif  /* __QCOM_SPM_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-23 23:51 ` Lina Iyer
@ 2014-09-23 23:51   ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm, Lina Iyer

Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
power manager and controls the logic around the cores (cpu and L2).

Each core has an instance of SPM and controls only that core. Each cpu
SPM is configured to support WFI and SPC (standalone-power collapse).

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
 2 files changed, 75 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi

diff --git a/arch/arm/boot/dts/qcom-msm8974-pm.dtsi b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
new file mode 100644
index 0000000..bbfb1d5
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
@@ -0,0 +1,69 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	spm@f9089000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,cpu = <&CPU0>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+
+	spm@f9099000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9099000 0x1000>;
+		qcom,cpu = <&CPU1>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+
+	spm@f90a9000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf90a9000 0x1000>;
+		qcom,cpu = <&CPU2>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+
+	spm@f90b9000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf90b9000 0x1000>;
+		qcom,cpu = <&CPU3>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+};
diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 69dca2a..0580bc2 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -14,7 +14,7 @@
 		#size-cells = <0>;
 		interrupts = <1 9 0xf04>;
 
-		cpu@0 {
+		CPU0: cpu@0 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -23,7 +23,7 @@
 			qcom,acc = <&acc0>;
 		};
 
-		cpu@1 {
+		CPU1: cpu@1 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -32,7 +32,7 @@
 			qcom,acc = <&acc1>;
 		};
 
-		cpu@2 {
+		CPU2: cpu@2 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -41,7 +41,7 @@
 			qcom,acc = <&acc2>;
 		};
 
-		cpu@3 {
+		CPU3: cpu@3 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -238,3 +238,5 @@
 		};
 	};
 };
+
+#include "qcom-msm8974-pm.dtsi"
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-23 23:51   ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
power manager and controls the logic around the cores (cpu and L2).

Each core has an instance of SPM and controls only that core. Each cpu
SPM is configured to support WFI and SPC (standalone-power collapse).

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
 2 files changed, 75 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi

diff --git a/arch/arm/boot/dts/qcom-msm8974-pm.dtsi b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
new file mode 100644
index 0000000..bbfb1d5
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
@@ -0,0 +1,69 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	spm at f9089000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,cpu = <&CPU0>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+
+	spm at f9099000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9099000 0x1000>;
+		qcom,cpu = <&CPU1>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+
+	spm at f90a9000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf90a9000 0x1000>;
+		qcom,cpu = <&CPU2>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+
+	spm at f90b9000 {
+		compatible = "qcom,spm-v2.1";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf90b9000 0x1000>;
+		qcom,cpu = <&CPU3>;
+		qcom,saw2-clk-div = <0x01>;
+		qcom,saw2-delays = <0x3C102800>;
+		qcom,saw2-enable = <0x01>;
+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+			30 06 26 30 0F];
+	};
+};
diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 69dca2a..0580bc2 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -14,7 +14,7 @@
 		#size-cells = <0>;
 		interrupts = <1 9 0xf04>;
 
-		cpu at 0 {
+		CPU0: cpu at 0 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -23,7 +23,7 @@
 			qcom,acc = <&acc0>;
 		};
 
-		cpu at 1 {
+		CPU1: cpu at 1 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -32,7 +32,7 @@
 			qcom,acc = <&acc1>;
 		};
 
-		cpu at 2 {
+		CPU2: cpu at 2 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -41,7 +41,7 @@
 			qcom,acc = <&acc2>;
 		};
 
-		cpu at 3 {
+		CPU3: cpu at 3 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
@@ -238,3 +238,5 @@
 		};
 	};
 };
+
+#include "qcom-msm8974-pm.dtsi"
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 3/5] qcom: msm-pm: Add cpu low power mode functions
  2014-09-23 23:51 ` Lina Iyer
@ 2014-09-23 23:51   ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm, Lina Iyer

Based on work by many authors, available at codeaurora.org

Add interface layer to abstract and handle hardware specific
functionality for executing various cpu low power modes in QCOM
chipsets.

QCOM cpus support multiple low power modes. The C-States are defined as -

    * WFI (clock gating)
    * Retention (clock gating at lower power)
    * Standalone Power Collapse (Standalone PC or SPC) - The power to
    	the cpu is lost and the cpu warmboots.
    * Power Collapse (PC) - Same as SPC, but is a cognizant of the fact
    	that the SoC may do deeper sleep modes.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
[lina: simplify the driver for an initial submission, add commit text
description of idle states]
---
 drivers/soc/qcom/Makefile |   2 +-
 drivers/soc/qcom/msm-pm.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++
 include/soc/qcom/pm.h     |  31 ++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soc/qcom/msm-pm.c
 create mode 100644 include/soc/qcom/pm.h

diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 20b329f..dc25007 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
-obj-$(CONFIG_QCOM_PM)	+=	spm.o
+obj-$(CONFIG_QCOM_PM)	+=	spm.o msm-pm.o
 CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
 obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
diff --git a/drivers/soc/qcom/msm-pm.c b/drivers/soc/qcom/msm-pm.c
new file mode 100644
index 0000000..8926c71
--- /dev/null
+++ b/drivers/soc/qcom/msm-pm.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+
+#include <soc/qcom/pm.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+#include <soc/qcom/spm.h>
+
+#define SCM_CMD_TERMINATE_PC	(0x2)
+#define SCM_FLUSH_FLAG_MASK	(0x3)
+
+static int set_up_boot_address(void *entry, int cpu)
+{
+	static int flags[NR_CPUS] = {
+		SCM_FLAG_WARMBOOT_CPU0,
+		SCM_FLAG_WARMBOOT_CPU1,
+		SCM_FLAG_WARMBOOT_CPU2,
+		SCM_FLAG_WARMBOOT_CPU3,
+	};
+	static DEFINE_PER_CPU(void *, last_known_entry);
+
+	if (entry == per_cpu(last_known_entry, cpu))
+		return 0;
+
+	per_cpu(last_known_entry, cpu) = entry;
+	return scm_set_boot_addr(virt_to_phys(entry), flags[cpu]);
+}
+
+static int msm_pm_collapse(unsigned long int unused)
+{
+	int ret;
+	enum msm_pm_l2_scm_flag flag;
+
+	ret = set_up_boot_address(cpu_resume, raw_smp_processor_id());
+	if (ret) {
+		pr_err("Failed to set warm boot address for cpu %d\n",
+				raw_smp_processor_id());
+		return ret;
+	}
+
+	flag = MSM_SCM_L2_ON & SCM_FLUSH_FLAG_MASK;
+	scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
+
+	return 0;
+}
+
+/**
+ * msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu
+ *
+ * @mode - sleep mode to enter
+ *
+ * The code should be called with interrupts disabled and on the core on
+ * which the low power mode is to be executed.
+ *
+ */
+static int msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode)
+{
+	int ret;
+
+	switch (mode) {
+	case MSM_PM_SLEEP_MODE_SPC:
+		msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE);
+		ret = cpu_suspend(0, msm_pm_collapse);
+		break;
+	default:
+	case MSM_PM_SLEEP_MODE_WFI:
+		msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING);
+		ret = cpu_do_idle();
+		break;
+	}
+
+	local_irq_enable();
+
+	return ret;
+}
+
+static struct platform_device qcom_cpuidle_device = {
+	.name              = "qcom_cpuidle",
+	.id                = -1,
+	.dev.platform_data = msm_cpu_pm_enter_sleep,
+};
+
+static int __init msm_pm_device_init(void)
+{
+	platform_device_register(&qcom_cpuidle_device);
+
+	return 0;
+}
+device_initcall(msm_pm_device_init);
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
new file mode 100644
index 0000000..c2f006b
--- /dev/null
+++ b/include/soc/qcom/pm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __QCOM_PM_H
+#define __QCOM_PM_H
+
+enum msm_pm_sleep_mode {
+	MSM_PM_SLEEP_MODE_WFI,
+	MSM_PM_SLEEP_MODE_RET,
+	MSM_PM_SLEEP_MODE_SPC,
+	MSM_PM_SLEEP_MODE_PC,
+	MSM_PM_SLEEP_MODE_NR,
+};
+
+enum msm_pm_l2_scm_flag {
+	MSM_SCM_L2_ON = 0,
+	MSM_SCM_L2_OFF = 1
+};
+
+#endif  /* __QCOM_PM_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 3/5] qcom: msm-pm: Add cpu low power mode functions
@ 2014-09-23 23:51   ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

Based on work by many authors, available at codeaurora.org

Add interface layer to abstract and handle hardware specific
functionality for executing various cpu low power modes in QCOM
chipsets.

QCOM cpus support multiple low power modes. The C-States are defined as -

    * WFI (clock gating)
    * Retention (clock gating at lower power)
    * Standalone Power Collapse (Standalone PC or SPC) - The power to
    	the cpu is lost and the cpu warmboots.
    * Power Collapse (PC) - Same as SPC, but is a cognizant of the fact
    	that the SoC may do deeper sleep modes.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
[lina: simplify the driver for an initial submission, add commit text
description of idle states]
---
 drivers/soc/qcom/Makefile |   2 +-
 drivers/soc/qcom/msm-pm.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++
 include/soc/qcom/pm.h     |  31 ++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soc/qcom/msm-pm.c
 create mode 100644 include/soc/qcom/pm.h

diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 20b329f..dc25007 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
-obj-$(CONFIG_QCOM_PM)	+=	spm.o
+obj-$(CONFIG_QCOM_PM)	+=	spm.o msm-pm.o
 CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
 obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
diff --git a/drivers/soc/qcom/msm-pm.c b/drivers/soc/qcom/msm-pm.c
new file mode 100644
index 0000000..8926c71
--- /dev/null
+++ b/drivers/soc/qcom/msm-pm.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+
+#include <soc/qcom/pm.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+#include <soc/qcom/spm.h>
+
+#define SCM_CMD_TERMINATE_PC	(0x2)
+#define SCM_FLUSH_FLAG_MASK	(0x3)
+
+static int set_up_boot_address(void *entry, int cpu)
+{
+	static int flags[NR_CPUS] = {
+		SCM_FLAG_WARMBOOT_CPU0,
+		SCM_FLAG_WARMBOOT_CPU1,
+		SCM_FLAG_WARMBOOT_CPU2,
+		SCM_FLAG_WARMBOOT_CPU3,
+	};
+	static DEFINE_PER_CPU(void *, last_known_entry);
+
+	if (entry == per_cpu(last_known_entry, cpu))
+		return 0;
+
+	per_cpu(last_known_entry, cpu) = entry;
+	return scm_set_boot_addr(virt_to_phys(entry), flags[cpu]);
+}
+
+static int msm_pm_collapse(unsigned long int unused)
+{
+	int ret;
+	enum msm_pm_l2_scm_flag flag;
+
+	ret = set_up_boot_address(cpu_resume, raw_smp_processor_id());
+	if (ret) {
+		pr_err("Failed to set warm boot address for cpu %d\n",
+				raw_smp_processor_id());
+		return ret;
+	}
+
+	flag = MSM_SCM_L2_ON & SCM_FLUSH_FLAG_MASK;
+	scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
+
+	return 0;
+}
+
+/**
+ * msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu
+ *
+ * @mode - sleep mode to enter
+ *
+ * The code should be called with interrupts disabled and on the core on
+ * which the low power mode is to be executed.
+ *
+ */
+static int msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode)
+{
+	int ret;
+
+	switch (mode) {
+	case MSM_PM_SLEEP_MODE_SPC:
+		msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE);
+		ret = cpu_suspend(0, msm_pm_collapse);
+		break;
+	default:
+	case MSM_PM_SLEEP_MODE_WFI:
+		msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING);
+		ret = cpu_do_idle();
+		break;
+	}
+
+	local_irq_enable();
+
+	return ret;
+}
+
+static struct platform_device qcom_cpuidle_device = {
+	.name              = "qcom_cpuidle",
+	.id                = -1,
+	.dev.platform_data = msm_cpu_pm_enter_sleep,
+};
+
+static int __init msm_pm_device_init(void)
+{
+	platform_device_register(&qcom_cpuidle_device);
+
+	return 0;
+}
+device_initcall(msm_pm_device_init);
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
new file mode 100644
index 0000000..c2f006b
--- /dev/null
+++ b/include/soc/qcom/pm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __QCOM_PM_H
+#define __QCOM_PM_H
+
+enum msm_pm_sleep_mode {
+	MSM_PM_SLEEP_MODE_WFI,
+	MSM_PM_SLEEP_MODE_RET,
+	MSM_PM_SLEEP_MODE_SPC,
+	MSM_PM_SLEEP_MODE_PC,
+	MSM_PM_SLEEP_MODE_NR,
+};
+
+enum msm_pm_l2_scm_flag {
+	MSM_SCM_L2_ON = 0,
+	MSM_SCM_L2_OFF = 1
+};
+
+#endif  /* __QCOM_PM_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 4/5] qcom: cpuidle: Add cpuidle driver for QCOM cpus
  2014-09-23 23:51 ` Lina Iyer
@ 2014-09-23 23:51   ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm, Lina Iyer

Add cpuidle driver interface to allow cpus to go into C-States. Use the
cpuidle DT interface common across ARM architectures to provide the
C-State information to the cpuidle framework.

Supported modes at this time are clock gating (wfi) and cpu power down
(Standalone PC or spc).

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 .../bindings/arm/msm/qcom,idle-state.txt           | 72 +++++++++++++++++
 drivers/cpuidle/Kconfig.arm                        |  7 ++
 drivers/cpuidle/Makefile                           |  1 +
 drivers/cpuidle/cpuidle-qcom.c                     | 89 ++++++++++++++++++++++
 4 files changed, 169 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
 create mode 100644 drivers/cpuidle/cpuidle-qcom.c

diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
new file mode 100644
index 0000000..47095b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
@@ -0,0 +1,72 @@
+QCOM Idle States for cpuidle driver
+
+ARM provides idle-state node to define the cpuidle states, as defined in [1].
+cpuidle-qcom is the cpuidle driver for Qualcomm SoCs and uses these idle
+states. Idle states have different enter/exit latency and residency values.
+The idle states supported by the QCOM SoC are defined as -
+
+    * WFI
+    * Retention
+    * Standalone Power Collapse (Standalone PC or SPC)
+    * Power Collapse (PC)
+
+WFI: WFI does a little more in addition to architectural clock gating.  ARM
+processors when execute the wfi instruction will gate their internal clocks.
+QCOM cpus use this instruction as a trigger for the SPM state machine. Usually
+with a cpu entering WFI, the SPM is configured to do clock-gating as well. The
+SPM state machine waits for the interrrupt to trigger the core back in to
+active. When all CPUs in the SoC, clock gate using the ARM wfi instruction, the
+second level cache usually can also clock gate sensing no cpu activity. When a
+cpu is ready to run, it needs the cache to be active before starting execution.
+Allowing the SPM to execute the clock gating statemachine and waiting for
+interrupt on behalf of the processor has a benefit of guaranteeing that the
+system state is conducive for the core to resume execution.
+
+Retention: Retention is a low power state where the core is clockgated and the
+memory and the registers associated with the core are retained.  The voltage
+may be reduced to the minimum value needed to keep the processor registers
+active. Retention is triggered when the core executes wfi instruction. The SPM
+should be configured to execute the retention sequence and would wait for
+interrupt, before restoring the cpu to execution state. Retention may have a
+slightly higher latency than WFI.
+
+Standalone PC: A cpu can power down and warmboot if there is a sufficient time
+between now and the next know wake up. SPC mode is used to indicate a core
+entering a power down state without consulting any other cpu or the system
+resources. This helps save power only on that core. Like WFI and Retention, the
+core executes wfi and the SPM programmed to do SPC would use the cpu control
+logic to power down the core's supply and restore it back when woken up by an
+interrupt.  Applying power and reseting the core causes the core to warmboot
+back into secure mode which trampolines the control back to the kernel. To
+enter a power down state the kernel needs to call into the secure layer which
+would then execute the ARM wfi instruction. Failing to do so, would result in a
+crash enforced by the warm boot code in the secure layer. On a SoC with
+write-back L1 cache, the cache would need to be flushed.
+
+Power Collapse: This state is similiar to the SPC mode, but distinguishes
+itself in the fact that the cpu acknowledges and permits the SoC to enter
+deeper sleep modes. In a hierarchical power domain SoC, this means L2 and other
+caches can be flushed, system bus, clocks - lowered, and SoC main XO turned off
+and voltages reduced, provided all cpus enter this state. In other words, it is
+a coupled idle state.  Since the span of low power modes possible at this state
+is vast, the exit latency and the residency of this low power mode would be
+considered high even though at a cpu level, this essentially is cpu power down.
+The SPM in this state also may handshake with the Resource power manager
+processor in the SoC to indicate a complete subsystem shut down.
+
+The idle-state for QCOM SoCs are distinguished by the compatible property of
+the node. They indicate to the cpuidle driver the entry point to use for
+cpuidle. The devicetree representation of the idle state should be -
+
+Required properties:
+
+- compatible: Must be "arm,idle-state"
+		and one of -
+			"qcom,idle-state-wfi",
+			"qcom,idle-state-ret",
+			"qcom,idle-state-spc",
+			"qcom,idle-state-pc",
+
+Other required and optional properties are specified in [1].
+
+[1]. Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 38cff69..6a9ee12 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -62,3 +62,10 @@ config ARM_MVEBU_V7_CPUIDLE
 	depends on ARCH_MVEBU
 	help
 	  Select this to enable cpuidle on Armada 370, 38x and XP processors.
+
+config ARM_QCOM_CPUIDLE
+	bool "CPU Idle drivers for Qualcomm processors"
+	depends on QCOM_PM
+	select DT_IDLE_STATES
+	help
+	  Select this to enable cpuidle for QCOM processors
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 4d177b9..6c222d5 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE)		+= cpuidle-zynq.o
 obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
 obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
 obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
+obj-$(CONFIG_ARM_QCOM_CPUIDLE)		+= cpuidle-qcom.o
 
 ###############################################################################
 # MIPS drivers
diff --git a/drivers/cpuidle/cpuidle-qcom.c b/drivers/cpuidle/cpuidle-qcom.c
new file mode 100644
index 0000000..d226ac1
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-qcom.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014, Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cpu_pm.h>
+#include <linux/cpuidle.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include <soc/qcom/pm.h>
+#include "dt_idle_states.h"
+
+static void (*qcom_idle_enter)(enum msm_pm_sleep_mode);
+
+static int qcom_lpm_enter_wfi(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv, int index)
+{
+	qcom_idle_enter(MSM_PM_SLEEP_MODE_WFI);
+
+	return index;
+}
+
+static int qcom_lpm_enter_spc(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv, int index)
+{
+	cpu_pm_enter();
+	qcom_idle_enter(MSM_PM_SLEEP_MODE_SPC);
+	cpu_pm_exit();
+
+	return index;
+}
+
+static struct cpuidle_driver qcom_cpuidle_driver = {
+	.name	= "qcom_cpuidle",
+	.owner	= THIS_MODULE,
+};
+
+static const struct of_device_id qcom_idle_state_match[] __initconst = {
+	{ .compatible = "qcom,idle-state-wfi", .data = qcom_lpm_enter_wfi },
+	{ .compatible = "qcom,idle-state-spc", .data = qcom_lpm_enter_spc },
+	{ },
+};
+
+static int qcom_cpuidle_probe(struct platform_device *pdev)
+{
+	struct cpuidle_driver *drv = &qcom_cpuidle_driver;
+	int ret;
+
+	qcom_idle_enter = (void *)(pdev->dev.platform_data);
+	if (!qcom_idle_enter)
+		return -EFAULT;
+
+	 /* Probe for other states including platform WFI */
+	ret = dt_init_idle_driver(drv, qcom_idle_state_match, 0);
+	if (ret <= 0) {
+		pr_err("%s: No cpuidle state found.\n", __func__);
+		return ret;
+	}
+
+	ret = cpuidle_register(drv, NULL);
+	if (ret) {
+		pr_err("%s: failed to register cpuidle driver\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver qcom_cpuidle_plat_driver = {
+	.probe	= qcom_cpuidle_probe,
+	.driver = {
+		.name = "qcom_cpuidle",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(qcom_cpuidle_plat_driver);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 4/5] qcom: cpuidle: Add cpuidle driver for QCOM cpus
@ 2014-09-23 23:51   ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

Add cpuidle driver interface to allow cpus to go into C-States. Use the
cpuidle DT interface common across ARM architectures to provide the
C-State information to the cpuidle framework.

Supported modes at this time are clock gating (wfi) and cpu power down
(Standalone PC or spc).

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 .../bindings/arm/msm/qcom,idle-state.txt           | 72 +++++++++++++++++
 drivers/cpuidle/Kconfig.arm                        |  7 ++
 drivers/cpuidle/Makefile                           |  1 +
 drivers/cpuidle/cpuidle-qcom.c                     | 89 ++++++++++++++++++++++
 4 files changed, 169 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
 create mode 100644 drivers/cpuidle/cpuidle-qcom.c

diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
new file mode 100644
index 0000000..47095b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
@@ -0,0 +1,72 @@
+QCOM Idle States for cpuidle driver
+
+ARM provides idle-state node to define the cpuidle states, as defined in [1].
+cpuidle-qcom is the cpuidle driver for Qualcomm SoCs and uses these idle
+states. Idle states have different enter/exit latency and residency values.
+The idle states supported by the QCOM SoC are defined as -
+
+    * WFI
+    * Retention
+    * Standalone Power Collapse (Standalone PC or SPC)
+    * Power Collapse (PC)
+
+WFI: WFI does a little more in addition to architectural clock gating.  ARM
+processors when execute the wfi instruction will gate their internal clocks.
+QCOM cpus use this instruction as a trigger for the SPM state machine. Usually
+with a cpu entering WFI, the SPM is configured to do clock-gating as well. The
+SPM state machine waits for the interrrupt to trigger the core back in to
+active. When all CPUs in the SoC, clock gate using the ARM wfi instruction, the
+second level cache usually can also clock gate sensing no cpu activity. When a
+cpu is ready to run, it needs the cache to be active before starting execution.
+Allowing the SPM to execute the clock gating statemachine and waiting for
+interrupt on behalf of the processor has a benefit of guaranteeing that the
+system state is conducive for the core to resume execution.
+
+Retention: Retention is a low power state where the core is clockgated and the
+memory and the registers associated with the core are retained.  The voltage
+may be reduced to the minimum value needed to keep the processor registers
+active. Retention is triggered when the core executes wfi instruction. The SPM
+should be configured to execute the retention sequence and would wait for
+interrupt, before restoring the cpu to execution state. Retention may have a
+slightly higher latency than WFI.
+
+Standalone PC: A cpu can power down and warmboot if there is a sufficient time
+between now and the next know wake up. SPC mode is used to indicate a core
+entering a power down state without consulting any other cpu or the system
+resources. This helps save power only on that core. Like WFI and Retention, the
+core executes wfi and the SPM programmed to do SPC would use the cpu control
+logic to power down the core's supply and restore it back when woken up by an
+interrupt.  Applying power and reseting the core causes the core to warmboot
+back into secure mode which trampolines the control back to the kernel. To
+enter a power down state the kernel needs to call into the secure layer which
+would then execute the ARM wfi instruction. Failing to do so, would result in a
+crash enforced by the warm boot code in the secure layer. On a SoC with
+write-back L1 cache, the cache would need to be flushed.
+
+Power Collapse: This state is similiar to the SPC mode, but distinguishes
+itself in the fact that the cpu acknowledges and permits the SoC to enter
+deeper sleep modes. In a hierarchical power domain SoC, this means L2 and other
+caches can be flushed, system bus, clocks - lowered, and SoC main XO turned off
+and voltages reduced, provided all cpus enter this state. In other words, it is
+a coupled idle state.  Since the span of low power modes possible at this state
+is vast, the exit latency and the residency of this low power mode would be
+considered high even though at a cpu level, this essentially is cpu power down.
+The SPM in this state also may handshake with the Resource power manager
+processor in the SoC to indicate a complete subsystem shut down.
+
+The idle-state for QCOM SoCs are distinguished by the compatible property of
+the node. They indicate to the cpuidle driver the entry point to use for
+cpuidle. The devicetree representation of the idle state should be -
+
+Required properties:
+
+- compatible: Must be "arm,idle-state"
+		and one of -
+			"qcom,idle-state-wfi",
+			"qcom,idle-state-ret",
+			"qcom,idle-state-spc",
+			"qcom,idle-state-pc",
+
+Other required and optional properties are specified in [1].
+
+[1]. Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 38cff69..6a9ee12 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -62,3 +62,10 @@ config ARM_MVEBU_V7_CPUIDLE
 	depends on ARCH_MVEBU
 	help
 	  Select this to enable cpuidle on Armada 370, 38x and XP processors.
+
+config ARM_QCOM_CPUIDLE
+	bool "CPU Idle drivers for Qualcomm processors"
+	depends on QCOM_PM
+	select DT_IDLE_STATES
+	help
+	  Select this to enable cpuidle for QCOM processors
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 4d177b9..6c222d5 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE)		+= cpuidle-zynq.o
 obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
 obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
 obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
+obj-$(CONFIG_ARM_QCOM_CPUIDLE)		+= cpuidle-qcom.o
 
 ###############################################################################
 # MIPS drivers
diff --git a/drivers/cpuidle/cpuidle-qcom.c b/drivers/cpuidle/cpuidle-qcom.c
new file mode 100644
index 0000000..d226ac1
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-qcom.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014, Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cpu_pm.h>
+#include <linux/cpuidle.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include <soc/qcom/pm.h>
+#include "dt_idle_states.h"
+
+static void (*qcom_idle_enter)(enum msm_pm_sleep_mode);
+
+static int qcom_lpm_enter_wfi(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv, int index)
+{
+	qcom_idle_enter(MSM_PM_SLEEP_MODE_WFI);
+
+	return index;
+}
+
+static int qcom_lpm_enter_spc(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv, int index)
+{
+	cpu_pm_enter();
+	qcom_idle_enter(MSM_PM_SLEEP_MODE_SPC);
+	cpu_pm_exit();
+
+	return index;
+}
+
+static struct cpuidle_driver qcom_cpuidle_driver = {
+	.name	= "qcom_cpuidle",
+	.owner	= THIS_MODULE,
+};
+
+static const struct of_device_id qcom_idle_state_match[] __initconst = {
+	{ .compatible = "qcom,idle-state-wfi", .data = qcom_lpm_enter_wfi },
+	{ .compatible = "qcom,idle-state-spc", .data = qcom_lpm_enter_spc },
+	{ },
+};
+
+static int qcom_cpuidle_probe(struct platform_device *pdev)
+{
+	struct cpuidle_driver *drv = &qcom_cpuidle_driver;
+	int ret;
+
+	qcom_idle_enter = (void *)(pdev->dev.platform_data);
+	if (!qcom_idle_enter)
+		return -EFAULT;
+
+	 /* Probe for other states including platform WFI */
+	ret = dt_init_idle_driver(drv, qcom_idle_state_match, 0);
+	if (ret <= 0) {
+		pr_err("%s: No cpuidle state found.\n", __func__);
+		return ret;
+	}
+
+	ret = cpuidle_register(drv, NULL);
+	if (ret) {
+		pr_err("%s: failed to register cpuidle driver\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver qcom_cpuidle_plat_driver = {
+	.probe	= qcom_cpuidle_probe,
+	.driver = {
+		.name = "qcom_cpuidle",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(qcom_cpuidle_plat_driver);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 5/5] arm: dts: qcom: Add idle states device nodes for 8974
  2014-09-23 23:51 ` Lina Iyer
@ 2014-09-23 23:51   ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm, Lina Iyer

Add allowable C-States for each cpu using the cpu-idle-states node.
ARM spec dictates WFI as the default idle state at 0. Support standalone
power collapse (power down that does not affect any SoC idle states) for
each cpu.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/boot/dts/qcom-msm8974.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 0580bc2..e87b24f 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -21,6 +21,7 @@
 			reg = <0>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc0>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		CPU1: cpu@1 {
@@ -30,6 +31,7 @@
 			reg = <1>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc1>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		CPU2: cpu@2 {
@@ -39,6 +41,7 @@
 			reg = <2>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc2>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		CPU3: cpu@3 {
@@ -48,6 +51,7 @@
 			reg = <3>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc3>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		L2: l2-cache {
@@ -55,6 +59,22 @@
 			cache-level = <2>;
 			qcom,saw = <&saw_l2>;
 		};
+
+		idle-states {
+			CPU_WFI: wfi {
+				compatible = "qcom,idle-state-wfi", "arm,idle-state";
+				entry-latency-us = <1>;
+				exit-latency-us = <1>;
+				min-residency-us = <2>;
+			};
+
+			CPU_SPC: spc {
+				compatible = "qcom,idle-state-spc", "arm,idle-state";
+				entry-latency-us = <150>;
+				exit-latency-us = <200>;
+				min-residency-us = <2000>;
+			};
+		};
 	};
 
 	cpu-pmu {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v6 5/5] arm: dts: qcom: Add idle states device nodes for 8974
@ 2014-09-23 23:51   ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-23 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

Add allowable C-States for each cpu using the cpu-idle-states node.
ARM spec dictates WFI as the default idle state at 0. Support standalone
power collapse (power down that does not affect any SoC idle states) for
each cpu.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/boot/dts/qcom-msm8974.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 0580bc2..e87b24f 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -21,6 +21,7 @@
 			reg = <0>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc0>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		CPU1: cpu at 1 {
@@ -30,6 +31,7 @@
 			reg = <1>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc1>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		CPU2: cpu at 2 {
@@ -39,6 +41,7 @@
 			reg = <2>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc2>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		CPU3: cpu at 3 {
@@ -48,6 +51,7 @@
 			reg = <3>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc3>;
+			cpu-idle-states = <&CPU_WFI &CPU_SPC>;
 		};
 
 		L2: l2-cache {
@@ -55,6 +59,22 @@
 			cache-level = <2>;
 			qcom,saw = <&saw_l2>;
 		};
+
+		idle-states {
+			CPU_WFI: wfi {
+				compatible = "qcom,idle-state-wfi", "arm,idle-state";
+				entry-latency-us = <1>;
+				exit-latency-us = <1>;
+				min-residency-us = <2>;
+			};
+
+			CPU_SPC: spc {
+				compatible = "qcom,idle-state-spc", "arm,idle-state";
+				entry-latency-us = <150>;
+				exit-latency-us = <200>;
+				min-residency-us = <2000>;
+			};
+		};
 	};
 
 	cpu-pmu {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-23 23:51   ` Lina Iyer
@ 2014-09-24  1:58     ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24  1:58 UTC (permalink / raw)
  To: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel
  Cc: khilman, msivasub, lorenzo.pieralisi, linux-pm

On Tue, Sep 23 2014 at 17:51 -0600, Lina Iyer wrote:
>Based on work by many authors, available at codeaurora.org
>
>SPM is a hardware block that controls the peripheral logic surrounding
>the application cores (cpu/l$). When the core executes WFI instruction,
>the SPM takes over the putting the core in low power state as
>configured. The wake up for the SPM is an interrupt at the GIC, which
>then completes the rest of low power mode sequence and brings the core
>out of low power mode.
>
>The SPM has a set of control registers that configure the SPMs
>individually based on the type of the core and the runtime conditions.
>SPM is a finite state machine block to which a sequence is provided and
>it interprets the bytes  and executes them in sequence. Each low power
>mode that the core can enter into is provided to the SPM as a sequence.
>
>Configure the SPM to set the core (cpu or L2) into its low power mode,
>the index of the first command in the sequence is set in the SPM_CTL
>register. When the core executes ARM wfi instruction, it triggers the
>SPM state machine to start executing from that index. The SPM state
>machine waits until the interrupt occurs and starts executing the rest
>of the sequence until it hits the end of the sequence. The end of the
>sequence jumps the core out of its low power mode.
>
>Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>[lina: simplify the driver for initial submission, clean up and update
>commit text]
>---
> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
> drivers/soc/qcom/Kconfig                          |   8 +
> drivers/soc/qcom/Makefile                         |   1 +
> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
> include/soc/qcom/spm.h                            |  38 +++
> 5 files changed, 478 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 include/soc/qcom/spm.h
>
>diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>new file mode 100644
>index 0000000..2ff2454
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>@@ -0,0 +1,43 @@
>+* Subsystem Power Manager (SPM)
>+
>+Qualcomm Snapdragons have SPM hardware blocks to control the Application
>+Processor Sub-System power. These SPM blocks run individual state machine
>+to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>+instruction is executed by the core.
>+
>+The devicetree representation of the SPM block should be:
>+
>+Required properties
>+
>+- compatible: Must be -
>+		 "qcom,spm-v2.1"
>+- reg: The physical address and the size of the SPM's memory mapped registers
>+- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>+	This field is required on only for SPMs that control the CPU.
>+- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>+	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>+- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>+	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>+- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>+	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>+
>+Optional properties
>+
>+- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>+- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>+
>+Example:
>+	spm@f9089000 {
>+		compatible = "qcom,spm-v2.1";
>+		#address-cells = <1>;
>+		#size-cells = <1>;
>+		reg = <0xf9089000 0x1000>;
>+		qcom,cpu = <&CPU0>;
>+		qcom,saw2-clk-div = <0x1>;
>+		qcom,saw2-delays = <0x20000400>;
>+		qcom,saw2-enable = <0x1>;
>+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>+		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>+				a0 b0 03 68 70 3b 92 a0 b0
>+				82 2b 50 10 30 02 22 30 0f];
>+	};
>diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>index 7dcd554..cd249c4 100644
>--- a/drivers/soc/qcom/Kconfig
>+++ b/drivers/soc/qcom/Kconfig
>@@ -11,3 +11,11 @@ config QCOM_GSBI
>
> config QCOM_SCM
> 	bool
>+
>+config QCOM_PM
>+	bool "Qualcomm Power Management"
>+	depends on PM && ARCH_QCOM
>+	help
>+	  QCOM Platform specific power driver to manage cores and L2 low power
>+	  modes. It interface with various system drivers to put the cores in
>+	  low power modes.
>diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>index 70d52ed..20b329f 100644
>--- a/drivers/soc/qcom/Makefile
>+++ b/drivers/soc/qcom/Makefile
>@@ -1,3 +1,4 @@
> obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>+obj-$(CONFIG_QCOM_PM)	+=	spm.o
> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
>diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
>new file mode 100644
>index 0000000..1fa6a96
>--- /dev/null
>+++ b/drivers/soc/qcom/spm.c
>@@ -0,0 +1,388 @@
>+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
>+ *
>+ * This program is free software; you can redistribute it and/or modify
>+ * it under the terms of the GNU General Public License version 2 and
>+ * only version 2 as published by the Free Software Foundation.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ *
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <linux/delay.h>
>+#include <linux/init.h>
>+#include <linux/io.h>
>+#include <linux/slab.h>
>+#include <linux/of.h>
>+#include <linux/of_address.h>
>+#include <linux/err.h>
>+#include <linux/platform_device.h>
>+
>+#include <soc/qcom/spm.h>
>+
>+#define NUM_SEQ_ENTRY 32
>+#define SPM_CTL_ENABLE BIT(0)
>+
>+enum {
>+	MSM_SPM_REG_SAW2_CFG,
>+	MSM_SPM_REG_SAW2_AVS_CTL,
>+	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
>+	MSM_SPM_REG_SAW2_SPM_CTL,
>+	MSM_SPM_REG_SAW2_PMIC_DLY,
>+	MSM_SPM_REG_SAW2_AVS_LIMIT,
>+	MSM_SPM_REG_SAW2_AVS_DLY,
>+	MSM_SPM_REG_SAW2_SPM_DLY,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_0,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_1,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_2,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_3,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_4,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_5,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_6,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_7,
>+	MSM_SPM_REG_SAW2_RST,
>+
>+	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
>+
>+	MSM_SPM_REG_SAW2_ID,
>+	MSM_SPM_REG_SAW2_SECURE,
>+	MSM_SPM_REG_SAW2_STS0,
>+	MSM_SPM_REG_SAW2_STS1,
>+	MSM_SPM_REG_SAW2_STS2,
>+	MSM_SPM_REG_SAW2_VCTL,
>+	MSM_SPM_REG_SAW2_SEQ_ENTRY,
>+	MSM_SPM_REG_SAW2_SPM_STS,
>+	MSM_SPM_REG_SAW2_AVS_STS,
>+	MSM_SPM_REG_SAW2_PMIC_STS,
>+	MSM_SPM_REG_SAW2_VERSION,
>+
>+	MSM_SPM_REG_NR,
>+};
>+
>+static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
>+	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
>+	[MSM_SPM_REG_SAW2_ID]			= 0x04,
>+	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
>+	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
>+	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
>+	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
>+	[MSM_SPM_REG_SAW2_RST]			= 0x18,
>+	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
>+	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
>+	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
>+	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
>+	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
>+	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
>+	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
>+	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
>+	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
>+};
I should probably remove the registers that we would not ever read/write
in this driver.

>+
>+struct spm_of {
>+	char *key;
>+	u32 id;
>+};
>+
>+struct msm_spm_mode {
>+	u32 mode;
>+	u32 start_addr;
>+};
>+
>+struct msm_spm_driver_data {
>+	void __iomem *reg_base_addr;
>+	u32 *reg_offsets;
>+	struct msm_spm_mode *modes;
>+	u32 num_modes;
>+};
>+
>+struct msm_spm_device {
>+	bool initialized;
>+	struct msm_spm_driver_data drv;
>+};
>+
>+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
>+
>+static const struct of_device_id msm_spm_match_table[] __initconst;
>+
>+static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
>+						u32 mode)
>+{
>+	int i;
>+	u32 start_addr = 0;
>+	u32 ctl_val;
>+
>+	for (i = 0; i < drv->num_modes; i++) {
>+		if (drv->modes[i].mode == mode) {
>+			start_addr = drv->modes[i].start_addr;
>+			break;
>+		}
>+	}
>+
>+	if (i == drv->num_modes)
>+		return -EINVAL;
>+
>+	/* Update bits 10:4 in the SPM CTL register */
>+	ctl_val = readl_relaxed(drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+	start_addr &= 0x7F;
>+	start_addr <<= 4;
>+	ctl_val &= 0xFFFFF80F;
>+	ctl_val |= start_addr;
>+	writel_relaxed(ctl_val, drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+	/* Ensure we have written the start address */
>+	wmb();
>+
>+	return 0;
>+}
>+
>+static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
>+					bool enable)
>+{
>+	u32 value = enable ? 0x01 : 0x00;
>+	u32 ctl_val;
>+
>+	ctl_val = readl_relaxed(drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+
>+	/* Update SPM_CTL to enable/disable the SPM */
>+	if ((ctl_val & SPM_CTL_ENABLE) != value) {
>+		/* Clear the existing value and update */
>+		ctl_val &= ~0x1;
>+		ctl_val |= value;
>+		writel_relaxed(ctl_val, drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+
>+		/* Ensure we have enabled/disabled before returning */
>+		wmb();
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
>+ * @mode: SPM LPM mode to enter
>+ */
>+int msm_spm_set_low_power_mode(u32 mode)
>+{
>+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
>+	int ret = -EINVAL;
>+
>+	if (!dev->initialized)
>+		return -ENXIO;
>+
>+	if (mode == MSM_SPM_MODE_DISABLED)
>+		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
>+	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
>+		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
>+
>+static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
>+{
>+	u32 cmd_w;
>+	u32 offset_w = *offset / 4;
>+	u8 last_cmd;
>+
>+	while (1) {
>+		int i;
>+
>+		cmd_w = 0;
>+		last_cmd = 0;
>+		cmd_w = reg_seq_entry[offset_w];
>+
>+		for (i = (*offset % 4); i < 4; i++) {
>+			last_cmd = *(cmd++);
>+			cmd_w |=  last_cmd << (i * 8);
>+			(*offset)++;
>+			if (last_cmd == 0x0f)
>+				break;
>+		}
>+
>+		reg_seq_entry[offset_w++] = cmd_w;
>+		if (last_cmd == 0x0f)
>+			break;
>+	}
>+}
>+
>+static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
>+			struct platform_device *pdev)
>+{
>+	int i;
>+	u8 *cmd;
>+	void *addr;
>+	u32 val;
>+	u32 count = 0;
>+	int offset = 0;
>+	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
>+	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
>+	struct msm_spm_driver_data *drv = &spm_dev->drv;
>+
>+	/* SPM sleep sequences */
>+	struct spm_of mode_of_data[] = {
>+		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
>+		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
>+	};
>+
>+	/**
>+	 * Compose the u32 array based on the individual bytes of the SPM
>+	 * sequence for each low power mode that we read from the DT.
>+	 * The sequences are appended if there is space available in the
>+	 * u32 after the end of the previous sequence.
>+	 */
>+
>+	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
>+		cmd = (u8 *)of_get_property(pdev->dev.of_node,
>+						mode_of_data[i].key, &val);
>+		if (!cmd)
>+			continue;
>+		/* The last in the sequence should be 0x0F */
>+		if (cmd[val - 1] != 0x0F)
>+			continue;
>+		modes[count].mode = mode_of_data[i].id;
>+		modes[count].start_addr = offset;
>+		append_seq_data(&sequences[0], cmd, &offset);
>+		count++;
>+	}
>+
>+	/* Write the idle state sequences to SPM */
>+	drv->modes = devm_kcalloc(&pdev->dev, count,
>+					sizeof(modes[0]), GFP_KERNEL);
>+	if (!drv->modes)
>+		return -ENOMEM;
>+
>+	drv->num_modes = count;
>+	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
>+
>+	/* Flush the integer array */
>+	addr = drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
>+	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
>+		writel_relaxed(sequences[i], addr);
>+
>+	/* Ensure we flush the writes */
>+	wmb();
>+
>+	return 0;
>+}
>+
>+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
>+{
>+	struct msm_spm_device *dev = NULL;
>+	struct device_node *cpu_node;
>+	u32 cpu;
>+
>+	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
>+	if (cpu_node) {
>+		for_each_possible_cpu(cpu) {
>+			if (of_get_cpu_node(cpu, NULL) == cpu_node)
>+				dev = &per_cpu(msm_cpu_spm_device, cpu);
>+		}
>+	}
>+
>+	return dev;
>+}
>+
>+static int msm_spm_dev_probe(struct platform_device *pdev)
>+{
>+	int ret;
>+	int i;
>+	u32 val;
>+	struct msm_spm_device *spm_dev;
>+	struct resource *res;
>+	const struct of_device_id *match_id;
>+
>+	/* SPM Configuration registers */
>+	struct spm_of spm_of_data[] = {
>+		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
>+		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
>+		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
>+	};
>+
>+	 /* Get the right SPM device */
>+	spm_dev = msm_spm_get_device(pdev);
>+	if (IS_ERR_OR_NULL(spm_dev))
>+		return -EINVAL;
>+
>+	/* Get the SPM start address */
>+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>+	if (!res) {
>+		ret = -EINVAL;
>+		goto fail;
>+	}
>+	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
>+					resource_size(res));
>+	if (!spm_dev->drv.reg_base_addr) {
>+		ret = -ENOMEM;
>+		goto fail;
>+	}
>+
>+	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
>+	if (!match_id)
>+		return -ENODEV;
>+
>+	/* Use the register offsets for the SPM version in use */
>+	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
>+	if (!spm_dev->drv.reg_offsets)
>+		return -EFAULT;
>+
>+	/* Read the SPM idle state sequences */
>+	ret = msm_spm_seq_init(spm_dev, pdev);
>+	if (ret)
>+		return ret;
>+
>+	/* Read the SPM register values */
>+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
>+		ret = of_property_read_u32(pdev->dev.of_node,
>+					spm_of_data[i].key, &val);
>+		if (ret)
>+			continue;
>+		writel_relaxed(val, spm_dev->drv.reg_base_addr +
>+				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
>+	}
>+
>+	/* Flush all writes */
>+	wmb();
>+
>+	spm_dev->initialized = true;
>+	return ret;
>+fail:
>+	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
>+	return ret;
>+}
>+
>+static const struct of_device_id msm_spm_match_table[] __initconst = {
>+	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
>+	{ },
>+};
>+
>+
>+static struct platform_driver msm_spm_device_driver = {
>+	.probe = msm_spm_dev_probe,
>+	.driver = {
>+		.name = "spm",
>+		.owner = THIS_MODULE,
>+		.of_match_table = msm_spm_match_table,
>+	},
>+};
>+
>+static int __init msm_spm_device_init(void)
>+{
>+	return platform_driver_register(&msm_spm_device_driver);
>+}
>+device_initcall(msm_spm_device_init);
>diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
>new file mode 100644
>index 0000000..29686ef
>--- /dev/null
>+++ b/include/soc/qcom/spm.h
>@@ -0,0 +1,38 @@
>+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
>+ *
>+ * This program is free software; you can redistribute it and/or modify
>+ * it under the terms of the GNU General Public License version 2 and
>+ * only version 2 as published by the Free Software Foundation.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ */
>+
>+#ifndef __QCOM_SPM_H
>+#define __QCOM_SPM_H
>+
>+enum {
>+	MSM_SPM_MODE_DISABLED,
>+	MSM_SPM_MODE_CLOCK_GATING,
>+	MSM_SPM_MODE_RETENTION,
>+	MSM_SPM_MODE_GDHS,
>+	MSM_SPM_MODE_POWER_COLLAPSE,
>+	MSM_SPM_MODE_NR
>+};
>+
>+struct msm_spm_device;
>+
>+#if defined(CONFIG_QCOM_PM)
>+
>+int msm_spm_set_low_power_mode(u32 mode);
>+
>+#else
>+
>+static inline int msm_spm_set_low_power_mode(u32 mode)
>+{ return -ENOSYS; }
>+
>+#endif  /* CONFIG_QCOM_PM */
>+
>+#endif  /* __QCOM_SPM_H */
>-- 
>1.9.1
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24  1:58     ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 23 2014 at 17:51 -0600, Lina Iyer wrote:
>Based on work by many authors, available at codeaurora.org
>
>SPM is a hardware block that controls the peripheral logic surrounding
>the application cores (cpu/l$). When the core executes WFI instruction,
>the SPM takes over the putting the core in low power state as
>configured. The wake up for the SPM is an interrupt at the GIC, which
>then completes the rest of low power mode sequence and brings the core
>out of low power mode.
>
>The SPM has a set of control registers that configure the SPMs
>individually based on the type of the core and the runtime conditions.
>SPM is a finite state machine block to which a sequence is provided and
>it interprets the bytes  and executes them in sequence. Each low power
>mode that the core can enter into is provided to the SPM as a sequence.
>
>Configure the SPM to set the core (cpu or L2) into its low power mode,
>the index of the first command in the sequence is set in the SPM_CTL
>register. When the core executes ARM wfi instruction, it triggers the
>SPM state machine to start executing from that index. The SPM state
>machine waits until the interrupt occurs and starts executing the rest
>of the sequence until it hits the end of the sequence. The end of the
>sequence jumps the core out of its low power mode.
>
>Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>[lina: simplify the driver for initial submission, clean up and update
>commit text]
>---
> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
> drivers/soc/qcom/Kconfig                          |   8 +
> drivers/soc/qcom/Makefile                         |   1 +
> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
> include/soc/qcom/spm.h                            |  38 +++
> 5 files changed, 478 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 include/soc/qcom/spm.h
>
>diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>new file mode 100644
>index 0000000..2ff2454
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>@@ -0,0 +1,43 @@
>+* Subsystem Power Manager (SPM)
>+
>+Qualcomm Snapdragons have SPM hardware blocks to control the Application
>+Processor Sub-System power. These SPM blocks run individual state machine
>+to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>+instruction is executed by the core.
>+
>+The devicetree representation of the SPM block should be:
>+
>+Required properties
>+
>+- compatible: Must be -
>+		 "qcom,spm-v2.1"
>+- reg: The physical address and the size of the SPM's memory mapped registers
>+- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>+	This field is required on only for SPMs that control the CPU.
>+- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>+	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>+- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>+	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>+- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>+	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>+
>+Optional properties
>+
>+- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>+- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>+
>+Example:
>+	spm at f9089000 {
>+		compatible = "qcom,spm-v2.1";
>+		#address-cells = <1>;
>+		#size-cells = <1>;
>+		reg = <0xf9089000 0x1000>;
>+		qcom,cpu = <&CPU0>;
>+		qcom,saw2-clk-div = <0x1>;
>+		qcom,saw2-delays = <0x20000400>;
>+		qcom,saw2-enable = <0x1>;
>+		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>+		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>+				a0 b0 03 68 70 3b 92 a0 b0
>+				82 2b 50 10 30 02 22 30 0f];
>+	};
>diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>index 7dcd554..cd249c4 100644
>--- a/drivers/soc/qcom/Kconfig
>+++ b/drivers/soc/qcom/Kconfig
>@@ -11,3 +11,11 @@ config QCOM_GSBI
>
> config QCOM_SCM
> 	bool
>+
>+config QCOM_PM
>+	bool "Qualcomm Power Management"
>+	depends on PM && ARCH_QCOM
>+	help
>+	  QCOM Platform specific power driver to manage cores and L2 low power
>+	  modes. It interface with various system drivers to put the cores in
>+	  low power modes.
>diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>index 70d52ed..20b329f 100644
>--- a/drivers/soc/qcom/Makefile
>+++ b/drivers/soc/qcom/Makefile
>@@ -1,3 +1,4 @@
> obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>+obj-$(CONFIG_QCOM_PM)	+=	spm.o
> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
>diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
>new file mode 100644
>index 0000000..1fa6a96
>--- /dev/null
>+++ b/drivers/soc/qcom/spm.c
>@@ -0,0 +1,388 @@
>+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
>+ *
>+ * This program is free software; you can redistribute it and/or modify
>+ * it under the terms of the GNU General Public License version 2 and
>+ * only version 2 as published by the Free Software Foundation.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ *
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <linux/delay.h>
>+#include <linux/init.h>
>+#include <linux/io.h>
>+#include <linux/slab.h>
>+#include <linux/of.h>
>+#include <linux/of_address.h>
>+#include <linux/err.h>
>+#include <linux/platform_device.h>
>+
>+#include <soc/qcom/spm.h>
>+
>+#define NUM_SEQ_ENTRY 32
>+#define SPM_CTL_ENABLE BIT(0)
>+
>+enum {
>+	MSM_SPM_REG_SAW2_CFG,
>+	MSM_SPM_REG_SAW2_AVS_CTL,
>+	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
>+	MSM_SPM_REG_SAW2_SPM_CTL,
>+	MSM_SPM_REG_SAW2_PMIC_DLY,
>+	MSM_SPM_REG_SAW2_AVS_LIMIT,
>+	MSM_SPM_REG_SAW2_AVS_DLY,
>+	MSM_SPM_REG_SAW2_SPM_DLY,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_0,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_1,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_2,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_3,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_4,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_5,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_6,
>+	MSM_SPM_REG_SAW2_PMIC_DATA_7,
>+	MSM_SPM_REG_SAW2_RST,
>+
>+	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
>+
>+	MSM_SPM_REG_SAW2_ID,
>+	MSM_SPM_REG_SAW2_SECURE,
>+	MSM_SPM_REG_SAW2_STS0,
>+	MSM_SPM_REG_SAW2_STS1,
>+	MSM_SPM_REG_SAW2_STS2,
>+	MSM_SPM_REG_SAW2_VCTL,
>+	MSM_SPM_REG_SAW2_SEQ_ENTRY,
>+	MSM_SPM_REG_SAW2_SPM_STS,
>+	MSM_SPM_REG_SAW2_AVS_STS,
>+	MSM_SPM_REG_SAW2_PMIC_STS,
>+	MSM_SPM_REG_SAW2_VERSION,
>+
>+	MSM_SPM_REG_NR,
>+};
>+
>+static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
>+	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
>+	[MSM_SPM_REG_SAW2_ID]			= 0x04,
>+	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
>+	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
>+	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
>+	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
>+	[MSM_SPM_REG_SAW2_RST]			= 0x18,
>+	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
>+	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
>+	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
>+	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
>+	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
>+	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
>+	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
>+	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
>+	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
>+	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
>+};
I should probably remove the registers that we would not ever read/write
in this driver.

>+
>+struct spm_of {
>+	char *key;
>+	u32 id;
>+};
>+
>+struct msm_spm_mode {
>+	u32 mode;
>+	u32 start_addr;
>+};
>+
>+struct msm_spm_driver_data {
>+	void __iomem *reg_base_addr;
>+	u32 *reg_offsets;
>+	struct msm_spm_mode *modes;
>+	u32 num_modes;
>+};
>+
>+struct msm_spm_device {
>+	bool initialized;
>+	struct msm_spm_driver_data drv;
>+};
>+
>+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
>+
>+static const struct of_device_id msm_spm_match_table[] __initconst;
>+
>+static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
>+						u32 mode)
>+{
>+	int i;
>+	u32 start_addr = 0;
>+	u32 ctl_val;
>+
>+	for (i = 0; i < drv->num_modes; i++) {
>+		if (drv->modes[i].mode == mode) {
>+			start_addr = drv->modes[i].start_addr;
>+			break;
>+		}
>+	}
>+
>+	if (i == drv->num_modes)
>+		return -EINVAL;
>+
>+	/* Update bits 10:4 in the SPM CTL register */
>+	ctl_val = readl_relaxed(drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+	start_addr &= 0x7F;
>+	start_addr <<= 4;
>+	ctl_val &= 0xFFFFF80F;
>+	ctl_val |= start_addr;
>+	writel_relaxed(ctl_val, drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+	/* Ensure we have written the start address */
>+	wmb();
>+
>+	return 0;
>+}
>+
>+static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
>+					bool enable)
>+{
>+	u32 value = enable ? 0x01 : 0x00;
>+	u32 ctl_val;
>+
>+	ctl_val = readl_relaxed(drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+
>+	/* Update SPM_CTL to enable/disable the SPM */
>+	if ((ctl_val & SPM_CTL_ENABLE) != value) {
>+		/* Clear the existing value and update */
>+		ctl_val &= ~0x1;
>+		ctl_val |= value;
>+		writel_relaxed(ctl_val, drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>+
>+		/* Ensure we have enabled/disabled before returning */
>+		wmb();
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
>+ * @mode: SPM LPM mode to enter
>+ */
>+int msm_spm_set_low_power_mode(u32 mode)
>+{
>+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
>+	int ret = -EINVAL;
>+
>+	if (!dev->initialized)
>+		return -ENXIO;
>+
>+	if (mode == MSM_SPM_MODE_DISABLED)
>+		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
>+	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
>+		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
>+
>+static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
>+{
>+	u32 cmd_w;
>+	u32 offset_w = *offset / 4;
>+	u8 last_cmd;
>+
>+	while (1) {
>+		int i;
>+
>+		cmd_w = 0;
>+		last_cmd = 0;
>+		cmd_w = reg_seq_entry[offset_w];
>+
>+		for (i = (*offset % 4); i < 4; i++) {
>+			last_cmd = *(cmd++);
>+			cmd_w |=  last_cmd << (i * 8);
>+			(*offset)++;
>+			if (last_cmd == 0x0f)
>+				break;
>+		}
>+
>+		reg_seq_entry[offset_w++] = cmd_w;
>+		if (last_cmd == 0x0f)
>+			break;
>+	}
>+}
>+
>+static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
>+			struct platform_device *pdev)
>+{
>+	int i;
>+	u8 *cmd;
>+	void *addr;
>+	u32 val;
>+	u32 count = 0;
>+	int offset = 0;
>+	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
>+	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
>+	struct msm_spm_driver_data *drv = &spm_dev->drv;
>+
>+	/* SPM sleep sequences */
>+	struct spm_of mode_of_data[] = {
>+		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
>+		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
>+	};
>+
>+	/**
>+	 * Compose the u32 array based on the individual bytes of the SPM
>+	 * sequence for each low power mode that we read from the DT.
>+	 * The sequences are appended if there is space available in the
>+	 * u32 after the end of the previous sequence.
>+	 */
>+
>+	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
>+		cmd = (u8 *)of_get_property(pdev->dev.of_node,
>+						mode_of_data[i].key, &val);
>+		if (!cmd)
>+			continue;
>+		/* The last in the sequence should be 0x0F */
>+		if (cmd[val - 1] != 0x0F)
>+			continue;
>+		modes[count].mode = mode_of_data[i].id;
>+		modes[count].start_addr = offset;
>+		append_seq_data(&sequences[0], cmd, &offset);
>+		count++;
>+	}
>+
>+	/* Write the idle state sequences to SPM */
>+	drv->modes = devm_kcalloc(&pdev->dev, count,
>+					sizeof(modes[0]), GFP_KERNEL);
>+	if (!drv->modes)
>+		return -ENOMEM;
>+
>+	drv->num_modes = count;
>+	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
>+
>+	/* Flush the integer array */
>+	addr = drv->reg_base_addr +
>+			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
>+	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
>+		writel_relaxed(sequences[i], addr);
>+
>+	/* Ensure we flush the writes */
>+	wmb();
>+
>+	return 0;
>+}
>+
>+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
>+{
>+	struct msm_spm_device *dev = NULL;
>+	struct device_node *cpu_node;
>+	u32 cpu;
>+
>+	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
>+	if (cpu_node) {
>+		for_each_possible_cpu(cpu) {
>+			if (of_get_cpu_node(cpu, NULL) == cpu_node)
>+				dev = &per_cpu(msm_cpu_spm_device, cpu);
>+		}
>+	}
>+
>+	return dev;
>+}
>+
>+static int msm_spm_dev_probe(struct platform_device *pdev)
>+{
>+	int ret;
>+	int i;
>+	u32 val;
>+	struct msm_spm_device *spm_dev;
>+	struct resource *res;
>+	const struct of_device_id *match_id;
>+
>+	/* SPM Configuration registers */
>+	struct spm_of spm_of_data[] = {
>+		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
>+		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
>+		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
>+	};
>+
>+	 /* Get the right SPM device */
>+	spm_dev = msm_spm_get_device(pdev);
>+	if (IS_ERR_OR_NULL(spm_dev))
>+		return -EINVAL;
>+
>+	/* Get the SPM start address */
>+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>+	if (!res) {
>+		ret = -EINVAL;
>+		goto fail;
>+	}
>+	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
>+					resource_size(res));
>+	if (!spm_dev->drv.reg_base_addr) {
>+		ret = -ENOMEM;
>+		goto fail;
>+	}
>+
>+	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
>+	if (!match_id)
>+		return -ENODEV;
>+
>+	/* Use the register offsets for the SPM version in use */
>+	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
>+	if (!spm_dev->drv.reg_offsets)
>+		return -EFAULT;
>+
>+	/* Read the SPM idle state sequences */
>+	ret = msm_spm_seq_init(spm_dev, pdev);
>+	if (ret)
>+		return ret;
>+
>+	/* Read the SPM register values */
>+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
>+		ret = of_property_read_u32(pdev->dev.of_node,
>+					spm_of_data[i].key, &val);
>+		if (ret)
>+			continue;
>+		writel_relaxed(val, spm_dev->drv.reg_base_addr +
>+				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
>+	}
>+
>+	/* Flush all writes */
>+	wmb();
>+
>+	spm_dev->initialized = true;
>+	return ret;
>+fail:
>+	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
>+	return ret;
>+}
>+
>+static const struct of_device_id msm_spm_match_table[] __initconst = {
>+	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
>+	{ },
>+};
>+
>+
>+static struct platform_driver msm_spm_device_driver = {
>+	.probe = msm_spm_dev_probe,
>+	.driver = {
>+		.name = "spm",
>+		.owner = THIS_MODULE,
>+		.of_match_table = msm_spm_match_table,
>+	},
>+};
>+
>+static int __init msm_spm_device_init(void)
>+{
>+	return platform_driver_register(&msm_spm_device_driver);
>+}
>+device_initcall(msm_spm_device_init);
>diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
>new file mode 100644
>index 0000000..29686ef
>--- /dev/null
>+++ b/include/soc/qcom/spm.h
>@@ -0,0 +1,38 @@
>+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
>+ *
>+ * This program is free software; you can redistribute it and/or modify
>+ * it under the terms of the GNU General Public License version 2 and
>+ * only version 2 as published by the Free Software Foundation.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ */
>+
>+#ifndef __QCOM_SPM_H
>+#define __QCOM_SPM_H
>+
>+enum {
>+	MSM_SPM_MODE_DISABLED,
>+	MSM_SPM_MODE_CLOCK_GATING,
>+	MSM_SPM_MODE_RETENTION,
>+	MSM_SPM_MODE_GDHS,
>+	MSM_SPM_MODE_POWER_COLLAPSE,
>+	MSM_SPM_MODE_NR
>+};
>+
>+struct msm_spm_device;
>+
>+#if defined(CONFIG_QCOM_PM)
>+
>+int msm_spm_set_low_power_mode(u32 mode);
>+
>+#else
>+
>+static inline int msm_spm_set_low_power_mode(u32 mode)
>+{ return -ENOSYS; }
>+
>+#endif  /* CONFIG_QCOM_PM */
>+
>+#endif  /* __QCOM_SPM_H */
>-- 
>1.9.1
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-23 23:51   ` Lina Iyer
@ 2014-09-24  6:18     ` Pramod Gurav
  -1 siblings, 0 replies; 60+ messages in thread
From: Pramod Gurav @ 2014-09-24  6:18 UTC (permalink / raw)
  To: Lina Iyer
  Cc: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel,
	khilman, msivasub, lorenzo.pieralisi, linux-pm

Hi Lina,

On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
> power manager and controls the logic around the cores (cpu and L2).
> 
> Each core has an instance of SPM and controls only that core. Each cpu
> SPM is configured to support WFI and SPC (standalone-power collapse).
> 
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>  2 files changed, 75 insertions(+), 4 deletions(-)
>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi

<snip>

> +};
> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
> index 69dca2a..0580bc2 100644
> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
> @@ -14,7 +14,7 @@
>  		#size-cells = <0>;
>  		interrupts = <1 9 0xf04>;
>  
> -		cpu@0 {
> +		CPU0: cpu@0 {
Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
renames this node to "cpu0: cpu@0". If you both could sync up and agree
on a common naming('cpu0' with caps or not caps) for this node.

Best Regards
Pramod

>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -23,7 +23,7 @@
>  			qcom,acc = <&acc0>;
>  		};
>  
> -		cpu@1 {
> +		CPU1: cpu@1 {
Ditto
>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -32,7 +32,7 @@
>  			qcom,acc = <&acc1>;
>  		};
>  
> -		cpu@2 {
> +		CPU2: cpu@2 {
Ditto
>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -41,7 +41,7 @@
>  			qcom,acc = <&acc2>;
>  		};
>  
> -		cpu@3 {
> +		CPU3: cpu@3 {
Ditto
>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -238,3 +238,5 @@
>  		};
>  	};
>  };
> +
> +#include "qcom-msm8974-pm.dtsi"
> 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24  6:18     ` Pramod Gurav
  0 siblings, 0 replies; 60+ messages in thread
From: Pramod Gurav @ 2014-09-24  6:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lina,

On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
> power manager and controls the logic around the cores (cpu and L2).
> 
> Each core has an instance of SPM and controls only that core. Each cpu
> SPM is configured to support WFI and SPC (standalone-power collapse).
> 
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>  2 files changed, 75 insertions(+), 4 deletions(-)
>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi

<snip>

> +};
> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
> index 69dca2a..0580bc2 100644
> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
> @@ -14,7 +14,7 @@
>  		#size-cells = <0>;
>  		interrupts = <1 9 0xf04>;
>  
> -		cpu at 0 {
> +		CPU0: cpu at 0 {
Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
renames this node to "cpu0: cpu at 0". If you both could sync up and agree
on a common naming('cpu0' with caps or not caps) for this node.

Best Regards
Pramod

>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -23,7 +23,7 @@
>  			qcom,acc = <&acc0>;
>  		};
>  
> -		cpu at 1 {
> +		CPU1: cpu at 1 {
Ditto
>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -32,7 +32,7 @@
>  			qcom,acc = <&acc1>;
>  		};
>  
> -		cpu at 2 {
> +		CPU2: cpu at 2 {
Ditto
>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -41,7 +41,7 @@
>  			qcom,acc = <&acc2>;
>  		};
>  
> -		cpu at 3 {
> +		CPU3: cpu at 3 {
Ditto
>  			compatible = "qcom,krait";
>  			enable-method = "qcom,kpss-acc-v2";
>  			device_type = "cpu";
> @@ -238,3 +238,5 @@
>  		};
>  	};
>  };
> +
> +#include "qcom-msm8974-pm.dtsi"
> 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-24  6:18     ` Pramod Gurav
@ 2014-09-24 13:49       ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 13:49 UTC (permalink / raw)
  To: Pramod Gurav
  Cc: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel,
	khilman, msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>Hi Lina,
>
>On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>> power manager and controls the logic around the cores (cpu and L2).
>>
>> Each core has an instance of SPM and controls only that core. Each cpu
>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> ---
>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>
><snip>
>
>> +};
>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
>> index 69dca2a..0580bc2 100644
>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>> @@ -14,7 +14,7 @@
>>  		#size-cells = <0>;
>>  		interrupts = <1 9 0xf04>;
>>
>> -		cpu@0 {
>> +		CPU0: cpu@0 {
>Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
>renames this node to "cpu0: cpu@0". If you both could sync up and agree
>on a common naming('cpu0' with caps or not caps) for this node.
>
Sure. Will work with Stephen on that.

>Best Regards
>Pramod
>
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -23,7 +23,7 @@
>>  			qcom,acc = <&acc0>;
>>  		};
>>
>> -		cpu@1 {
>> +		CPU1: cpu@1 {
>Ditto
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -32,7 +32,7 @@
>>  			qcom,acc = <&acc1>;
>>  		};
>>
>> -		cpu@2 {
>> +		CPU2: cpu@2 {
>Ditto
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -41,7 +41,7 @@
>>  			qcom,acc = <&acc2>;
>>  		};
>>
>> -		cpu@3 {
>> +		CPU3: cpu@3 {
>Ditto
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -238,3 +238,5 @@
>>  		};
>>  	};
>>  };
>> +
>> +#include "qcom-msm8974-pm.dtsi"
>>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24 13:49       ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 13:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>Hi Lina,
>
>On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>> power manager and controls the logic around the cores (cpu and L2).
>>
>> Each core has an instance of SPM and controls only that core. Each cpu
>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> ---
>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>
><snip>
>
>> +};
>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
>> index 69dca2a..0580bc2 100644
>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>> @@ -14,7 +14,7 @@
>>  		#size-cells = <0>;
>>  		interrupts = <1 9 0xf04>;
>>
>> -		cpu at 0 {
>> +		CPU0: cpu at 0 {
>Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
>renames this node to "cpu0: cpu at 0". If you both could sync up and agree
>on a common naming('cpu0' with caps or not caps) for this node.
>
Sure. Will work with Stephen on that.

>Best Regards
>Pramod
>
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -23,7 +23,7 @@
>>  			qcom,acc = <&acc0>;
>>  		};
>>
>> -		cpu at 1 {
>> +		CPU1: cpu at 1 {
>Ditto
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -32,7 +32,7 @@
>>  			qcom,acc = <&acc1>;
>>  		};
>>
>> -		cpu at 2 {
>> +		CPU2: cpu at 2 {
>Ditto
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -41,7 +41,7 @@
>>  			qcom,acc = <&acc2>;
>>  		};
>>
>> -		cpu at 3 {
>> +		CPU3: cpu at 3 {
>Ditto
>>  			compatible = "qcom,krait";
>>  			enable-method = "qcom,kpss-acc-v2";
>>  			device_type = "cpu";
>> @@ -238,3 +238,5 @@
>>  		};
>>  	};
>>  };
>> +
>> +#include "qcom-msm8974-pm.dtsi"
>>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-24 13:49       ` Lina Iyer
@ 2014-09-24 14:03         ` Kumar Gala
  -1 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 14:03 UTC (permalink / raw)
  To: Lina Iyer
  Cc: Pramod Gurav, sboyd, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, khilman, msivasub, lorenzo.pieralisi, linux-pm


On Sep 24, 2014, at 8:49 AM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>> Hi Lina,
>> 
>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>> power manager and controls the logic around the cores (cpu and L2).
>>> 
>>> Each core has an instance of SPM and controls only that core. Each cpu
>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>> 
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> ---
>>> arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>>> arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>> 2 files changed, 75 insertions(+), 4 deletions(-)
>>> create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi

please also have a dts for 8084 as part of this patch series.

- k

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24 14:03         ` Kumar Gala
  0 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 14:03 UTC (permalink / raw)
  To: linux-arm-kernel


On Sep 24, 2014, at 8:49 AM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>> Hi Lina,
>> 
>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>> power manager and controls the logic around the cores (cpu and L2).
>>> 
>>> Each core has an instance of SPM and controls only that core. Each cpu
>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>> 
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> ---
>>> arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>>> arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>> 2 files changed, 75 insertions(+), 4 deletions(-)
>>> create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi

please also have a dts for 8084 as part of this patch series.

- k

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-24 14:03         ` Kumar Gala
@ 2014-09-24 14:13           ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 14:13 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Pramod Gurav, sboyd, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, khilman, msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 08:03 -0600, Kumar Gala wrote:
>
>On Sep 24, 2014, at 8:49 AM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>>> Hi Lina,
>>>
>>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>>> power manager and controls the logic around the cores (cpu and L2).
>>>>
>>>> Each core has an instance of SPM and controls only that core. Each cpu
>>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> ---
>>>> arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>>>> arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>> 2 files changed, 75 insertions(+), 4 deletions(-)
>>>> create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>
>please also have a dts for 8084 as part of this patch series.

Sure. Any particular interest at this time? I dont have a device to
test, I will order one, may take a few weeks.

>
>- k
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24 14:13           ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 14:13 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 08:03 -0600, Kumar Gala wrote:
>
>On Sep 24, 2014, at 8:49 AM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>>> Hi Lina,
>>>
>>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>>> power manager and controls the logic around the cores (cpu and L2).
>>>>
>>>> Each core has an instance of SPM and controls only that core. Each cpu
>>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> ---
>>>> arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69 ++++++++++++++++++++++++++++++++++
>>>> arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>> 2 files changed, 75 insertions(+), 4 deletions(-)
>>>> create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>
>please also have a dts for 8084 as part of this patch series.

Sure. Any particular interest at this time? I dont have a device to
test, I will order one, may take a few weeks.

>
>- k
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-23 23:51   ` Lina Iyer
@ 2014-09-24 16:33     ` Kumar Gala
  -1 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 16:33 UTC (permalink / raw)
  To: Lina Iyer
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm


On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> Based on work by many authors, available at codeaurora.org
> 
> SPM is a hardware block that controls the peripheral logic surrounding
> the application cores (cpu/l$). When the core executes WFI instruction,
> the SPM takes over the putting the core in low power state as
> configured. The wake up for the SPM is an interrupt at the GIC, which
> then completes the rest of low power mode sequence and brings the core
> out of low power mode.
> 
> The SPM has a set of control registers that configure the SPMs
> individually based on the type of the core and the runtime conditions.
> SPM is a finite state machine block to which a sequence is provided and
> it interprets the bytes  and executes them in sequence. Each low power
> mode that the core can enter into is provided to the SPM as a sequence.
> 
> Configure the SPM to set the core (cpu or L2) into its low power mode,
> the index of the first command in the sequence is set in the SPM_CTL
> register. When the core executes ARM wfi instruction, it triggers the
> SPM state machine to start executing from that index. The SPM state
> machine waits until the interrupt occurs and starts executing the rest
> of the sequence until it hits the end of the sequence. The end of the
> sequence jumps the core out of its low power mode.
> 
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> [lina: simplify the driver for initial submission, clean up and update
> commit text]
> ---
> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
> drivers/soc/qcom/Kconfig                          |   8 +
> drivers/soc/qcom/Makefile                         |   1 +
> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
> include/soc/qcom/spm.h                            |  38 +++
> 5 files changed, 478 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 include/soc/qcom/spm.h
> 
> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
> new file mode 100644
> index 0000000..2ff2454
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
> @@ -0,0 +1,43 @@
> +* Subsystem Power Manager (SPM)
> +
> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
> +Processor Sub-System power. These SPM blocks run individual state machine
> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
> +instruction is executed by the core.
> +
> +The devicetree representation of the SPM block should be:
> +
> +Required properties
> +
> +- compatible: Must be -
> +		 "qcom,spm-v2.1"
> +- reg: The physical address and the size of the SPM's memory mapped registers
> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
> +	This field is required on only for SPMs that control the CPU.

Let’s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.

> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.

(add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)

> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.

Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?

> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.

Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?

> +
> +Optional properties
> +
> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence

probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)

> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence

probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)

> +
> +Example:
> +	spm@f9089000 {
> +		compatible = "qcom,spm-v2.1";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		reg = <0xf9089000 0x1000>;
> +		qcom,cpu = <&CPU0>;
> +		qcom,saw2-clk-div = <0x1>;
> +		qcom,saw2-delays = <0x20000400>;
> +		qcom,saw2-enable = <0x1>;
> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
> +				a0 b0 03 68 70 3b 92 a0 b0
> +				82 2b 50 10 30 02 22 30 0f];
> +	};

- k


-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 16:33     ` Kumar Gala
  0 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 16:33 UTC (permalink / raw)
  To: linux-arm-kernel


On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> Based on work by many authors, available at codeaurora.org
> 
> SPM is a hardware block that controls the peripheral logic surrounding
> the application cores (cpu/l$). When the core executes WFI instruction,
> the SPM takes over the putting the core in low power state as
> configured. The wake up for the SPM is an interrupt at the GIC, which
> then completes the rest of low power mode sequence and brings the core
> out of low power mode.
> 
> The SPM has a set of control registers that configure the SPMs
> individually based on the type of the core and the runtime conditions.
> SPM is a finite state machine block to which a sequence is provided and
> it interprets the bytes  and executes them in sequence. Each low power
> mode that the core can enter into is provided to the SPM as a sequence.
> 
> Configure the SPM to set the core (cpu or L2) into its low power mode,
> the index of the first command in the sequence is set in the SPM_CTL
> register. When the core executes ARM wfi instruction, it triggers the
> SPM state machine to start executing from that index. The SPM state
> machine waits until the interrupt occurs and starts executing the rest
> of the sequence until it hits the end of the sequence. The end of the
> sequence jumps the core out of its low power mode.
> 
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> [lina: simplify the driver for initial submission, clean up and update
> commit text]
> ---
> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
> drivers/soc/qcom/Kconfig                          |   8 +
> drivers/soc/qcom/Makefile                         |   1 +
> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
> include/soc/qcom/spm.h                            |  38 +++
> 5 files changed, 478 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 include/soc/qcom/spm.h
> 
> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
> new file mode 100644
> index 0000000..2ff2454
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
> @@ -0,0 +1,43 @@
> +* Subsystem Power Manager (SPM)
> +
> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
> +Processor Sub-System power. These SPM blocks run individual state machine
> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
> +instruction is executed by the core.
> +
> +The devicetree representation of the SPM block should be:
> +
> +Required properties
> +
> +- compatible: Must be -
> +		 "qcom,spm-v2.1"
> +- reg: The physical address and the size of the SPM's memory mapped registers
> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
> +	This field is required on only for SPMs that control the CPU.

Let?s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.

> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.

(add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)

> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.

Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?

> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.

Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?

> +
> +Optional properties
> +
> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence

probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)

> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence

probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)

> +
> +Example:
> +	spm at f9089000 {
> +		compatible = "qcom,spm-v2.1";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		reg = <0xf9089000 0x1000>;
> +		qcom,cpu = <&CPU0>;
> +		qcom,saw2-clk-div = <0x1>;
> +		qcom,saw2-delays = <0x20000400>;
> +		qcom,saw2-enable = <0x1>;
> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
> +				a0 b0 03 68 70 3b 92 a0 b0
> +				82 2b 50 10 30 02 22 30 0f];
> +	};

- k


-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-24 13:49       ` Lina Iyer
@ 2014-09-24 17:21         ` Stephen Boyd
  -1 siblings, 0 replies; 60+ messages in thread
From: Stephen Boyd @ 2014-09-24 17:21 UTC (permalink / raw)
  To: Lina Iyer, Pramod Gurav
  Cc: galak, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm

On 09/24/14 06:49, Lina Iyer wrote:
> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>> Hi Lina,
>>
>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>> power manager and controls the logic around the cores (cpu and L2).
>>>
>>> Each core has an instance of SPM and controls only that core. Each cpu
>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> ---
>>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69
>>> ++++++++++++++++++++++++++++++++++
>>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>>
>> <snip>
>>
>>> +};
>>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> index 69dca2a..0580bc2 100644
>>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> @@ -14,7 +14,7 @@
>>>          #size-cells = <0>;
>>>          interrupts = <1 9 0xf04>;
>>>
>>> -        cpu@0 {
>>> +        CPU0: cpu@0 {
>> Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
>> renames this node to "cpu0: cpu@0". If you both could sync up and agree
>> on a common naming('cpu0' with caps or not caps) for this node.
>>
> Sure. Will work with Stephen on that.
>

This doesn't seem like a big deal. I imagine Kumar can resolve the
conflict if the two patches merge at the same time.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24 17:21         ` Stephen Boyd
  0 siblings, 0 replies; 60+ messages in thread
From: Stephen Boyd @ 2014-09-24 17:21 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/24/14 06:49, Lina Iyer wrote:
> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>> Hi Lina,
>>
>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>> power manager and controls the logic around the cores (cpu and L2).
>>>
>>> Each core has an instance of SPM and controls only that core. Each cpu
>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> ---
>>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69
>>> ++++++++++++++++++++++++++++++++++
>>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>>
>> <snip>
>>
>>> +};
>>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> index 69dca2a..0580bc2 100644
>>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>> @@ -14,7 +14,7 @@
>>>          #size-cells = <0>;
>>>          interrupts = <1 9 0xf04>;
>>>
>>> -        cpu at 0 {
>>> +        CPU0: cpu at 0 {
>> Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
>> renames this node to "cpu0: cpu at 0". If you both could sync up and agree
>> on a common naming('cpu0' with caps or not caps) for this node.
>>
> Sure. Will work with Stephen on that.
>

This doesn't seem like a big deal. I imagine Kumar can resolve the
conflict if the two patches merge at the same time.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 16:33     ` Kumar Gala
@ 2014-09-24 17:21       ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 17:21 UTC (permalink / raw)
  To: Kumar Gala
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>
>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> Based on work by many authors, available at codeaurora.org
>>
>> SPM is a hardware block that controls the peripheral logic surrounding
>> the application cores (cpu/l$). When the core executes WFI instruction,
>> the SPM takes over the putting the core in low power state as
>> configured. The wake up for the SPM is an interrupt at the GIC, which
>> then completes the rest of low power mode sequence and brings the core
>> out of low power mode.
>>
>> The SPM has a set of control registers that configure the SPMs
>> individually based on the type of the core and the runtime conditions.
>> SPM is a finite state machine block to which a sequence is provided and
>> it interprets the bytes  and executes them in sequence. Each low power
>> mode that the core can enter into is provided to the SPM as a sequence.
>>
>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>> the index of the first command in the sequence is set in the SPM_CTL
>> register. When the core executes ARM wfi instruction, it triggers the
>> SPM state machine to start executing from that index. The SPM state
>> machine waits until the interrupt occurs and starts executing the rest
>> of the sequence until it hits the end of the sequence. The end of the
>> sequence jumps the core out of its low power mode.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> [lina: simplify the driver for initial submission, clean up and update
>> commit text]
>> ---
>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>> drivers/soc/qcom/Kconfig                          |   8 +
>> drivers/soc/qcom/Makefile                         |   1 +
>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>> include/soc/qcom/spm.h                            |  38 +++
>> 5 files changed, 478 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>> create mode 100644 drivers/soc/qcom/spm.c
>> create mode 100644 include/soc/qcom/spm.h
>>
>> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>> new file mode 100644
>> index 0000000..2ff2454
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>> @@ -0,0 +1,43 @@
>> +* Subsystem Power Manager (SPM)
>> +
>> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
>> +Processor Sub-System power. These SPM blocks run individual state machine
>> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>> +instruction is executed by the core.
>> +
>> +The devicetree representation of the SPM block should be:
>> +
>> +Required properties
>> +
>> +- compatible: Must be -
>> +		 "qcom,spm-v2.1"
>> +- reg: The physical address and the size of the SPM's memory mapped registers
>> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>> +	This field is required on only for SPMs that control the CPU.
>
>Let’s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.
>
Okay. Will look into it.
You mean just the property name, right?

>> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>
>(add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)
>
The SPM spec is not available for open use. The range of values is
irrelevant for the SPM clocks, usually, its a constant for an SoC, but
may vary between the SoC. Its how the SPM on the SoC interprets it. 

>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>
>Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>
Yes he did. My response was similar to the clk-div values, its not
something you can change without hardware spec documentation.
And I need to mix the three values up, anyways before I write to the
register. Splitting it up, doesnt help understanding/configuring the SPM
any better, so didnt change it.

>> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>
>Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?
>
Okay, sure.

>> +
>> +Optional properties
>> +
>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>
>probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)
>
Okay.

>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>
>probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)
>
Okay.

>> +
>> +Example:
>> +	spm@f9089000 {
>> +		compatible = "qcom,spm-v2.1";
>> +		#address-cells = <1>;
>> +		#size-cells = <1>;
>> +		reg = <0xf9089000 0x1000>;
>> +		qcom,cpu = <&CPU0>;
>> +		qcom,saw2-clk-div = <0x1>;
>> +		qcom,saw2-delays = <0x20000400>;
>> +		qcom,saw2-enable = <0x1>;
>> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>> +				a0 b0 03 68 70 3b 92 a0 b0
>> +				82 2b 50 10 30 02 22 30 0f];
>> +	};
>
>- k
>
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 17:21       ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 17:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>
>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> Based on work by many authors, available at codeaurora.org
>>
>> SPM is a hardware block that controls the peripheral logic surrounding
>> the application cores (cpu/l$). When the core executes WFI instruction,
>> the SPM takes over the putting the core in low power state as
>> configured. The wake up for the SPM is an interrupt at the GIC, which
>> then completes the rest of low power mode sequence and brings the core
>> out of low power mode.
>>
>> The SPM has a set of control registers that configure the SPMs
>> individually based on the type of the core and the runtime conditions.
>> SPM is a finite state machine block to which a sequence is provided and
>> it interprets the bytes  and executes them in sequence. Each low power
>> mode that the core can enter into is provided to the SPM as a sequence.
>>
>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>> the index of the first command in the sequence is set in the SPM_CTL
>> register. When the core executes ARM wfi instruction, it triggers the
>> SPM state machine to start executing from that index. The SPM state
>> machine waits until the interrupt occurs and starts executing the rest
>> of the sequence until it hits the end of the sequence. The end of the
>> sequence jumps the core out of its low power mode.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> [lina: simplify the driver for initial submission, clean up and update
>> commit text]
>> ---
>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>> drivers/soc/qcom/Kconfig                          |   8 +
>> drivers/soc/qcom/Makefile                         |   1 +
>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>> include/soc/qcom/spm.h                            |  38 +++
>> 5 files changed, 478 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>> create mode 100644 drivers/soc/qcom/spm.c
>> create mode 100644 include/soc/qcom/spm.h
>>
>> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>> new file mode 100644
>> index 0000000..2ff2454
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>> @@ -0,0 +1,43 @@
>> +* Subsystem Power Manager (SPM)
>> +
>> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
>> +Processor Sub-System power. These SPM blocks run individual state machine
>> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>> +instruction is executed by the core.
>> +
>> +The devicetree representation of the SPM block should be:
>> +
>> +Required properties
>> +
>> +- compatible: Must be -
>> +		 "qcom,spm-v2.1"
>> +- reg: The physical address and the size of the SPM's memory mapped registers
>> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>> +	This field is required on only for SPMs that control the CPU.
>
>Let?s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.
>
Okay. Will look into it.
You mean just the property name, right?

>> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>
>(add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)
>
The SPM spec is not available for open use. The range of values is
irrelevant for the SPM clocks, usually, its a constant for an SoC, but
may vary between the SoC. Its how the SPM on the SoC interprets it. 

>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>
>Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>
Yes he did. My response was similar to the clk-div values, its not
something you can change without hardware spec documentation.
And I need to mix the three values up, anyways before I write to the
register. Splitting it up, doesnt help understanding/configuring the SPM
any better, so didnt change it.

>> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>
>Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?
>
Okay, sure.

>> +
>> +Optional properties
>> +
>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>
>probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)
>
Okay.

>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>
>probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)
>
Okay.

>> +
>> +Example:
>> +	spm at f9089000 {
>> +		compatible = "qcom,spm-v2.1";
>> +		#address-cells = <1>;
>> +		#size-cells = <1>;
>> +		reg = <0xf9089000 0x1000>;
>> +		qcom,cpu = <&CPU0>;
>> +		qcom,saw2-clk-div = <0x1>;
>> +		qcom,saw2-delays = <0x20000400>;
>> +		qcom,saw2-enable = <0x1>;
>> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>> +				a0 b0 03 68 70 3b 92 a0 b0
>> +				82 2b 50 10 30 02 22 30 0f];
>> +	};
>
>- k
>
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-24 17:21         ` Stephen Boyd
@ 2014-09-24 17:23           ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 17:23 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Pramod Gurav, galak, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, khilman, msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 11:21 -0600, Stephen Boyd wrote:
>On 09/24/14 06:49, Lina Iyer wrote:
>> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>>> Hi Lina,
>>>
>>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>>> power manager and controls the logic around the cores (cpu and L2).
>>>>
>>>> Each core has an instance of SPM and controls only that core. Each cpu
>>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> ---
>>>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69
>>>> ++++++++++++++++++++++++++++++++++
>>>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>>>
>>> <snip>
>>>
>>>> +};
>>>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> index 69dca2a..0580bc2 100644
>>>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> @@ -14,7 +14,7 @@
>>>>          #size-cells = <0>;
>>>>          interrupts = <1 9 0xf04>;
>>>>
>>>> -        cpu@0 {
>>>> +        CPU0: cpu@0 {
>>> Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
>>> renames this node to "cpu0: cpu@0". If you both could sync up and agree
>>> on a common naming('cpu0' with caps or not caps) for this node.
>>>
>> Sure. Will work with Stephen on that.
>>
>
>This doesn't seem like a big deal. I imagine Kumar can resolve the
>conflict if the two patches merge at the same time.
>
I can use the lower case names, if thats common. I see that being used
for referring to <&acc> in that file, but ofcourse <&L2> exists as well.
Any preference?

>-- 
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
>hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24 17:23           ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 17:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 11:21 -0600, Stephen Boyd wrote:
>On 09/24/14 06:49, Lina Iyer wrote:
>> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>>> Hi Lina,
>>>
>>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the sub-system
>>>> power manager and controls the logic around the cores (cpu and L2).
>>>>
>>>> Each core has an instance of SPM and controls only that core. Each cpu
>>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> ---
>>>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69
>>>> ++++++++++++++++++++++++++++++++++
>>>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>>>
>>> <snip>
>>>
>>>> +};
>>>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> index 69dca2a..0580bc2 100644
>>>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>> @@ -14,7 +14,7 @@
>>>>          #size-cells = <0>;
>>>>          interrupts = <1 9 0xf04>;
>>>>
>>>> -        cpu at 0 {
>>>> +        CPU0: cpu at 0 {
>>> Lina, Stephen boyd has sent some DT change for krait-cpufreq which also
>>> renames this node to "cpu0: cpu at 0". If you both could sync up and agree
>>> on a common naming('cpu0' with caps or not caps) for this node.
>>>
>> Sure. Will work with Stephen on that.
>>
>
>This doesn't seem like a big deal. I imagine Kumar can resolve the
>conflict if the two patches merge at the same time.
>
I can use the lower case names, if thats common. I see that being used
for referring to <&acc> in that file, but ofcourse <&L2> exists as well.
Any preference?

>-- 
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
>hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-23 23:51   ` Lina Iyer
@ 2014-09-24 17:43     ` Josh Cartwright
  -1 siblings, 0 replies; 60+ messages in thread
From: Josh Cartwright @ 2014-09-24 17:43 UTC (permalink / raw)
  To: Lina Iyer
  Cc: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel,
	khilman, msivasub, lorenzo.pieralisi, linux-pm

Hey Lina-

A few comments inline:

On Tue, Sep 23, 2014 at 05:51:17PM -0600, Lina Iyer wrote:
> +++ b/drivers/soc/qcom/spm.c
[..]
> +
> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {

const?

> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
> +};
> +
> +struct spm_of {
> +	char *key;

const char *key?

> +	u32 id;
> +};
> +
> +struct msm_spm_mode {
> +	u32 mode;
> +	u32 start_addr;
> +};
> +
> +struct msm_spm_driver_data {
> +	void __iomem *reg_base_addr;
> +	u32 *reg_offsets;
> +	struct msm_spm_mode *modes;
> +	u32 num_modes;

Why u32?

Actually, the maximum modes is fixed, and really all you need to keep
around is the start_addr per-mode (which is only 5 bits), and an
additional bit indicating whether that mode is valid. I'd recommend
folding msm_spm_mode into msm_spm_driver_data completely.  Something
like this, maybe:

	struct msm_spm_driver_data {
		void __iomem *reg_base_addr;
		const u32 *reg_offsets;
		struct {
			u8 is_valid;
			u8 start_addr;
		} modes[MSM_SPM_MODE_NR];
	};

> +};
> +
> +struct msm_spm_device {
> +	bool initialized;
> +	struct msm_spm_driver_data drv;
> +};
> +
> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);

Why have both msm_spm_device and msm_spm_driver_data?

Would it be easier if you instead used 'struct msm_spm_device *', and
used NULL to indicate it has not been initialized?

> +static const struct of_device_id msm_spm_match_table[] __initconst;

Just move the table above probe.

> +
> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
> +						u32 mode)
> +{
> +	int i;
> +	u32 start_addr = 0;
> +	u32 ctl_val;
> +
> +	for (i = 0; i < drv->num_modes; i++) {
> +		if (drv->modes[i].mode == mode) {
> +			start_addr = drv->modes[i].start_addr;
> +			break;
> +		}
> +	}
> +
> +	if (i == drv->num_modes)
> +		return -EINVAL;
> +
> +	/* Update bits 10:4 in the SPM CTL register */
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	start_addr &= 0x7F;
> +	start_addr <<= 4;
> +	ctl_val &= 0xFFFFF80F;
> +	ctl_val |= start_addr;
> +	writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	/* Ensure we have written the start address */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
> +					bool enable)
> +{
> +	u32 value = enable ? 0x01 : 0x00;
> +	u32 ctl_val;
> +
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +	/* Update SPM_CTL to enable/disable the SPM */
> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
> +		/* Clear the existing value and update */
> +		ctl_val &= ~0x1;
> +		ctl_val |= value;
> +		writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +		/* Ensure we have enabled/disabled before returning */
> +		wmb();
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
> + * @mode: SPM LPM mode to enter
> + */
> +int msm_spm_set_low_power_mode(u32 mode)
> +{
> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
> +	int ret = -EINVAL;
> +
> +	if (!dev->initialized)
> +		return -ENXIO;
> +
> +	if (mode == MSM_SPM_MODE_DISABLED)
> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);

I would suggest not modeling "DISABLED" as a "mode", as it's not a state
like the others.  Instead, perhaps you could expect users to call
msm_spm_drv_set_spm_enable() directly.

> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);

Is this actually to be used by modules?

[..]
> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
> +			struct platform_device *pdev)
> +{
> +	int i;
> +	u8 *cmd;

const u8 *cmd; will save you the cast below.

> +	void *addr;
> +	u32 val;
> +	u32 count = 0;
> +	int offset = 0;
> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
> +
> +	/* SPM sleep sequences */
> +	struct spm_of mode_of_data[] = {

static const?

> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
> +	};
> +
> +	/**
> +	 * Compose the u32 array based on the individual bytes of the SPM
> +	 * sequence for each low power mode that we read from the DT.
> +	 * The sequences are appended if there is space available in the
> +	 * u32 after the end of the previous sequence.
> +	 */
> +
> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
> +						mode_of_data[i].key, &val);
> +		if (!cmd)
> +			continue;
> +		/* The last in the sequence should be 0x0F */
> +		if (cmd[val - 1] != 0x0F)
> +			continue;
> +		modes[count].mode = mode_of_data[i].id;
> +		modes[count].start_addr = offset;
> +		append_seq_data(&sequences[0], cmd, &offset);
> +		count++;
> +	}
> +
> +	/* Write the idle state sequences to SPM */
> +	drv->modes = devm_kcalloc(&pdev->dev, count,
> +					sizeof(modes[0]), GFP_KERNEL);
> +	if (!drv->modes)
> +		return -ENOMEM;
> +
> +	drv->num_modes = count;
> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
> +
> +	/* Flush the integer array */
> +	addr = drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
> +		writel_relaxed(sequences[i], addr);
> +
> +	/* Ensure we flush the writes */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
> +{
> +	struct msm_spm_device *dev = NULL;
> +	struct device_node *cpu_node;
> +	u32 cpu;
> +
> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
> +	if (cpu_node) {
> +		for_each_possible_cpu(cpu) {
> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
> +		}
> +	}
> +
> +	return dev;
> +}
> +
> +static int msm_spm_dev_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	int i;
> +	u32 val;
> +	struct msm_spm_device *spm_dev;
> +	struct resource *res;
> +	const struct of_device_id *match_id;
> +
> +	/* SPM Configuration registers */
> +	struct spm_of spm_of_data[] = {

static const?

> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
> +	};
> +
> +	 /* Get the right SPM device */
> +	spm_dev = msm_spm_get_device(pdev);
> +	if (IS_ERR_OR_NULL(spm_dev))

Should this just be a simple NULL check?

> +		return -EINVAL;
> +
> +	/* Get the SPM start address */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -EINVAL;
> +		goto fail;
> +	}
> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
> +					resource_size(res));

devm_ioremap_resource()?

> +	if (!spm_dev->drv.reg_base_addr) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
> +	if (!match_id)
> +		return -ENODEV;
> +
> +	/* Use the register offsets for the SPM version in use */
> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
> +	if (!spm_dev->drv.reg_offsets)
> +		return -EFAULT;
> +
> +	/* Read the SPM idle state sequences */
> +	ret = msm_spm_seq_init(spm_dev, pdev);
> +	if (ret)
> +		return ret;
> +
> +	/* Read the SPM register values */
> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
> +		ret = of_property_read_u32(pdev->dev.of_node,
> +					spm_of_data[i].key, &val);
> +		if (ret)
> +			continue;
> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
> +	}
> +
> +	/* Flush all writes */

This isn't very descriptive.  Perhaps:

/*
 * Ensure all observers see the above register writes before the
 * updating of spm_dev->initialized
 */

> +	wmb();
> +
> +	spm_dev->initialized = true;
> +	return ret;
> +fail:
> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id msm_spm_match_table[] __initconst = {
> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
> +	{ },
> +};
> +
> +
> +static struct platform_driver msm_spm_device_driver = {
> +	.probe = msm_spm_dev_probe,
> +	.driver = {
> +		.name = "spm",
> +		.owner = THIS_MODULE,

This assignment is not necessary.

> +		.of_match_table = msm_spm_match_table,
> +	},
> +};
> +
> +static int __init msm_spm_device_init(void)
> +{
> +	return platform_driver_register(&msm_spm_device_driver);
> +}
> +device_initcall(msm_spm_device_init);
> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
> new file mode 100644
> index 0000000..29686ef
> --- /dev/null
> +++ b/include/soc/qcom/spm.h
> @@ -0,0 +1,38 @@
> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __QCOM_SPM_H
> +#define __QCOM_SPM_H
> +
> +enum {
> +	MSM_SPM_MODE_DISABLED,
> +	MSM_SPM_MODE_CLOCK_GATING,
> +	MSM_SPM_MODE_RETENTION,
> +	MSM_SPM_MODE_GDHS,
> +	MSM_SPM_MODE_POWER_COLLAPSE,
> +	MSM_SPM_MODE_NR
> +};

Is there a particular reason you aren't naming this enumeration, and
using it's type in msm_spm_set_low_power_mode()?

> +
> +struct msm_spm_device;

Why this forward declaration?

> +
> +#if defined(CONFIG_QCOM_PM)
> +
> +int msm_spm_set_low_power_mode(u32 mode);
> +
> +#else
> +
> +static inline int msm_spm_set_low_power_mode(u32 mode)
> +{ return -ENOSYS; }
> +
> +#endif  /* CONFIG_QCOM_PM */
> +
> +#endif  /* __QCOM_SPM_H */
> -- 
> 1.9.1
> 

Thanks,
  Josh

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 17:43     ` Josh Cartwright
  0 siblings, 0 replies; 60+ messages in thread
From: Josh Cartwright @ 2014-09-24 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

Hey Lina-

A few comments inline:

On Tue, Sep 23, 2014 at 05:51:17PM -0600, Lina Iyer wrote:
> +++ b/drivers/soc/qcom/spm.c
[..]
> +
> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {

const?

> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
> +};
> +
> +struct spm_of {
> +	char *key;

const char *key?

> +	u32 id;
> +};
> +
> +struct msm_spm_mode {
> +	u32 mode;
> +	u32 start_addr;
> +};
> +
> +struct msm_spm_driver_data {
> +	void __iomem *reg_base_addr;
> +	u32 *reg_offsets;
> +	struct msm_spm_mode *modes;
> +	u32 num_modes;

Why u32?

Actually, the maximum modes is fixed, and really all you need to keep
around is the start_addr per-mode (which is only 5 bits), and an
additional bit indicating whether that mode is valid. I'd recommend
folding msm_spm_mode into msm_spm_driver_data completely.  Something
like this, maybe:

	struct msm_spm_driver_data {
		void __iomem *reg_base_addr;
		const u32 *reg_offsets;
		struct {
			u8 is_valid;
			u8 start_addr;
		} modes[MSM_SPM_MODE_NR];
	};

> +};
> +
> +struct msm_spm_device {
> +	bool initialized;
> +	struct msm_spm_driver_data drv;
> +};
> +
> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);

Why have both msm_spm_device and msm_spm_driver_data?

Would it be easier if you instead used 'struct msm_spm_device *', and
used NULL to indicate it has not been initialized?

> +static const struct of_device_id msm_spm_match_table[] __initconst;

Just move the table above probe.

> +
> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
> +						u32 mode)
> +{
> +	int i;
> +	u32 start_addr = 0;
> +	u32 ctl_val;
> +
> +	for (i = 0; i < drv->num_modes; i++) {
> +		if (drv->modes[i].mode == mode) {
> +			start_addr = drv->modes[i].start_addr;
> +			break;
> +		}
> +	}
> +
> +	if (i == drv->num_modes)
> +		return -EINVAL;
> +
> +	/* Update bits 10:4 in the SPM CTL register */
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	start_addr &= 0x7F;
> +	start_addr <<= 4;
> +	ctl_val &= 0xFFFFF80F;
> +	ctl_val |= start_addr;
> +	writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	/* Ensure we have written the start address */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
> +					bool enable)
> +{
> +	u32 value = enable ? 0x01 : 0x00;
> +	u32 ctl_val;
> +
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +	/* Update SPM_CTL to enable/disable the SPM */
> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
> +		/* Clear the existing value and update */
> +		ctl_val &= ~0x1;
> +		ctl_val |= value;
> +		writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +		/* Ensure we have enabled/disabled before returning */
> +		wmb();
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
> + * @mode: SPM LPM mode to enter
> + */
> +int msm_spm_set_low_power_mode(u32 mode)
> +{
> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
> +	int ret = -EINVAL;
> +
> +	if (!dev->initialized)
> +		return -ENXIO;
> +
> +	if (mode == MSM_SPM_MODE_DISABLED)
> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);

I would suggest not modeling "DISABLED" as a "mode", as it's not a state
like the others.  Instead, perhaps you could expect users to call
msm_spm_drv_set_spm_enable() directly.

> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);

Is this actually to be used by modules?

[..]
> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
> +			struct platform_device *pdev)
> +{
> +	int i;
> +	u8 *cmd;

const u8 *cmd; will save you the cast below.

> +	void *addr;
> +	u32 val;
> +	u32 count = 0;
> +	int offset = 0;
> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
> +
> +	/* SPM sleep sequences */
> +	struct spm_of mode_of_data[] = {

static const?

> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
> +	};
> +
> +	/**
> +	 * Compose the u32 array based on the individual bytes of the SPM
> +	 * sequence for each low power mode that we read from the DT.
> +	 * The sequences are appended if there is space available in the
> +	 * u32 after the end of the previous sequence.
> +	 */
> +
> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
> +						mode_of_data[i].key, &val);
> +		if (!cmd)
> +			continue;
> +		/* The last in the sequence should be 0x0F */
> +		if (cmd[val - 1] != 0x0F)
> +			continue;
> +		modes[count].mode = mode_of_data[i].id;
> +		modes[count].start_addr = offset;
> +		append_seq_data(&sequences[0], cmd, &offset);
> +		count++;
> +	}
> +
> +	/* Write the idle state sequences to SPM */
> +	drv->modes = devm_kcalloc(&pdev->dev, count,
> +					sizeof(modes[0]), GFP_KERNEL);
> +	if (!drv->modes)
> +		return -ENOMEM;
> +
> +	drv->num_modes = count;
> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
> +
> +	/* Flush the integer array */
> +	addr = drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
> +		writel_relaxed(sequences[i], addr);
> +
> +	/* Ensure we flush the writes */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
> +{
> +	struct msm_spm_device *dev = NULL;
> +	struct device_node *cpu_node;
> +	u32 cpu;
> +
> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
> +	if (cpu_node) {
> +		for_each_possible_cpu(cpu) {
> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
> +		}
> +	}
> +
> +	return dev;
> +}
> +
> +static int msm_spm_dev_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	int i;
> +	u32 val;
> +	struct msm_spm_device *spm_dev;
> +	struct resource *res;
> +	const struct of_device_id *match_id;
> +
> +	/* SPM Configuration registers */
> +	struct spm_of spm_of_data[] = {

static const?

> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
> +	};
> +
> +	 /* Get the right SPM device */
> +	spm_dev = msm_spm_get_device(pdev);
> +	if (IS_ERR_OR_NULL(spm_dev))

Should this just be a simple NULL check?

> +		return -EINVAL;
> +
> +	/* Get the SPM start address */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -EINVAL;
> +		goto fail;
> +	}
> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
> +					resource_size(res));

devm_ioremap_resource()?

> +	if (!spm_dev->drv.reg_base_addr) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
> +	if (!match_id)
> +		return -ENODEV;
> +
> +	/* Use the register offsets for the SPM version in use */
> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
> +	if (!spm_dev->drv.reg_offsets)
> +		return -EFAULT;
> +
> +	/* Read the SPM idle state sequences */
> +	ret = msm_spm_seq_init(spm_dev, pdev);
> +	if (ret)
> +		return ret;
> +
> +	/* Read the SPM register values */
> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
> +		ret = of_property_read_u32(pdev->dev.of_node,
> +					spm_of_data[i].key, &val);
> +		if (ret)
> +			continue;
> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
> +	}
> +
> +	/* Flush all writes */

This isn't very descriptive.  Perhaps:

/*
 * Ensure all observers see the above register writes before the
 * updating of spm_dev->initialized
 */

> +	wmb();
> +
> +	spm_dev->initialized = true;
> +	return ret;
> +fail:
> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id msm_spm_match_table[] __initconst = {
> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
> +	{ },
> +};
> +
> +
> +static struct platform_driver msm_spm_device_driver = {
> +	.probe = msm_spm_dev_probe,
> +	.driver = {
> +		.name = "spm",
> +		.owner = THIS_MODULE,

This assignment is not necessary.

> +		.of_match_table = msm_spm_match_table,
> +	},
> +};
> +
> +static int __init msm_spm_device_init(void)
> +{
> +	return platform_driver_register(&msm_spm_device_driver);
> +}
> +device_initcall(msm_spm_device_init);
> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
> new file mode 100644
> index 0000000..29686ef
> --- /dev/null
> +++ b/include/soc/qcom/spm.h
> @@ -0,0 +1,38 @@
> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __QCOM_SPM_H
> +#define __QCOM_SPM_H
> +
> +enum {
> +	MSM_SPM_MODE_DISABLED,
> +	MSM_SPM_MODE_CLOCK_GATING,
> +	MSM_SPM_MODE_RETENTION,
> +	MSM_SPM_MODE_GDHS,
> +	MSM_SPM_MODE_POWER_COLLAPSE,
> +	MSM_SPM_MODE_NR
> +};

Is there a particular reason you aren't naming this enumeration, and
using it's type in msm_spm_set_low_power_mode()?

> +
> +struct msm_spm_device;

Why this forward declaration?

> +
> +#if defined(CONFIG_QCOM_PM)
> +
> +int msm_spm_set_low_power_mode(u32 mode);
> +
> +#else
> +
> +static inline int msm_spm_set_low_power_mode(u32 mode)
> +{ return -ENOSYS; }
> +
> +#endif  /* CONFIG_QCOM_PM */
> +
> +#endif  /* __QCOM_SPM_H */
> -- 
> 1.9.1
> 

Thanks,
  Josh

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 17:21       ` Lina Iyer
@ 2014-09-24 17:53         ` Kumar Gala
  -1 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 17:53 UTC (permalink / raw)
  To: Lina Iyer
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm


On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>> 
>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>> 
>>> Based on work by many authors, available at codeaurora.org
>>> 
>>> SPM is a hardware block that controls the peripheral logic surrounding
>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>> the SPM takes over the putting the core in low power state as
>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>> then completes the rest of low power mode sequence and brings the core
>>> out of low power mode.
>>> 
>>> The SPM has a set of control registers that configure the SPMs
>>> individually based on the type of the core and the runtime conditions.
>>> SPM is a finite state machine block to which a sequence is provided and
>>> it interprets the bytes  and executes them in sequence. Each low power
>>> mode that the core can enter into is provided to the SPM as a sequence.
>>> 
>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>> the index of the first command in the sequence is set in the SPM_CTL
>>> register. When the core executes ARM wfi instruction, it triggers the
>>> SPM state machine to start executing from that index. The SPM state
>>> machine waits until the interrupt occurs and starts executing the rest
>>> of the sequence until it hits the end of the sequence. The end of the
>>> sequence jumps the core out of its low power mode.
>>> 
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> [lina: simplify the driver for initial submission, clean up and update
>>> commit text]
>>> ---
>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>> drivers/soc/qcom/Kconfig                          |   8 +
>>> drivers/soc/qcom/Makefile                         |   1 +
>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>> include/soc/qcom/spm.h                            |  38 +++
>>> 5 files changed, 478 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>> create mode 100644 drivers/soc/qcom/spm.c
>>> create mode 100644 include/soc/qcom/spm.h
>>> 
>>> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>> new file mode 100644
>>> index 0000000..2ff2454
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>> @@ -0,0 +1,43 @@
>>> +* Subsystem Power Manager (SPM)
>>> +
>>> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
>>> +Processor Sub-System power. These SPM blocks run individual state machine
>>> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>>> +instruction is executed by the core.
>>> +
>>> +The devicetree representation of the SPM block should be:
>>> +
>>> +Required properties
>>> +
>>> +- compatible: Must be -
>>> +		 "qcom,spm-v2.1"
>>> +- reg: The physical address and the size of the SPM's memory mapped registers
>>> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>>> +	This field is required on only for SPMs that control the CPU.
>> 
>> Let’s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.
>> 
> Okay. Will look into it.
> You mean just the property name, right?

Correct.

> 
>>> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>>> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>> 
>> (add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)
>> 
> The SPM spec is not available for open use. The range of values is
> irrelevant for the SPM clocks, usually, its a constant for an SoC, but
> may vary between the SoC. Its how the SPM on the SoC interprets it.

Does the meaning of the divisor value change from SoC to SoC (not the value itself)?

Is it not always:

timer tick = sys_ref_clk / (qcom,saw2-clk-div + 1)
 
>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>> 
>> Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>> 
> Yes he did. My response was similar to the clk-div values, its not
> something you can change without hardware spec documentation.
> And I need to mix the three values up, anyways before I write to the
> register. Splitting it up, doesnt help understanding/configuring the SPM
> any better, so didnt change it.

Hmm, will this value change from SPM to SPM on the same SoC?  I’m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.

>>> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>>> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>> 
>> Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?
>> 
> Okay, sure.
> 
>>> +
>>> +Optional properties
>>> +
>>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>> 
>> probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)
>> 
> Okay.
> 
>>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>> 
>> probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)
>> 
> Okay.
> 
>>> +
>>> +Example:
>>> +	spm@f9089000 {
>>> +		compatible = "qcom,spm-v2.1";
>>> +		#address-cells = <1>;
>>> +		#size-cells = <1>;
>>> +		reg = <0xf9089000 0x1000>;
>>> +		qcom,cpu = <&CPU0>;
>>> +		qcom,saw2-clk-div = <0x1>;
>>> +		qcom,saw2-delays = <0x20000400>;
>>> +		qcom,saw2-enable = <0x1>;
>>> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>>> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>>> +				a0 b0 03 68 70 3b 92 a0 b0
>>> +				82 2b 50 10 30 02 22 30 0f];
>>> +	};
>> 
>> - k
>> 
>> 
>> -- 
>> Employee of Qualcomm Innovation Center, Inc.
>> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 17:53         ` Kumar Gala
  0 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 17:53 UTC (permalink / raw)
  To: linux-arm-kernel


On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>> 
>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>> 
>>> Based on work by many authors, available at codeaurora.org
>>> 
>>> SPM is a hardware block that controls the peripheral logic surrounding
>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>> the SPM takes over the putting the core in low power state as
>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>> then completes the rest of low power mode sequence and brings the core
>>> out of low power mode.
>>> 
>>> The SPM has a set of control registers that configure the SPMs
>>> individually based on the type of the core and the runtime conditions.
>>> SPM is a finite state machine block to which a sequence is provided and
>>> it interprets the bytes  and executes them in sequence. Each low power
>>> mode that the core can enter into is provided to the SPM as a sequence.
>>> 
>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>> the index of the first command in the sequence is set in the SPM_CTL
>>> register. When the core executes ARM wfi instruction, it triggers the
>>> SPM state machine to start executing from that index. The SPM state
>>> machine waits until the interrupt occurs and starts executing the rest
>>> of the sequence until it hits the end of the sequence. The end of the
>>> sequence jumps the core out of its low power mode.
>>> 
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> [lina: simplify the driver for initial submission, clean up and update
>>> commit text]
>>> ---
>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>> drivers/soc/qcom/Kconfig                          |   8 +
>>> drivers/soc/qcom/Makefile                         |   1 +
>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>> include/soc/qcom/spm.h                            |  38 +++
>>> 5 files changed, 478 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>> create mode 100644 drivers/soc/qcom/spm.c
>>> create mode 100644 include/soc/qcom/spm.h
>>> 
>>> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>> new file mode 100644
>>> index 0000000..2ff2454
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>> @@ -0,0 +1,43 @@
>>> +* Subsystem Power Manager (SPM)
>>> +
>>> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
>>> +Processor Sub-System power. These SPM blocks run individual state machine
>>> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>>> +instruction is executed by the core.
>>> +
>>> +The devicetree representation of the SPM block should be:
>>> +
>>> +Required properties
>>> +
>>> +- compatible: Must be -
>>> +		 "qcom,spm-v2.1"
>>> +- reg: The physical address and the size of the SPM's memory mapped registers
>>> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>>> +	This field is required on only for SPMs that control the CPU.
>> 
>> Let?s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.
>> 
> Okay. Will look into it.
> You mean just the property name, right?

Correct.

> 
>>> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>>> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>> 
>> (add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)
>> 
> The SPM spec is not available for open use. The range of values is
> irrelevant for the SPM clocks, usually, its a constant for an SoC, but
> may vary between the SoC. Its how the SPM on the SoC interprets it.

Does the meaning of the divisor value change from SoC to SoC (not the value itself)?

Is it not always:

timer tick = sys_ref_clk / (qcom,saw2-clk-div + 1)
 
>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>> 
>> Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>> 
> Yes he did. My response was similar to the clk-div values, its not
> something you can change without hardware spec documentation.
> And I need to mix the three values up, anyways before I write to the
> register. Splitting it up, doesnt help understanding/configuring the SPM
> any better, so didnt change it.

Hmm, will this value change from SPM to SPM on the same SoC?  I?m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.

>>> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>>> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>> 
>> Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?
>> 
> Okay, sure.
> 
>>> +
>>> +Optional properties
>>> +
>>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>> 
>> probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)
>> 
> Okay.
> 
>>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>> 
>> probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)
>> 
> Okay.
> 
>>> +
>>> +Example:
>>> +	spm at f9089000 {
>>> +		compatible = "qcom,spm-v2.1";
>>> +		#address-cells = <1>;
>>> +		#size-cells = <1>;
>>> +		reg = <0xf9089000 0x1000>;
>>> +		qcom,cpu = <&CPU0>;
>>> +		qcom,saw2-clk-div = <0x1>;
>>> +		qcom,saw2-delays = <0x20000400>;
>>> +		qcom,saw2-enable = <0x1>;
>>> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>>> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>>> +				a0 b0 03 68 70 3b 92 a0 b0
>>> +				82 2b 50 10 30 02 22 30 0f];
>>> +	};
>> 
>> - k
>> 
>> 
>> -- 
>> Employee of Qualcomm Innovation Center, Inc.
>> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-23 23:51   ` Lina Iyer
@ 2014-09-24 18:07     ` Kumar Gala
  -1 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 18:07 UTC (permalink / raw)
  To: Lina Iyer
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm


On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> Based on work by many authors, available at codeaurora.org
> 
> SPM is a hardware block that controls the peripheral logic surrounding
> the application cores (cpu/l$). When the core executes WFI instruction,
> the SPM takes over the putting the core in low power state as
> configured. The wake up for the SPM is an interrupt at the GIC, which
> then completes the rest of low power mode sequence and brings the core
> out of low power mode.
> 
> The SPM has a set of control registers that configure the SPMs
> individually based on the type of the core and the runtime conditions.
> SPM is a finite state machine block to which a sequence is provided and
> it interprets the bytes  and executes them in sequence. Each low power
> mode that the core can enter into is provided to the SPM as a sequence.
> 
> Configure the SPM to set the core (cpu or L2) into its low power mode,
> the index of the first command in the sequence is set in the SPM_CTL
> register. When the core executes ARM wfi instruction, it triggers the
> SPM state machine to start executing from that index. The SPM state
> machine waits until the interrupt occurs and starts executing the rest
> of the sequence until it hits the end of the sequence. The end of the
> sequence jumps the core out of its low power mode.
> 
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> [lina: simplify the driver for initial submission, clean up and update
> commit text]
> ---
> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
> drivers/soc/qcom/Kconfig                          |   8 +
> drivers/soc/qcom/Makefile                         |   1 +
> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
> include/soc/qcom/spm.h                            |  38 +++
> 5 files changed, 478 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 include/soc/qcom/spm.h

General comment, lets use qcom instead of msm for various things.

[snip]

> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 7dcd554..cd249c4 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -11,3 +11,11 @@ config QCOM_GSBI
> 
> config QCOM_SCM
> 	bool
> +
> +config QCOM_PM
> +	bool "Qualcomm Power Management"
> +	depends on PM && ARCH_QCOM
> +	help
> +	  QCOM Platform specific power driver to manage cores and L2 low power
> +	  modes. It interface with various system drivers to put the cores in
> +	  low power modes.
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 70d52ed..20b329f 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
> +obj-$(CONFIG_QCOM_PM)	+=	spm.o
> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
> diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
> new file mode 100644
> index 0000000..1fa6a96
> --- /dev/null
> +++ b/drivers/soc/qcom/spm.c
> @@ -0,0 +1,388 @@
> +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +
> +#include <soc/qcom/spm.h>
> +
> +#define NUM_SEQ_ENTRY 32
> +#define SPM_CTL_ENABLE BIT(0)
> +
> +enum {
> +	MSM_SPM_REG_SAW2_CFG,
> +	MSM_SPM_REG_SAW2_AVS_CTL,
> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
> +	MSM_SPM_REG_SAW2_SPM_CTL,
> +	MSM_SPM_REG_SAW2_PMIC_DLY,
> +	MSM_SPM_REG_SAW2_AVS_LIMIT,
> +	MSM_SPM_REG_SAW2_AVS_DLY,
> +	MSM_SPM_REG_SAW2_SPM_DLY,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_0,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_1,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_2,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_3,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_4,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_5,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_6,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_7,
> +	MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_SAW2_ID,
> +	MSM_SPM_REG_SAW2_SECURE,
> +	MSM_SPM_REG_SAW2_STS0,
> +	MSM_SPM_REG_SAW2_STS1,
> +	MSM_SPM_REG_SAW2_STS2,
> +	MSM_SPM_REG_SAW2_VCTL,
> +	MSM_SPM_REG_SAW2_SEQ_ENTRY,
> +	MSM_SPM_REG_SAW2_SPM_STS,
> +	MSM_SPM_REG_SAW2_AVS_STS,
> +	MSM_SPM_REG_SAW2_PMIC_STS,
> +	MSM_SPM_REG_SAW2_VERSION,
> +
> +	MSM_SPM_REG_NR,
> +};
> +
> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {

Why an array, can’t this just be an enum?

> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
> +};
> +
> +struct spm_of {
> +	char *key;
> +	u32 id;
> +};
> +
> +struct msm_spm_mode {
> +	u32 mode;
> +	u32 start_addr;
> +};
> +
> +struct msm_spm_driver_data {
> +	void __iomem *reg_base_addr;
> +	u32 *reg_offsets;
> +	struct msm_spm_mode *modes;
> +	u32 num_modes;
> +};
> +
> +struct msm_spm_device {
> +	bool initialized;
> +	struct msm_spm_driver_data drv;
> +};
> +
> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
> +
> +static const struct of_device_id msm_spm_match_table[] __initconst;
> +
> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
> +						u32 mode)
> +{

Can we just fold this into msm_spm_set_low_power_mode
> 
> +	int i;
> +	u32 start_addr = 0;
> +	u32 ctl_val;
> +
> +	for (i = 0; i < drv->num_modes; i++) {
> +		if (drv->modes[i].mode == mode) {
> +			start_addr = drv->modes[i].start_addr;
> +			break;
> +		}
> +	}
> +
> +	if (i == drv->num_modes)
> +		return -EINVAL;
> +
> +	/* Update bits 10:4 in the SPM CTL register */
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	start_addr &= 0x7F;
> +	start_addr <<= 4;
> +	ctl_val &= 0xFFFFF80F;
> +	ctl_val |= start_addr;
> +	writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	/* Ensure we have written the start address */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
> +					bool enable)
> +{
> +	u32 value = enable ? 0x01 : 0x00;
> +	u32 ctl_val;
> +
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +	/* Update SPM_CTL to enable/disable the SPM */
> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
> +		/* Clear the existing value and update */
> +		ctl_val &= ~0x1;
> +		ctl_val |= value;
> +		writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +		/* Ensure we have enabled/disabled before returning */
> +		wmb();
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
> + * @mode: SPM LPM mode to enter
> + */
> +int msm_spm_set_low_power_mode(u32 mode)
> +{
> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
> +	int ret = -EINVAL;
> +
> +	if (!dev->initialized)
> +		return -ENXIO;
> +
> +	if (mode == MSM_SPM_MODE_DISABLED)
> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
> +

This could become:

	if (mode == MSM_SPM_MODE_DISABLED)
		return msm_spm_drv_set_spm_enable(&dev->drv, false);

	if (msm_spm_drv_set_spm_enable(&dev->drv, true))
		return ret;

	code from msm_spm_drv_set_low_power_mode

> +	return ret;
> +}
> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);
> +
> +static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
> +{
> +	u32 cmd_w;
> +	u32 offset_w = *offset / 4;
> +	u8 last_cmd;
> +
> +	while (1) {
> +		int i;
> +
> +		cmd_w = 0;
> +		last_cmd = 0;
> +		cmd_w = reg_seq_entry[offset_w];
> +
> +		for (i = (*offset % 4); i < 4; i++) {
> +			last_cmd = *(cmd++);
> +			cmd_w |=  last_cmd << (i * 8);
> +			(*offset)++;
> +			if (last_cmd == 0x0f)
> +				break;
> +		}
> +
> +		reg_seq_entry[offset_w++] = cmd_w;
> +		if (last_cmd == 0x0f)
> +			break;
> +	}
> +}
> +
> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
> +			struct platform_device *pdev)
> +{
> +	int i;
> +	u8 *cmd;
> +	void *addr;
> +	u32 val;
> +	u32 count = 0;
> +	int offset = 0;
> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
> +
> +	/* SPM sleep sequences */
> +	struct spm_of mode_of_data[] = {
> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
> +	};
> +
> +	/**
> +	 * Compose the u32 array based on the individual bytes of the SPM
> +	 * sequence for each low power mode that we read from the DT.
> +	 * The sequences are appended if there is space available in the
> +	 * u32 after the end of the previous sequence.
> +	 */
> +
> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
> +						mode_of_data[i].key, &val);
> +		if (!cmd)
> +			continue;
> +		/* The last in the sequence should be 0x0F */
> +		if (cmd[val - 1] != 0x0F)
> +			continue;
> +		modes[count].mode = mode_of_data[i].id;
> +		modes[count].start_addr = offset;
> +		append_seq_data(&sequences[0], cmd, &offset);
> +		count++;
> +	}
> +
> +	/* Write the idle state sequences to SPM */
> +	drv->modes = devm_kcalloc(&pdev->dev, count,
> +					sizeof(modes[0]), GFP_KERNEL);
> +	if (!drv->modes)
> +		return -ENOMEM;
> +
> +	drv->num_modes = count;
> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
> +
> +	/* Flush the integer array */
> +	addr = drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
> +		writel_relaxed(sequences[i], addr);
> +
> +	/* Ensure we flush the writes */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
> +{
> +	struct msm_spm_device *dev = NULL;
> +	struct device_node *cpu_node;
> +	u32 cpu;
> +
> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
> +	if (cpu_node) {
> +		for_each_possible_cpu(cpu) {
> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
> +		}
> +	}
> +
> +	return dev;
> +}
> +
> +static int msm_spm_dev_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	int i;
> +	u32 val;
> +	struct msm_spm_device *spm_dev;
> +	struct resource *res;
> +	const struct of_device_id *match_id;
> +
> +	/* SPM Configuration registers */
> +	struct spm_of spm_of_data[] = {
> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
> +	};

Remove this array and do explicit of parsing and register setting, so only explicitly set what we should.

> +
> +	 /* Get the right SPM device */
> +	spm_dev = msm_spm_get_device(pdev);
> +	if (IS_ERR_OR_NULL(spm_dev))
> +		return -EINVAL;
> +
> +	/* Get the SPM start address */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -EINVAL;
> +		goto fail;
> +	}
> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
> +					resource_size(res));
> +	if (!spm_dev->drv.reg_base_addr) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}

Can we move to using devm_ioremap_resource() to reduce the platform_get_resource/devm_ioremap combo

> +
> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
> +	if (!match_id)
> +		return -ENODEV;
> +
> +	/* Use the register offsets for the SPM version in use */
> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
> +	if (!spm_dev->drv.reg_offsets)
> +		return -EFAULT;
> +
> +	/* Read the SPM idle state sequences */
> +	ret = msm_spm_seq_init(spm_dev, pdev);
> +	if (ret)
> +		return ret;
> +
> +	/* Read the SPM register values */
> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
> +		ret = of_property_read_u32(pdev->dev.of_node,
> +					spm_of_data[i].key, &val);
> +		if (ret)
> +			continue;
> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
> +	}

Change this to explicit parsing of each of prop and add proper read/modify/writes so we only change the things in the registers we should be touching

> +
> +	/* Flush all writes */
> +	wmb();
> +
> +	spm_dev->initialized = true;
> +	return ret;
> +fail:
> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id msm_spm_match_table[] __initconst = {
> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
> +	{ },
> +};
> +
> +
> +static struct platform_driver msm_spm_device_driver = {
> +	.probe = msm_spm_dev_probe,
> +	.driver = {
> +		.name = "spm",
> +		.owner = THIS_MODULE,
> +		.of_match_table = msm_spm_match_table,
> +	},
> +};
> +
> +static int __init msm_spm_device_init(void)
> +{
> +	return platform_driver_register(&msm_spm_device_driver);
> +}
> +device_initcall(msm_spm_device_init);
> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
> new file mode 100644
> index 0000000..29686ef
> --- /dev/null
> +++ b/include/soc/qcom/spm.h
> @@ -0,0 +1,38 @@
> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __QCOM_SPM_H
> +#define __QCOM_SPM_H
> +
> +enum {
> +	MSM_SPM_MODE_DISABLED,
> +	MSM_SPM_MODE_CLOCK_GATING,
> +	MSM_SPM_MODE_RETENTION,
> +	MSM_SPM_MODE_GDHS,
> +	MSM_SPM_MODE_POWER_COLLAPSE,
> +	MSM_SPM_MODE_NR
> +};

Why don’t we make this a named enum, than msm_spm_set_low_power_mode can take that enum
> 
> +
> +struct msm_spm_device;
> +
> +#if defined(CONFIG_QCOM_PM)
> +
> +int msm_spm_set_low_power_mode(u32 mode);

So this could become

int qcom_spm_set_low_power_mode(enum qcom_spm_mode mode)

> +
> +#else
> +
> +static inline int msm_spm_set_low_power_mode(u32 mode)
> +{ return -ENOSYS; }
> +
> +#endif  /* CONFIG_QCOM_PM */
> +
> +#endif  /* __QCOM_SPM_H */
> -- 
> 1.9.1
> 

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 18:07     ` Kumar Gala
  0 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-24 18:07 UTC (permalink / raw)
  To: linux-arm-kernel


On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> Based on work by many authors, available at codeaurora.org
> 
> SPM is a hardware block that controls the peripheral logic surrounding
> the application cores (cpu/l$). When the core executes WFI instruction,
> the SPM takes over the putting the core in low power state as
> configured. The wake up for the SPM is an interrupt at the GIC, which
> then completes the rest of low power mode sequence and brings the core
> out of low power mode.
> 
> The SPM has a set of control registers that configure the SPMs
> individually based on the type of the core and the runtime conditions.
> SPM is a finite state machine block to which a sequence is provided and
> it interprets the bytes  and executes them in sequence. Each low power
> mode that the core can enter into is provided to the SPM as a sequence.
> 
> Configure the SPM to set the core (cpu or L2) into its low power mode,
> the index of the first command in the sequence is set in the SPM_CTL
> register. When the core executes ARM wfi instruction, it triggers the
> SPM state machine to start executing from that index. The SPM state
> machine waits until the interrupt occurs and starts executing the rest
> of the sequence until it hits the end of the sequence. The end of the
> sequence jumps the core out of its low power mode.
> 
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> [lina: simplify the driver for initial submission, clean up and update
> commit text]
> ---
> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
> drivers/soc/qcom/Kconfig                          |   8 +
> drivers/soc/qcom/Makefile                         |   1 +
> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
> include/soc/qcom/spm.h                            |  38 +++
> 5 files changed, 478 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 include/soc/qcom/spm.h

General comment, lets use qcom instead of msm for various things.

[snip]

> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 7dcd554..cd249c4 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -11,3 +11,11 @@ config QCOM_GSBI
> 
> config QCOM_SCM
> 	bool
> +
> +config QCOM_PM
> +	bool "Qualcomm Power Management"
> +	depends on PM && ARCH_QCOM
> +	help
> +	  QCOM Platform specific power driver to manage cores and L2 low power
> +	  modes. It interface with various system drivers to put the cores in
> +	  low power modes.
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 70d52ed..20b329f 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
> +obj-$(CONFIG_QCOM_PM)	+=	spm.o
> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
> diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
> new file mode 100644
> index 0000000..1fa6a96
> --- /dev/null
> +++ b/drivers/soc/qcom/spm.c
> @@ -0,0 +1,388 @@
> +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +
> +#include <soc/qcom/spm.h>
> +
> +#define NUM_SEQ_ENTRY 32
> +#define SPM_CTL_ENABLE BIT(0)
> +
> +enum {
> +	MSM_SPM_REG_SAW2_CFG,
> +	MSM_SPM_REG_SAW2_AVS_CTL,
> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
> +	MSM_SPM_REG_SAW2_SPM_CTL,
> +	MSM_SPM_REG_SAW2_PMIC_DLY,
> +	MSM_SPM_REG_SAW2_AVS_LIMIT,
> +	MSM_SPM_REG_SAW2_AVS_DLY,
> +	MSM_SPM_REG_SAW2_SPM_DLY,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_0,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_1,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_2,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_3,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_4,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_5,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_6,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_7,
> +	MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_SAW2_ID,
> +	MSM_SPM_REG_SAW2_SECURE,
> +	MSM_SPM_REG_SAW2_STS0,
> +	MSM_SPM_REG_SAW2_STS1,
> +	MSM_SPM_REG_SAW2_STS2,
> +	MSM_SPM_REG_SAW2_VCTL,
> +	MSM_SPM_REG_SAW2_SEQ_ENTRY,
> +	MSM_SPM_REG_SAW2_SPM_STS,
> +	MSM_SPM_REG_SAW2_AVS_STS,
> +	MSM_SPM_REG_SAW2_PMIC_STS,
> +	MSM_SPM_REG_SAW2_VERSION,
> +
> +	MSM_SPM_REG_NR,
> +};
> +
> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {

Why an array, can?t this just be an enum?

> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
> +};
> +
> +struct spm_of {
> +	char *key;
> +	u32 id;
> +};
> +
> +struct msm_spm_mode {
> +	u32 mode;
> +	u32 start_addr;
> +};
> +
> +struct msm_spm_driver_data {
> +	void __iomem *reg_base_addr;
> +	u32 *reg_offsets;
> +	struct msm_spm_mode *modes;
> +	u32 num_modes;
> +};
> +
> +struct msm_spm_device {
> +	bool initialized;
> +	struct msm_spm_driver_data drv;
> +};
> +
> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
> +
> +static const struct of_device_id msm_spm_match_table[] __initconst;
> +
> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
> +						u32 mode)
> +{

Can we just fold this into msm_spm_set_low_power_mode
> 
> +	int i;
> +	u32 start_addr = 0;
> +	u32 ctl_val;
> +
> +	for (i = 0; i < drv->num_modes; i++) {
> +		if (drv->modes[i].mode == mode) {
> +			start_addr = drv->modes[i].start_addr;
> +			break;
> +		}
> +	}
> +
> +	if (i == drv->num_modes)
> +		return -EINVAL;
> +
> +	/* Update bits 10:4 in the SPM CTL register */
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	start_addr &= 0x7F;
> +	start_addr <<= 4;
> +	ctl_val &= 0xFFFFF80F;
> +	ctl_val |= start_addr;
> +	writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +	/* Ensure we have written the start address */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
> +					bool enable)
> +{
> +	u32 value = enable ? 0x01 : 0x00;
> +	u32 ctl_val;
> +
> +	ctl_val = readl_relaxed(drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +	/* Update SPM_CTL to enable/disable the SPM */
> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
> +		/* Clear the existing value and update */
> +		ctl_val &= ~0x1;
> +		ctl_val |= value;
> +		writel_relaxed(ctl_val, drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
> +
> +		/* Ensure we have enabled/disabled before returning */
> +		wmb();
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
> + * @mode: SPM LPM mode to enter
> + */
> +int msm_spm_set_low_power_mode(u32 mode)
> +{
> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
> +	int ret = -EINVAL;
> +
> +	if (!dev->initialized)
> +		return -ENXIO;
> +
> +	if (mode == MSM_SPM_MODE_DISABLED)
> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
> +

This could become:

	if (mode == MSM_SPM_MODE_DISABLED)
		return msm_spm_drv_set_spm_enable(&dev->drv, false);

	if (msm_spm_drv_set_spm_enable(&dev->drv, true))
		return ret;

	code from msm_spm_drv_set_low_power_mode

> +	return ret;
> +}
> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);
> +
> +static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
> +{
> +	u32 cmd_w;
> +	u32 offset_w = *offset / 4;
> +	u8 last_cmd;
> +
> +	while (1) {
> +		int i;
> +
> +		cmd_w = 0;
> +		last_cmd = 0;
> +		cmd_w = reg_seq_entry[offset_w];
> +
> +		for (i = (*offset % 4); i < 4; i++) {
> +			last_cmd = *(cmd++);
> +			cmd_w |=  last_cmd << (i * 8);
> +			(*offset)++;
> +			if (last_cmd == 0x0f)
> +				break;
> +		}
> +
> +		reg_seq_entry[offset_w++] = cmd_w;
> +		if (last_cmd == 0x0f)
> +			break;
> +	}
> +}
> +
> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
> +			struct platform_device *pdev)
> +{
> +	int i;
> +	u8 *cmd;
> +	void *addr;
> +	u32 val;
> +	u32 count = 0;
> +	int offset = 0;
> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
> +
> +	/* SPM sleep sequences */
> +	struct spm_of mode_of_data[] = {
> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
> +	};
> +
> +	/**
> +	 * Compose the u32 array based on the individual bytes of the SPM
> +	 * sequence for each low power mode that we read from the DT.
> +	 * The sequences are appended if there is space available in the
> +	 * u32 after the end of the previous sequence.
> +	 */
> +
> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
> +						mode_of_data[i].key, &val);
> +		if (!cmd)
> +			continue;
> +		/* The last in the sequence should be 0x0F */
> +		if (cmd[val - 1] != 0x0F)
> +			continue;
> +		modes[count].mode = mode_of_data[i].id;
> +		modes[count].start_addr = offset;
> +		append_seq_data(&sequences[0], cmd, &offset);
> +		count++;
> +	}
> +
> +	/* Write the idle state sequences to SPM */
> +	drv->modes = devm_kcalloc(&pdev->dev, count,
> +					sizeof(modes[0]), GFP_KERNEL);
> +	if (!drv->modes)
> +		return -ENOMEM;
> +
> +	drv->num_modes = count;
> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
> +
> +	/* Flush the integer array */
> +	addr = drv->reg_base_addr +
> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
> +		writel_relaxed(sequences[i], addr);
> +
> +	/* Ensure we flush the writes */
> +	wmb();
> +
> +	return 0;
> +}
> +
> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
> +{
> +	struct msm_spm_device *dev = NULL;
> +	struct device_node *cpu_node;
> +	u32 cpu;
> +
> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
> +	if (cpu_node) {
> +		for_each_possible_cpu(cpu) {
> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
> +		}
> +	}
> +
> +	return dev;
> +}
> +
> +static int msm_spm_dev_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	int i;
> +	u32 val;
> +	struct msm_spm_device *spm_dev;
> +	struct resource *res;
> +	const struct of_device_id *match_id;
> +
> +	/* SPM Configuration registers */
> +	struct spm_of spm_of_data[] = {
> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
> +	};

Remove this array and do explicit of parsing and register setting, so only explicitly set what we should.

> +
> +	 /* Get the right SPM device */
> +	spm_dev = msm_spm_get_device(pdev);
> +	if (IS_ERR_OR_NULL(spm_dev))
> +		return -EINVAL;
> +
> +	/* Get the SPM start address */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -EINVAL;
> +		goto fail;
> +	}
> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
> +					resource_size(res));
> +	if (!spm_dev->drv.reg_base_addr) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}

Can we move to using devm_ioremap_resource() to reduce the platform_get_resource/devm_ioremap combo

> +
> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
> +	if (!match_id)
> +		return -ENODEV;
> +
> +	/* Use the register offsets for the SPM version in use */
> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
> +	if (!spm_dev->drv.reg_offsets)
> +		return -EFAULT;
> +
> +	/* Read the SPM idle state sequences */
> +	ret = msm_spm_seq_init(spm_dev, pdev);
> +	if (ret)
> +		return ret;
> +
> +	/* Read the SPM register values */
> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
> +		ret = of_property_read_u32(pdev->dev.of_node,
> +					spm_of_data[i].key, &val);
> +		if (ret)
> +			continue;
> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
> +	}

Change this to explicit parsing of each of prop and add proper read/modify/writes so we only change the things in the registers we should be touching

> +
> +	/* Flush all writes */
> +	wmb();
> +
> +	spm_dev->initialized = true;
> +	return ret;
> +fail:
> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id msm_spm_match_table[] __initconst = {
> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
> +	{ },
> +};
> +
> +
> +static struct platform_driver msm_spm_device_driver = {
> +	.probe = msm_spm_dev_probe,
> +	.driver = {
> +		.name = "spm",
> +		.owner = THIS_MODULE,
> +		.of_match_table = msm_spm_match_table,
> +	},
> +};
> +
> +static int __init msm_spm_device_init(void)
> +{
> +	return platform_driver_register(&msm_spm_device_driver);
> +}
> +device_initcall(msm_spm_device_init);
> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
> new file mode 100644
> index 0000000..29686ef
> --- /dev/null
> +++ b/include/soc/qcom/spm.h
> @@ -0,0 +1,38 @@
> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __QCOM_SPM_H
> +#define __QCOM_SPM_H
> +
> +enum {
> +	MSM_SPM_MODE_DISABLED,
> +	MSM_SPM_MODE_CLOCK_GATING,
> +	MSM_SPM_MODE_RETENTION,
> +	MSM_SPM_MODE_GDHS,
> +	MSM_SPM_MODE_POWER_COLLAPSE,
> +	MSM_SPM_MODE_NR
> +};

Why don?t we make this a named enum, than msm_spm_set_low_power_mode can take that enum
> 
> +
> +struct msm_spm_device;
> +
> +#if defined(CONFIG_QCOM_PM)
> +
> +int msm_spm_set_low_power_mode(u32 mode);

So this could become

int qcom_spm_set_low_power_mode(enum qcom_spm_mode mode)

> +
> +#else
> +
> +static inline int msm_spm_set_low_power_mode(u32 mode)
> +{ return -ENOSYS; }
> +
> +#endif  /* CONFIG_QCOM_PM */
> +
> +#endif  /* __QCOM_SPM_H */
> -- 
> 1.9.1
> 

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 18:07     ` Kumar Gala
@ 2014-09-24 18:47       ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 18:47 UTC (permalink / raw)
  To: Kumar Gala
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:
>
>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> Based on work by many authors, available at codeaurora.org
>>
>> SPM is a hardware block that controls the peripheral logic surrounding
>> the application cores (cpu/l$). When the core executes WFI instruction,
>> the SPM takes over the putting the core in low power state as
>> configured. The wake up for the SPM is an interrupt at the GIC, which
>> then completes the rest of low power mode sequence and brings the core
>> out of low power mode.
>>
>> The SPM has a set of control registers that configure the SPMs
>> individually based on the type of the core and the runtime conditions.
>> SPM is a finite state machine block to which a sequence is provided and
>> it interprets the bytes  and executes them in sequence. Each low power
>> mode that the core can enter into is provided to the SPM as a sequence.
>>
>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>> the index of the first command in the sequence is set in the SPM_CTL
>> register. When the core executes ARM wfi instruction, it triggers the
>> SPM state machine to start executing from that index. The SPM state
>> machine waits until the interrupt occurs and starts executing the rest
>> of the sequence until it hits the end of the sequence. The end of the
>> sequence jumps the core out of its low power mode.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> [lina: simplify the driver for initial submission, clean up and update
>> commit text]
>> ---
>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>> drivers/soc/qcom/Kconfig                          |   8 +
>> drivers/soc/qcom/Makefile                         |   1 +
>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>> include/soc/qcom/spm.h                            |  38 +++
>> 5 files changed, 478 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>> create mode 100644 drivers/soc/qcom/spm.c
>> create mode 100644 include/soc/qcom/spm.h
>
>General comment, lets use qcom instead of msm for various things.
>
>[snip]
>
OK, Done. I renamed all msm_ functions to qcom_ functions as well.

>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>> index 7dcd554..cd249c4 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -11,3 +11,11 @@ config QCOM_GSBI
>>
>> config QCOM_SCM
>> 	bool
>> +
>> +config QCOM_PM
>> +	bool "Qualcomm Power Management"
>> +	depends on PM && ARCH_QCOM
>> +	help
>> +	  QCOM Platform specific power driver to manage cores and L2 low power
>> +	  modes. It interface with various system drivers to put the cores in
>> +	  low power modes.
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index 70d52ed..20b329f 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -1,3 +1,4 @@
>> obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>> +obj-$(CONFIG_QCOM_PM)	+=	spm.o
>> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
>> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
>> diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
>> new file mode 100644
>> index 0000000..1fa6a96
>> --- /dev/null
>> +++ b/drivers/soc/qcom/spm.c
>> @@ -0,0 +1,388 @@
>> +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/delay.h>
>> +#include <linux/init.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include <soc/qcom/spm.h>
>> +
>> +#define NUM_SEQ_ENTRY 32
>> +#define SPM_CTL_ENABLE BIT(0)
>> +
>> +enum {
>> +	MSM_SPM_REG_SAW2_CFG,
>> +	MSM_SPM_REG_SAW2_AVS_CTL,
>> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
>> +	MSM_SPM_REG_SAW2_SPM_CTL,
>> +	MSM_SPM_REG_SAW2_PMIC_DLY,
>> +	MSM_SPM_REG_SAW2_AVS_LIMIT,
>> +	MSM_SPM_REG_SAW2_AVS_DLY,
>> +	MSM_SPM_REG_SAW2_SPM_DLY,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_0,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_1,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_2,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_3,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_4,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_5,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_6,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_7,
>> +	MSM_SPM_REG_SAW2_RST,
>> +
>> +	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
>> +
>> +	MSM_SPM_REG_SAW2_ID,
>> +	MSM_SPM_REG_SAW2_SECURE,
>> +	MSM_SPM_REG_SAW2_STS0,
>> +	MSM_SPM_REG_SAW2_STS1,
>> +	MSM_SPM_REG_SAW2_STS2,
>> +	MSM_SPM_REG_SAW2_VCTL,
>> +	MSM_SPM_REG_SAW2_SEQ_ENTRY,
>> +	MSM_SPM_REG_SAW2_SPM_STS,
>> +	MSM_SPM_REG_SAW2_AVS_STS,
>> +	MSM_SPM_REG_SAW2_PMIC_STS,
>> +	MSM_SPM_REG_SAW2_VERSION,
>> +
>> +	MSM_SPM_REG_NR,
>> +};
>> +
>> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
>
>Why an array, can’t this just be an enum?
>
An array makes it easier to have multiple SPM versions. The value of the
enums are not very malleable as the driver scales to support multiple
SPM revisions.

>> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
>> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
>> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
>> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
>> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
>> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
>> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
>> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
>> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
>> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
>> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
>> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
>> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
>> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
>> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
>> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
>> +};
>> +
>> +struct spm_of {
>> +	char *key;
>> +	u32 id;
>> +};
>> +
>> +struct msm_spm_mode {
>> +	u32 mode;
>> +	u32 start_addr;
>> +};
>> +
>> +struct msm_spm_driver_data {
>> +	void __iomem *reg_base_addr;
>> +	u32 *reg_offsets;
>> +	struct msm_spm_mode *modes;
>> +	u32 num_modes;
>> +};
>> +
>> +struct msm_spm_device {
>> +	bool initialized;
>> +	struct msm_spm_driver_data drv;
>> +};
>> +
>> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
>> +
>> +static const struct of_device_id msm_spm_match_table[] __initconst;
>> +
>> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
>> +						u32 mode)
>> +{
>
>Can we just fold this into msm_spm_set_low_power_mode

OK.

>>
>> +	int i;
>> +	u32 start_addr = 0;
>> +	u32 ctl_val;
>> +
>> +	for (i = 0; i < drv->num_modes; i++) {
>> +		if (drv->modes[i].mode == mode) {
>> +			start_addr = drv->modes[i].start_addr;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (i == drv->num_modes)
>> +		return -EINVAL;
>> +
>> +	/* Update bits 10:4 in the SPM CTL register */
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	start_addr &= 0x7F;
>> +	start_addr <<= 4;
>> +	ctl_val &= 0xFFFFF80F;
>> +	ctl_val |= start_addr;
>> +	writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	/* Ensure we have written the start address */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
>> +					bool enable)
>> +{
>> +	u32 value = enable ? 0x01 : 0x00;
>> +	u32 ctl_val;
>> +
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +	/* Update SPM_CTL to enable/disable the SPM */
>> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
>> +		/* Clear the existing value and update */
>> +		ctl_val &= ~0x1;
>> +		ctl_val |= value;
>> +		writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +		/* Ensure we have enabled/disabled before returning */
>> +		wmb();
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
>> + * @mode: SPM LPM mode to enter
>> + */
>> +int msm_spm_set_low_power_mode(u32 mode)
>> +{
>> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
>> +	int ret = -EINVAL;
>> +
>> +	if (!dev->initialized)
>> +		return -ENXIO;
>> +
>> +	if (mode == MSM_SPM_MODE_DISABLED)
>> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
>> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
>> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
>> +
>
>This could become:
>
>	if (mode == MSM_SPM_MODE_DISABLED)
>		return msm_spm_drv_set_spm_enable(&dev->drv, false);
>
>	if (msm_spm_drv_set_spm_enable(&dev->drv, true))
>		return ret;
>
>	code from msm_spm_drv_set_low_power_mode
>
Sure

>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);
>> +
>> +static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
>> +{
>> +	u32 cmd_w;
>> +	u32 offset_w = *offset / 4;
>> +	u8 last_cmd;
>> +
>> +	while (1) {
>> +		int i;
>> +
>> +		cmd_w = 0;
>> +		last_cmd = 0;
>> +		cmd_w = reg_seq_entry[offset_w];
>> +
>> +		for (i = (*offset % 4); i < 4; i++) {
>> +			last_cmd = *(cmd++);
>> +			cmd_w |=  last_cmd << (i * 8);
>> +			(*offset)++;
>> +			if (last_cmd == 0x0f)
>> +				break;
>> +		}
>> +
>> +		reg_seq_entry[offset_w++] = cmd_w;
>> +		if (last_cmd == 0x0f)
>> +			break;
>> +	}
>> +}
>> +
>> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
>> +			struct platform_device *pdev)
>> +{
>> +	int i;
>> +	u8 *cmd;
>> +	void *addr;
>> +	u32 val;
>> +	u32 count = 0;
>> +	int offset = 0;
>> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
>> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
>> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
>> +
>> +	/* SPM sleep sequences */
>> +	struct spm_of mode_of_data[] = {
>> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
>> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
>> +	};
>> +
>> +	/**
>> +	 * Compose the u32 array based on the individual bytes of the SPM
>> +	 * sequence for each low power mode that we read from the DT.
>> +	 * The sequences are appended if there is space available in the
>> +	 * u32 after the end of the previous sequence.
>> +	 */
>> +
>> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
>> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
>> +						mode_of_data[i].key, &val);
>> +		if (!cmd)
>> +			continue;
>> +		/* The last in the sequence should be 0x0F */
>> +		if (cmd[val - 1] != 0x0F)
>> +			continue;
>> +		modes[count].mode = mode_of_data[i].id;
>> +		modes[count].start_addr = offset;
>> +		append_seq_data(&sequences[0], cmd, &offset);
>> +		count++;
>> +	}
>> +
>> +	/* Write the idle state sequences to SPM */
>> +	drv->modes = devm_kcalloc(&pdev->dev, count,
>> +					sizeof(modes[0]), GFP_KERNEL);
>> +	if (!drv->modes)
>> +		return -ENOMEM;
>> +
>> +	drv->num_modes = count;
>> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
>> +
>> +	/* Flush the integer array */
>> +	addr = drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
>> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
>> +		writel_relaxed(sequences[i], addr);
>> +
>> +	/* Ensure we flush the writes */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
>> +{
>> +	struct msm_spm_device *dev = NULL;
>> +	struct device_node *cpu_node;
>> +	u32 cpu;
>> +
>> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
>> +	if (cpu_node) {
>> +		for_each_possible_cpu(cpu) {
>> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
>> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
>> +		}
>> +	}
>> +
>> +	return dev;
>> +}
>> +
>> +static int msm_spm_dev_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +	int i;
>> +	u32 val;
>> +	struct msm_spm_device *spm_dev;
>> +	struct resource *res;
>> +	const struct of_device_id *match_id;
>> +
>> +	/* SPM Configuration registers */
>> +	struct spm_of spm_of_data[] = {
>> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
>> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
>> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
>> +	};
>
>Remove this array and do explicit of parsing and register setting, so only explicitly set what we should.
>
too many of_get_property() calls then Would it be okay if I use a
function to read the property and write to the SPM and call that
repeatedly based on key/enum passed?
I found this array method to be easy to scale.

Just for my curiosity, what is the drawback of this approach?

>> +
>> +	 /* Get the right SPM device */
>> +	spm_dev = msm_spm_get_device(pdev);
>> +	if (IS_ERR_OR_NULL(spm_dev))
>> +		return -EINVAL;
>> +
>> +	/* Get the SPM start address */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		ret = -EINVAL;
>> +		goto fail;
>> +	}
>> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
>> +					resource_size(res));
>> +	if (!spm_dev->drv.reg_base_addr) {
>> +		ret = -ENOMEM;
>> +		goto fail;
>> +	}
>
>Can we move to using devm_ioremap_resource() to reduce the platform_get_resource/devm_ioremap combo
>
Okay, will fix.

>> +
>> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
>> +	if (!match_id)
>> +		return -ENODEV;
>> +
>> +	/* Use the register offsets for the SPM version in use */
>> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
>> +	if (!spm_dev->drv.reg_offsets)
>> +		return -EFAULT;
>> +
>> +	/* Read the SPM idle state sequences */
>> +	ret = msm_spm_seq_init(spm_dev, pdev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Read the SPM register values */
>> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
>> +		ret = of_property_read_u32(pdev->dev.of_node,
>> +					spm_of_data[i].key, &val);
>> +		if (ret)
>> +			continue;
>> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
>> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
>> +	}
>
>Change this to explicit parsing of each of prop and add proper read/modify/writes so we only change the things in the registers we should be touching
>
Well these registers are complete writes, so i am thinking, i could
create a function and call that function with different arguments, No?

>> +
>> +	/* Flush all writes */
>> +	wmb();
>> +
>> +	spm_dev->initialized = true;
>> +	return ret;
>> +fail:
>> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id msm_spm_match_table[] __initconst = {
>> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
>> +	{ },
>> +};
>> +
>> +
>> +static struct platform_driver msm_spm_device_driver = {
>> +	.probe = msm_spm_dev_probe,
>> +	.driver = {
>> +		.name = "spm",
>> +		.owner = THIS_MODULE,
>> +		.of_match_table = msm_spm_match_table,
>> +	},
>> +};
>> +
>> +static int __init msm_spm_device_init(void)
>> +{
>> +	return platform_driver_register(&msm_spm_device_driver);
>> +}
>> +device_initcall(msm_spm_device_init);
>> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
>> new file mode 100644
>> index 0000000..29686ef
>> --- /dev/null
>> +++ b/include/soc/qcom/spm.h
>> @@ -0,0 +1,38 @@
>> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __QCOM_SPM_H
>> +#define __QCOM_SPM_H
>> +
>> +enum {
>> +	MSM_SPM_MODE_DISABLED,
>> +	MSM_SPM_MODE_CLOCK_GATING,
>> +	MSM_SPM_MODE_RETENTION,
>> +	MSM_SPM_MODE_GDHS,
>> +	MSM_SPM_MODE_POWER_COLLAPSE,
>> +	MSM_SPM_MODE_NR
>> +};
>
>Why don’t we make this a named enum, than msm_spm_set_low_power_mode can take that enum
>>
Sure.
>> +
>> +struct msm_spm_device;
>> +
Need to remove this.

>> +#if defined(CONFIG_QCOM_PM)
>> +
>> +int msm_spm_set_low_power_mode(u32 mode);
>
>So this could become
>
>int qcom_spm_set_low_power_mode(enum qcom_spm_mode mode)
>
Agreed.

>> +
>> +#else
>> +
>> +static inline int msm_spm_set_low_power_mode(u32 mode)
>> +{ return -ENOSYS; }
>> +
>> +#endif  /* CONFIG_QCOM_PM */
>> +
>> +#endif  /* __QCOM_SPM_H */
>> --
>> 1.9.1
>>
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 18:47       ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 18:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:
>
>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> Based on work by many authors, available at codeaurora.org
>>
>> SPM is a hardware block that controls the peripheral logic surrounding
>> the application cores (cpu/l$). When the core executes WFI instruction,
>> the SPM takes over the putting the core in low power state as
>> configured. The wake up for the SPM is an interrupt at the GIC, which
>> then completes the rest of low power mode sequence and brings the core
>> out of low power mode.
>>
>> The SPM has a set of control registers that configure the SPMs
>> individually based on the type of the core and the runtime conditions.
>> SPM is a finite state machine block to which a sequence is provided and
>> it interprets the bytes  and executes them in sequence. Each low power
>> mode that the core can enter into is provided to the SPM as a sequence.
>>
>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>> the index of the first command in the sequence is set in the SPM_CTL
>> register. When the core executes ARM wfi instruction, it triggers the
>> SPM state machine to start executing from that index. The SPM state
>> machine waits until the interrupt occurs and starts executing the rest
>> of the sequence until it hits the end of the sequence. The end of the
>> sequence jumps the core out of its low power mode.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> [lina: simplify the driver for initial submission, clean up and update
>> commit text]
>> ---
>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>> drivers/soc/qcom/Kconfig                          |   8 +
>> drivers/soc/qcom/Makefile                         |   1 +
>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>> include/soc/qcom/spm.h                            |  38 +++
>> 5 files changed, 478 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>> create mode 100644 drivers/soc/qcom/spm.c
>> create mode 100644 include/soc/qcom/spm.h
>
>General comment, lets use qcom instead of msm for various things.
>
>[snip]
>
OK, Done. I renamed all msm_ functions to qcom_ functions as well.

>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>> index 7dcd554..cd249c4 100644
>> --- a/drivers/soc/qcom/Kconfig
>> +++ b/drivers/soc/qcom/Kconfig
>> @@ -11,3 +11,11 @@ config QCOM_GSBI
>>
>> config QCOM_SCM
>> 	bool
>> +
>> +config QCOM_PM
>> +	bool "Qualcomm Power Management"
>> +	depends on PM && ARCH_QCOM
>> +	help
>> +	  QCOM Platform specific power driver to manage cores and L2 low power
>> +	  modes. It interface with various system drivers to put the cores in
>> +	  low power modes.
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index 70d52ed..20b329f 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -1,3 +1,4 @@
>> obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>> +obj-$(CONFIG_QCOM_PM)	+=	spm.o
>> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
>> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
>> diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
>> new file mode 100644
>> index 0000000..1fa6a96
>> --- /dev/null
>> +++ b/drivers/soc/qcom/spm.c
>> @@ -0,0 +1,388 @@
>> +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/delay.h>
>> +#include <linux/init.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include <soc/qcom/spm.h>
>> +
>> +#define NUM_SEQ_ENTRY 32
>> +#define SPM_CTL_ENABLE BIT(0)
>> +
>> +enum {
>> +	MSM_SPM_REG_SAW2_CFG,
>> +	MSM_SPM_REG_SAW2_AVS_CTL,
>> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
>> +	MSM_SPM_REG_SAW2_SPM_CTL,
>> +	MSM_SPM_REG_SAW2_PMIC_DLY,
>> +	MSM_SPM_REG_SAW2_AVS_LIMIT,
>> +	MSM_SPM_REG_SAW2_AVS_DLY,
>> +	MSM_SPM_REG_SAW2_SPM_DLY,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_0,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_1,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_2,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_3,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_4,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_5,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_6,
>> +	MSM_SPM_REG_SAW2_PMIC_DATA_7,
>> +	MSM_SPM_REG_SAW2_RST,
>> +
>> +	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
>> +
>> +	MSM_SPM_REG_SAW2_ID,
>> +	MSM_SPM_REG_SAW2_SECURE,
>> +	MSM_SPM_REG_SAW2_STS0,
>> +	MSM_SPM_REG_SAW2_STS1,
>> +	MSM_SPM_REG_SAW2_STS2,
>> +	MSM_SPM_REG_SAW2_VCTL,
>> +	MSM_SPM_REG_SAW2_SEQ_ENTRY,
>> +	MSM_SPM_REG_SAW2_SPM_STS,
>> +	MSM_SPM_REG_SAW2_AVS_STS,
>> +	MSM_SPM_REG_SAW2_PMIC_STS,
>> +	MSM_SPM_REG_SAW2_VERSION,
>> +
>> +	MSM_SPM_REG_NR,
>> +};
>> +
>> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
>
>Why an array, can?t this just be an enum?
>
An array makes it easier to have multiple SPM versions. The value of the
enums are not very malleable as the driver scales to support multiple
SPM revisions.

>> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
>> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
>> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
>> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
>> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
>> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
>> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
>> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
>> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
>> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
>> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
>> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
>> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
>> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
>> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
>> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
>> +};
>> +
>> +struct spm_of {
>> +	char *key;
>> +	u32 id;
>> +};
>> +
>> +struct msm_spm_mode {
>> +	u32 mode;
>> +	u32 start_addr;
>> +};
>> +
>> +struct msm_spm_driver_data {
>> +	void __iomem *reg_base_addr;
>> +	u32 *reg_offsets;
>> +	struct msm_spm_mode *modes;
>> +	u32 num_modes;
>> +};
>> +
>> +struct msm_spm_device {
>> +	bool initialized;
>> +	struct msm_spm_driver_data drv;
>> +};
>> +
>> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
>> +
>> +static const struct of_device_id msm_spm_match_table[] __initconst;
>> +
>> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
>> +						u32 mode)
>> +{
>
>Can we just fold this into msm_spm_set_low_power_mode

OK.

>>
>> +	int i;
>> +	u32 start_addr = 0;
>> +	u32 ctl_val;
>> +
>> +	for (i = 0; i < drv->num_modes; i++) {
>> +		if (drv->modes[i].mode == mode) {
>> +			start_addr = drv->modes[i].start_addr;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (i == drv->num_modes)
>> +		return -EINVAL;
>> +
>> +	/* Update bits 10:4 in the SPM CTL register */
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	start_addr &= 0x7F;
>> +	start_addr <<= 4;
>> +	ctl_val &= 0xFFFFF80F;
>> +	ctl_val |= start_addr;
>> +	writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	/* Ensure we have written the start address */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
>> +					bool enable)
>> +{
>> +	u32 value = enable ? 0x01 : 0x00;
>> +	u32 ctl_val;
>> +
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +	/* Update SPM_CTL to enable/disable the SPM */
>> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
>> +		/* Clear the existing value and update */
>> +		ctl_val &= ~0x1;
>> +		ctl_val |= value;
>> +		writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +		/* Ensure we have enabled/disabled before returning */
>> +		wmb();
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
>> + * @mode: SPM LPM mode to enter
>> + */
>> +int msm_spm_set_low_power_mode(u32 mode)
>> +{
>> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
>> +	int ret = -EINVAL;
>> +
>> +	if (!dev->initialized)
>> +		return -ENXIO;
>> +
>> +	if (mode == MSM_SPM_MODE_DISABLED)
>> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
>> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
>> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
>> +
>
>This could become:
>
>	if (mode == MSM_SPM_MODE_DISABLED)
>		return msm_spm_drv_set_spm_enable(&dev->drv, false);
>
>	if (msm_spm_drv_set_spm_enable(&dev->drv, true))
>		return ret;
>
>	code from msm_spm_drv_set_low_power_mode
>
Sure

>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);
>> +
>> +static void append_seq_data(u32 *reg_seq_entry, u8 *cmd, u32 *offset)
>> +{
>> +	u32 cmd_w;
>> +	u32 offset_w = *offset / 4;
>> +	u8 last_cmd;
>> +
>> +	while (1) {
>> +		int i;
>> +
>> +		cmd_w = 0;
>> +		last_cmd = 0;
>> +		cmd_w = reg_seq_entry[offset_w];
>> +
>> +		for (i = (*offset % 4); i < 4; i++) {
>> +			last_cmd = *(cmd++);
>> +			cmd_w |=  last_cmd << (i * 8);
>> +			(*offset)++;
>> +			if (last_cmd == 0x0f)
>> +				break;
>> +		}
>> +
>> +		reg_seq_entry[offset_w++] = cmd_w;
>> +		if (last_cmd == 0x0f)
>> +			break;
>> +	}
>> +}
>> +
>> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
>> +			struct platform_device *pdev)
>> +{
>> +	int i;
>> +	u8 *cmd;
>> +	void *addr;
>> +	u32 val;
>> +	u32 count = 0;
>> +	int offset = 0;
>> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
>> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
>> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
>> +
>> +	/* SPM sleep sequences */
>> +	struct spm_of mode_of_data[] = {
>> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
>> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
>> +	};
>> +
>> +	/**
>> +	 * Compose the u32 array based on the individual bytes of the SPM
>> +	 * sequence for each low power mode that we read from the DT.
>> +	 * The sequences are appended if there is space available in the
>> +	 * u32 after the end of the previous sequence.
>> +	 */
>> +
>> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
>> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
>> +						mode_of_data[i].key, &val);
>> +		if (!cmd)
>> +			continue;
>> +		/* The last in the sequence should be 0x0F */
>> +		if (cmd[val - 1] != 0x0F)
>> +			continue;
>> +		modes[count].mode = mode_of_data[i].id;
>> +		modes[count].start_addr = offset;
>> +		append_seq_data(&sequences[0], cmd, &offset);
>> +		count++;
>> +	}
>> +
>> +	/* Write the idle state sequences to SPM */
>> +	drv->modes = devm_kcalloc(&pdev->dev, count,
>> +					sizeof(modes[0]), GFP_KERNEL);
>> +	if (!drv->modes)
>> +		return -ENOMEM;
>> +
>> +	drv->num_modes = count;
>> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
>> +
>> +	/* Flush the integer array */
>> +	addr = drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
>> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
>> +		writel_relaxed(sequences[i], addr);
>> +
>> +	/* Ensure we flush the writes */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
>> +{
>> +	struct msm_spm_device *dev = NULL;
>> +	struct device_node *cpu_node;
>> +	u32 cpu;
>> +
>> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
>> +	if (cpu_node) {
>> +		for_each_possible_cpu(cpu) {
>> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
>> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
>> +		}
>> +	}
>> +
>> +	return dev;
>> +}
>> +
>> +static int msm_spm_dev_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +	int i;
>> +	u32 val;
>> +	struct msm_spm_device *spm_dev;
>> +	struct resource *res;
>> +	const struct of_device_id *match_id;
>> +
>> +	/* SPM Configuration registers */
>> +	struct spm_of spm_of_data[] = {
>> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
>> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
>> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
>> +	};
>
>Remove this array and do explicit of parsing and register setting, so only explicitly set what we should.
>
too many of_get_property() calls then Would it be okay if I use a
function to read the property and write to the SPM and call that
repeatedly based on key/enum passed?
I found this array method to be easy to scale.

Just for my curiosity, what is the drawback of this approach?

>> +
>> +	 /* Get the right SPM device */
>> +	spm_dev = msm_spm_get_device(pdev);
>> +	if (IS_ERR_OR_NULL(spm_dev))
>> +		return -EINVAL;
>> +
>> +	/* Get the SPM start address */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		ret = -EINVAL;
>> +		goto fail;
>> +	}
>> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
>> +					resource_size(res));
>> +	if (!spm_dev->drv.reg_base_addr) {
>> +		ret = -ENOMEM;
>> +		goto fail;
>> +	}
>
>Can we move to using devm_ioremap_resource() to reduce the platform_get_resource/devm_ioremap combo
>
Okay, will fix.

>> +
>> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
>> +	if (!match_id)
>> +		return -ENODEV;
>> +
>> +	/* Use the register offsets for the SPM version in use */
>> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
>> +	if (!spm_dev->drv.reg_offsets)
>> +		return -EFAULT;
>> +
>> +	/* Read the SPM idle state sequences */
>> +	ret = msm_spm_seq_init(spm_dev, pdev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Read the SPM register values */
>> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
>> +		ret = of_property_read_u32(pdev->dev.of_node,
>> +					spm_of_data[i].key, &val);
>> +		if (ret)
>> +			continue;
>> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
>> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
>> +	}
>
>Change this to explicit parsing of each of prop and add proper read/modify/writes so we only change the things in the registers we should be touching
>
Well these registers are complete writes, so i am thinking, i could
create a function and call that function with different arguments, No?

>> +
>> +	/* Flush all writes */
>> +	wmb();
>> +
>> +	spm_dev->initialized = true;
>> +	return ret;
>> +fail:
>> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id msm_spm_match_table[] __initconst = {
>> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
>> +	{ },
>> +};
>> +
>> +
>> +static struct platform_driver msm_spm_device_driver = {
>> +	.probe = msm_spm_dev_probe,
>> +	.driver = {
>> +		.name = "spm",
>> +		.owner = THIS_MODULE,
>> +		.of_match_table = msm_spm_match_table,
>> +	},
>> +};
>> +
>> +static int __init msm_spm_device_init(void)
>> +{
>> +	return platform_driver_register(&msm_spm_device_driver);
>> +}
>> +device_initcall(msm_spm_device_init);
>> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
>> new file mode 100644
>> index 0000000..29686ef
>> --- /dev/null
>> +++ b/include/soc/qcom/spm.h
>> @@ -0,0 +1,38 @@
>> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __QCOM_SPM_H
>> +#define __QCOM_SPM_H
>> +
>> +enum {
>> +	MSM_SPM_MODE_DISABLED,
>> +	MSM_SPM_MODE_CLOCK_GATING,
>> +	MSM_SPM_MODE_RETENTION,
>> +	MSM_SPM_MODE_GDHS,
>> +	MSM_SPM_MODE_POWER_COLLAPSE,
>> +	MSM_SPM_MODE_NR
>> +};
>
>Why don?t we make this a named enum, than msm_spm_set_low_power_mode can take that enum
>>
Sure.
>> +
>> +struct msm_spm_device;
>> +
Need to remove this.

>> +#if defined(CONFIG_QCOM_PM)
>> +
>> +int msm_spm_set_low_power_mode(u32 mode);
>
>So this could become
>
>int qcom_spm_set_low_power_mode(enum qcom_spm_mode mode)
>
Agreed.

>> +
>> +#else
>> +
>> +static inline int msm_spm_set_low_power_mode(u32 mode)
>> +{ return -ENOSYS; }
>> +
>> +#endif  /* CONFIG_QCOM_PM */
>> +
>> +#endif  /* __QCOM_SPM_H */
>> --
>> 1.9.1
>>
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 17:43     ` Josh Cartwright
@ 2014-09-24 19:01       ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 19:01 UTC (permalink / raw)
  To: Josh Cartwright
  Cc: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel,
	khilman, msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 11:49 -0600, Josh Cartwright wrote:
>Hey Lina-
>
>A few comments inline:
>
>On Tue, Sep 23, 2014 at 05:51:17PM -0600, Lina Iyer wrote:
>> +++ b/drivers/soc/qcom/spm.c
>[..]
>> +
>> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
>
>const?
>
sure
>> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
>> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
>> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
>> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
>> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
>> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
>> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
>> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
>> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
>> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
>> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
>> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
>> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
>> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
>> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
>> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
>> +};
>> +
>> +struct spm_of {
>> +	char *key;
>
>const char *key?
>
>> +	u32 id;
>> +};
>> +
>> +struct msm_spm_mode {
>> +	u32 mode;
>> +	u32 start_addr;
>> +};
>> +
>> +struct msm_spm_driver_data {
>> +	void __iomem *reg_base_addr;
>> +	u32 *reg_offsets;
>> +	struct msm_spm_mode *modes;
>> +	u32 num_modes;
>
>Why u32?
>
ssize_t, perhaps?

>Actually, the maximum modes is fixed, and really all you need to keep
>around is the start_addr per-mode (which is only 5 bits), and an
>additional bit indicating whether that mode is valid. I'd recommend
>folding msm_spm_mode into msm_spm_driver_data completely.  Something
>like this, maybe:
>
>	struct msm_spm_driver_data {
>		void __iomem *reg_base_addr;
>		const u32 *reg_offsets;
>		struct {
>			u8 is_valid;
>			u8 start_addr;
>		} modes[MSM_SPM_MODE_NR];
>	};
>
Sure, I thought about it, but between the MSM_SPM_MODE is a common
enumeration for cpus and L2. L2 can do additional low power mode - like
GDHS (Globally Distributed Head Switch off) which retains memory, which
do not exist for the cpu. Ends up consuming a lot of memory for each SPM
instance. May be with u8, that may be a lesser impact.

>> +};
>> +
>> +struct msm_spm_device {
>> +	bool initialized;
>> +	struct msm_spm_driver_data drv;
>> +};
>> +
>> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
>
>Why have both msm_spm_device and msm_spm_driver_data?
>
Ah. the role of msm_spm_device is greatly simplified in this patch. The
structure also helps manage a collective of SPM, used in a list, when we
have L2s and CCIs and other device SPMs. Also voltage control representation would
be part of this structure.
But I could use pointers, without the need for initialized variables.

>Would it be easier if you instead used 'struct msm_spm_device *', and
>used NULL to indicate it has not been initialized?
>
>> +static const struct of_device_id msm_spm_match_table[] __initconst;
>
>Just move the table above probe.
>
It looked out of place above probe :(. Ah well, I wil remove the fwd declaration.

>> +
>> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
>> +						u32 mode)
>> +{
>> +	int i;
>> +	u32 start_addr = 0;
>> +	u32 ctl_val;
>> +
>> +	for (i = 0; i < drv->num_modes; i++) {
>> +		if (drv->modes[i].mode == mode) {
>> +			start_addr = drv->modes[i].start_addr;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (i == drv->num_modes)
>> +		return -EINVAL;
>> +
>> +	/* Update bits 10:4 in the SPM CTL register */
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	start_addr &= 0x7F;
>> +	start_addr <<= 4;
>> +	ctl_val &= 0xFFFFF80F;
>> +	ctl_val |= start_addr;
>> +	writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	/* Ensure we have written the start address */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
>> +					bool enable)
>> +{
>> +	u32 value = enable ? 0x01 : 0x00;
>> +	u32 ctl_val;
>> +
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +	/* Update SPM_CTL to enable/disable the SPM */
>> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
>> +		/* Clear the existing value and update */
>> +		ctl_val &= ~0x1;
>> +		ctl_val |= value;
>> +		writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +		/* Ensure we have enabled/disabled before returning */
>> +		wmb();
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
>> + * @mode: SPM LPM mode to enter
>> + */
>> +int msm_spm_set_low_power_mode(u32 mode)
>> +{
>> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
>> +	int ret = -EINVAL;
>> +
>> +	if (!dev->initialized)
>> +		return -ENXIO;
>> +
>> +	if (mode == MSM_SPM_MODE_DISABLED)
>> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
>
>I would suggest not modeling "DISABLED" as a "mode", as it's not a state
>like the others.  Instead, perhaps you could expect users to call
>msm_spm_drv_set_spm_enable() directly.
>
>> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
>> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);
>
>Is this actually to be used by modules?
>
Nope.. Just msm-pm.c, which should be renamed to qcom-pm.c

>[..]
>> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
>> +			struct platform_device *pdev)
>> +{
>> +	int i;
>> +	u8 *cmd;
>
>const u8 *cmd; will save you the cast below.
>
ok

>> +	void *addr;
>> +	u32 val;
>> +	u32 count = 0;
>> +	int offset = 0;
>> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
>> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
>> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
>> +
>> +	/* SPM sleep sequences */
>> +	struct spm_of mode_of_data[] = {
>
>static const?
>
OK
>> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
>> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
>> +	};
>> +
>> +	/**
>> +	 * Compose the u32 array based on the individual bytes of the SPM
>> +	 * sequence for each low power mode that we read from the DT.
>> +	 * The sequences are appended if there is space available in the
>> +	 * u32 after the end of the previous sequence.
>> +	 */
>> +
>> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
>> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
>> +						mode_of_data[i].key, &val);
>> +		if (!cmd)
>> +			continue;
>> +		/* The last in the sequence should be 0x0F */
>> +		if (cmd[val - 1] != 0x0F)
>> +			continue;
>> +		modes[count].mode = mode_of_data[i].id;
>> +		modes[count].start_addr = offset;
>> +		append_seq_data(&sequences[0], cmd, &offset);
>> +		count++;
>> +	}
>> +
>> +	/* Write the idle state sequences to SPM */
>> +	drv->modes = devm_kcalloc(&pdev->dev, count,
>> +					sizeof(modes[0]), GFP_KERNEL);
>> +	if (!drv->modes)
>> +		return -ENOMEM;
>> +
>> +	drv->num_modes = count;
>> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
>> +
>> +	/* Flush the integer array */
>> +	addr = drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
>> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
>> +		writel_relaxed(sequences[i], addr);
>> +
>> +	/* Ensure we flush the writes */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
>> +{
>> +	struct msm_spm_device *dev = NULL;
>> +	struct device_node *cpu_node;
>> +	u32 cpu;
>> +
>> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
>> +	if (cpu_node) {
>> +		for_each_possible_cpu(cpu) {
>> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
>> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
>> +		}
>> +	}
>> +
>> +	return dev;
>> +}
>> +
>> +static int msm_spm_dev_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +	int i;
>> +	u32 val;
>> +	struct msm_spm_device *spm_dev;
>> +	struct resource *res;
>> +	const struct of_device_id *match_id;
>> +
>> +	/* SPM Configuration registers */
>> +	struct spm_of spm_of_data[] = {
>
>static const?
>
OK

>> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
>> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
>> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
>> +	};
>> +
>> +	 /* Get the right SPM device */
>> +	spm_dev = msm_spm_get_device(pdev);
>> +	if (IS_ERR_OR_NULL(spm_dev))
>
>Should this just be a simple NULL check?
>
Yeah, that should go, this is a reminiscent of the previous
implementation.

>> +		return -EINVAL;
>> +
>> +	/* Get the SPM start address */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		ret = -EINVAL;
>> +		goto fail;
>> +	}
>> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
>> +					resource_size(res));
>
>devm_ioremap_resource()?
>
Yes. sure.

>> +	if (!spm_dev->drv.reg_base_addr) {
>> +		ret = -ENOMEM;
>> +		goto fail;
>> +	}
>> +
>> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
>> +	if (!match_id)
>> +		return -ENODEV;
>> +
>> +	/* Use the register offsets for the SPM version in use */
>> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
>> +	if (!spm_dev->drv.reg_offsets)
>> +		return -EFAULT;
>> +
>> +	/* Read the SPM idle state sequences */
>> +	ret = msm_spm_seq_init(spm_dev, pdev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Read the SPM register values */
>> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
>> +		ret = of_property_read_u32(pdev->dev.of_node,
>> +					spm_of_data[i].key, &val);
>> +		if (ret)
>> +			continue;
>> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
>> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
>> +	}
>> +
>> +	/* Flush all writes */
>
>This isn't very descriptive.  Perhaps:
>
>/*
> * Ensure all observers see the above register writes before the
> * updating of spm_dev->initialized
> */
>
ok

>> +	wmb();
>> +
>> +	spm_dev->initialized = true;
>> +	return ret;
>> +fail:
>> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id msm_spm_match_table[] __initconst = {
>> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
>> +	{ },
>> +};
>> +
>> +
>> +static struct platform_driver msm_spm_device_driver = {
>> +	.probe = msm_spm_dev_probe,
>> +	.driver = {
>> +		.name = "spm",
>> +		.owner = THIS_MODULE,
>
>This assignment is not necessary.
>
agreed.

>> +		.of_match_table = msm_spm_match_table,
>> +	},
>> +};
>> +
>> +static int __init msm_spm_device_init(void)
>> +{
>> +	return platform_driver_register(&msm_spm_device_driver);
>> +}
>> +device_initcall(msm_spm_device_init);
>> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
>> new file mode 100644
>> index 0000000..29686ef
>> --- /dev/null
>> +++ b/include/soc/qcom/spm.h
>> @@ -0,0 +1,38 @@
>> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __QCOM_SPM_H
>> +#define __QCOM_SPM_H
>> +
>> +enum {
>> +	MSM_SPM_MODE_DISABLED,
>> +	MSM_SPM_MODE_CLOCK_GATING,
>> +	MSM_SPM_MODE_RETENTION,
>> +	MSM_SPM_MODE_GDHS,
>> +	MSM_SPM_MODE_POWER_COLLAPSE,
>> +	MSM_SPM_MODE_NR
>> +};
>
>Is there a particular reason you aren't naming this enumeration, and
>using it's type in msm_spm_set_low_power_mode()?
>
Will change.

>> +
>> +struct msm_spm_device;
>
>Why this forward declaration?
>
This should go.

>> +
>> +#if defined(CONFIG_QCOM_PM)
>> +
>> +int msm_spm_set_low_power_mode(u32 mode);
>> +
>> +#else
>> +
>> +static inline int msm_spm_set_low_power_mode(u32 mode)
>> +{ return -ENOSYS; }
>> +
>> +#endif  /* CONFIG_QCOM_PM */
>> +
>> +#endif  /* __QCOM_SPM_H */
>> --
>> 1.9.1
>>
>
>Thanks,
>  Josh
>
Thanks for your patience in reviewing the code.

Lina

>-- 
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
>hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 19:01       ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 11:49 -0600, Josh Cartwright wrote:
>Hey Lina-
>
>A few comments inline:
>
>On Tue, Sep 23, 2014 at 05:51:17PM -0600, Lina Iyer wrote:
>> +++ b/drivers/soc/qcom/spm.c
>[..]
>> +
>> +static u32 reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
>
>const?
>
sure
>> +	[MSM_SPM_REG_SAW2_SECURE]		= 0x00,
>> +	[MSM_SPM_REG_SAW2_ID]			= 0x04,
>> +	[MSM_SPM_REG_SAW2_CFG]			= 0x08,
>> +	[MSM_SPM_REG_SAW2_SPM_STS]		= 0x0C,
>> +	[MSM_SPM_REG_SAW2_AVS_STS]		= 0x10,
>> +	[MSM_SPM_REG_SAW2_PMIC_STS]		= 0x14,
>> +	[MSM_SPM_REG_SAW2_RST]			= 0x18,
>> +	[MSM_SPM_REG_SAW2_VCTL]			= 0x1C,
>> +	[MSM_SPM_REG_SAW2_AVS_CTL]		= 0x20,
>> +	[MSM_SPM_REG_SAW2_AVS_LIMIT]		= 0x24,
>> +	[MSM_SPM_REG_SAW2_AVS_DLY]		= 0x28,
>> +	[MSM_SPM_REG_SAW2_AVS_HYSTERESIS]	= 0x2C,
>> +	[MSM_SPM_REG_SAW2_SPM_CTL]		= 0x30,
>> +	[MSM_SPM_REG_SAW2_SPM_DLY]		= 0x34,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_0]		= 0x40,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_1]		= 0x44,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_2]		= 0x48,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_3]		= 0x4C,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_4]		= 0x50,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_5]		= 0x54,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_6]		= 0x58,
>> +	[MSM_SPM_REG_SAW2_PMIC_DATA_7]		= 0x5C,
>> +	[MSM_SPM_REG_SAW2_SEQ_ENTRY]		= 0x80,
>> +	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
>> +};
>> +
>> +struct spm_of {
>> +	char *key;
>
>const char *key?
>
>> +	u32 id;
>> +};
>> +
>> +struct msm_spm_mode {
>> +	u32 mode;
>> +	u32 start_addr;
>> +};
>> +
>> +struct msm_spm_driver_data {
>> +	void __iomem *reg_base_addr;
>> +	u32 *reg_offsets;
>> +	struct msm_spm_mode *modes;
>> +	u32 num_modes;
>
>Why u32?
>
ssize_t, perhaps?

>Actually, the maximum modes is fixed, and really all you need to keep
>around is the start_addr per-mode (which is only 5 bits), and an
>additional bit indicating whether that mode is valid. I'd recommend
>folding msm_spm_mode into msm_spm_driver_data completely.  Something
>like this, maybe:
>
>	struct msm_spm_driver_data {
>		void __iomem *reg_base_addr;
>		const u32 *reg_offsets;
>		struct {
>			u8 is_valid;
>			u8 start_addr;
>		} modes[MSM_SPM_MODE_NR];
>	};
>
Sure, I thought about it, but between the MSM_SPM_MODE is a common
enumeration for cpus and L2. L2 can do additional low power mode - like
GDHS (Globally Distributed Head Switch off) which retains memory, which
do not exist for the cpu. Ends up consuming a lot of memory for each SPM
instance. May be with u8, that may be a lesser impact.

>> +};
>> +
>> +struct msm_spm_device {
>> +	bool initialized;
>> +	struct msm_spm_driver_data drv;
>> +};
>> +
>> +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
>
>Why have both msm_spm_device and msm_spm_driver_data?
>
Ah. the role of msm_spm_device is greatly simplified in this patch. The
structure also helps manage a collective of SPM, used in a list, when we
have L2s and CCIs and other device SPMs. Also voltage control representation would
be part of this structure.
But I could use pointers, without the need for initialized variables.

>Would it be easier if you instead used 'struct msm_spm_device *', and
>used NULL to indicate it has not been initialized?
>
>> +static const struct of_device_id msm_spm_match_table[] __initconst;
>
>Just move the table above probe.
>
It looked out of place above probe :(. Ah well, I wil remove the fwd declaration.

>> +
>> +static int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *drv,
>> +						u32 mode)
>> +{
>> +	int i;
>> +	u32 start_addr = 0;
>> +	u32 ctl_val;
>> +
>> +	for (i = 0; i < drv->num_modes; i++) {
>> +		if (drv->modes[i].mode == mode) {
>> +			start_addr = drv->modes[i].start_addr;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (i == drv->num_modes)
>> +		return -EINVAL;
>> +
>> +	/* Update bits 10:4 in the SPM CTL register */
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	start_addr &= 0x7F;
>> +	start_addr <<= 4;
>> +	ctl_val &= 0xFFFFF80F;
>> +	ctl_val |= start_addr;
>> +	writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +	/* Ensure we have written the start address */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *drv,
>> +					bool enable)
>> +{
>> +	u32 value = enable ? 0x01 : 0x00;
>> +	u32 ctl_val;
>> +
>> +	ctl_val = readl_relaxed(drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +	/* Update SPM_CTL to enable/disable the SPM */
>> +	if ((ctl_val & SPM_CTL_ENABLE) != value) {
>> +		/* Clear the existing value and update */
>> +		ctl_val &= ~0x1;
>> +		ctl_val |= value;
>> +		writel_relaxed(ctl_val, drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]);
>> +
>> +		/* Ensure we have enabled/disabled before returning */
>> +		wmb();
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
>> + * @mode: SPM LPM mode to enter
>> + */
>> +int msm_spm_set_low_power_mode(u32 mode)
>> +{
>> +	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
>> +	int ret = -EINVAL;
>> +
>> +	if (!dev->initialized)
>> +		return -ENXIO;
>> +
>> +	if (mode == MSM_SPM_MODE_DISABLED)
>> +		ret = msm_spm_drv_set_spm_enable(&dev->drv, false);
>
>I would suggest not modeling "DISABLED" as a "mode", as it's not a state
>like the others.  Instead, perhaps you could expect users to call
>msm_spm_drv_set_spm_enable() directly.
>
>> +	else if (!msm_spm_drv_set_spm_enable(&dev->drv, true))
>> +		ret = msm_spm_drv_set_low_power_mode(&dev->drv, mode);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(msm_spm_set_low_power_mode);
>
>Is this actually to be used by modules?
>
Nope.. Just msm-pm.c, which should be renamed to qcom-pm.c

>[..]
>> +static int msm_spm_seq_init(struct msm_spm_device *spm_dev,
>> +			struct platform_device *pdev)
>> +{
>> +	int i;
>> +	u8 *cmd;
>
>const u8 *cmd; will save you the cast below.
>
ok

>> +	void *addr;
>> +	u32 val;
>> +	u32 count = 0;
>> +	int offset = 0;
>> +	struct msm_spm_mode modes[MSM_SPM_MODE_NR];
>> +	u32 sequences[NUM_SEQ_ENTRY/4] = {0};
>> +	struct msm_spm_driver_data *drv = &spm_dev->drv;
>> +
>> +	/* SPM sleep sequences */
>> +	struct spm_of mode_of_data[] = {
>
>static const?
>
OK
>> +		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING},
>> +		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE},
>> +	};
>> +
>> +	/**
>> +	 * Compose the u32 array based on the individual bytes of the SPM
>> +	 * sequence for each low power mode that we read from the DT.
>> +	 * The sequences are appended if there is space available in the
>> +	 * u32 after the end of the previous sequence.
>> +	 */
>> +
>> +	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
>> +		cmd = (u8 *)of_get_property(pdev->dev.of_node,
>> +						mode_of_data[i].key, &val);
>> +		if (!cmd)
>> +			continue;
>> +		/* The last in the sequence should be 0x0F */
>> +		if (cmd[val - 1] != 0x0F)
>> +			continue;
>> +		modes[count].mode = mode_of_data[i].id;
>> +		modes[count].start_addr = offset;
>> +		append_seq_data(&sequences[0], cmd, &offset);
>> +		count++;
>> +	}
>> +
>> +	/* Write the idle state sequences to SPM */
>> +	drv->modes = devm_kcalloc(&pdev->dev, count,
>> +					sizeof(modes[0]), GFP_KERNEL);
>> +	if (!drv->modes)
>> +		return -ENOMEM;
>> +
>> +	drv->num_modes = count;
>> +	memcpy(drv->modes, modes, sizeof(modes[0]) * count);
>> +
>> +	/* Flush the integer array */
>> +	addr = drv->reg_base_addr +
>> +			drv->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY];
>> +	for (i = 0; i < ARRAY_SIZE(sequences); i++, addr += 4)
>> +		writel_relaxed(sequences[i], addr);
>> +
>> +	/* Ensure we flush the writes */
>> +	wmb();
>> +
>> +	return 0;
>> +}
>> +
>> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
>> +{
>> +	struct msm_spm_device *dev = NULL;
>> +	struct device_node *cpu_node;
>> +	u32 cpu;
>> +
>> +	cpu_node = of_parse_phandle(pdev->dev.of_node, "qcom,cpu", 0);
>> +	if (cpu_node) {
>> +		for_each_possible_cpu(cpu) {
>> +			if (of_get_cpu_node(cpu, NULL) == cpu_node)
>> +				dev = &per_cpu(msm_cpu_spm_device, cpu);
>> +		}
>> +	}
>> +
>> +	return dev;
>> +}
>> +
>> +static int msm_spm_dev_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +	int i;
>> +	u32 val;
>> +	struct msm_spm_device *spm_dev;
>> +	struct resource *res;
>> +	const struct of_device_id *match_id;
>> +
>> +	/* SPM Configuration registers */
>> +	struct spm_of spm_of_data[] = {
>
>static const?
>
OK

>> +		{"qcom,saw2-clk-div", MSM_SPM_REG_SAW2_CFG},
>> +		{"qcom,saw2-enable", MSM_SPM_REG_SAW2_SPM_CTL},
>> +		{"qcom,saw2-delays", MSM_SPM_REG_SAW2_SPM_DLY},
>> +	};
>> +
>> +	 /* Get the right SPM device */
>> +	spm_dev = msm_spm_get_device(pdev);
>> +	if (IS_ERR_OR_NULL(spm_dev))
>
>Should this just be a simple NULL check?
>
Yeah, that should go, this is a reminiscent of the previous
implementation.

>> +		return -EINVAL;
>> +
>> +	/* Get the SPM start address */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		ret = -EINVAL;
>> +		goto fail;
>> +	}
>> +	spm_dev->drv.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
>> +					resource_size(res));
>
>devm_ioremap_resource()?
>
Yes. sure.

>> +	if (!spm_dev->drv.reg_base_addr) {
>> +		ret = -ENOMEM;
>> +		goto fail;
>> +	}
>> +
>> +	match_id = of_match_node(msm_spm_match_table, pdev->dev.of_node);
>> +	if (!match_id)
>> +		return -ENODEV;
>> +
>> +	/* Use the register offsets for the SPM version in use */
>> +	spm_dev->drv.reg_offsets = (u32 *)match_id->data;
>> +	if (!spm_dev->drv.reg_offsets)
>> +		return -EFAULT;
>> +
>> +	/* Read the SPM idle state sequences */
>> +	ret = msm_spm_seq_init(spm_dev, pdev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Read the SPM register values */
>> +	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
>> +		ret = of_property_read_u32(pdev->dev.of_node,
>> +					spm_of_data[i].key, &val);
>> +		if (ret)
>> +			continue;
>> +		writel_relaxed(val, spm_dev->drv.reg_base_addr +
>> +				spm_dev->drv.reg_offsets[spm_of_data[i].id]);
>> +	}
>> +
>> +	/* Flush all writes */
>
>This isn't very descriptive.  Perhaps:
>
>/*
> * Ensure all observers see the above register writes before the
> * updating of spm_dev->initialized
> */
>
ok

>> +	wmb();
>> +
>> +	spm_dev->initialized = true;
>> +	return ret;
>> +fail:
>> +	dev_err(&pdev->dev, "SPM device probe failed: %d\n", ret);
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id msm_spm_match_table[] __initconst = {
>> +	{.compatible = "qcom,spm-v2.1", .data = reg_offsets_saw2_v2_1},
>> +	{ },
>> +};
>> +
>> +
>> +static struct platform_driver msm_spm_device_driver = {
>> +	.probe = msm_spm_dev_probe,
>> +	.driver = {
>> +		.name = "spm",
>> +		.owner = THIS_MODULE,
>
>This assignment is not necessary.
>
agreed.

>> +		.of_match_table = msm_spm_match_table,
>> +	},
>> +};
>> +
>> +static int __init msm_spm_device_init(void)
>> +{
>> +	return platform_driver_register(&msm_spm_device_driver);
>> +}
>> +device_initcall(msm_spm_device_init);
>> diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
>> new file mode 100644
>> index 0000000..29686ef
>> --- /dev/null
>> +++ b/include/soc/qcom/spm.h
>> @@ -0,0 +1,38 @@
>> +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __QCOM_SPM_H
>> +#define __QCOM_SPM_H
>> +
>> +enum {
>> +	MSM_SPM_MODE_DISABLED,
>> +	MSM_SPM_MODE_CLOCK_GATING,
>> +	MSM_SPM_MODE_RETENTION,
>> +	MSM_SPM_MODE_GDHS,
>> +	MSM_SPM_MODE_POWER_COLLAPSE,
>> +	MSM_SPM_MODE_NR
>> +};
>
>Is there a particular reason you aren't naming this enumeration, and
>using it's type in msm_spm_set_low_power_mode()?
>
Will change.

>> +
>> +struct msm_spm_device;
>
>Why this forward declaration?
>
This should go.

>> +
>> +#if defined(CONFIG_QCOM_PM)
>> +
>> +int msm_spm_set_low_power_mode(u32 mode);
>> +
>> +#else
>> +
>> +static inline int msm_spm_set_low_power_mode(u32 mode)
>> +{ return -ENOSYS; }
>> +
>> +#endif  /* CONFIG_QCOM_PM */
>> +
>> +#endif  /* __QCOM_SPM_H */
>> --
>> 1.9.1
>>
>
>Thanks,
>  Josh
>
Thanks for your patience in reviewing the code.

Lina

>-- 
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
>hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 17:53         ` Kumar Gala
@ 2014-09-24 19:29           ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 19:29 UTC (permalink / raw)
  To: Kumar Gala
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>
>On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>
>>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>
>>>> Based on work by many authors, available at codeaurora.org
>>>>
>>>> SPM is a hardware block that controls the peripheral logic surrounding
>>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>>> the SPM takes over the putting the core in low power state as
>>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>>> then completes the rest of low power mode sequence and brings the core
>>>> out of low power mode.
>>>>
>>>> The SPM has a set of control registers that configure the SPMs
>>>> individually based on the type of the core and the runtime conditions.
>>>> SPM is a finite state machine block to which a sequence is provided and
>>>> it interprets the bytes  and executes them in sequence. Each low power
>>>> mode that the core can enter into is provided to the SPM as a sequence.
>>>>
>>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>>> the index of the first command in the sequence is set in the SPM_CTL
>>>> register. When the core executes ARM wfi instruction, it triggers the
>>>> SPM state machine to start executing from that index. The SPM state
>>>> machine waits until the interrupt occurs and starts executing the rest
>>>> of the sequence until it hits the end of the sequence. The end of the
>>>> sequence jumps the core out of its low power mode.
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> [lina: simplify the driver for initial submission, clean up and update
>>>> commit text]
>>>> ---
>>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>>> drivers/soc/qcom/Kconfig                          |   8 +
>>>> drivers/soc/qcom/Makefile                         |   1 +
>>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>>> include/soc/qcom/spm.h                            |  38 +++
>>>> 5 files changed, 478 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> create mode 100644 drivers/soc/qcom/spm.c
>>>> create mode 100644 include/soc/qcom/spm.h
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> new file mode 100644
>>>> index 0000000..2ff2454
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> @@ -0,0 +1,43 @@
>>>> +* Subsystem Power Manager (SPM)
>>>> +
>>>> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
>>>> +Processor Sub-System power. These SPM blocks run individual state machine
>>>> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>>>> +instruction is executed by the core.
>>>> +
>>>> +The devicetree representation of the SPM block should be:
>>>> +
>>>> +Required properties
>>>> +
>>>> +- compatible: Must be -
>>>> +		 "qcom,spm-v2.1"
>>>> +- reg: The physical address and the size of the SPM's memory mapped registers
>>>> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>>>> +	This field is required on only for SPMs that control the CPU.
>>>
>>> Let’s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.
>>>
>> Okay. Will look into it.
>> You mean just the property name, right?
>
>Correct.
>
>>
>>>> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>>>> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>>>
>>> (add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)
>>>
>> The SPM spec is not available for open use. The range of values is
>> irrelevant for the SPM clocks, usually, its a constant for an SoC, but
>> may vary between the SoC. Its how the SPM on the SoC interprets it.
>
>Does the meaning of the divisor value change from SoC to SoC (not the value itself)?
>
>Is it not always:
>
>timer tick = sys_ref_clk / (qcom,saw2-clk-div + 1)
>
It is, in this case. But its not something you deviate from the spec.

>>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>
>>> Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>
>> Yes he did. My response was similar to the clk-div values, its not
>> something you can change without hardware spec documentation.
>> And I need to mix the three values up, anyways before I write to the
>> register. Splitting it up, doesnt help understanding/configuring the SPM
>> any better, so didnt change it.
>
>Hmm, will this value change from SPM to SPM on the same SoC?  I’m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>
Ah. Tough proposition! The SPM sequence is a bunch of random register
values, which is not open to interpretation without the programming
guides. I am not sure how I can use DT for saving all the register data
then.

I agree its nice to have nice readable parameters in the DT, but, isnt
the purpose of the DT, the hardware configuration? In an alternate way
to do this, I could put all these register writes into the driver itself
for each SoC. Ugly as it may be, it would solve the problem. However,
the device node then just has the compatible string in it and may be
some configurable elements. I fail to see the big picture of the use of
DT in such a case.

FWIW, I do understand your stance with DT, and for the most part agree
with it.


>>>> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>>>> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>>>
>>> Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?
>>>
>> Okay, sure.
>>
>>>> +
>>>> +Optional properties
>>>> +
>>>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>>>
>>> probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)
>>>
>> Okay.
>>
>>>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>>>
>>> probably add something like: “array of bytes …” (want to convey the data type somehow, is there a max length?)
>>>
>> Okay.
>>
>>>> +
>>>> +Example:
>>>> +	spm@f9089000 {
>>>> +		compatible = "qcom,spm-v2.1";
>>>> +		#address-cells = <1>;
>>>> +		#size-cells = <1>;
>>>> +		reg = <0xf9089000 0x1000>;
>>>> +		qcom,cpu = <&CPU0>;
>>>> +		qcom,saw2-clk-div = <0x1>;
>>>> +		qcom,saw2-delays = <0x20000400>;
>>>> +		qcom,saw2-enable = <0x1>;
>>>> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>>>> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>>>> +				a0 b0 03 68 70 3b 92 a0 b0
>>>> +				82 2b 50 10 30 02 22 30 0f];
>>>> +	};
>>>
>>> - k
>>>
>>>
>>> --
>>> Employee of Qualcomm Innovation Center, Inc.
>>> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-24 19:29           ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-24 19:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>
>On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>
>>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>
>>>> Based on work by many authors, available at codeaurora.org
>>>>
>>>> SPM is a hardware block that controls the peripheral logic surrounding
>>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>>> the SPM takes over the putting the core in low power state as
>>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>>> then completes the rest of low power mode sequence and brings the core
>>>> out of low power mode.
>>>>
>>>> The SPM has a set of control registers that configure the SPMs
>>>> individually based on the type of the core and the runtime conditions.
>>>> SPM is a finite state machine block to which a sequence is provided and
>>>> it interprets the bytes  and executes them in sequence. Each low power
>>>> mode that the core can enter into is provided to the SPM as a sequence.
>>>>
>>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>>> the index of the first command in the sequence is set in the SPM_CTL
>>>> register. When the core executes ARM wfi instruction, it triggers the
>>>> SPM state machine to start executing from that index. The SPM state
>>>> machine waits until the interrupt occurs and starts executing the rest
>>>> of the sequence until it hits the end of the sequence. The end of the
>>>> sequence jumps the core out of its low power mode.
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> [lina: simplify the driver for initial submission, clean up and update
>>>> commit text]
>>>> ---
>>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>>> drivers/soc/qcom/Kconfig                          |   8 +
>>>> drivers/soc/qcom/Makefile                         |   1 +
>>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>>> include/soc/qcom/spm.h                            |  38 +++
>>>> 5 files changed, 478 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> create mode 100644 drivers/soc/qcom/spm.c
>>>> create mode 100644 include/soc/qcom/spm.h
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/arm/msm/spm.txt b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> new file mode 100644
>>>> index 0000000..2ff2454
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> @@ -0,0 +1,43 @@
>>>> +* Subsystem Power Manager (SPM)
>>>> +
>>>> +Qualcomm Snapdragons have SPM hardware blocks to control the Application
>>>> +Processor Sub-System power. These SPM blocks run individual state machine
>>>> +to determine what the core (L2 or Krait/Scorpion) would do when the WFI
>>>> +instruction is executed by the core.
>>>> +
>>>> +The devicetree representation of the SPM block should be:
>>>> +
>>>> +Required properties
>>>> +
>>>> +- compatible: Must be -
>>>> +		 "qcom,spm-v2.1"
>>>> +- reg: The physical address and the size of the SPM's memory mapped registers
>>>> +- qcom,cpu: phandle for the CPU that the SPM block is attached to.
>>>> +	This field is required on only for SPMs that control the CPU.
>>>
>>> Let?s make this just cpu-handle instead of qcom,cpu.  The concept of a handle to a cpu is pretty generic.
>>>
>> Okay. Will look into it.
>> You mean just the property name, right?
>
>Correct.
>
>>
>>>> +- qcom,saw2-clk-div: SAW2 configuration register to program the SPM runtime
>>>> +	clocks. The register for this property is MSM_SPM_REG_SAW2_CFG.
>>>
>>> (add details on how this is used to compute timer tick.  Is it timer tick = saw_clk/saw2-clk-div?  What is valid range of values)
>>>
>> The SPM spec is not available for open use. The range of values is
>> irrelevant for the SPM clocks, usually, its a constant for an SoC, but
>> may vary between the SoC. Its how the SPM on the SoC interprets it.
>
>Does the meaning of the divisor value change from SoC to SoC (not the value itself)?
>
>Is it not always:
>
>timer tick = sys_ref_clk / (qcom,saw2-clk-div + 1)
>
It is, in this case. But its not something you deviate from the spec.

>>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>
>>> Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>
>> Yes he did. My response was similar to the clk-div values, its not
>> something you can change without hardware spec documentation.
>> And I need to mix the three values up, anyways before I write to the
>> register. Splitting it up, doesnt help understanding/configuring the SPM
>> any better, so didnt change it.
>
>Hmm, will this value change from SPM to SPM on the same SoC?  I?m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>
Ah. Tough proposition! The SPM sequence is a bunch of random register
values, which is not open to interpretation without the programming
guides. I am not sure how I can use DT for saving all the register data
then.

I agree its nice to have nice readable parameters in the DT, but, isnt
the purpose of the DT, the hardware configuration? In an alternate way
to do this, I could put all these register writes into the driver itself
for each SoC. Ugly as it may be, it would solve the problem. However,
the device node then just has the compatible string in it and may be
some configurable elements. I fail to see the big picture of the use of
DT in such a case.

FWIW, I do understand your stance with DT, and for the most part agree
with it.


>>>> +- qcom,saw2-enable: The SPM control register to enable/disable the sleep state
>>>> +	machine. The register for this property is MSM_SPM_REG_SAW2_SPM_CTL.
>>>
>>> Can this just be a boolean (exist or not), if so, probably change it to qcom,saw2-disable (so lack of property means enable)?
>>>
>> Okay, sure.
>>
>>>> +
>>>> +Optional properties
>>>> +
>>>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>>>
>>> probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)
>>>
>> Okay.
>>
>>>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>>>
>>> probably add something like: ?array of bytes ?? (want to convey the data type somehow, is there a max length?)
>>>
>> Okay.
>>
>>>> +
>>>> +Example:
>>>> +	spm at f9089000 {
>>>> +		compatible = "qcom,spm-v2.1";
>>>> +		#address-cells = <1>;
>>>> +		#size-cells = <1>;
>>>> +		reg = <0xf9089000 0x1000>;
>>>> +		qcom,cpu = <&CPU0>;
>>>> +		qcom,saw2-clk-div = <0x1>;
>>>> +		qcom,saw2-delays = <0x20000400>;
>>>> +		qcom,saw2-enable = <0x1>;
>>>> +		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
>>>> +		qcom,saw2-spm-cmd-spc = [00 20 50 80 60 70 10 92
>>>> +				a0 b0 03 68 70 3b 92 a0 b0
>>>> +				82 2b 50 10 30 02 22 30 0f];
>>>> +	};
>>>
>>> - k
>>>
>>>
>>> --
>>> Employee of Qualcomm Innovation Center, Inc.
>>> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
  2014-09-24 17:23           ` Lina Iyer
@ 2014-09-24 21:46             ` Stephen Boyd
  -1 siblings, 0 replies; 60+ messages in thread
From: Stephen Boyd @ 2014-09-24 21:46 UTC (permalink / raw)
  To: Lina Iyer
  Cc: Pramod Gurav, galak, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, khilman, msivasub, lorenzo.pieralisi, linux-pm

On 09/24/14 10:23, Lina Iyer wrote:
> On Wed, Sep 24 2014 at 11:21 -0600, Stephen Boyd wrote:
>> On 09/24/14 06:49, Lina Iyer wrote:
>>> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>>>> Hi Lina,
>>>>
>>>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the
>>>>> sub-system
>>>>> power manager and controls the logic around the cores (cpu and L2).
>>>>>
>>>>> Each core has an instance of SPM and controls only that core. Each
>>>>> cpu
>>>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>>>
>>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>>> ---
>>>>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69
>>>>> ++++++++++++++++++++++++++++++++++
>>>>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>>>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>>>>
>>>> <snip>
>>>>
>>>>> +};
>>>>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> index 69dca2a..0580bc2 100644
>>>>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> @@ -14,7 +14,7 @@
>>>>>          #size-cells = <0>;
>>>>>          interrupts = <1 9 0xf04>;
>>>>>
>>>>> -        cpu@0 {
>>>>> +        CPU0: cpu@0 {
>>>> Lina, Stephen boyd has sent some DT change for krait-cpufreq which
>>>> also
>>>> renames this node to "cpu0: cpu@0". If you both could sync up and
>>>> agree
>>>> on a common naming('cpu0' with caps or not caps) for this node.
>>>>
>>> Sure. Will work with Stephen on that.
>>>
>>
>> This doesn't seem like a big deal. I imagine Kumar can resolve the
>> conflict if the two patches merge at the same time.
>>
> I can use the lower case names, if thats common. I see that being used
> for referring to <&acc> in that file, but ofcourse <&L2> exists as well.
> Any preference?
>

No preference. Uppercase at least matches L2 and that matches what's in
the ePAPR so that seems nice.


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974
@ 2014-09-24 21:46             ` Stephen Boyd
  0 siblings, 0 replies; 60+ messages in thread
From: Stephen Boyd @ 2014-09-24 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/24/14 10:23, Lina Iyer wrote:
> On Wed, Sep 24 2014 at 11:21 -0600, Stephen Boyd wrote:
>> On 09/24/14 06:49, Lina Iyer wrote:
>>> On Wed, Sep 24 2014 at 00:14 -0600, Pramod Gurav wrote:
>>>> Hi Lina,
>>>>
>>>> On Wednesday 24 September 2014 05:21 AM, Lina Iyer wrote:
>>>>> Add SPM device bindings for QCOM 8974 based cpus. SPM is the
>>>>> sub-system
>>>>> power manager and controls the logic around the cores (cpu and L2).
>>>>>
>>>>> Each core has an instance of SPM and controls only that core. Each
>>>>> cpu
>>>>> SPM is configured to support WFI and SPC (standalone-power collapse).
>>>>>
>>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>>> ---
>>>>>  arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 69
>>>>> ++++++++++++++++++++++++++++++++++
>>>>>  arch/arm/boot/dts/qcom-msm8974.dtsi    | 10 +++--
>>>>>  2 files changed, 75 insertions(+), 4 deletions(-)
>>>>>  create mode 100644 arch/arm/boot/dts/qcom-msm8974-pm.dtsi
>>>>
>>>> <snip>
>>>>
>>>>> +};
>>>>> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> index 69dca2a..0580bc2 100644
>>>>> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
>>>>> @@ -14,7 +14,7 @@
>>>>>          #size-cells = <0>;
>>>>>          interrupts = <1 9 0xf04>;
>>>>>
>>>>> -        cpu at 0 {
>>>>> +        CPU0: cpu at 0 {
>>>> Lina, Stephen boyd has sent some DT change for krait-cpufreq which
>>>> also
>>>> renames this node to "cpu0: cpu at 0". If you both could sync up and
>>>> agree
>>>> on a common naming('cpu0' with caps or not caps) for this node.
>>>>
>>> Sure. Will work with Stephen on that.
>>>
>>
>> This doesn't seem like a big deal. I imagine Kumar can resolve the
>> conflict if the two patches merge at the same time.
>>
> I can use the lower case names, if thats common. I see that being used
> for referring to <&acc> in that file, but ofcourse <&L2> exists as well.
> Any preference?
>

No preference. Uppercase at least matches L2 and that matches what's in
the ePAPR so that seems nice.


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 19:29           ` Lina Iyer
@ 2014-09-26 14:45             ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 14:45 UTC (permalink / raw)
  To: Kumar Gala
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm

On Wed, Sep 24 2014 at 13:30 -0600, Lina Iyer wrote:
>On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>>
>>On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>
>>>On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>>
>>>>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>
>>>>+- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>>>+	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>>
>>>>Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>>
>>>Yes he did. My response was similar to the clk-div values, its not
>>>something you can change without hardware spec documentation.
>>>And I need to mix the three values up, anyways before I write to the
>>>register. Splitting it up, doesnt help understanding/configuring the SPM
>>>any better, so didnt change it.
>>
>>Hmm, will this value change from SPM to SPM on the same SoC?  I’m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>>
>Ah. Tough proposition! The SPM sequence is a bunch of random register
>values, which is not open to interpretation without the programming
>guides. I am not sure how I can use DT for saving all the register data
>then.
>
>I agree its nice to have nice readable parameters in the DT, but, isnt
>the purpose of the DT, the hardware configuration? In an alternate way
>to do this, I could put all these register writes into the driver itself
>for each SoC. Ugly as it may be, it would solve the problem. However,
>the device node then just has the compatible string in it and may be
>some configurable elements. I fail to see the big picture of the use of
>DT in such a case.
>
>FWIW, I do understand your stance with DT, and for the most part agree
>with it.
>
Based on our offline discussion, I will make the changes to move these
proprietary register values into the driver. I will submit a patch with
the changes soon.

Thanks,
Lina.


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 14:45             ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 14:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 24 2014 at 13:30 -0600, Lina Iyer wrote:
>On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>>
>>On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>
>>>On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>>
>>>>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>
>>>>+- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>>>+	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>>
>>>>Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>>
>>>Yes he did. My response was similar to the clk-div values, its not
>>>something you can change without hardware spec documentation.
>>>And I need to mix the three values up, anyways before I write to the
>>>register. Splitting it up, doesnt help understanding/configuring the SPM
>>>any better, so didnt change it.
>>
>>Hmm, will this value change from SPM to SPM on the same SoC?  I?m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>>
>Ah. Tough proposition! The SPM sequence is a bunch of random register
>values, which is not open to interpretation without the programming
>guides. I am not sure how I can use DT for saving all the register data
>then.
>
>I agree its nice to have nice readable parameters in the DT, but, isnt
>the purpose of the DT, the hardware configuration? In an alternate way
>to do this, I could put all these register writes into the driver itself
>for each SoC. Ugly as it may be, it would solve the problem. However,
>the device node then just has the compatible string in it and may be
>some configurable elements. I fail to see the big picture of the use of
>DT in such a case.
>
>FWIW, I do understand your stance with DT, and for the most part agree
>with it.
>
Based on our offline discussion, I will make the changes to move these
proprietary register values into the driver. I will submit a patch with
the changes soon.

Thanks,
Lina.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-26 14:45             ` Lina Iyer
@ 2014-09-26 14:53               ` Kumar Gala
  -1 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-26 14:53 UTC (permalink / raw)
  To: Lina Iyer
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm


On Sep 26, 2014, at 9:45 AM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Wed, Sep 24 2014 at 13:30 -0600, Lina Iyer wrote:
>> On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>>> 
>>> On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>> 
>>>> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>>> 
>>>>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>> 
>>>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>>> 
>>>>> Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>>> 
>>>> Yes he did. My response was similar to the clk-div values, its not
>>>> something you can change without hardware spec documentation.
>>>> And I need to mix the three values up, anyways before I write to the
>>>> register. Splitting it up, doesnt help understanding/configuring the SPM
>>>> any better, so didnt change it.
>>> 
>>> Hmm, will this value change from SPM to SPM on the same SoC?  I’m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>>> 
>> Ah. Tough proposition! The SPM sequence is a bunch of random register
>> values, which is not open to interpretation without the programming
>> guides. I am not sure how I can use DT for saving all the register data
>> then.
>> 
>> I agree its nice to have nice readable parameters in the DT, but, isnt
>> the purpose of the DT, the hardware configuration? In an alternate way
>> to do this, I could put all these register writes into the driver itself
>> for each SoC. Ugly as it may be, it would solve the problem. However,
>> the device node then just has the compatible string in it and may be
>> some configurable elements. I fail to see the big picture of the use of
>> DT in such a case.
>> 
>> FWIW, I do understand your stance with DT, and for the most part agree
>> with it.
>> 
> Based on our offline discussion, I will make the changes to move these
> proprietary register values into the driver. I will submit a patch with
> the changes soon.

Curious, do the command sequences vary SPM to SPM on the same SoC?  I’m guessing the L2 SPM sequence is probably different from the CPU SPM.

Also, stephen pointed out that the SPMs should probably be part of the SAW nodes and binding.

thanks

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 14:53               ` Kumar Gala
  0 siblings, 0 replies; 60+ messages in thread
From: Kumar Gala @ 2014-09-26 14:53 UTC (permalink / raw)
  To: linux-arm-kernel


On Sep 26, 2014, at 9:45 AM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Wed, Sep 24 2014 at 13:30 -0600, Lina Iyer wrote:
>> On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>>> 
>>> On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>> 
>>>> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>>> 
>>>>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>> 
>>>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>>> 
>>>>> Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>>> 
>>>> Yes he did. My response was similar to the clk-div values, its not
>>>> something you can change without hardware spec documentation.
>>>> And I need to mix the three values up, anyways before I write to the
>>>> register. Splitting it up, doesnt help understanding/configuring the SPM
>>>> any better, so didnt change it.
>>> 
>>> Hmm, will this value change from SPM to SPM on the same SoC?  I?m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>>> 
>> Ah. Tough proposition! The SPM sequence is a bunch of random register
>> values, which is not open to interpretation without the programming
>> guides. I am not sure how I can use DT for saving all the register data
>> then.
>> 
>> I agree its nice to have nice readable parameters in the DT, but, isnt
>> the purpose of the DT, the hardware configuration? In an alternate way
>> to do this, I could put all these register writes into the driver itself
>> for each SoC. Ugly as it may be, it would solve the problem. However,
>> the device node then just has the compatible string in it and may be
>> some configurable elements. I fail to see the big picture of the use of
>> DT in such a case.
>> 
>> FWIW, I do understand your stance with DT, and for the most part agree
>> with it.
>> 
> Based on our offline discussion, I will make the changes to move these
> proprietary register values into the driver. I will submit a patch with
> the changes soon.

Curious, do the command sequences vary SPM to SPM on the same SoC?  I?m guessing the L2 SPM sequence is probably different from the CPU SPM.

Also, stephen pointed out that the SPMs should probably be part of the SAW nodes and binding.

thanks

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-26 14:53               ` Kumar Gala
@ 2014-09-26 15:07                 ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 15:07 UTC (permalink / raw)
  To: Kumar Gala
  Cc: sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel, khilman,
	msivasub, lorenzo.pieralisi, linux-pm

On Fri, Sep 26 2014 at 08:53 -0600, Kumar Gala wrote:
>
>On Sep 26, 2014, at 9:45 AM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> On Wed, Sep 24 2014 at 13:30 -0600, Lina Iyer wrote:
>>> On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>>>>
>>>> On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>
>>>>> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>>>>
>>>>>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>>>
>>>>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>>>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>>>>
>>>>>> Didn’t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>>>>
>>>>> Yes he did. My response was similar to the clk-div values, its not
>>>>> something you can change without hardware spec documentation.
>>>>> And I need to mix the three values up, anyways before I write to the
>>>>> register. Splitting it up, doesnt help understanding/configuring the SPM
>>>>> any better, so didnt change it.
>>>>
>>>> Hmm, will this value change from SPM to SPM on the same SoC?  I’m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>>>>
>>> Ah. Tough proposition! The SPM sequence is a bunch of random register
>>> values, which is not open to interpretation without the programming
>>> guides. I am not sure how I can use DT for saving all the register data
>>> then.
>>>
>>> I agree its nice to have nice readable parameters in the DT, but, isnt
>>> the purpose of the DT, the hardware configuration? In an alternate way
>>> to do this, I could put all these register writes into the driver itself
>>> for each SoC. Ugly as it may be, it would solve the problem. However,
>>> the device node then just has the compatible string in it and may be
>>> some configurable elements. I fail to see the big picture of the use of
>>> DT in such a case.
>>>
>>> FWIW, I do understand your stance with DT, and for the most part agree
>>> with it.
>>>
>> Based on our offline discussion, I will make the changes to move these
>> proprietary register values into the driver. I will submit a patch with
>> the changes soon.
>
>Curious, do the command sequences vary SPM to SPM on the same SoC?  I’m guessing the L2 SPM sequence is probably different from the CPU SPM.
Not really. CPUs are the mostly the same in an SoC but yes, they may
differ from the L2.

>
>Also, stephen pointed out that the SPMs should probably be part of the SAW nodes and binding.
Well, the SAW nodes are essentially regulator bindings. They have no
bearing on idle part of the SAW functionality.

SPM driver will provide an API to change voltage. SPM used to need to
know the voltage and will need to for 8064, but 8074 onwards, SPM can be
skipped in setting the voltage. It might be a tad bit faster to use SPM
to communicate to the PMIC, though.

For 8064, the voltage is turned off when powering down the core and
would need to be restored back to the original value when powering back
on. The value is captured in the PMIC_DATA registers and used by the
SPM sequence.

The SAW regulator information is not related to the SPM functionality
that I am implementing here.
It seems unnecessary to have two varied functionality be bound together
just because we need to shadow the register value. We could find better
ways to do that.

>
>thanks
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 15:07                 ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 15:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 26 2014 at 08:53 -0600, Kumar Gala wrote:
>
>On Sep 26, 2014, at 9:45 AM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
>> On Wed, Sep 24 2014 at 13:30 -0600, Lina Iyer wrote:
>>> On Wed, Sep 24 2014 at 11:53 -0600, Kumar Gala wrote:
>>>>
>>>> On Sep 24, 2014, at 12:21 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>
>>>>> On Wed, Sep 24 2014 at 10:33 -0600, Kumar Gala wrote:
>>>>>>
>>>>>> On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>>>>
>>>>>> +- qcom,saw2-delays: The SPM delay values that SPM sequences would refer to.
>>>>>>> +	The register for this property is MSM_SPM_REG_SAW2_SPM_DLY.
>>>>>>
>>>>>> Didn?t Stephen asked about splitting this up? Or at least treating it as an array of 3 values?
>>>>>>
>>>>> Yes he did. My response was similar to the clk-div values, its not
>>>>> something you can change without hardware spec documentation.
>>>>> And I need to mix the three values up, anyways before I write to the
>>>>> register. Splitting it up, doesnt help understanding/configuring the SPM
>>>>> any better, so didnt change it.
>>>>
>>>> Hmm, will this value change from SPM to SPM on the same SoC?  I?m not a fan of allowing random register values to get poked into the HW from DT.  While this one case might end up being acceptable, its a terrible practice and not something I want use in the habit of doing.
>>>>
>>> Ah. Tough proposition! The SPM sequence is a bunch of random register
>>> values, which is not open to interpretation without the programming
>>> guides. I am not sure how I can use DT for saving all the register data
>>> then.
>>>
>>> I agree its nice to have nice readable parameters in the DT, but, isnt
>>> the purpose of the DT, the hardware configuration? In an alternate way
>>> to do this, I could put all these register writes into the driver itself
>>> for each SoC. Ugly as it may be, it would solve the problem. However,
>>> the device node then just has the compatible string in it and may be
>>> some configurable elements. I fail to see the big picture of the use of
>>> DT in such a case.
>>>
>>> FWIW, I do understand your stance with DT, and for the most part agree
>>> with it.
>>>
>> Based on our offline discussion, I will make the changes to move these
>> proprietary register values into the driver. I will submit a patch with
>> the changes soon.
>
>Curious, do the command sequences vary SPM to SPM on the same SoC?  I?m guessing the L2 SPM sequence is probably different from the CPU SPM.
Not really. CPUs are the mostly the same in an SoC but yes, they may
differ from the L2.

>
>Also, stephen pointed out that the SPMs should probably be part of the SAW nodes and binding.
Well, the SAW nodes are essentially regulator bindings. They have no
bearing on idle part of the SAW functionality.

SPM driver will provide an API to change voltage. SPM used to need to
know the voltage and will need to for 8064, but 8074 onwards, SPM can be
skipped in setting the voltage. It might be a tad bit faster to use SPM
to communicate to the PMIC, though.

For 8064, the voltage is turned off when powering down the core and
would need to be restored back to the original value when powering back
on. The value is captured in the PMIC_DATA registers and used by the
SPM sequence.

The SAW regulator information is not related to the SPM functionality
that I am implementing here.
It seems unnecessary to have two varied functionality be bound together
just because we need to shadow the register value. We could find better
ways to do that.

>
>thanks
>
>-- 
>Employee of Qualcomm Innovation Center, Inc.
>Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-23 23:51   ` Lina Iyer
@ 2014-09-26 16:59     ` Kevin Hilman
  -1 siblings, 0 replies; 60+ messages in thread
From: Kevin Hilman @ 2014-09-26 16:59 UTC (permalink / raw)
  To: Lina Iyer
  Cc: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel,
	msivasub, lorenzo.pieralisi, linux-pm

Lina Iyer <lina.iyer@linaro.org> writes:

> Based on work by many authors, available at codeaurora.org
>
> SPM is a hardware block that controls the peripheral logic surrounding
> the application cores (cpu/l$). When the core executes WFI instruction,
> the SPM takes over the putting the core in low power state as
> configured. The wake up for the SPM is an interrupt at the GIC, which
> then completes the rest of low power mode sequence and brings the core
> out of low power mode.
>
> The SPM has a set of control registers that configure the SPMs
> individually based on the type of the core and the runtime conditions.
> SPM is a finite state machine block to which a sequence is provided and
> it interprets the bytes  and executes them in sequence. Each low power
> mode that the core can enter into is provided to the SPM as a sequence.
>
> Configure the SPM to set the core (cpu or L2) into its low power mode,
> the index of the first command in the sequence is set in the SPM_CTL
> register. When the core executes ARM wfi instruction, it triggers the
> SPM state machine to start executing from that index. The SPM state
> machine waits until the interrupt occurs and starts executing the rest
> of the sequence until it hits the end of the sequence. The end of the
> sequence jumps the core out of its low power mode.
>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>

[...]

> +enum {
> +	MSM_SPM_REG_SAW2_CFG,
> +	MSM_SPM_REG_SAW2_AVS_CTL,
> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
> +	MSM_SPM_REG_SAW2_SPM_CTL,
> +	MSM_SPM_REG_SAW2_PMIC_DLY,
> +	MSM_SPM_REG_SAW2_AVS_LIMIT,
> +	MSM_SPM_REG_SAW2_AVS_DLY,
> +	MSM_SPM_REG_SAW2_SPM_DLY,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_0,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_1,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_2,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_3,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_4,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_5,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_6,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_7,
> +	MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_SAW2_ID,
> +	MSM_SPM_REG_SAW2_SECURE,
> +	MSM_SPM_REG_SAW2_STS0,
> +	MSM_SPM_REG_SAW2_STS1,
> +	MSM_SPM_REG_SAW2_STS2,
> +	MSM_SPM_REG_SAW2_VCTL,
> +	MSM_SPM_REG_SAW2_SEQ_ENTRY,
> +	MSM_SPM_REG_SAW2_SPM_STS,
> +	MSM_SPM_REG_SAW2_AVS_STS,
> +	MSM_SPM_REG_SAW2_PMIC_STS,
> +	MSM_SPM_REG_SAW2_VERSION,
> +
> +	MSM_SPM_REG_NR,
> +};

Do you really need the MSM_SPM_REG_ prefix on all of these?  As these
names are all local to the driver, the extra prefix isn't needed IMO.

Also, consdiering that SPM seems to be sub-block of SAW2, the fact that
the SPM shows up twice in some of the names is a bit confusing.

Kevin

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 16:59     ` Kevin Hilman
  0 siblings, 0 replies; 60+ messages in thread
From: Kevin Hilman @ 2014-09-26 16:59 UTC (permalink / raw)
  To: linux-arm-kernel

Lina Iyer <lina.iyer@linaro.org> writes:

> Based on work by many authors, available at codeaurora.org
>
> SPM is a hardware block that controls the peripheral logic surrounding
> the application cores (cpu/l$). When the core executes WFI instruction,
> the SPM takes over the putting the core in low power state as
> configured. The wake up for the SPM is an interrupt at the GIC, which
> then completes the rest of low power mode sequence and brings the core
> out of low power mode.
>
> The SPM has a set of control registers that configure the SPMs
> individually based on the type of the core and the runtime conditions.
> SPM is a finite state machine block to which a sequence is provided and
> it interprets the bytes  and executes them in sequence. Each low power
> mode that the core can enter into is provided to the SPM as a sequence.
>
> Configure the SPM to set the core (cpu or L2) into its low power mode,
> the index of the first command in the sequence is set in the SPM_CTL
> register. When the core executes ARM wfi instruction, it triggers the
> SPM state machine to start executing from that index. The SPM state
> machine waits until the interrupt occurs and starts executing the rest
> of the sequence until it hits the end of the sequence. The end of the
> sequence jumps the core out of its low power mode.
>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>

[...]

> +enum {
> +	MSM_SPM_REG_SAW2_CFG,
> +	MSM_SPM_REG_SAW2_AVS_CTL,
> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,
> +	MSM_SPM_REG_SAW2_SPM_CTL,
> +	MSM_SPM_REG_SAW2_PMIC_DLY,
> +	MSM_SPM_REG_SAW2_AVS_LIMIT,
> +	MSM_SPM_REG_SAW2_AVS_DLY,
> +	MSM_SPM_REG_SAW2_SPM_DLY,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_0,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_1,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_2,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_3,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_4,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_5,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_6,
> +	MSM_SPM_REG_SAW2_PMIC_DATA_7,
> +	MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST,
> +
> +	MSM_SPM_REG_SAW2_ID,
> +	MSM_SPM_REG_SAW2_SECURE,
> +	MSM_SPM_REG_SAW2_STS0,
> +	MSM_SPM_REG_SAW2_STS1,
> +	MSM_SPM_REG_SAW2_STS2,
> +	MSM_SPM_REG_SAW2_VCTL,
> +	MSM_SPM_REG_SAW2_SEQ_ENTRY,
> +	MSM_SPM_REG_SAW2_SPM_STS,
> +	MSM_SPM_REG_SAW2_AVS_STS,
> +	MSM_SPM_REG_SAW2_PMIC_STS,
> +	MSM_SPM_REG_SAW2_VERSION,
> +
> +	MSM_SPM_REG_NR,
> +};

Do you really need the MSM_SPM_REG_ prefix on all of these?  As these
names are all local to the driver, the extra prefix isn't needed IMO.

Also, consdiering that SPM seems to be sub-block of SAW2, the fact that
the SPM shows up twice in some of the names is a bit confusing.

Kevin

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-26 16:59     ` Kevin Hilman
@ 2014-09-26 17:19       ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 17:19 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: galak, sboyd, daniel.lezcano, linux-arm-msm, linux-arm-kernel,
	msivasub, lorenzo.pieralisi, linux-pm

On Fri, Sep 26 2014 at 10:59 -0600, Kevin Hilman wrote:
>Lina Iyer <lina.iyer@linaro.org> writes:
>
>> Based on work by many authors, available at codeaurora.org
>>
>> SPM is a hardware block that controls the peripheral logic surrounding
>> the application cores (cpu/l$). When the core executes WFI instruction,
>> the SPM takes over the putting the core in low power state as
>> configured. The wake up for the SPM is an interrupt at the GIC, which
>> then completes the rest of low power mode sequence and brings the core
>> out of low power mode.
>>
>> The SPM has a set of control registers that configure the SPMs
>> individually based on the type of the core and the runtime conditions.
>> SPM is a finite state machine block to which a sequence is provided and
>> it interprets the bytes  and executes them in sequence. Each low power
>> mode that the core can enter into is provided to the SPM as a sequence.
>>
>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>> the index of the first command in the sequence is set in the SPM_CTL
>> register. When the core executes ARM wfi instruction, it triggers the
>> SPM state machine to start executing from that index. The SPM state
>> machine waits until the interrupt occurs and starts executing the rest
>> of the sequence until it hits the end of the sequence. The end of the
>> sequence jumps the core out of its low power mode.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>
>[...]
>
>> +enum {
>> +	MSM_SPM_REG_SAW2_CFG,
>> +	MSM_SPM_REG_SAW2_AVS_CTL,
>> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,

>Do you really need the MSM_SPM_REG_ prefix on all of these?  As these
>names are all local to the driver, the extra prefix isn't needed IMO.
>
Nope, I dont.. I will remove them.

>Also, consdiering that SPM seems to be sub-block of SAW2, the fact that
>the SPM shows up twice in some of the names is a bit confusing.
>
I understand. I will fix that.

>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 17:19       ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 17:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 26 2014 at 10:59 -0600, Kevin Hilman wrote:
>Lina Iyer <lina.iyer@linaro.org> writes:
>
>> Based on work by many authors, available at codeaurora.org
>>
>> SPM is a hardware block that controls the peripheral logic surrounding
>> the application cores (cpu/l$). When the core executes WFI instruction,
>> the SPM takes over the putting the core in low power state as
>> configured. The wake up for the SPM is an interrupt at the GIC, which
>> then completes the rest of low power mode sequence and brings the core
>> out of low power mode.
>>
>> The SPM has a set of control registers that configure the SPMs
>> individually based on the type of the core and the runtime conditions.
>> SPM is a finite state machine block to which a sequence is provided and
>> it interprets the bytes  and executes them in sequence. Each low power
>> mode that the core can enter into is provided to the SPM as a sequence.
>>
>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>> the index of the first command in the sequence is set in the SPM_CTL
>> register. When the core executes ARM wfi instruction, it triggers the
>> SPM state machine to start executing from that index. The SPM state
>> machine waits until the interrupt occurs and starts executing the rest
>> of the sequence until it hits the end of the sequence. The end of the
>> sequence jumps the core out of its low power mode.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>
>[...]
>
>> +enum {
>> +	MSM_SPM_REG_SAW2_CFG,
>> +	MSM_SPM_REG_SAW2_AVS_CTL,
>> +	MSM_SPM_REG_SAW2_AVS_HYSTERESIS,

>Do you really need the MSM_SPM_REG_ prefix on all of these?  As these
>names are all local to the driver, the extra prefix isn't needed IMO.
>
Nope, I dont.. I will remove them.

>Also, consdiering that SPM seems to be sub-block of SAW2, the fact that
>the SPM shows up twice in some of the names is a bit confusing.
>
I understand. I will fix that.

>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-24 18:47       ` Lina Iyer
@ 2014-09-26 19:04         ` Kevin Hilman
  -1 siblings, 0 replies; 60+ messages in thread
From: Kevin Hilman @ 2014-09-26 19:04 UTC (permalink / raw)
  To: Lina Iyer
  Cc: Kumar Gala, sboyd, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, msivasub, lorenzo.pieralisi, linux-pm

Lina Iyer <lina.iyer@linaro.org> writes:

> On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:
>>
>>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>
>>> Based on work by many authors, available at codeaurora.org
>>>
>>> SPM is a hardware block that controls the peripheral logic surrounding
>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>> the SPM takes over the putting the core in low power state as
>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>> then completes the rest of low power mode sequence and brings the core
>>> out of low power mode.
>>>
>>> The SPM has a set of control registers that configure the SPMs
>>> individually based on the type of the core and the runtime conditions.
>>> SPM is a finite state machine block to which a sequence is provided and
>>> it interprets the bytes  and executes them in sequence. Each low power
>>> mode that the core can enter into is provided to the SPM as a sequence.
>>>
>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>> the index of the first command in the sequence is set in the SPM_CTL
>>> register. When the core executes ARM wfi instruction, it triggers the
>>> SPM state machine to start executing from that index. The SPM state
>>> machine waits until the interrupt occurs and starts executing the rest
>>> of the sequence until it hits the end of the sequence. The end of the
>>> sequence jumps the core out of its low power mode.
>>>
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> [lina: simplify the driver for initial submission, clean up and update
>>> commit text]
>>> ---
>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>> drivers/soc/qcom/Kconfig                          |   8 +
>>> drivers/soc/qcom/Makefile                         |   1 +
>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>> include/soc/qcom/spm.h                            |  38 +++
>>> 5 files changed, 478 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>> create mode 100644 drivers/soc/qcom/spm.c
>>> create mode 100644 include/soc/qcom/spm.h
>>
>>General comment, lets use qcom instead of msm for various things.
>>
>>[snip]
>>
> OK, Done. I renamed all msm_ functions to qcom_ functions as well.
>

Does that apply to the other parts of this series too?  like the msm-pm
and cpuidle layers?

Kevin

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 19:04         ` Kevin Hilman
  0 siblings, 0 replies; 60+ messages in thread
From: Kevin Hilman @ 2014-09-26 19:04 UTC (permalink / raw)
  To: linux-arm-kernel

Lina Iyer <lina.iyer@linaro.org> writes:

> On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:
>>
>>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>
>>> Based on work by many authors, available at codeaurora.org
>>>
>>> SPM is a hardware block that controls the peripheral logic surrounding
>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>> the SPM takes over the putting the core in low power state as
>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>> then completes the rest of low power mode sequence and brings the core
>>> out of low power mode.
>>>
>>> The SPM has a set of control registers that configure the SPMs
>>> individually based on the type of the core and the runtime conditions.
>>> SPM is a finite state machine block to which a sequence is provided and
>>> it interprets the bytes  and executes them in sequence. Each low power
>>> mode that the core can enter into is provided to the SPM as a sequence.
>>>
>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>> the index of the first command in the sequence is set in the SPM_CTL
>>> register. When the core executes ARM wfi instruction, it triggers the
>>> SPM state machine to start executing from that index. The SPM state
>>> machine waits until the interrupt occurs and starts executing the rest
>>> of the sequence until it hits the end of the sequence. The end of the
>>> sequence jumps the core out of its low power mode.
>>>
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> [lina: simplify the driver for initial submission, clean up and update
>>> commit text]
>>> ---
>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>> drivers/soc/qcom/Kconfig                          |   8 +
>>> drivers/soc/qcom/Makefile                         |   1 +
>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>> include/soc/qcom/spm.h                            |  38 +++
>>> 5 files changed, 478 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>> create mode 100644 drivers/soc/qcom/spm.c
>>> create mode 100644 include/soc/qcom/spm.h
>>
>>General comment, lets use qcom instead of msm for various things.
>>
>>[snip]
>>
> OK, Done. I renamed all msm_ functions to qcom_ functions as well.
>

Does that apply to the other parts of this series too?  like the msm-pm
and cpuidle layers?

Kevin

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-26 19:04         ` Kevin Hilman
@ 2014-09-26 19:12           ` Lina Iyer
  -1 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 19:12 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Kumar Gala, sboyd, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, msivasub, lorenzo.pieralisi, linux-pm

On Fri, Sep 26 2014 at 13:04 -0600, Kevin Hilman wrote:
>Lina Iyer <lina.iyer@linaro.org> writes:
>
>> On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:
>>>
>>>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>
>>>> Based on work by many authors, available at codeaurora.org
>>>>
>>>> SPM is a hardware block that controls the peripheral logic surrounding
>>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>>> the SPM takes over the putting the core in low power state as
>>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>>> then completes the rest of low power mode sequence and brings the core
>>>> out of low power mode.
>>>>
>>>> The SPM has a set of control registers that configure the SPMs
>>>> individually based on the type of the core and the runtime conditions.
>>>> SPM is a finite state machine block to which a sequence is provided and
>>>> it interprets the bytes  and executes them in sequence. Each low power
>>>> mode that the core can enter into is provided to the SPM as a sequence.
>>>>
>>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>>> the index of the first command in the sequence is set in the SPM_CTL
>>>> register. When the core executes ARM wfi instruction, it triggers the
>>>> SPM state machine to start executing from that index. The SPM state
>>>> machine waits until the interrupt occurs and starts executing the rest
>>>> of the sequence until it hits the end of the sequence. The end of the
>>>> sequence jumps the core out of its low power mode.
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> [lina: simplify the driver for initial submission, clean up and update
>>>> commit text]
>>>> ---
>>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>>> drivers/soc/qcom/Kconfig                          |   8 +
>>>> drivers/soc/qcom/Makefile                         |   1 +
>>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>>> include/soc/qcom/spm.h                            |  38 +++
>>>> 5 files changed, 478 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> create mode 100644 drivers/soc/qcom/spm.c
>>>> create mode 100644 include/soc/qcom/spm.h
>>>
>>>General comment, lets use qcom instead of msm for various things.
>>>
>>>[snip]
>>>
>> OK, Done. I renamed all msm_ functions to qcom_ functions as well.
>>
>
>Does that apply to the other parts of this series too?  like the msm-pm
>and cpuidle layers?
>
Yes, there are no more msm's anymore :)

>Kevin
>
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 19:12           ` Lina Iyer
  0 siblings, 0 replies; 60+ messages in thread
From: Lina Iyer @ 2014-09-26 19:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 26 2014 at 13:04 -0600, Kevin Hilman wrote:
>Lina Iyer <lina.iyer@linaro.org> writes:
>
>> On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:
>>>
>>>On Sep 23, 2014, at 6:51 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>
>>>> Based on work by many authors, available at codeaurora.org
>>>>
>>>> SPM is a hardware block that controls the peripheral logic surrounding
>>>> the application cores (cpu/l$). When the core executes WFI instruction,
>>>> the SPM takes over the putting the core in low power state as
>>>> configured. The wake up for the SPM is an interrupt at the GIC, which
>>>> then completes the rest of low power mode sequence and brings the core
>>>> out of low power mode.
>>>>
>>>> The SPM has a set of control registers that configure the SPMs
>>>> individually based on the type of the core and the runtime conditions.
>>>> SPM is a finite state machine block to which a sequence is provided and
>>>> it interprets the bytes  and executes them in sequence. Each low power
>>>> mode that the core can enter into is provided to the SPM as a sequence.
>>>>
>>>> Configure the SPM to set the core (cpu or L2) into its low power mode,
>>>> the index of the first command in the sequence is set in the SPM_CTL
>>>> register. When the core executes ARM wfi instruction, it triggers the
>>>> SPM state machine to start executing from that index. The SPM state
>>>> machine waits until the interrupt occurs and starts executing the rest
>>>> of the sequence until it hits the end of the sequence. The end of the
>>>> sequence jumps the core out of its low power mode.
>>>>
>>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>>> [lina: simplify the driver for initial submission, clean up and update
>>>> commit text]
>>>> ---
>>>> Documentation/devicetree/bindings/arm/msm/spm.txt |  43 +++
>>>> drivers/soc/qcom/Kconfig                          |   8 +
>>>> drivers/soc/qcom/Makefile                         |   1 +
>>>> drivers/soc/qcom/spm.c                            | 388 ++++++++++++++++++++++
>>>> include/soc/qcom/spm.h                            |  38 +++
>>>> 5 files changed, 478 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm.txt
>>>> create mode 100644 drivers/soc/qcom/spm.c
>>>> create mode 100644 include/soc/qcom/spm.h
>>>
>>>General comment, lets use qcom instead of msm for various things.
>>>
>>>[snip]
>>>
>> OK, Done. I renamed all msm_ functions to qcom_ functions as well.
>>
>
>Does that apply to the other parts of this series too?  like the msm-pm
>and cpuidle layers?
>
Yes, there are no more msm's anymore :)

>Kevin
>
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
  2014-09-26 19:12           ` Lina Iyer
@ 2014-09-26 19:30             ` Kevin Hilman
  -1 siblings, 0 replies; 60+ messages in thread
From: Kevin Hilman @ 2014-09-26 19:30 UTC (permalink / raw)
  To: Lina Iyer
  Cc: Kumar Gala, sboyd, daniel.lezcano, linux-arm-msm,
	linux-arm-kernel, msivasub, lorenzo.pieralisi, linux-pm

Lina Iyer <lina.iyer@linaro.org> writes:

> On Fri, Sep 26 2014 at 13:04 -0600, Kevin Hilman wrote:
>>Lina Iyer <lina.iyer@linaro.org> writes:
>>
>>> On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:

[...]

>>>>
>>>>General comment, lets use qcom instead of msm for various things.
>>>>
>>>>[snip]
>>>>
>>> OK, Done. I renamed all msm_ functions to qcom_ functions as well.
>>>
>>
>>Does that apply to the other parts of this series too?  like the msm-pm
>>and cpuidle layers?
>>
> Yes, there are no more msm's anymore :)
>

<can of worms>

What about Documentation/devicetree/bindings/arm/msm?

</can of worms>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver
@ 2014-09-26 19:30             ` Kevin Hilman
  0 siblings, 0 replies; 60+ messages in thread
From: Kevin Hilman @ 2014-09-26 19:30 UTC (permalink / raw)
  To: linux-arm-kernel

Lina Iyer <lina.iyer@linaro.org> writes:

> On Fri, Sep 26 2014 at 13:04 -0600, Kevin Hilman wrote:
>>Lina Iyer <lina.iyer@linaro.org> writes:
>>
>>> On Wed, Sep 24 2014 at 12:07 -0600, Kumar Gala wrote:

[...]

>>>>
>>>>General comment, lets use qcom instead of msm for various things.
>>>>
>>>>[snip]
>>>>
>>> OK, Done. I renamed all msm_ functions to qcom_ functions as well.
>>>
>>
>>Does that apply to the other parts of this series too?  like the msm-pm
>>and cpuidle layers?
>>
> Yes, there are no more msm's anymore :)
>

<can of worms>

What about Documentation/devicetree/bindings/arm/msm?

</can of worms>

^ permalink raw reply	[flat|nested] 60+ messages in thread

end of thread, other threads:[~2014-09-26 19:30 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-23 23:51 [PATCH v6 0/5] QCOM 8074 cpuidle driver Lina Iyer
2014-09-23 23:51 ` Lina Iyer
2014-09-23 23:51 ` [PATCH v6 1/5] qcom: spm: Add Subsystem Power Manager driver Lina Iyer
2014-09-23 23:51   ` Lina Iyer
2014-09-24  1:58   ` Lina Iyer
2014-09-24  1:58     ` Lina Iyer
2014-09-24 16:33   ` Kumar Gala
2014-09-24 16:33     ` Kumar Gala
2014-09-24 17:21     ` Lina Iyer
2014-09-24 17:21       ` Lina Iyer
2014-09-24 17:53       ` Kumar Gala
2014-09-24 17:53         ` Kumar Gala
2014-09-24 19:29         ` Lina Iyer
2014-09-24 19:29           ` Lina Iyer
2014-09-26 14:45           ` Lina Iyer
2014-09-26 14:45             ` Lina Iyer
2014-09-26 14:53             ` Kumar Gala
2014-09-26 14:53               ` Kumar Gala
2014-09-26 15:07               ` Lina Iyer
2014-09-26 15:07                 ` Lina Iyer
2014-09-24 17:43   ` Josh Cartwright
2014-09-24 17:43     ` Josh Cartwright
2014-09-24 19:01     ` Lina Iyer
2014-09-24 19:01       ` Lina Iyer
2014-09-24 18:07   ` Kumar Gala
2014-09-24 18:07     ` Kumar Gala
2014-09-24 18:47     ` Lina Iyer
2014-09-24 18:47       ` Lina Iyer
2014-09-26 19:04       ` Kevin Hilman
2014-09-26 19:04         ` Kevin Hilman
2014-09-26 19:12         ` Lina Iyer
2014-09-26 19:12           ` Lina Iyer
2014-09-26 19:30           ` Kevin Hilman
2014-09-26 19:30             ` Kevin Hilman
2014-09-26 16:59   ` Kevin Hilman
2014-09-26 16:59     ` Kevin Hilman
2014-09-26 17:19     ` Lina Iyer
2014-09-26 17:19       ` Lina Iyer
2014-09-23 23:51 ` [PATCH v6 2/5] arm: dts: qcom: Add SPM device bindings for 8974 Lina Iyer
2014-09-23 23:51   ` Lina Iyer
2014-09-24  6:18   ` Pramod Gurav
2014-09-24  6:18     ` Pramod Gurav
2014-09-24 13:49     ` Lina Iyer
2014-09-24 13:49       ` Lina Iyer
2014-09-24 14:03       ` Kumar Gala
2014-09-24 14:03         ` Kumar Gala
2014-09-24 14:13         ` Lina Iyer
2014-09-24 14:13           ` Lina Iyer
2014-09-24 17:21       ` Stephen Boyd
2014-09-24 17:21         ` Stephen Boyd
2014-09-24 17:23         ` Lina Iyer
2014-09-24 17:23           ` Lina Iyer
2014-09-24 21:46           ` Stephen Boyd
2014-09-24 21:46             ` Stephen Boyd
2014-09-23 23:51 ` [PATCH v6 3/5] qcom: msm-pm: Add cpu low power mode functions Lina Iyer
2014-09-23 23:51   ` Lina Iyer
2014-09-23 23:51 ` [PATCH v6 4/5] qcom: cpuidle: Add cpuidle driver for QCOM cpus Lina Iyer
2014-09-23 23:51   ` Lina Iyer
2014-09-23 23:51 ` [PATCH v6 5/5] arm: dts: qcom: Add idle states device nodes for 8974 Lina Iyer
2014-09-23 23:51   ` Lina Iyer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.