All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver
@ 2014-08-08  4:05 Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc Lina Iyer
                   ` (12 more replies)
  0 siblings, 13 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Hello everybody,

Here are the set of patches for enabling cpuidle driver for 8074 based targets.

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 patchsets do the following

- Move scm-boot files from arm/mach-qcom to drivers/soc, following convention.
They are based on Stephen Boyd's series of patches
[http://www.spinics.net/lists/linux-arm-msm/msg10482.html]

- Add new Secure Monitor flags to support warmboot of a quad core system.

- 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 and one for L2 and one for L2.

- 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.  Also, when powering down the
core, let the SCM know the state of L2 as set up in Linux. This allows secure
monitor to flush the secure lines when Linux knows the cache may be powered
off. 

- Add ability to recognize the power down status of the core, to ensure that
the core is indeed powered down before powering down peripheral h/w.

- Add support for deeper idle states than just clock gating for hotplug and warmboot

- 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 driver
supports 4 low power modes, but not all SoCs, support all low power modes. The
modes supported are configured in device tree nodes.

- 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.

- KConfig option to enable the driver

I have tested them on the Dragonboard 8074. The drivers should be fairly
extensible to 8084.

Thanks
Lina


Lina Iyer (13):
  msm: scm: Move scm-boot files to drivers/soc and include/soc
  msm: scm: Add SCM warmboot flags for quad core targets.
  qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  arm: dts: qcom: Add SPM device bindings for 8974
  qcom: msm-pm: Add cpu low power mode functions
  qcom: msm-pm: Add support for hotplug and secondary startup
  qcom: sleep-status: Add ability to recognize cpu power down state
  arm: dts: qcom: Add device binding for sleep status
  soc: qcom: Add QCOM Power management config
  qcom: platsmp: Enable deeper idle states for hotplug
  qcom: cpuidle: Add cpuidle driver for QCOM cpus
  qcom: cpuidle: Add cpuidle device nodes for 8974 chipset
  qcom: cpuidle: Config option to enable QCOM cpuidle driver

 .../bindings/arm/msm/qcom,cpu-sleep-status.txt     |  41 ++
 .../devicetree/bindings/arm/msm/qcom,cpuidle.txt   |  73 +++
 .../devicetree/bindings/arm/msm/spm-v2.txt         | 104 +++
 arch/arm/boot/dts/qcom-msm8974-pm.dtsi             | 146 +++++
 arch/arm/boot/dts/qcom-msm8974.dtsi                |   2 +
 arch/arm/mach-qcom/Makefile                        |   1 -
 arch/arm/mach-qcom/platsmp.c                       |  22 +-
 drivers/cpuidle/Kconfig.arm                        |   6 +
 drivers/cpuidle/Makefile                           |   1 +
 drivers/cpuidle/cpuidle-qcom.c                     | 140 ++++
 drivers/soc/qcom/Kconfig                           |   8 +
 drivers/soc/qcom/Makefile                          |   4 +-
 drivers/soc/qcom/msm-pm.c                          | 254 ++++++++
 .../arm/mach-qcom => drivers/soc/qcom}/scm-boot.c  |   4 +-
 drivers/soc/qcom/sleep-status.c                    | 178 ++++++
 drivers/soc/qcom/spm.c                             | 559 ++++++++++++++++
 drivers/soc/qcom/spm_devices.c                     | 709 +++++++++++++++++++++
 drivers/soc/qcom/spm_driver.h                      | 118 ++++
 include/soc/qcom/pm.h                              |  45 ++
 .../arm/mach-qcom => include/soc/qcom}/scm-boot.h  |   3 +
 include/soc/qcom/spm.h                             | 106 +++
 21 files changed, 2519 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,cpu-sleep-status.txt
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,cpuidle.txt
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm-v2.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
 rename {arch/arm/mach-qcom => drivers/soc/qcom}/scm-boot.c (97%)
 create mode 100644 drivers/soc/qcom/sleep-status.c
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 drivers/soc/qcom/spm_devices.c
 create mode 100644 drivers/soc/qcom/spm_driver.h
 create mode 100644 include/soc/qcom/pm.h
 rename {arch/arm/mach-qcom => include/soc/qcom}/scm-boot.h (91%)
 create mode 100644 include/soc/qcom/spm.h

-- 
1.9.1

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

* [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  7:42   ` Pramod Gurav
  2014-08-08  4:05 ` [RFC] [PATCH 02/13] msm: scm: Add SCM warmboot flags for quad core targets Lina Iyer
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/mach-qcom/Makefile                         | 1 -
 drivers/soc/qcom/Makefile                           | 2 +-
 {arch/arm/mach-qcom => drivers/soc/qcom}/scm-boot.c | 4 ++--
 {arch/arm/mach-qcom => include/soc/qcom}/scm-boot.h | 0
 4 files changed, 3 insertions(+), 4 deletions(-)
 rename {arch/arm/mach-qcom => drivers/soc/qcom}/scm-boot.c (97%)
 rename {arch/arm/mach-qcom => include/soc/qcom}/scm-boot.h (100%)

diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index db41e8c..e324375 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,3 +1,2 @@
 obj-y			:= board.o
 obj-$(CONFIG_SMP)	+= platsmp.o
-obj-$(CONFIG_QCOM_SCM)	+= scm-boot.o
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index a39446d..70d52ed 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
 CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
-obj-$(CONFIG_QCOM_SCM) += scm.o
+obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
diff --git a/arch/arm/mach-qcom/scm-boot.c b/drivers/soc/qcom/scm-boot.c
similarity index 97%
rename from arch/arm/mach-qcom/scm-boot.c
rename to drivers/soc/qcom/scm-boot.c
index 5add20e..60ff7b4 100644
--- a/arch/arm/mach-qcom/scm-boot.c
+++ b/drivers/soc/qcom/scm-boot.c
@@ -17,9 +17,9 @@
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <soc/qcom/scm.h>
 
-#include "scm-boot.h"
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
 
 /*
  * Set the cold/warm boot address for one of the CPU cores.
diff --git a/arch/arm/mach-qcom/scm-boot.h b/include/soc/qcom/scm-boot.h
similarity index 100%
rename from arch/arm/mach-qcom/scm-boot.h
rename to include/soc/qcom/scm-boot.h
-- 
1.9.1

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

* [RFC] [PATCH 02/13] msm: scm: Add SCM warmboot flags for quad core targets.
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets Lina Iyer
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Quad core targets like APQ8074, 78064, 8084 need SCM support set up
warm boot addresses in the Secure Monitor. Extend the SCM flags to
support warmboot addresses for seconday cores.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 include/soc/qcom/scm-boot.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/soc/qcom/scm-boot.h b/include/soc/qcom/scm-boot.h
index 6aabb24..7ae2152 100644
--- a/include/soc/qcom/scm-boot.h
+++ b/include/soc/qcom/scm-boot.h
@@ -18,6 +18,9 @@
 #define SCM_FLAG_COLDBOOT_CPU3		0x20
 #define SCM_FLAG_WARMBOOT_CPU0		0x04
 #define SCM_FLAG_WARMBOOT_CPU1		0x02
+#define SCM_FLAG_WARMBOOT_CPU2		0x10
+#define SCM_FLAG_WARMBOOT_CPU3		0x40
+
 
 int scm_set_boot_addr(phys_addr_t addr, int flags);
 
-- 
1.9.1

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

* [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 02/13] msm: scm: Add SCM warmboot flags for quad core targets Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  9:18   ` Pramod Gurav
  2014-08-08 16:16   ` Kumar Gala
  2014-08-08  4:05 ` [RFC] [PATCH 04/13] arm: dts: qcom: Add SPM device bindings for 8974 Lina Iyer
                   ` (9 subsequent siblings)
  12 siblings, 2 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer, Praveen Chidamabram, Murali Nalajala

Qualcomm chipsets use an separate h/w block to control the logic around
the processor cores (cpu and L2). The SPM h/w block regulates power to
the cores and controls the power when the core enter low power modes.

Each core has its own instance of SPM. The SPM has the following key
functions
	- Configure the h/w dependencies when entering low power modes
	- Wait for interrupt and wake up on interrupt
	- Ensure the dependencies are ready before bringing the core out
	  of sleep
	- Regulating voltage to the core, interfacing with the PMIC.
	- Optimize power based on runtime recommendations.

The driver identifies and configures the SPMs, by reading the nodes and
the register values from the devicetree. The SPMs need to be configured
to allow the processor to be idled in a low power state.

Signed-off-by: Praveen Chidamabram <pchidamb@codeaurora.org>
Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 .../devicetree/bindings/arm/msm/spm-v2.txt         | 104 +++
 drivers/soc/qcom/Makefile                          |   2 +
 drivers/soc/qcom/spm.c                             | 559 ++++++++++++++++
 drivers/soc/qcom/spm_devices.c                     | 709 +++++++++++++++++++++
 drivers/soc/qcom/spm_driver.h                      | 118 ++++
 include/soc/qcom/spm.h                             | 106 +++
 6 files changed, 1598 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm-v2.txt
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 drivers/soc/qcom/spm_devices.c
 create mode 100644 drivers/soc/qcom/spm_driver.h
 create mode 100644 include/soc/qcom/spm.h

diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
new file mode 100644
index 0000000..ea0fc7a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -0,0 +1,104 @@
+* MSM Subsystem Power Manager (spm-v2)
+
+S4 generation of MSMs 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 SAW hardware block handles SPM and
+AVS functionality for the cores.
+
+The devicetree representation of the SPM block should be:
+
+Required properties
+
+- compatible: "qcom,spm-v2"
+- 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. On targets
+	that dont support CPU phandles the driver would support qcom,core-id.
+	This field is required on only for SPMs that control the CPU.
+- qcom, core-id: This property will be deprecated once all targets start
+	supporting CPU phandles. This field will be used to identify SPMs
+	that control the CPU.
+	{0..n} for cores {0..n}
+- qcom,saw2-ver-reg: The location of the version register
+- qcom,saw2-cfg: SAW2 configuration register
+- qcom,saw2-avs-ctl: The AVS control register
+- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
+	controller requests
+- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
+	sequence
+- qcom,saw2-spm-ctl: The SPM control register
+- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
+	after sending the voltage command to the PMIC
+- qcom,name: The name with which a SPM device is identified by the power
+management code.
+
+Optional properties
+
+- qcom,saw2-avs-limit: The AVS limit register
+- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
+	between AVS controller requests
+- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
+	index to send the PMIC data to
+- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+	voltage
+- qcom,phase-port: The PVC port used for changing the number of phases
+- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
+- qcom,saw2-spm-cmd-wfi: The WFI command sequence
+- qcom,saw2-spm-cmd-ret: The Retention command sequence
+- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
+- qcom,saw2-spm-cmd-pc-no-rpm: The Power Collapse command sequence where APPS
+	proc won't inform the RPM.
+- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
+- qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence
+- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
+	Depricated: Replaced with cpu-vctl-list when cpu phandles are available.
+- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
+	can control.
+
+Example 1:
+	qcom,spm@f9089000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,core-id = <0>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x1b>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x20000400>;
+		qcom,saw2-spm-ctl = <0x1>;
+		qcom,cpu-vctl-mask = <0xf>;
+		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];
+		qcom,saw2-spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92
+				a0 b0 82 10 30 02 22 30 0f];
+	};
+
+Example 2:
+	qcom,spm@f9089000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,core-id = <0>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x1b>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x20000400>;
+		qcom,saw2-spm-ctl = <0x1>;
+		qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
+		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];
+		qcom,saw2-spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92
+				a0 b0 82 10 30 02 22 30 0f];
+	};
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 70d52ed..3de7ed9 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,3 +1,5 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
+obj-$(CONFIG_QCOM_PM) +=	spm_devices.o 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..984aca6
--- /dev/null
+++ b/drivers/soc/qcom/spm.c
@@ -0,0 +1,559 @@
+/* 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 "spm_driver.h"
+
+#define MSM_SPM_PMIC_STATE_IDLE  0
+
+enum {
+	MSM_SPM_DEBUG_SHADOW = 1U << 0,
+	MSM_SPM_DEBUG_VCTL = 1U << 1,
+};
+
+static int msm_spm_debug_mask;
+module_param_named(
+	debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+struct saw2_data {
+	const char *ver_name;
+	uint32_t major;
+	uint32_t minor;
+	uint32_t *spm_reg_offset_ptr;
+};
+
+static uint32_t msm_spm_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,
+};
+
+static uint32_t msm_spm_reg_offsets_saw2_v3_0[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_STS2]			= 0x38,
+	[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]		= 0x400,
+	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
+};
+
+static struct saw2_data saw2_info[] = {
+	[0] = {
+		"SAW2_v2.1",
+		0x2,
+		0x1,
+		msm_spm_reg_offsets_saw2_v2_1,
+	},
+	[1] = {
+		"SAW2_v3.0",
+		0x3,
+		0x0,
+		msm_spm_reg_offsets_saw2_v3_0,
+	},
+};
+
+static uint32_t num_pmic_data;
+
+static inline uint32_t msm_spm_drv_get_num_spm_entry(
+		struct msm_spm_driver_data *dev)
+{
+	return 32;
+}
+
+static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
+		unsigned int reg_index)
+{
+	__raw_writel(dev->reg_shadow[reg_index],
+		dev->reg_base_addr + dev->reg_offsets[reg_index]);
+}
+
+static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
+		unsigned int reg_index)
+{
+	dev->reg_shadow[reg_index] =
+		__raw_readl(dev->reg_base_addr +
+				dev->reg_offsets[reg_index]);
+}
+
+static inline void msm_spm_drv_set_start_addr(
+		struct msm_spm_driver_data *dev, uint32_t addr, bool pc_mode)
+{
+	addr &= 0x7F;
+	addr <<= 4;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr;
+
+	if (dev->major != 0x3)
+		return;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFEFFFF;
+	if (pc_mode)
+		dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= 0x00010000;
+}
+
+static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID);
+	return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1;
+}
+
+static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
+		uint32_t vlevel)
+{
+	unsigned int pmic_data = 0;
+
+	/**
+	 * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
+	 * Ensure that vctl_port is always set to 0.
+	 */
+	WARN_ON(dev->vctl_port);
+
+	pmic_data |= vlevel;
+	pmic_data |= (dev->vctl_port & 0x7) << 16;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] &= ~0x700FF;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] |= pmic_data;
+
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_3);
+}
+
+static inline uint32_t msm_spm_drv_get_num_pmic_data(
+		struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID);
+	mb();
+	return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 4) & 0x7;
+}
+
+static inline uint32_t msm_spm_drv_get_sts_pmic_state(
+		struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
+	return (dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] >> 16) &
+				0x03;
+}
+
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+		struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
+	return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] & 0xFF;
+}
+
+static inline void msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev,
+		uint32_t *major, uint32_t *minor)
+{
+	uint32_t val = 0;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION] =
+			__raw_readl(dev->reg_base_addr + dev->ver_reg);
+
+	val = dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION];
+
+	*major = (val >> 28) & 0xF;
+	*minor = (val >> 16) & 0xFFF;
+}
+
+inline int msm_spm_drv_set_spm_enable(
+		struct msm_spm_driver_data *dev, bool enable)
+{
+	uint32_t value = enable ? 0x01 : 0x00;
+
+	if (!dev)
+		return -EINVAL;
+
+	if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) {
+
+		dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1;
+		dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value;
+
+		msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
+		wmb();
+	}
+	return 0;
+}
+void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
+{
+	int i;
+	int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
+
+	if (!dev) {
+		__WARN();
+		return;
+	}
+
+	for (i = 0; i < num_spm_entry; i++) {
+		__raw_writel(dev->reg_seq_entry_shadow[i],
+			dev->reg_base_addr
+			+ dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY]
+			+ 4 * i);
+	}
+	mb();
+}
+
+void dump_regs(struct msm_spm_driver_data *dev, int cpu)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_STS);
+	mb();
+	pr_err("CPU%d: spm register MSM_SPM_REG_SAW2_SPM_STS: 0x%x\n", cpu,
+			dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_STS]);
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
+	mb();
+	pr_err("CPU%d: spm register MSM_SPM_REG_SAW2_SPM_CTL: 0x%x\n", cpu,
+			dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL]);
+}
+
+int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
+		uint8_t *cmd, uint32_t *offset)
+{
+	uint32_t cmd_w;
+	uint32_t offset_w = *offset / 4;
+	uint8_t last_cmd;
+
+	if (!cmd)
+		return -EINVAL;
+
+	while (1) {
+		int i;
+
+		cmd_w = 0;
+		last_cmd = 0;
+		cmd_w = dev->reg_seq_entry_shadow[offset_w];
+
+		for (i = (*offset % 4); i < 4; i++) {
+			last_cmd = *(cmd++);
+			cmd_w |=  last_cmd << (i * 8);
+			(*offset)++;
+			if (last_cmd == 0x0f)
+				break;
+		}
+
+		dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
+		if (last_cmd == 0x0f)
+			break;
+	}
+
+	return 0;
+}
+
+int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
+		uint32_t addr, bool pc_mode)
+{
+
+	/* SPM is configured to reset start address to zero after end of Program
+	 */
+	if (!dev)
+		return -EINVAL;
+
+	msm_spm_drv_set_start_addr(dev, addr, pc_mode);
+
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
+	wmb();
+
+	if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
+		int i;
+
+		for (i = 0; i < MSM_SPM_REG_NR; i++)
+			pr_info("%s: reg %02x = 0x%08x\n", __func__,
+				dev->reg_offsets[i], dev->reg_shadow[i]);
+	}
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_STS);
+
+	return 0;
+}
+
+#ifdef CONFIG_MSM_AVS_HW
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+	return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(0);
+}
+
+static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~BIT(27);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+}
+
+static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev)
+{
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= BIT(27);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+}
+
+static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
+		unsigned int vlevel)
+{
+	vlevel &= 0x3f;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~0x7efc00;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= ((vlevel - 4) << 10);
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= (vlevel << 17);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+}
+
+#else
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+	return false;
+}
+
+static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
+
+static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev) { }
+
+static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
+		unsigned int vlevel) { }
+#endif
+
+int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
+{
+	uint32_t timeout_us, new_level;
+	bool avs_enabled;
+
+	if (!dev)
+		return -EINVAL;
+
+	avs_enabled  = msm_spm_drv_is_avs_enabled(dev);
+
+	if (!msm_spm_pmic_arb_present(dev))
+		return -ENOSYS;
+
+	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
+		pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
+
+	if (avs_enabled)
+		msm_spm_drv_disable_avs(dev);
+
+	/* Kick the state machine back to idle */
+	dev->reg_shadow[MSM_SPM_REG_SAW2_RST] = 1;
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_RST);
+
+	msm_spm_drv_set_vctl2(dev, vlevel);
+
+	timeout_us = dev->vctl_timeout_us;
+	/* Confirm the voltage we set was what hardware sent */
+	do {
+		new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
+		if (new_level == vlevel)
+			break;
+		udelay(1);
+	} while (--timeout_us);
+	if (!timeout_us) {
+		pr_info("Wrong level %#x\n", new_level);
+		goto set_vdd_bail;
+	}
+
+	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
+		pr_info("%s: done, remaining timeout %u us\n",
+			__func__, timeout_us);
+
+	/* Set AVS min/max */
+	if (avs_enabled) {
+		msm_spm_drv_set_avs_vlevel(dev, vlevel);
+		msm_spm_drv_enable_avs(dev);
+	}
+
+	return 0;
+
+set_vdd_bail:
+	if (avs_enabled)
+		msm_spm_drv_enable_avs(dev);
+
+	pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
+		__func__, vlevel, timeout_us, new_level);
+	return -EIO;
+}
+
+static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port)
+{
+	int index = -1;
+
+	switch (port) {
+	case MSM_SPM_PMIC_VCTL_PORT:
+		index = dev->vctl_port;
+		break;
+	case MSM_SPM_PMIC_PHASE_PORT:
+		index = dev->phase_port;
+		break;
+	case MSM_SPM_PMIC_PFM_PORT:
+		index = dev->pfm_port;
+		break;
+	default:
+		break;
+	}
+
+	return index;
+}
+
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port, unsigned int data)
+{
+	unsigned int pmic_data = 0;
+	unsigned int timeout_us = 0;
+	int index = 0;
+
+	if (!msm_spm_pmic_arb_present(dev))
+		return -ENOSYS;
+
+	index = msm_spm_drv_get_pmic_port(dev, port);
+	if (index < 0)
+		return -ENODEV;
+
+	pmic_data |= data & 0xFF;
+	pmic_data |= (index & 0x7) << 16;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
+	mb();
+
+	timeout_us = dev->vctl_timeout_us;
+	/**
+	 * Confirm the pmic data set was what hardware sent by
+	 * checking the PMIC FSM state.
+	 * We cannot use the sts_pmic_data and check it against
+	 * the value like we do fot set_vdd, since the PMIC_STS
+	 * is only updated for SAW_VCTL sent with port index 0.
+	 */
+	do {
+		if (msm_spm_drv_get_sts_pmic_state(dev) ==
+				MSM_SPM_PMIC_STATE_IDLE)
+			break;
+		udelay(1);
+	} while (--timeout_us);
+
+	if (!timeout_us) {
+		pr_err("%s: failed, remaining timeout %u us, data %d\n",
+				__func__, timeout_us, data);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
+{
+	int i;
+
+	msm_spm_drv_flush_seq_entry(dev);
+	for (i = 0; i < MSM_SPM_REG_SAW2_PMIC_DATA_0 + num_pmic_data; i++)
+		msm_spm_drv_flush_shadow(dev, i);
+
+	mb();
+
+	for (i = MSM_SPM_REG_NR_INITIALIZE + 1; i < MSM_SPM_REG_NR; i++)
+		msm_spm_drv_load_shadow(dev, i);
+}
+
+int msm_spm_drv_init(struct msm_spm_driver_data *dev,
+		struct msm_spm_platform_data *data)
+{
+	int i;
+	int num_spm_entry;
+	bool found = false;
+
+	BUG_ON(!dev || !data);
+
+	dev->vctl_port = data->vctl_port;
+	dev->phase_port = data->phase_port;
+	dev->pfm_port = data->pfm_port;
+	dev->reg_base_addr = data->reg_base_addr;
+	memcpy(dev->reg_shadow, data->reg_init_values,
+			sizeof(data->reg_init_values));
+
+	dev->vctl_timeout_us = data->vctl_timeout_us;
+
+	msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor);
+
+	for (i = 0; i < ARRAY_SIZE(saw2_info); i++)
+		if (dev->major == saw2_info[i].major &&
+			dev->minor == saw2_info[i].minor) {
+			pr_debug("%s: Version found\n",
+					saw2_info[i].ver_name);
+			dev->reg_offsets = saw2_info[i].spm_reg_offset_ptr;
+			found = true;
+			break;
+		}
+
+	if (!found) {
+		pr_err("%s: No SAW2 version found\n", __func__);
+		BUG_ON(!found);
+	}
+
+	if (!num_pmic_data)
+		num_pmic_data = msm_spm_drv_get_num_pmic_data(dev);
+
+	num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
+
+	dev->reg_seq_entry_shadow =
+		kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry,
+				GFP_KERNEL);
+
+	if (!dev->reg_seq_entry_shadow)
+		return -ENOMEM;
+
+	return 0;
+}
diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c
new file mode 100644
index 0000000..755c165
--- /dev/null
+++ b/drivers/soc/qcom/spm_devices.c
@@ -0,0 +1,709 @@
+/* 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 <linux/err.h>
+
+#include <soc/qcom/spm.h>
+
+#include "spm_driver.h"
+
+#define VDD_DEFAULT 0xDEADF00D
+
+struct msm_spm_power_modes {
+	uint32_t mode;
+	bool notify_rpm;
+	uint32_t start_addr;
+};
+
+struct msm_spm_device {
+	struct list_head list;
+	bool initialized;
+	const char *name;
+	struct msm_spm_driver_data reg_data;
+	struct msm_spm_power_modes *modes;
+	uint32_t num_modes;
+	uint32_t cpu_vdd;
+	struct cpumask mask;
+	void __iomem *q2s_reg;
+};
+
+struct msm_spm_vdd_info {
+	struct msm_spm_device *vctl_dev;
+	uint32_t vlevel;
+	int err;
+};
+
+static LIST_HEAD(spm_list);
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
+static DEFINE_PER_CPU(struct msm_spm_device *, cpu_vctl_device);
+
+static void msm_spm_smp_set_vdd(void *data)
+{
+	struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
+	struct msm_spm_device *dev = info->vctl_dev;
+
+	dev->cpu_vdd = info->vlevel;
+	info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
+}
+
+/**
+ * msm_spm_probe_done(): Verify and return the status of the cpu(s) and l2
+ * probe.
+ * Return: 0 if all spm devices have been probed, else return -EPROBE_DEFER.
+ * if probe failed, then return the err number for that failure.
+ */
+int msm_spm_probe_done(void)
+{
+	struct msm_spm_device *dev;
+	int cpu;
+	int ret = 0;
+
+	for_each_possible_cpu(cpu) {
+		dev = per_cpu(cpu_vctl_device, cpu);
+		if (!dev)
+			return -EPROBE_DEFER;
+
+		ret = IS_ERR(dev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_spm_probe_done);
+
+void msm_spm_dump_regs(unsigned int cpu)
+{
+	dump_regs(&per_cpu(msm_cpu_spm_device, cpu).reg_data, cpu);
+}
+
+/**
+ * msm_spm_set_vdd(): Set core voltage
+ * @cpu: core id
+ * @vlevel: Encoded PMIC data.
+ *
+ * Return: 0 on success or -(ERRNO) on failure.
+ */
+int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
+{
+	struct msm_spm_vdd_info info;
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+	int ret;
+
+	if (!dev)
+		return -EPROBE_DEFER;
+
+	ret = IS_ERR(dev);
+	if (ret)
+		return ret;
+
+	info.vctl_dev = dev;
+	info.vlevel = vlevel;
+
+	ret = smp_call_function_any(&dev->mask, msm_spm_smp_set_vdd, &info,
+					true);
+	if (ret)
+		return ret;
+
+	return info.err;
+}
+EXPORT_SYMBOL(msm_spm_set_vdd);
+
+/**
+ * msm_spm_get_vdd(): Get core voltage
+ * @cpu: core id
+ * @return: Returns encoded PMIC data.
+ */
+unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+	int ret;
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	if (!dev)
+		return -EPROBE_DEFER;
+
+	ret = IS_ERR(dev);
+	if (ret)
+		return ret;
+
+	return dev->cpu_vdd;
+}
+EXPORT_SYMBOL(msm_spm_get_vdd);
+
+static void msm_spm_config_q2s(struct msm_spm_device *dev, unsigned int mode)
+{
+	uint32_t spm_legacy_mode = 0;
+	uint32_t qchannel_ignore = 0;
+	uint32_t val = 0;
+
+	if (!dev->q2s_reg)
+		return;
+
+	switch (mode) {
+	case MSM_SPM_MODE_DISABLED:
+	case MSM_SPM_MODE_CLOCK_GATING:
+		qchannel_ignore = 1;
+		spm_legacy_mode = 0;
+		break;
+	case MSM_SPM_MODE_RETENTION:
+		qchannel_ignore = 0;
+		spm_legacy_mode = 0;
+		break;
+	case MSM_SPM_MODE_GDHS:
+	case MSM_SPM_MODE_POWER_COLLAPSE:
+		qchannel_ignore = 0;
+		spm_legacy_mode = 1;
+		break;
+	default:
+		break;
+	}
+
+	val = spm_legacy_mode << 2 | qchannel_ignore << 1;
+	__raw_writel(val, dev->q2s_reg);
+	mb();
+}
+
+static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	uint32_t i;
+	uint32_t start_addr = 0;
+	int ret = -EINVAL;
+	bool pc_mode = false;
+
+	if (!dev->initialized)
+		return -ENXIO;
+
+	if ((mode == MSM_SPM_MODE_POWER_COLLAPSE)
+			|| (mode == MSM_SPM_MODE_GDHS))
+		pc_mode = true;
+
+	if (mode == MSM_SPM_MODE_DISABLED) {
+		ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
+	} else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
+		for (i = 0; i < dev->num_modes; i++) {
+			if ((dev->modes[i].mode == mode) &&
+				(dev->modes[i].notify_rpm == notify_rpm)) {
+				start_addr = dev->modes[i].start_addr;
+				break;
+			}
+		}
+		ret = msm_spm_drv_set_low_power_mode(&dev->reg_data,
+					start_addr, pc_mode);
+	}
+
+	msm_spm_config_q2s(dev, mode);
+
+	return ret;
+}
+
+static int msm_spm_dev_init(struct msm_spm_device *dev,
+		struct msm_spm_platform_data *data)
+{
+	int i, ret = -ENOMEM;
+	uint32_t offset = 0;
+
+	dev->cpu_vdd = VDD_DEFAULT;
+	dev->num_modes = data->num_modes;
+	dev->modes = kmalloc(
+			sizeof(struct msm_spm_power_modes) * dev->num_modes,
+			GFP_KERNEL);
+
+	if (!dev->modes)
+		goto spm_failed_malloc;
+
+	dev->reg_data.ver_reg = data->ver_reg;
+	ret = msm_spm_drv_init(&dev->reg_data, data);
+
+	if (ret)
+		goto spm_failed_init;
+
+	for (i = 0; i < dev->num_modes; i++) {
+
+		/* Default offset is 0 and gets updated as we write more
+		 * sequences into SPM
+		 */
+		dev->modes[i].start_addr = offset;
+		ret = msm_spm_drv_write_seq_data(&dev->reg_data,
+						data->modes[i].cmd, &offset);
+		if (ret < 0)
+			goto spm_failed_init;
+
+		dev->modes[i].mode = data->modes[i].mode;
+		dev->modes[i].notify_rpm = data->modes[i].notify_rpm;
+	}
+	msm_spm_drv_reinit(&dev->reg_data);
+	dev->initialized = true;
+	return 0;
+
+spm_failed_init:
+	kfree(dev->modes);
+spm_failed_malloc:
+	return ret;
+}
+
+/**
+ * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core
+ * @base: The SAW VCTL register which would set the voltage up.
+ * @val: The value to be set on the rail
+ * @cpu: The cpu for this with rail is being powered on
+ */
+int msm_spm_turn_on_cpu_rail(void __iomem *base, unsigned int val, int cpu)
+{
+	uint32_t timeout = 2000; /* delay for voltage to settle on the core */
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	/*
+	 * If clock drivers have already set up the voltage,
+	 * do not overwrite that value.
+	 */
+	if (dev && (dev->cpu_vdd != VDD_DEFAULT))
+		return 0;
+
+	/* Set the CPU supply regulator voltage */
+	val = (val & 0xFF);
+	writel_relaxed(val, base);
+	mb();
+	udelay(timeout);
+
+	/* Enable the CPU supply regulator*/
+	val = 0x30080;
+	writel_relaxed(val, base);
+	mb();
+	udelay(timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
+
+void msm_spm_reinit(void)
+{
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu)
+		msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu));
+}
+EXPORT_SYMBOL(msm_spm_reinit);
+
+/*
+ * msm_spm_is_mode_avail() - Specifies if a mode is available for the cpu
+ * It should only be used to decide a mode before lpm driver is probed.
+ * @mode: SPM LPM mode to be selected
+ */
+bool msm_spm_is_mode_avail(unsigned int mode)
+{
+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
+	int i;
+
+	for (i = 0; i < dev->num_modes; i++) {
+		if (dev->modes[i].mode == mode)
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ * @notify_rpm: Notify RPM in this mode
+ */
+int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
+{
+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
+
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+}
+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
+
+/**
+ * msm_spm_init(): Board initalization function
+ * @data: platform specific SPM register configuration data
+ * @nr_devs: Number of SPM devices being initialized
+ */
+int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
+{
+	unsigned int cpu;
+	int ret = 0;
+
+	BUG_ON((nr_devs < num_possible_cpus()) || !data);
+
+	for_each_possible_cpu(cpu) {
+		struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
+
+		ret = msm_spm_dev_init(dev, &data[cpu]);
+		if (ret < 0) {
+			pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
+					cpu, ret);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+struct msm_spm_device *msm_spm_get_device_by_name(const char *name)
+{
+	struct list_head *list;
+
+	list_for_each(list, &spm_list) {
+		struct msm_spm_device *dev
+			= list_entry(list, typeof(*dev), list);
+		if (dev->name && !strcmp(dev->name, name))
+			return dev;
+	}
+	return ERR_PTR(-ENODEV);
+}
+
+int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+}
+#ifdef CONFIG_MSM_L2_SPM
+
+/**
+ * msm_spm_apcs_set_phase(): Set number of SMPS phases.
+ * @cpu: cpu which is requesting the change in number of phases.
+ * @phase_cnt: Number of phases to be set active
+ */
+int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
+{
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	if (!dev)
+		return -ENXIO;
+
+	return msm_spm_drv_set_pmic_data(&dev->reg_data,
+			MSM_SPM_PMIC_PHASE_PORT, phase_cnt);
+}
+EXPORT_SYMBOL(msm_spm_apcs_set_phase);
+
+/** msm_spm_enable_fts_lpm() : Enable FTS to switch to low power
+ *                             when the cores are in low power modes
+ * @cpu: cpu that is entering low power mode.
+ * @mode: The mode configuration for FTS
+ */
+int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
+{
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	if (!dev)
+		return -ENXIO;
+
+	return msm_spm_drv_set_pmic_data(&dev->reg_data,
+			MSM_SPM_PMIC_PFM_PORT, mode);
+}
+EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
+
+#endif
+
+static int get_cpu_id(struct device_node *node)
+{
+	struct device_node *cpu_node;
+	u32 cpu;
+	int ret = -EINVAL;
+	char *key = "qcom,cpu";
+
+	cpu_node = of_parse_phandle(node, key, 0);
+	if (cpu_node) {
+		for_each_possible_cpu(cpu) {
+			if (of_get_cpu_node(cpu, NULL) == cpu_node)
+				return cpu;
+		}
+	} else {
+		char *key = "qcom,core-id";
+
+		ret = of_property_read_u32(node, key, &cpu);
+		if (!ret)
+			return cpu;
+	}
+	return ret;
+}
+
+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
+{
+	struct msm_spm_device *dev = NULL;
+	const char *val = NULL;
+	char *key = "qcom,name";
+	int cpu = get_cpu_id(pdev->dev.of_node);
+
+	if ((cpu >= 0) && cpu < num_possible_cpus())
+		dev = &per_cpu(msm_cpu_spm_device, cpu);
+	else if ((cpu == 0xffff) || (cpu < 0))
+		dev = devm_kzalloc(&pdev->dev, sizeof(struct msm_spm_device),
+					GFP_KERNEL);
+
+	if (!dev)
+		return NULL;
+
+	if (of_property_read_string(pdev->dev.of_node, key, &val)) {
+		pr_err("%s(): Cannot find a required node key:%s\n",
+				__func__, key);
+		return NULL;
+	}
+	dev->name = val;
+	list_add(&dev->list, &spm_list);
+
+	return dev;
+}
+
+static void get_cpumask(struct device_node *node, struct cpumask *mask)
+{
+	unsigned long vctl_mask = 0;
+	unsigned c = 0;
+	int idx = 0;
+	struct device_node *cpu_node = NULL;
+	int ret = 0;
+	char *key = "qcom,cpu-vctl-list";
+	bool found = false;
+
+	cpu_node = of_parse_phandle(node, key, idx++);
+	while (cpu_node) {
+		found = true;
+		for_each_possible_cpu(c) {
+			if (of_get_cpu_node(c, NULL) == cpu_node)
+				cpumask_set_cpu(c, mask);
+		}
+		cpu_node = of_parse_phandle(node, key, idx++);
+	};
+
+	if (found)
+		return;
+
+	key = "qcom,cpu-vctl-mask";
+	ret = of_property_read_u32(node, key, (u32 *) &vctl_mask);
+	if (!ret) {
+		for_each_set_bit(c, &vctl_mask, num_possible_cpus()) {
+			cpumask_set_cpu(c, mask);
+		}
+	}
+}
+
+static int msm_spm_dev_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int cpu = 0;
+	int i = 0;
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_spm_platform_data spm_data;
+	char *key = NULL;
+	uint32_t val = 0;
+	struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
+	int len = 0;
+	struct msm_spm_device *dev = NULL;
+	struct resource *res = NULL;
+	uint32_t mode_count = 0;
+
+	struct spm_of {
+		char *key;
+		uint32_t id;
+	};
+
+	struct spm_of spm_of_data[] = {
+		{"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG},
+		{"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL},
+		{"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS},
+		{"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT},
+		{"qcom,saw2-avs-dly", MSM_SPM_REG_SAW2_AVS_DLY},
+		{"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY},
+		{"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL},
+		{"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0},
+		{"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1},
+		{"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2},
+		{"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3},
+		{"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4},
+		{"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5},
+		{"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6},
+		{"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7},
+	};
+
+	struct mode_of {
+		char *key;
+		uint32_t id;
+		uint32_t notify_rpm;
+	};
+
+	struct mode_of mode_of_data[] = {
+		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0},
+		{"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_RETENTION, 0},
+		{"qcom,saw2-spm-cmd-gdhs", MSM_SPM_MODE_GDHS, 1},
+		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0},
+		{"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1},
+	};
+
+	dev = msm_spm_get_device(pdev);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	get_cpumask(node, &dev->mask);
+
+	memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
+	memset(&modes, 0,
+		(MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
+
+	key = "qcom,saw2-ver-reg";
+	ret = of_property_read_u32(node, key, &val);
+	if (ret)
+		goto fail;
+	spm_data.ver_reg = val;
+
+	key = "qcom,vctl-timeout-us";
+	ret = of_property_read_u32(node, key, &val);
+	if (!ret)
+		spm_data.vctl_timeout_us = val;
+
+	/* SAW start address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!spm_data.reg_base_addr) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	spm_data.vctl_port = -1;
+	spm_data.phase_port = -1;
+	spm_data.pfm_port = -1;
+
+	key = "qcom,vctl-port";
+	of_property_read_u32(node, key, &spm_data.vctl_port);
+
+	key = "qcom,phase-port";
+	of_property_read_u32(node, key, &spm_data.phase_port);
+
+	key = "qcom,pfm-port";
+	of_property_read_u32(node, key, &spm_data.pfm_port);
+
+	/* Q2S (QChannel-2-SPM) register */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		dev->q2s_reg = devm_ioremap(&pdev->dev, res->start,
+						resource_size(res));
+		if (!dev->q2s_reg) {
+			pr_err("%s(): Unable to iomap Q2S register\n",
+					__func__);
+			ret = -EADDRNOTAVAIL;
+			goto fail;
+		}
+	}
+	/*
+	 * At system boot, cpus and or clusters can remain in reset. CCI SPM
+	 * will not be triggered unless SPM_LEGACY_MODE bit is set for the
+	 * cluster in reset. Initialize q2s registers and set the
+	 * SPM_LEGACY_MODE bit.
+	 */
+	msm_spm_config_q2s(dev, MSM_SPM_MODE_POWER_COLLAPSE);
+
+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+		ret = of_property_read_u32(node, spm_of_data[i].key, &val);
+		if (ret)
+			continue;
+		spm_data.reg_init_values[spm_of_data[i].id] = val;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
+		key = mode_of_data[i].key;
+		modes[mode_count].cmd =
+			(uint8_t *)of_get_property(node, key, &len);
+		if (!modes[mode_count].cmd)
+			continue;
+		modes[mode_count].mode = mode_of_data[i].id;
+		modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm;
+		pr_debug("%s(): dev: %s cmd:%s, mode:%d rpm:%d\n", __func__,
+				dev->name, key, modes[mode_count].mode,
+				modes[mode_count].notify_rpm);
+		mode_count++;
+	}
+
+	spm_data.modes = modes;
+	spm_data.num_modes = mode_count;
+
+	ret = msm_spm_dev_init(dev, &spm_data);
+	if (ret)
+		goto fail;
+
+	platform_set_drvdata(pdev, dev);
+
+	for_each_cpu(cpu, &dev->mask)
+		per_cpu(cpu_vctl_device, cpu) = dev;
+
+	return ret;
+
+fail:
+	cpu = get_cpu_id(pdev->dev.of_node);
+	if (dev && (cpu >= num_possible_cpus() || (cpu < 0))) {
+		for_each_cpu(cpu, &dev->mask)
+			per_cpu(cpu_vctl_device, cpu) = ERR_PTR(ret);
+	}
+
+	pr_err("%s: CPU%d SPM device probe failed: %d\n", __func__, cpu, ret);
+
+	return ret;
+}
+
+static int msm_spm_dev_remove(struct platform_device *pdev)
+{
+	struct msm_spm_device *dev = platform_get_drvdata(pdev);
+
+	list_del(&dev->list);
+
+	return 0;
+}
+
+static struct of_device_id msm_spm_match_table[] = {
+	{.compatible = "qcom,spm-v2"},
+	{},
+};
+
+static struct platform_driver msm_spm_device_driver = {
+	.probe = msm_spm_dev_probe,
+	.remove = msm_spm_dev_remove,
+	.driver = {
+		.name = "spm-v2",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_spm_match_table,
+	},
+};
+
+/**
+ * msm_spm_device_init(): Device tree initialization function
+ */
+int __init msm_spm_device_init(void)
+{
+	static bool registered;
+
+	if (registered)
+		return 0;
+
+	registered = true;
+
+	return platform_driver_register(&msm_spm_device_driver);
+}
+device_initcall(msm_spm_device_init);
diff --git a/drivers/soc/qcom/spm_driver.h b/drivers/soc/qcom/spm_driver.h
new file mode 100644
index 0000000..49cd16a
--- /dev/null
+++ b/drivers/soc/qcom/spm_driver.h
@@ -0,0 +1,118 @@
+/* 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.
+ */
+#ifndef __ARCH_ARM_MACH_MSM_SPM_DRIVER_H
+#define __ARCH_ARM_MACH_MSM_SPM_DRIVER_H
+
+#include <soc/qcom/spm.h>
+
+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,
+};
+
+struct msm_spm_seq_entry {
+	uint32_t mode;
+	uint8_t *cmd;
+	bool  notify_rpm;
+};
+
+struct msm_spm_platform_data {
+	void __iomem *reg_base_addr;
+	uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE];
+
+	uint32_t ver_reg;
+	uint32_t vctl_port;
+	uint32_t phase_port;
+	uint32_t pfm_port;
+
+	uint8_t awake_vlevel;
+	uint32_t vctl_timeout_us;
+	uint32_t avs_timeout_us;
+
+	uint32_t num_modes;
+	struct msm_spm_seq_entry *modes;
+};
+
+enum msm_spm_pmic_port {
+	MSM_SPM_PMIC_VCTL_PORT,
+	MSM_SPM_PMIC_PHASE_PORT,
+	MSM_SPM_PMIC_PFM_PORT,
+};
+
+struct msm_spm_driver_data {
+	uint32_t major;
+	uint32_t minor;
+	uint32_t ver_reg;
+	uint32_t vctl_port;
+	uint32_t phase_port;
+	uint32_t pfm_port;
+	void __iomem *reg_base_addr;
+	uint32_t vctl_timeout_us;
+	uint32_t avs_timeout_us;
+	uint32_t reg_shadow[MSM_SPM_REG_NR];
+	uint32_t *reg_seq_entry_shadow;
+	uint32_t *reg_offsets;
+};
+
+int msm_spm_drv_init(struct msm_spm_driver_data *dev,
+		struct msm_spm_platform_data *data);
+void msm_spm_drv_reinit(struct msm_spm_driver_data *dev);
+int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
+		uint32_t addr, bool pc_mode);
+int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev,
+		unsigned int vlevel);
+void dump_regs(struct msm_spm_driver_data *dev, int cpu);
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+		struct msm_spm_driver_data *dev);
+int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
+		uint8_t *cmd, uint32_t *offset);
+void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
+int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
+		bool enable);
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port, unsigned int data);
+
+void msm_spm_reinit(void);
+int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs);
+
+#endif
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
new file mode 100644
index 0000000..185ddaf
--- /dev/null
+++ b/include/soc/qcom/spm.h
@@ -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.
+ */
+
+#ifndef __MSM_SPM_H
+#define __MSM_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(unsigned int mode, bool notify_rpm);
+int msm_spm_probe_done(void);
+int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
+unsigned int msm_spm_get_vdd(unsigned int cpu);
+int msm_spm_turn_on_cpu_rail(void __iomem *base, unsigned int val, int cpu);
+struct msm_spm_device *msm_spm_get_device_by_name(const char *name);
+int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm);
+int msm_spm_device_init(void);
+bool msm_spm_is_mode_avail(unsigned int mode);
+void msm_spm_dump_regs(unsigned int cpu);
+int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt);
+int msm_spm_enable_fts_lpm(int cpu, uint32_t mode);
+
+#else /* defined(CONFIG_QCOM_PM) */
+static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_probe_done(void)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
+{
+	return -ENOSYS;
+}
+
+static inline unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+	return 0;
+}
+
+static inline int msm_spm_turn_on_cpu_rail(void __iomem *base,
+		unsigned int val, int cpu)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_device_init(void)
+{
+	return -ENOSYS;
+}
+
+static void msm_spm_dump_regs(unsigned int cpu)
+{
+}
+
+static inline int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	return -ENODEV;
+}
+static inline struct msm_spm_device *msm_spm_get_device_by_name(
+		const char *name)
+{
+	return NULL;
+}
+
+static inline bool msm_spm_is_mode_avail(unsigned int mode)
+{
+	return false;
+}
+
+static inline int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
+{
+	return -ENOSYS;
+}
+
+#endif  /* defined (CONFIG_QCOM_PM) */
+#endif  /* __MSM_SPM_H */
-- 
1.9.1

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

* [RFC] [PATCH 04/13] arm: dts: qcom: Add SPM device bindings for 8974
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (2 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 05/13] qcom: msm-pm: Add cpu low power mode functions Lina Iyer
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer, Praveen Chidambaram

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) and
L2 can do retention (clock-gating).

Signed-off-by: Praveen Chidambaram <pchidamb@codeaurora.org>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/boot/dts/qcom-msm8974-pm.dtsi | 118 +++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/qcom-msm8974.dtsi    |   2 +
 2 files changed, 120 insertions(+)
 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..8eb934e
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
@@ -0,0 +1,118 @@
+/* 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 {
+	qcom,spm@f9089000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,name = "core0";
+		qcom,core-id = <0>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x01>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x3C102800>;
+		qcom,saw2-spm-ctl = <0x1>;
+		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];
+	};
+
+	qcom,spm@f9099000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9099000 0x1000>;
+		qcom,name = "core1";
+		qcom,core-id = <1>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x01>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x3C102800>;
+		qcom,saw2-spm-ctl = <0x1>;
+		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];
+	};
+
+	qcom,spm@f90a9000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf90a9000 0x1000>;
+		qcom,name = "core2";
+		qcom,core-id = <2>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x01>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x3C102800>;
+		qcom,saw2-spm-ctl = <0x1>;
+		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];
+	};
+
+	qcom,spm@f90b9000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf90b9000 0x1000>;
+		qcom,name = "core3";
+		qcom,core-id = <3>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x01>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x3C102800>;
+		qcom,saw2-spm-ctl = <0x1>;
+		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];
+	};
+
+	qcom,spm@f9012000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9012000 0x1000>;
+		qcom,name = "system-l2";
+		qcom,core-id = <0xffff>; /* L2/APCS SAW */
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x14>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x3C102800>;
+		qcom,saw2-spm-ctl = <0x1>;
+		qcom,saw2-pmic-data0 = <0x02030080>;
+		qcom,saw2-pmic-data1 = <0x00030000>;
+		qcom,vctl-timeout-us = <50>;
+		qcom,vctl-port = <0x0>;
+		qcom,phase-port = <0x1>;
+		qcom,pfm-port = <0x2>;
+		qcom,cpu-vctl-mask = <0xf>;
+		qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
+	};
+};
diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 69dca2a..964ecd2 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -238,3 +238,5 @@
 		};
 	};
 };
+
+#include "qcom-msm8974-pm.dtsi"
-- 
1.9.1

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

* [RFC] [PATCH 05/13] qcom: msm-pm: Add cpu low power mode functions
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (3 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 04/13] arm: dts: qcom: Add SPM device bindings for 8974 Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 06/13] qcom: msm-pm: Add support for hotplug and secondary startup Lina Iyer
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer, Venkat Devarasetty

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

Signed-off-by: Venkat Devarasetty <vdevaras@codeaurora.org>
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 drivers/soc/qcom/Makefile |   2 +-
 drivers/soc/qcom/msm-pm.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++
 include/soc/qcom/pm.h     |  39 +++++++++
 3 files changed, 258 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 3de7ed9..87c3b9704 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,5 +1,5 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
-obj-$(CONFIG_QCOM_PM) +=	spm_devices.o spm.o
+obj-$(CONFIG_QCOM_PM) +=	spm_devices.o 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..17a7c8c
--- /dev/null
+++ b/drivers/soc/qcom/msm-pm.c
@@ -0,0 +1,218 @@
+/* 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/tick.h>
+#include <linux/platform_device.h>
+#include <linux/cpu_pm.h>
+#include <linux/uaccess.h>
+
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+
+#include <asm/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+
+#define SCM_CMD_TERMINATE_PC	(0x2)
+#define SCM_CMD_CORE_HOTPLUGGED (0x10)
+#define SCM_FLUSH_FLAG_MASK	(0x3)
+
+static bool msm_pm_is_L1_writeback(void)
+{
+	u32 cache_id = 0;
+
+#if defined(CONFIG_CPU_V7)
+	u32 sel = 0;
+
+	asm volatile ("mcr p15, 2, %[ccselr], c0, c0, 0\n\t"
+		      "isb\n\t"
+		      "mrc p15, 1, %[ccsidr], c0, c0, 0\n\t"
+		      :[ccsidr]"=r" (cache_id)
+		      :[ccselr]"r" (sel)
+		     );
+	return cache_id & BIT(30);
+#elif defined(CONFIG_ARM64)
+	u32 sel = 0;
+	asm volatile("msr csselr_el1, %[ccselr]\n\t"
+		     "isb\n\t"
+		     "mrs %[ccsidr],ccsidr_el1\n\t"
+		     :[ccsidr]"=r" (cache_id)
+		     :[ccselr]"r" (sel)
+		    );
+	return cache_id & BIT(30);
+#else
+#error No valid CPU arch selected
+#endif
+}
+
+static inline void msm_arch_idle(void)
+{
+	mb();
+	wfi();
+}
+
+static bool msm_pm_swfi(bool from_idle)
+{
+	msm_arch_idle();
+	return true;
+}
+
+static bool msm_pm_retention(bool from_idle)
+{
+	int ret = 0;
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_RETENTION, false);
+	WARN_ON(ret);
+
+	msm_arch_idle();
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+	WARN_ON(ret);
+
+	return true;
+}
+
+static int msm_pm_collapse(unsigned long from_idle)
+{
+	enum msm_pm_l2_scm_flag flag = MSM_SCM_L2_ON;
+
+	/**
+	 * Single core processors need to have L2
+	 * flushed when powering down the core.
+	 * Notify SCM to flush secure L2 lines.
+	 */
+	if (num_possible_cpus() == 1)
+		flag = MSM_SCM_L2_OFF;
+
+	if (flag == MSM_SCM_L2_OFF)
+		flush_cache_all();
+	else if (msm_pm_is_L1_writeback())
+		flush_cache_louis();
+
+	flag &= SCM_FLUSH_FLAG_MASK;
+	if (!from_idle)
+		flag |= SCM_CMD_CORE_HOTPLUGGED;
+
+	scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
+
+	return 0;
+}
+
+static void set_up_boot_address(void *entry, int cpu, bool from_idle)
+{
+	static int flags[NR_CPUS] = {
+		SCM_FLAG_WARMBOOT_CPU0,
+		SCM_FLAG_WARMBOOT_CPU1,
+		SCM_FLAG_WARMBOOT_CPU2,
+		SCM_FLAG_WARMBOOT_CPU3,
+	};
+
+	scm_set_boot_addr(virt_to_phys(entry), flags[cpu]);
+}
+
+static bool __ref msm_pm_spm_power_collapse(
+	unsigned int cpu, bool from_idle)
+{
+	static DEFINE_PER_CPU(void *, last_known_entry);
+	void *entry;
+	bool collapsed = 0;
+	int ret;
+	bool save_cpu_regs = (cpu_online(cpu) || from_idle);
+
+	if (from_idle)
+		cpu_pm_enter();
+
+	ret = msm_spm_set_low_power_mode(
+			MSM_SPM_MODE_POWER_COLLAPSE, false);
+	WARN_ON(ret);
+
+	entry = save_cpu_regs ? cpu_resume : secondary_startup;
+	if (entry != per_cpu(last_known_entry, cpu)) {
+		per_cpu(last_known_entry, cpu) = entry;
+		set_up_boot_address(entry, cpu, from_idle);
+	}
+
+#ifdef CONFIG_CPU_V7
+	collapsed = !cpu_suspend(from_idle, msm_pm_collapse);
+#else
+	collapsed = !cpu_suspend(0);
+#endif
+
+	if (collapsed)
+		local_fiq_enable();
+
+	if (from_idle)
+		cpu_pm_exit();
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+	WARN_ON(ret);
+
+	return collapsed;
+}
+
+static bool msm_pm_power_collapse_standalone(bool from_idle)
+{
+	unsigned int cpu = smp_processor_id();
+	bool collapsed;
+
+	collapsed = msm_pm_spm_power_collapse(cpu, from_idle);
+
+	return collapsed;
+}
+
+static bool msm_pm_power_collapse(bool from_idle)
+{
+	unsigned int cpu = smp_processor_id();
+	bool collapsed;
+
+	collapsed = msm_pm_spm_power_collapse(cpu, from_idle);
+
+	return collapsed;
+}
+
+static bool (*execute[MSM_PM_SLEEP_MODE_NR])(bool idle) = {
+	[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = msm_pm_swfi,
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
+		msm_pm_power_collapse_standalone,
+	[MSM_PM_SLEEP_MODE_RETENTION] = msm_pm_retention,
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = msm_pm_power_collapse,
+};
+
+/**
+ * msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu
+ *
+ * @mode - sleep mode to enter
+ * @from_idle - bool to indicate that the mode is exercised during idle/suspend
+ *
+ * The code should be with interrupts disabled and on the core on which the
+ * low power is to be executed.
+ *
+ */
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
+{
+	bool exit_stat = false;
+
+	if (execute[mode])
+		exit_stat = execute[mode](from_idle);
+
+	return exit_stat;
+}
+EXPORT_SYMBOL(msm_cpu_pm_enter_sleep);
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
new file mode 100644
index 0000000..01872ad
--- /dev/null
+++ b/include/soc/qcom/pm.h
@@ -0,0 +1,39 @@
+/*
+ * 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_WAIT_FOR_INTERRUPT,
+	MSM_PM_SLEEP_MODE_RETENTION,
+	MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+	MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+	MSM_PM_SLEEP_MODE_NR,
+};
+
+enum msm_pm_l2_scm_flag {
+	MSM_SCM_L2_ON = 0,
+	MSM_SCM_L2_OFF = 1,
+};
+
+#ifdef CONFIG_QCOM_PM
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
+#else
+static inline bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode,
+						bool from_idle)
+{ return true; }
+#endif
+
+#endif  /* __QCOM_PM_H */
-- 
1.9.1

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

* [RFC] [PATCH 06/13] qcom: msm-pm: Add support for hotplug and secondary startup
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (4 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 05/13] qcom: msm-pm: Add cpu low power mode functions Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 07/13] qcom: sleep-status: Add ability to recognize cpu power down state Lina Iyer
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Add hotplug and secondary startup entry point to cold or warm boot
secondary cpus.

Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 drivers/soc/qcom/msm-pm.c | 36 ++++++++++++++++++++++++++++++++++++
 include/soc/qcom/pm.h     |  4 ++++
 2 files changed, 40 insertions(+)

diff --git a/drivers/soc/qcom/msm-pm.c b/drivers/soc/qcom/msm-pm.c
index 17a7c8c..4a6471d 100644
--- a/drivers/soc/qcom/msm-pm.c
+++ b/drivers/soc/qcom/msm-pm.c
@@ -216,3 +216,39 @@ bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
 	return exit_stat;
 }
 EXPORT_SYMBOL(msm_cpu_pm_enter_sleep);
+
+/**
+ * msm_pm_cpu_hotplug_enter - Entry point for SoC hotplug interface
+ * Set up cores to enter deeper sleep modes than just clock gating
+ * Find the best deepest low power mode that can enter
+ *
+ * @cpu - The cpu that is being hotplugged off
+ */
+int msm_pm_cpu_hotplug_enter(unsigned int cpu)
+{
+	enum msm_pm_sleep_mode mode = MSM_PM_SLEEP_MODE_NR;
+	int ret;
+
+	if (msm_spm_is_mode_avail(MSM_SPM_MODE_POWER_COLLAPSE))
+		mode = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
+	else if (msm_spm_is_mode_avail( MSM_SPM_MODE_RETENTION))
+		mode = MSM_PM_SLEEP_MODE_RETENTION;
+	else
+		mode = MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT;
+
+	ret = msm_cpu_pm_enter_sleep(mode, false) ? 0 : -EFAULT;
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pm_cpu_hotplug_enter);
+
+/**
+ * msm_pm_secondary_startup() - Restore after hotplug resume
+ *
+ * @ cpu: the cpu thats coming up.
+ */
+int msm_pm_secondary_startup(unsigned int cpu)
+{
+	return msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+}
+EXPORT_SYMBOL(msm_pm_secondary_startup);
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
index 01872ad..ed6124a 100644
--- a/include/soc/qcom/pm.h
+++ b/include/soc/qcom/pm.h
@@ -30,10 +30,14 @@ enum msm_pm_l2_scm_flag {
 
 #ifdef CONFIG_QCOM_PM
 bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
+int msm_pm_cpu_hotplug_enter(unsigned int cpu);
+int msm_pm_secondary_startup(unsigned int cpu);
 #else
 static inline bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode,
 						bool from_idle)
 { return true; }
+static inline int msm_pm_cpu_hotplug_enter(unsigned int cpu) { return 0; }
+static inline int msm_pm_secondary_startup(unsigned int cpu) { return 0; }
 #endif
 
 #endif  /* __QCOM_PM_H */
-- 
1.9.1

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

* [RFC] [PATCH 07/13] qcom: sleep-status: Add ability to recognize cpu power down state
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (5 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 06/13] qcom: msm-pm: Add support for hotplug and secondary startup Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 08/13] arm: dts: qcom: Add device binding for sleep status Lina Iyer
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

QCOM processors get notified by the processor subsystem logic that
recognizes when a processor has entered the low power state. This is
used by PM to guarantee that the processor is indeed in its low
power state, before powering down the associated resources.

Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 .../bindings/arm/msm/qcom,cpu-sleep-status.txt     |  41 +++++
 drivers/soc/qcom/Makefile                          |   2 +-
 drivers/soc/qcom/sleep-status.c                    | 178 +++++++++++++++++++++
 include/soc/qcom/pm.h                              |   2 +
 4 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,cpu-sleep-status.txt
 create mode 100644 drivers/soc/qcom/sleep-status.c

diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,cpu-sleep-status.txt b/Documentation/devicetree/bindings/arm/msm/qcom,cpu-sleep-status.txt
new file mode 100644
index 0000000..3d2974e
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,cpu-sleep-status.txt
@@ -0,0 +1,41 @@
+* MSM Sleep Status
+
+MSM Sleep status device is used to check the power collapsed status of a
+offlined core. The core that initiates the hotplug would wait on the
+sleep status device before CPU_DEAD notifications are sent out. Some hardware
+devices require that the offlined core is power collapsed before turning off
+the resources that are used by the offlined core.
+
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: Should be "qcom,cpu-sleep-status"
+
+- reg:
+	Usage: required
+	Value type: <register address>
+	Definition: The physical address of the sleep status register for
+	core0, the second element is the size of the register.
+
+- qcom,cpu-alias-addr:
+	Usage: required
+	Value type: <integer>
+	Definition: On MSM chipset, the each cores registers are at a
+	fixed offset each other.
+
+- qcom,cpu-sleep-status-mask:
+	Usage: required
+	Value type: <integer>
+	Definition: The bit mask within the status register that
+	indicates the Core's sleep state.
+
+Example:
+	qcom,cpu-sleep-status@f9088008 {
+		compatible = "qcom,cpu-sleep-status";
+		reg = <0xf9088008 0x4>;
+		qcom,cpu-alias-addr = <0x10000>;
+		qcom,sleep-status-mask = <0x80000>;
+	};
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 87c3b9704..d2cc9c0 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,5 +1,5 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
-obj-$(CONFIG_QCOM_PM) +=	spm_devices.o spm.o msm-pm.o
+obj-$(CONFIG_QCOM_PM) +=	spm_devices.o spm.o msm-pm.o sleep-status.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/sleep-status.c b/drivers/soc/qcom/sleep-status.c
new file mode 100644
index 0000000..aa6340c
--- /dev/null
+++ b/drivers/soc/qcom/sleep-status.c
@@ -0,0 +1,178 @@
+/* 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm.h>
+
+struct msm_pm_sleep_status_data {
+	void *base_addr;
+	uint32_t cpu_offset;
+	uint32_t mask;
+};
+
+static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
+
+/**
+ * msm_pm_wait_cpu_shutdown() - Wait for a core to be power collapsed during
+ *				hotplug
+ *
+ * @ cpu - cpu to wait on.
+ *
+ * Blocking function call that waits on the core to be power collapsed. This
+ * function is called from platform_cpu_die to ensure that a core is power
+ * collapsed before sending the CPU_DEAD notification so the drivers could
+ * remove the resource votes for this CPU(regulator and clock)
+ */
+int msm_pm_wait_cpu_shutdown(unsigned int cpu)
+{
+	int timeout = 0;
+
+	if (!msm_pm_slp_sts)
+		return 0;
+
+	if (!msm_pm_slp_sts[cpu].base_addr)
+		return 0;
+
+	while (1) {
+		/*
+		 * Check for the SPM of the core being hotplugged to set
+		 * its sleep state.The SPM sleep state indicates that the
+		 * core has been power collapsed.
+		 */
+		int acc_sts = __raw_readl(msm_pm_slp_sts[cpu].base_addr);
+
+		if (acc_sts & msm_pm_slp_sts[cpu].mask)
+			return 0;
+
+		udelay(100);
+		/*
+		 * Dump spm registers for debugging
+		 */
+		if (++timeout == 20) {
+			msm_spm_dump_regs(cpu);
+			__WARN_printf(
+			"CPU%u didn't collapse in 2ms, sleep status: 0x%x\n",
+					cpu, acc_sts);
+		}
+	}
+
+	return -EBUSY;
+}
+
+static int msm_cpu_status_probe(struct platform_device *pdev)
+{
+	struct msm_pm_sleep_status_data *pdata;
+	char *key;
+	u32 cpu;
+
+	if (!pdev)
+		return -EFAULT;
+
+	msm_pm_slp_sts = devm_kcalloc(&pdev->dev, num_possible_cpus(),
+					sizeof(*msm_pm_slp_sts), GFP_KERNEL);
+
+	if (!msm_pm_slp_sts)
+		return -ENOMEM;
+
+	if (pdev->dev.of_node) {
+		struct resource *res;
+		u32 offset;
+		int rc;
+		u32 mask;
+		bool offset_available = true;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (!res)
+			return -ENODEV;
+
+		key = "qcom,cpu-alias-addr";
+		rc = of_property_read_u32(pdev->dev.of_node, key, &offset);
+
+		if (rc)
+			offset_available = false;
+
+		key = "qcom,sleep-status-mask";
+		rc = of_property_read_u32(pdev->dev.of_node, key, &mask);
+
+		if (rc)
+			return -ENODEV;
+
+		for_each_possible_cpu(cpu) {
+			phys_addr_t base_c;
+
+			if (offset_available)
+				base_c = res->start + cpu * offset;
+			else {
+				res = platform_get_resource(pdev,
+							IORESOURCE_MEM, cpu);
+				if (!res)
+					return -ENODEV;
+				base_c = res->start;
+			}
+
+			msm_pm_slp_sts[cpu].base_addr =
+				devm_ioremap(&pdev->dev, base_c,
+						resource_size(res));
+			msm_pm_slp_sts[cpu].mask = mask;
+
+			if (!msm_pm_slp_sts[cpu].base_addr)
+				return -ENOMEM;
+		}
+	} else {
+		pdata = pdev->dev.platform_data;
+		if (!pdev->dev.platform_data)
+			return -EINVAL;
+
+		for_each_possible_cpu(cpu) {
+			msm_pm_slp_sts[cpu].base_addr =
+				pdata->base_addr + cpu * pdata->cpu_offset;
+			msm_pm_slp_sts[cpu].mask = pdata->mask;
+		}
+	}
+
+	return 0;
+};
+
+static struct of_device_id msm_slp_sts_match_tbl[] = {
+	{.compatible = "qcom,cpu-sleep-status"},
+	{},
+};
+
+static struct platform_driver msm_cpu_status_driver = {
+	.probe = msm_cpu_status_probe,
+	.driver = {
+		.name = "qcom,cpu-sleep-status",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_slp_sts_match_tbl,
+	},
+};
+
+int __init msm_pm_sleep_status_init(void)
+{
+	static bool registered;
+
+	if (registered)
+		return 0;
+	registered = true;
+
+	return platform_driver_register(&msm_cpu_status_driver);
+}
+arch_initcall(msm_pm_sleep_status_init);
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
index ed6124a..3de04b8 100644
--- a/include/soc/qcom/pm.h
+++ b/include/soc/qcom/pm.h
@@ -32,12 +32,14 @@ enum msm_pm_l2_scm_flag {
 bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
 int msm_pm_cpu_hotplug_enter(unsigned int cpu);
 int msm_pm_secondary_startup(unsigned int cpu);
+int msm_pm_wait_cpu_shutdown(unsigned int cpu);
 #else
 static inline bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode,
 						bool from_idle)
 { return true; }
 static inline int msm_pm_cpu_hotplug_enter(unsigned int cpu) { return 0; }
 static inline int msm_pm_secondary_startup(unsigned int cpu) { return 0; }
+static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
 #endif
 
 #endif  /* __QCOM_PM_H */
-- 
1.9.1

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

* [RFC] [PATCH 08/13] arm: dts: qcom: Add device binding for sleep status
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (6 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 07/13] qcom: sleep-status: Add ability to recognize cpu power down state Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 09/13] soc: qcom: Add QCOM Power management config Lina Iyer
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

QCOM processors get the feedback on the processor subsystem logic that
notifies when the processor has entered the low power state. This is
used for pm to gaurantee that the processor is indeed in its low
power state before the associated resources can be turned off.

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

diff --git a/arch/arm/boot/dts/qcom-msm8974-pm.dtsi b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
index 8eb934e..8fe00f1 100644
--- a/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
@@ -115,4 +115,11 @@
 		qcom,cpu-vctl-mask = <0xf>;
 		qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
 	};
+
+	qcom,cpu-sleep-status@f9088008 {
+		compatible = "qcom,cpu-sleep-status";
+		reg = <0xf9088008 0x100>;
+		qcom,cpu-alias-addr = <0x10000>;
+		qcom,sleep-status-mask= <0x80000>;
+	};
 };
-- 
1.9.1

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

* [RFC] [PATCH 09/13] soc: qcom: Add QCOM Power management config
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (7 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 08/13] arm: dts: qcom: Add device binding for sleep status Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 10/13] qcom: platsmp: Enable deeper idle states for hotplug Lina Iyer
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Allow power management drivers for QCOM chipsets.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 drivers/soc/qcom/Kconfig | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 7dcd554..1569410 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
+	tristate "Qualcomm Power Management"
+	depends on PM && ARCH_QCOM && OF
+	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.
-- 
1.9.1

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

* [RFC] [PATCH 10/13] qcom: platsmp: Enable deeper idle states for hotplug
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (8 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 09/13] soc: qcom: Add QCOM Power management config Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 11/13] qcom: cpuidle: Add cpuidle driver for QCOM cpus Lina Iyer
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Allow cpu and system deeper sleep modes when a cpu is hotplugged.
Calling wfi() executes architectural clock gating supported by the ARM
core, while this is useful, it does not save leakage power and does not
help in reducing power in the peripheral logic surrounding the cpu.

QCOM platforms have a sub-system power manager to control the logic
around the core. Pass the hotplug call over to SoC idle interface driver
to allow for the core and the peripheral logic to enter low power when
hotplugged.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 arch/arm/mach-qcom/platsmp.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
index d690856..c40f220 100644
--- a/arch/arm/mach-qcom/platsmp.c
+++ b/arch/arm/mach-qcom/platsmp.c
@@ -20,7 +20,8 @@
 
 #include <asm/smp_plat.h>
 
-#include "scm-boot.h"
+#include <soc/qcom/scm-boot.h>
+#include <soc/qcom/pm.h>
 
 #define VDD_SC1_ARRAY_CLAMP_GFS_CTL	0x35a0
 #define SCSS_CPU1CORE_RESET		0x2d80
@@ -51,12 +52,30 @@ static DEFINE_SPINLOCK(boot_lock);
 #ifdef CONFIG_HOTPLUG_CPU
 static void __ref qcom_cpu_die(unsigned int cpu)
 {
+#if defined CONFIG_QCOM_PM
+	for (;;)
+		msm_pm_cpu_hotplug_enter(cpu);
+#else
 	wfi();
+#endif
+}
+
+static int __ref qcom_cpu_kill(unsigned int cpu)
+{
+	int ret = 0;
+
+#if defined CONFIG_QCOM_PM
+	ret = msm_pm_wait_cpu_shutdown(cpu) ? 0 : 1;
+#endif
+	return ret;
 }
+
 #endif
 
 static void qcom_secondary_init(unsigned int cpu)
 {
+	msm_pm_secondary_startup(cpu);
+
 	/*
 	 * Synchronise with the boot thread.
 	 */
@@ -373,6 +392,7 @@ static struct smp_operations qcom_smp_kpssv2_ops __initdata = {
 	.smp_boot_secondary	= kpssv2_boot_secondary,
 #ifdef CONFIG_HOTPLUG_CPU
 	.cpu_die		= qcom_cpu_die,
+	.cpu_kill		= qcom_cpu_kill,
 #endif
 };
 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops);
-- 
1.9.1

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

* [RFC] [PATCH 11/13] qcom: cpuidle: Add cpuidle driver for QCOM cpus
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (9 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 10/13] qcom: platsmp: Enable deeper idle states for hotplug Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 12/13] qcom: cpuidle: Add cpuidle device nodes for 8974 chipset Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 13/13] qcom: cpuidle: Config option to enable QCOM cpuidle driver Lina Iyer
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Add cpuidle driver interface to allow cpus to go into C-States.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 .../devicetree/bindings/arm/msm/qcom,cpuidle.txt   |  73 +++++++++++
 drivers/cpuidle/Makefile                           |   1 +
 drivers/cpuidle/cpuidle-qcom.c                     | 140 +++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,cpuidle.txt
 create mode 100644 drivers/cpuidle/cpuidle-qcom.c

diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,cpuidle.txt b/Documentation/devicetree/bindings/arm/msm/qcom,cpuidle.txt
new file mode 100644
index 0000000..b094baf
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,cpuidle.txt
@@ -0,0 +1,73 @@
+Qualcomm CPUIdle driver
+
+The Qualcomm cpuidle driver enables the processor to enter low power modes
+when idle. The processors support 4 low power modes.
+	wfi - also known as clock gating
+	retention - processor clock gated and processor power is reduced.
+	standalone-pc - processor is powered down and when reset, the core
+		boots into secure mode and trampolines back to the kernel.
+		Every core can individually enter this low power mode without
+		affecting the state of the other cores.
+	pc - essentially standalone power collapse, but indicates that the
+		latency to put SoC into a low power state is tolerable.
+
+The cpuidle node is comprised of nodes, each of which represent a C-State the
+processor can achieve. Each node provides the latency and residency which
+helps the cpuidle governor to choose the appropriate low power mode based on
+the time available. Not all SoCs may support all the above low power modes.
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: Should be "qcom,cpuidle"
+
+- qcom,cpu-level:
+	Usage: required
+	Value type: { Node }
+	Definition: Describes a C-State of the processor
+
+	PROPERTIES of qcom,cpu-level
+
+	- reg:
+		Usage: required
+		Value type: <integer>
+		Definition: Index of the C-State
+
+	- qcom,state-name:
+		Usage: required
+		Value type: <string>
+		Definition: C-State moniker
+
+	- qcom,spm-cpu-mode:
+		Usage: required
+		Value type: <string>
+		Definition: The description of the h/w mode that will be
+			achieved in this C-State.
+
+	- qcom,latency-us:
+		Usage: required
+		Value type: <integer>
+		Defintion: Time taken to exit from the C-State
+
+	- qcom,residency-us:
+		Usage: required
+		Value type: <integer>
+		Defintion: Time to be spent in this C-State for the power
+			saving to be beneficial.
+
+Example:
+
+	qcom,cpuidle {
+		compatible = "qcom,cpuidle";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		qcom,cpu-level@0 {
+			reg = <0x0>;
+			qcom,state-name = "C1";
+			qcom,spm-cpu-mode = "wfi";
+			qcom,latency-us = <1>;
+			qcom,residency-us = <1>;
+		};
+	};
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 11edb31..4a2c446 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -16,6 +16,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..8e70a88
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-qcom.c
@@ -0,0 +1,140 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014 Linaro.
+ *
+ * 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/platform_device.h>
+#include <linux/of.h>
+#include <linux/cpuidle.h>
+
+#include <soc/qcom/pm.h>
+
+struct lookup {
+	enum msm_pm_sleep_mode mode;
+	char *name;
+};
+
+static enum msm_pm_sleep_mode spm_sleep_modes[MSM_PM_SLEEP_MODE_NR];
+
+static int qcom_lpm_enter(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
+				int index)
+{
+	return msm_cpu_pm_enter_sleep(spm_sleep_modes[index], true);
+}
+
+static struct cpuidle_driver qcom_cpuidle_driver = {
+	.name	= "qcom_cpuidle",
+	.owner	= THIS_MODULE,
+};
+
+static int qcom_cpuidle_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *top = pdev->dev.of_node;
+	struct device_node *n;
+	char *key;
+	const char *val;
+	int index = 0;
+	int i;
+	struct cpuidle_state *state;
+	static const struct lookup pm_sm_lookup[] = {
+		{MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+			"wfi"},
+		{MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+			"standalone_pc"},
+		{MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+			"pc"},
+		{MSM_PM_SLEEP_MODE_RETENTION,
+			"retention"},
+	};
+
+	if (!top)
+		return -ENODEV;
+
+	for_each_child_of_node(top, n) {
+		key = "qcom,cpu-level";
+		if (of_node_cmp(n->name, key))
+			continue;
+
+		state = &qcom_cpuidle_driver.states[index];
+
+		key = "qcom,spm-cpu-mode";
+		ret = of_property_read_string(n, key, &val);
+		if (ret)
+			goto failed;
+		for (i = 0; i < ARRAY_SIZE(pm_sm_lookup); i++) {
+			if (!strcmp(val, pm_sm_lookup[i].name)) {
+				spm_sleep_modes[index] = pm_sm_lookup[i].mode;
+				break;
+			}
+		}
+		if (i == ARRAY_SIZE(pm_sm_lookup)) {
+			ret = -EFAULT;
+			goto failed;
+		}
+
+		strncpy(state->desc, val, CPUIDLE_DESC_LEN);
+
+		key = "qcom,state-name";
+		ret = of_property_read_string(n, key, &val);
+		if (ret)
+			goto failed;
+		strncpy(state->name, val, CPUIDLE_NAME_LEN);
+
+		key = "qcom,latency-us";
+		ret = of_property_read_u32(n, key, &state->exit_latency);
+		if (ret)
+			goto failed;
+
+		key = "qcom,residency-us";
+		ret = of_property_read_u32(n, key, &state->target_residency);
+		if (ret)
+			goto failed;
+
+		state->flags = CPUIDLE_FLAG_TIME_VALID;
+		state->enter = qcom_lpm_enter;
+		index++;
+	}
+
+	qcom_cpuidle_driver.state_count = index;
+	qcom_cpuidle_driver.safe_state_index = 0;
+
+	ret = cpuidle_register(&qcom_cpuidle_driver, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register cpuidle driver\n");
+		return ret;
+	}
+
+	return 0;
+
+failed:
+	dev_err(&pdev->dev, "error parsing key: %s\n", key);
+	return ret;
+}
+
+static struct of_device_id qcom_cpuidle_match_tbl[] = {
+	{.compatible = "qcom,cpuidle"},
+	{},
+};
+
+static struct platform_driver qcom_cpuidle_platform_driver = {
+	.probe	= qcom_cpuidle_probe,
+	.driver	= {
+		.name = "qcom,cpuidle",
+		.owner = THIS_MODULE,
+		.of_match_table = qcom_cpuidle_match_tbl,
+	},
+};
+
+module_platform_driver(qcom_cpuidle_platform_driver);
-- 
1.9.1

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

* [RFC] [PATCH 12/13] qcom: cpuidle: Add cpuidle device nodes for 8974 chipset
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (10 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 11/13] qcom: cpuidle: Add cpuidle driver for QCOM cpus Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  2014-08-08  4:05 ` [RFC] [PATCH 13/13] qcom: cpuidle: Config option to enable QCOM cpuidle driver Lina Iyer
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Add C-States and the respective residencies supported by the QCOM 8974
chipset. Current support is for WFI (clock gating) and Standlone-PC
(power down of the core).

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

diff --git a/arch/arm/boot/dts/qcom-msm8974-pm.dtsi b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
index 8fe00f1..4e3c206 100644
--- a/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974-pm.dtsi
@@ -122,4 +122,25 @@
 		qcom,cpu-alias-addr = <0x10000>;
 		qcom,sleep-status-mask= <0x80000>;
 	};
+
+	qcom,cpuidle {
+		compatible = "qcom,cpuidle";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		qcom,cpu-level@0 {
+			reg = <0x0>;
+			qcom,state-name = "C1";
+			qcom,spm-cpu-mode = "wfi";
+			qcom,latency-us = <1>;
+			qcom,residency-us = <1>;
+		};
+
+		qcom,cpu-level@2 {
+			reg = <0x1>;
+			qcom,state-name = "C2";
+			qcom,spm-cpu-mode = "standalone_pc";
+			qcom,latency-us = <300>;
+			qcom,residency-us = <2000>;
+		};
+	};
 };
-- 
1.9.1

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

* [RFC] [PATCH 13/13] qcom: cpuidle: Config option to enable QCOM cpuidle driver
  2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
                   ` (11 preceding siblings ...)
  2014-08-08  4:05 ` [RFC] [PATCH 12/13] qcom: cpuidle: Add cpuidle device nodes for 8974 chipset Lina Iyer
@ 2014-08-08  4:05 ` Lina Iyer
  12 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:05 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm
  Cc: msivasub, bryanh, Lina Iyer

Allow cpuidle framework to determine the C-State of the QCOM cpus.

Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 drivers/cpuidle/Kconfig.arm | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 38cff69..ad52605 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -62,3 +62,9 @@ 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
+	help
+	  Select this to enable cpuidle for QCOM processors
-- 
1.9.1

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

* Re: [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc
  2014-08-08  4:05 ` [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc Lina Iyer
@ 2014-08-08  7:42   ` Pramod Gurav
  2014-08-08 14:39     ` Lina Iyer
  0 siblings, 1 reply; 24+ messages in thread
From: Pramod Gurav @ 2014-08-08  7:42 UTC (permalink / raw)
  To: Lina Iyer
  Cc: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm, msivasub, bryanh

Hi Lina,

On Friday 08 August 2014 09:35 AM, Lina Iyer wrote:
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
>  arch/arm/mach-qcom/Makefile                         | 1 -
>  drivers/soc/qcom/Makefile                           | 2 +-
>  {arch/arm/mach-qcom => drivers/soc/qcom}/scm-boot.c | 4 ++--
>  {arch/arm/mach-qcom => include/soc/qcom}/scm-boot.h | 0
>  4 files changed, 3 insertions(+), 4 deletions(-)
>  rename {arch/arm/mach-qcom => drivers/soc/qcom}/scm-boot.c (97%)
>  rename {arch/arm/mach-qcom => include/soc/qcom}/scm-boot.h (100%)

There remain references to "scm-boot.h" in platsmp.c. Can we replace
them with "soc/qcom/scm-boot.h" in this same patch so that build does
not break with only applying this patch. I see these changes in "qcom:
platsmp: Enable deeper idle states for hotplug" hence build may not
break with all your patches. Correct if I am wrong.

Note: I am trying to apply these patches on top on rpm changes in
drivers/soc/qoc hence have to manual apply your patches. Also building
kernel after each patch and found the break.

> 
> diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
> index db41e8c..e324375 100644
> --- a/arch/arm/mach-qcom/Makefile
> +++ b/arch/arm/mach-qcom/Makefile
> @@ -1,3 +1,2 @@
>  obj-y			:= board.o
>  obj-$(CONFIG_SMP)	+= platsmp.o
> -obj-$(CONFIG_QCOM_SCM)	+= scm-boot.o
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index a39446d..70d52ed 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,3 @@
>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>  CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
> -obj-$(CONFIG_QCOM_SCM) += scm.o
> +obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
> diff --git a/arch/arm/mach-qcom/scm-boot.c b/drivers/soc/qcom/scm-boot.c
> similarity index 97%
> rename from arch/arm/mach-qcom/scm-boot.c
> rename to drivers/soc/qcom/scm-boot.c
> index 5add20e..60ff7b4 100644
> --- a/arch/arm/mach-qcom/scm-boot.c
> +++ b/drivers/soc/qcom/scm-boot.c
> @@ -17,9 +17,9 @@
>  
>  #include <linux/module.h>
>  #include <linux/slab.h>
> -#include <soc/qcom/scm.h>
>  
> -#include "scm-boot.h"
> +#include <soc/qcom/scm.h>
> +#include <soc/qcom/scm-boot.h>
>  
>  /*
>   * Set the cold/warm boot address for one of the CPU cores.
> diff --git a/arch/arm/mach-qcom/scm-boot.h b/include/soc/qcom/scm-boot.h
> similarity index 100%
> rename from arch/arm/mach-qcom/scm-boot.h
> rename to include/soc/qcom/scm-boot.h
> 

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

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08  4:05 ` [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets Lina Iyer
@ 2014-08-08  9:18   ` Pramod Gurav
  2014-08-08 10:07     ` Pramod Gurav
  2014-08-08 16:16   ` Kumar Gala
  1 sibling, 1 reply; 24+ messages in thread
From: Pramod Gurav @ 2014-08-08  9:18 UTC (permalink / raw)
  To: Lina Iyer
  Cc: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm, msivasub, bryanh, Praveen Chidamabram,
	Murali Nalajala

Hi Lina,

On Friday 08 August 2014 09:35 AM, Lina Iyer wrote:
> Qualcomm chipsets use an separate h/w block to control the logic around
> the processor cores (cpu and L2). The SPM h/w block regulates power to
> the cores and controls the power when the core enter low power modes.
> 
> Each core has its own instance of SPM. The SPM has the following key
> functions
> 	- Configure the h/w dependencies when entering low power modes
> 	- Wait for interrupt and wake up on interrupt
> 	- Ensure the dependencies are ready before bringing the core out
> 	  of sleep
> 	- Regulating voltage to the core, interfacing with the PMIC.
> 	- Optimize power based on runtime recommendations.
> 
> The driver identifies and configures the SPMs, by reading the nodes and
> the register values from the devicetree. The SPMs need to be configured
> to allow the processor to be idled in a low power state.
 22 30 0f];

<snip>

> +	};
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 70d52ed..3de7ed9 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,5 @@
>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
> +obj-$(CONFIG_QCOM_PM) +=	spm_devices.o spm.o

Are we missing a Kconfig entry for CONFIG_QCOM_PM? This won't compile.
> +
>  CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
>  obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o

<snip>

> +#endif  /* defined (CONFIG_QCOM_PM) */
> +#endif  /* __MSM_SPM_H */
> 

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

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08  9:18   ` Pramod Gurav
@ 2014-08-08 10:07     ` Pramod Gurav
  2014-08-08 14:31       ` Lina Iyer
  0 siblings, 1 reply; 24+ messages in thread
From: Pramod Gurav @ 2014-08-08 10:07 UTC (permalink / raw)
  To: Lina Iyer
  Cc: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm, msivasub, bryanh, Praveen Chidamabram,
	Murali Nalajala

Hello Lina,

Sorry for spaming, Should have applied all of them.

On Friday 08 August 2014 02:48 PM, Pramod Gurav wrote:
> Hi Lina,
> 
> On Friday 08 August 2014 09:35 AM, Lina Iyer wrote:
>> Qualcomm chipsets use an separate h/w block to control the logic around
>> the processor cores (cpu and L2). The SPM h/w block regulates power to
>> the cores and controls the power when the core enter low power modes.
>>
>> Each core has its own instance of SPM. The SPM has the following key
>> functions
>> 	- Configure the h/w dependencies when entering low power modes
>> 	- Wait for interrupt and wake up on interrupt
>> 	- Ensure the dependencies are ready before bringing the core out
>> 	  of sleep
>> 	- Regulating voltage to the core, interfacing with the PMIC.
>> 	- Optimize power based on runtime recommendations.
>>
>> The driver identifies and configures the SPMs, by reading the nodes and
>> the register values from the devicetree. The SPMs need to be configured
>> to allow the processor to be idled in a low power state.
>  22 30 0f];
> 
> <snip>
> 
>> +	};
>> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
>> index 70d52ed..3de7ed9 100644
>> --- a/drivers/soc/qcom/Makefile
>> +++ b/drivers/soc/qcom/Makefile
>> @@ -1,3 +1,5 @@
>>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>> +obj-$(CONFIG_QCOM_PM) +=	spm_devices.o spm.o
> 
> Are we missing a Kconfig entry for CONFIG_QCOM_PM? This won't compile.
>> +
>>  CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
>>  obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
> 
> <snip>
> 
>> +#endif  /* defined (CONFIG_QCOM_PM) */
>> +#endif  /* __MSM_SPM_H */
>>
> --
> 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
> 

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

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08 10:07     ` Pramod Gurav
@ 2014-08-08 14:31       ` Lina Iyer
  0 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08 14:31 UTC (permalink / raw)
  To: Pramod Gurav
  Cc: Lina Iyer, daniel.lezcano, khilman, amit.kucheria, sboyd, davidb,
	galak, linux-arm-msm, msivasub, bryanh, Praveen Chidamabram,
	Murali Nalajala




On Fri, 8 Aug 2014, Pramod Gurav wrote:

> Hello Lina,
> 
> Sorry for spaming, Should have applied all of them.
Glad it worked out.

Lina

 

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

* Re: [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc
  2014-08-08  7:42   ` Pramod Gurav
@ 2014-08-08 14:39     ` Lina Iyer
  0 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08 14:39 UTC (permalink / raw)
  To: Pramod Gurav
  Cc: Lina Iyer, daniel.lezcano, khilman, amit.kucheria, sboyd, davidb,
	galak, linux-arm-msm, msivasub, bryanh




On Fri, 8 Aug 2014, Pramod Gurav wrote:

> Hi Lina,
> 
> There remain references to "scm-boot.h" in platsmp.c. Can we replace
> them with "soc/qcom/scm-boot.h" in this same patch so that build does
> not break with only applying this patch. I see these changes in "qcom:
> platsmp: Enable deeper idle states for hotplug" hence build may not
> break with all your patches. Correct if I am wrong.
> 
> Note: I am trying to apply these patches on top on rpm changes in
> drivers/soc/qoc hence have to manual apply your patches. Also building
> kernel after each patch and found the break.
Thanks, will take care of it in the next set.

Lina

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

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08  4:05 ` [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets Lina Iyer
  2014-08-08  9:18   ` Pramod Gurav
@ 2014-08-08 16:16   ` Kumar Gala
  2014-08-08 21:53     ` Lina Iyer
  1 sibling, 1 reply; 24+ messages in thread
From: Kumar Gala @ 2014-08-08 16:16 UTC (permalink / raw)
  To: Lina Iyer
  Cc: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb,
	linux-arm-msm, msivasub, bryanh, Praveen Chidamabram,
	Murali Nalajala


On Aug 7, 2014, at 11:05 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> Qualcomm chipsets use an separate h/w block to control the logic around
> the processor cores (cpu and L2). The SPM h/w block regulates power to
> the cores and controls the power when the core enter low power modes.
> 
> Each core has its own instance of SPM. The SPM has the following key
> functions
> 	- Configure the h/w dependencies when entering low power modes
> 	- Wait for interrupt and wake up on interrupt
> 	- Ensure the dependencies are ready before bringing the core out
> 	  of sleep
> 	- Regulating voltage to the core, interfacing with the PMIC.
> 	- Optimize power based on runtime recommendations.
> 
> The driver identifies and configures the SPMs, by reading the nodes and
> the register values from the devicetree. The SPMs need to be configured
> to allow the processor to be idled in a low power state.
> 
> Signed-off-by: Praveen Chidamabram <pchidamb@codeaurora.org>
> Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
> .../devicetree/bindings/arm/msm/spm-v2.txt         | 104 +++
> drivers/soc/qcom/Makefile                          |   2 +
> drivers/soc/qcom/spm.c                             | 559 ++++++++++++++++
> drivers/soc/qcom/spm_devices.c                     | 709 +++++++++++++++++++++
> drivers/soc/qcom/spm_driver.h                      | 118 ++++
> include/soc/qcom/spm.h                             | 106 +++
> 6 files changed, 1598 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/spm-v2.txt
> create mode 100644 drivers/soc/qcom/spm.c
> create mode 100644 drivers/soc/qcom/spm_devices.c
> create mode 100644 drivers/soc/qcom/spm_driver.h
> create mode 100644 include/soc/qcom/spm.h
> 
> diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
> new file mode 100644
> index 0000000..ea0fc7a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
> @@ -0,0 +1,104 @@
> +* MSM Subsystem Power Manager (spm-v2)
> +
> +S4 generation of MSMs 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 SAW hardware block handles SPM and
> +AVS functionality for the cores.
> +
> +The devicetree representation of the SPM block should be:
> +
> +Required properties
> +
> +- compatible: "qcom,spm-v2"
> +- 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. On targets
> +	that dont support CPU phandles the driver would support qcom,core-id.
> +	This field is required on only for SPMs that control the CPU.
> +- qcom, core-id: This property will be deprecated once all targets start
> +	supporting CPU phandles. This field will be used to identify SPMs
> +	that control the CPU.
> +	{0..n} for cores {0..n}

Why aren’t we just using cpu phandles for upstream?

> +- qcom,saw2-ver-reg: The location of the version register

I

> +- qcom,saw2-cfg: SAW2 configuration register

What does this even mean, why is this not part of the reg property?

> +- qcom,saw2-avs-ctl: The AVS control register

What does this even mean, why is this not part of the reg property?

> +- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
> +	controller requests

Why do we need this?

> +- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
> +	sequence

how many values?

> +- qcom,saw2-spm-ctl: The SPM control register

What does this mean?  Is it the offset of the register, and if so from what base?  Also, why does this vary, this should possibly be handled by different compatible values.

> +- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change

us -> microseconds

> +	after sending the voltage command to the PMIC

Have we really ever set this to any value other than 50 usec?

> +- qcom,name: The name with which a SPM device is identified by the power
> +management code.

what does this even mean?

> +
> +Optional properties
> +
> +- qcom,saw2-avs-limit: The AVS limit register

same comments above (as qcom,saw2-spm-ctl)

> +- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
> +	between AVS controller requests

is this a delay value, list of values, a register offset? not clear.

> +- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
> +	index to send the PMIC data to

what is FTS?

> +- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
> +	voltage
> +- qcom,phase-port: The PVC port used for changing the number of phases
> +- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
> +- qcom,saw2-spm-cmd-ret: The Retention command sequence
> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
> +- qcom,saw2-spm-cmd-pc-no-rpm: The Power Collapse command sequence where APPS
> +	proc won't inform the RPM.
> +- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
> +- qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence

GDHS?

> +- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
> +	Depricated: Replaced with cpu-vctl-list when cpu phandles are available.

if deprecated, remove it.

> +- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
> +	can control.
> +
> +Example 1:
> +	qcom,spm@f9089000 {
> +		compatible = "qcom,spm-v2";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		reg = <0xf9089000 0x1000>;
> +		qcom,core-id = <0>;
> +		qcom,saw2-ver-reg = <0xfd0>;
> +		qcom,saw2-cfg = <0x1b>;
> +		qcom,saw2-avs-ctl = <0>;
> +		qcom,saw2-avs-hysteresis = <0>;
> +		qcom,saw2-avs-limit = <0>;
> +		qcom,saw2-avs-dly= <0>;
> +		qcom,saw2-spm-dly= <0x20000400>;
> +		qcom,saw2-spm-ctl = <0x1>;
> +		qcom,cpu-vctl-mask = <0xf>;
> +		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];
> +		qcom,saw2-spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92
> +				a0 b0 82 10 30 02 22 30 0f];
> +	};
> +
> +Example 2:
> +	qcom,spm@f9089000 {
> +		compatible = "qcom,spm-v2";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		reg = <0xf9089000 0x1000>;
> +		qcom,core-id = <0>;
> +		qcom,saw2-ver-reg = <0xfd0>;
> +		qcom,saw2-cfg = <0x1b>;
> +		qcom,saw2-avs-ctl = <0>;
> +		qcom,saw2-avs-hysteresis = <0>;
> +		qcom,saw2-avs-limit = <0>;
> +		qcom,saw2-avs-dly= <0>;
> +		qcom,saw2-spm-dly= <0x20000400>;
> +		qcom,saw2-spm-ctl = <0x1>;
> +		qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
> +		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];
> +		qcom,saw2-spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92
> +				a0 b0 82 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] 24+ messages in thread

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08 16:16   ` Kumar Gala
@ 2014-08-08 21:53     ` Lina Iyer
  2014-08-11 19:54       ` Kumar Gala
  0 siblings, 1 reply; 24+ messages in thread
From: Lina Iyer @ 2014-08-08 21:53 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Daniel Lezcano, Kevin Hilman, Amit Kucheria, Stephen Boyd,
	David Brown, linux-arm-msm, Mahesh Sivasubramanian,
	Bryan Huntsman, Praveen Chidamabram, Murali Nalajala

On Fri, Aug 08, 2014 at 11:16:04AM -0500, Kumar Gala wrote:
>
> On Aug 7, 2014, at 11:05 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>
  > +Required properties
> > +
> > +- compatible: "qcom,spm-v2"
> > +- 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. On targets
> > + that dont support CPU phandles the driver would support qcom,core-id.
> > + This field is required on only for SPMs that control the CPU.
> > +- qcom, core-id: This property will be deprecated once all targets start
> > + supporting CPU phandles. This field will be used to identify SPMs
> > + that control the CPU.
> > + {0..n} for cores {0..n}
>
> Why aren’t we just using cpu phandles for upstream?
Need to specify L2 index as well. I dont know, but does cpu handle
define that?
>
> > +- qcom,saw2-ver-reg: The location of the version register
>
> I
>
> > +- qcom,saw2-cfg: SAW2 configuration register
>
> What does this even mean, why is this not part of the reg property?
This is a hardware register with that name. Using the same name as the 
h/w for consistency.

>
> > +- qcom,saw2-avs-ctl: The AVS control register
>
> What does this even mean, why is this not part of the reg property?
Same.

>
> > +- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
> > + controller requests
>
> Why do we need this?
Not all registers initialize to 0 on power on. So, its better to
initialize it. We dont use AVS currently in the chipset.
>
> > +- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
> > + sequence
>
> how many values?
HW configuration value.
>
> > +- qcom,saw2-spm-ctl: The SPM control register
>
> What does this mean?  Is it the offset of the register, and if so from what base?  Also, why does this vary, this should possibly be handled by different compatible values.
The register controls the SPM. 0x1 means its ON.
>
> > +- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
>
> us -> microseconds
>
> > + after sending the voltage command to the PMIC
>
> Have we really ever set this to any value other than 50 usec?
>
> > +- qcom,name: The name with which a SPM device is identified by the power
> > +management code.
>
> what does this even mean?
Power/idle drivers, that I am trying to split and get at, would use this
reference to match and configure the correct SPM.
>
> > +
> > +Optional properties
> > +
> > +- qcom,saw2-avs-limit: The AVS limit register
>
> same comments above (as qcom,saw2-spm-ctl)
>
> > +- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
> > + between AVS controller requests
>
> is this a delay value, list of values, a register offset? not clear.
>
> > +- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
> > + index to send the PMIC data to
>
> what is FTS?
Fast Transient Switch of the PMIC regulator

>
> > +- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
> > + voltage
> > +- qcom,phase-port: The PVC port used for changing the number of phases
> > +- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
> > +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
> > +- qcom,saw2-spm-cmd-ret: The Retention command sequence
> > +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
> > +- qcom,saw2-spm-cmd-pc-no-rpm: The Power Collapse command sequence where APPS
> > + proc won't inform the RPM.
> > +- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
> > +- qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence
>
> GDHS?
Globally Distributed Head Switch. Even though it doesnt match the state 
correctly, it has been a convention (internally) to use GDHS
to indicate that the L2 memory would be retained while the control logic
would be powered down as opposed to retention where both the memory and 
the control logic would be on.
>
> > +- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
> > + Depricated: Replaced with cpu-vctl-list when cpu phandles are available.
>
> if deprecated, remove it.
Will evaluate the need of this for 8064 and remove this.

- Lina 

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

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08 21:53     ` Lina Iyer
@ 2014-08-11 19:54       ` Kumar Gala
  2014-08-11 20:40         ` Lina Iyer
  0 siblings, 1 reply; 24+ messages in thread
From: Kumar Gala @ 2014-08-11 19:54 UTC (permalink / raw)
  To: Lina Iyer
  Cc: Daniel Lezcano, Kevin Hilman, Amit Kucheria, Stephen Boyd,
	David Brown, linux-arm-msm, Mahesh Sivasubramanian,
	Bryan Huntsman, Praveen Chidamabram, Murali Nalajala


On Aug 8, 2014, at 4:53 PM, Lina Iyer <lina.iyer@linaro.org> wrote:

> On Fri, Aug 08, 2014 at 11:16:04AM -0500, Kumar Gala wrote:
>> 
>> On Aug 7, 2014, at 11:05 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>> 
>> +Required properties
>>> +
>>> +- compatible: "qcom,spm-v2"
>>> +- 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. On targets
>>> + that dont support CPU phandles the driver would support qcom,core-id.
>>> + This field is required on only for SPMs that control the CPU.
>>> +- qcom, core-id: This property will be deprecated once all targets start
>>> + supporting CPU phandles. This field will be used to identify SPMs
>>> + that control the CPU.
>>> + {0..n} for cores {0..n}
>> 
>> Why aren’t we just using cpu phandles for upstream?
> Need to specify L2 index as well. I dont know, but does cpu handle
> define that?

If you have the cpu-phandle, you should be able to get the L2 cache via the 'next-level-cache’ property, is that not sufficient?

>> +- qcom,saw2-ver-reg: The location of the version register
>> 
>> I
>> 
>>> +- qcom,saw2-cfg: SAW2 configuration register
>> 
>> What does this even mean, why is this not part of the reg property?
> This is a hardware register with that name. Using the same name as the 
> h/w for consistency.

Ok, we do we need to specify these in the device tree?  Are these properties the register offsets or the values to program into the register?

> +- qcom,saw2-avs-ctl: The AVS control register
>> 
>> What does this even mean, why is this not part of the reg property?
> Same.

Same question as above

> +- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
>>> + controller requests
>> 
>> Why do we need this?
> Not all registers initialize to 0 on power on. So, its better to
> initialize it. We dont use AVS currently in the chipset.

So, one there is typo in the name compares to the property.  Again, trying to understand if this is the value to init the register to or the offset.

>> 
>>> +- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
>>> + sequence
>> 
>> how many values?
> HW configuration value.

Huh?  Are you saying the # of values varies?

>> 
>>> +- qcom,saw2-spm-ctl: The SPM control register
>> 
>> What does this mean?  Is it the offset of the register, and if so from what base?  Also, why does this vary, this should possibly be handled by different compatible values.
> The register controls the SPM. 0x1 means its ON.

Can you be more descriptive.  Is this the value to set the saw2-spm-ctl register to?

>> 
>>> +- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
>> 
>> us -> microseconds
>> 
>>> + after sending the voltage command to the PMIC
>> 
>> Have we really ever set this to any value other than 50 usec?
>> 
>>> +- qcom,name: The name with which a SPM device is identified by the power
>>> +management code.
>> 
>> what does this even mean?
> Power/idle drivers, that I am trying to split and get at, would use this
> reference to match and configure the correct SPM.

Hmm, I’m not sure how to handle this right now.  It seems a little chicken and egg case, since we don’t have support for qcom,name.

>> +
>>> +Optional properties
>>> +
>>> +- qcom,saw2-avs-limit: The AVS limit register
>> 
>> same comments above (as qcom,saw2-spm-ctl)
>> 
>>> +- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
>>> + between AVS controller requests
>> 
>> is this a delay value, list of values, a register offset? not clear.
>> 
>>> +- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
>>> + index to send the PMIC data to
>> 
>> what is FTS?
> Fast Transient Switch of the PMIC regulator

In general with acronyms, please do something like FTS (Fast Transient Switch) for the first reference.

> +- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
>>> + voltage
>>> +- qcom,phase-port: The PVC port used for changing the number of phases
>>> +- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
>>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>>> +- qcom,saw2-spm-cmd-ret: The Retention command sequence
>>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>>> +- qcom,saw2-spm-cmd-pc-no-rpm: The Power Collapse command sequence where APPS
>>> + proc won't inform the RPM.
>>> +- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
>>> +- qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence
>> 
>> GDHS?
> Globally Distributed Head Switch. Even though it doesnt match the state 
> correctly, it has been a convention (internally) to use GDHS
> to indicate that the L2 memory would be retained while the control logic
> would be powered down as opposed to retention where both the memory and 
> the control logic would be on.

Some comment about acronyms

>> 
>>> +- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
>>> + Depricated: Replaced with cpu-vctl-list when cpu phandles are available.
>> 
>> if deprecated, remove it.
> Will evaluate the need of this for 8064 and remove this.

We should fixup 8064 if need by to not require this if that is feasible.

- 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] 24+ messages in thread

* Re: [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-11 19:54       ` Kumar Gala
@ 2014-08-11 20:40         ` Lina Iyer
  0 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-11 20:40 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Daniel Lezcano, Kevin Hilman, Amit Kucheria, Stephen Boyd,
	David Brown, linux-arm-msm, Mahesh Sivasubramanian,
	Bryan Huntsman, Praveen Chidamabram, Murali Nalajala

On Mon, Aug 11, 2014 at 02:54:35PM -0500, Kumar Gala wrote:
>On Aug 8, 2014, at 4:53 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>> On Fri, Aug 08, 2014 at 11:16:04AM -0500, Kumar Gala wrote:
>>> On Aug 7, 2014, at 11:05 PM, Lina Iyer <lina.iyer@linaro.org> wrote:
>>>
>>> Why aren’t we just using cpu phandles for upstream?
>> Need to specify L2 index as well. I dont know, but does cpu handle
>> define that?
>
>If you have the cpu-phandle, you should be able to get the L2 cache via the 'next-level-cache’ property, is that not sufficient?
It may work here, but may not scale to cluster based targets, where the index
of the SPM does not scale well to a device that is not a cache. but
another h/w controller.
>
>>> +- qcom,saw2-ver-reg: The location of the version register

>>>> +- qcom,saw2-cfg: SAW2 configuration register
>>>
>>> What does this even mean, why is this not part of the reg property?
>> This is a hardware register with that name. Using the same name as the
>> h/w for consistency.
>
>Ok, we do we need to specify these in the device tree?  Are these properties the register offsets or the values to program into the register?
>
>> +- qcom,saw2-avs-ctl: The AVS control register
>>>
>>> What does this even mean, why is this not part of the reg property?
>> Same.
>
>Same question as above
>
>> +- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
>>>> + controller requests
>>>
>>> Why do we need this?
>> Not all registers initialize to 0 on power on. So, its better to
>> initialize it. We dont use AVS currently in the chipset.
>
>So, one there is typo in the name compares to the property.  Again, trying to understand if this is the value to init the register to or the offset.
Its is a register initialization value. Its not an offset. Only the
version register is an offset.

>
>>>
>>>> +- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
>>>> + sequence
>>>
>>> how many values?
>> HW configuration value.
>
>Huh?  Are you saying the # of values varies?
Its a single value. Will fix the typo.

>
>>>
>>>> +- qcom,saw2-spm-ctl: The SPM control register
>>>
>>> What does this mean?  Is it the offset of the register, and if so from what base?  Also, why does this vary, this should possibly be handled by different compatible values.
>> The register controls the SPM. 0x1 means its ON.
>
>Can you be more descriptive.  Is this the value to set the saw2-spm-ctl register to?
Yes, its the value we set the SPM hardware to enable it.
>
>>>
>>>> +- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
>>>
>>> us -> microseconds
>>>
>>>> + after sending the voltage command to the PMIC
>>>
>>> Have we really ever set this to any value other than 50 usec?
We probably havent, but it helps to play around with this value when
debugging issues.

>>>
>>>> +- qcom,name: The name with which a SPM device is identified by the power
>>>> +management code.
>>>
>>> what does this even mean?
>> Power/idle drivers, that I am trying to split and get at, would use this
>> reference to match and configure the correct SPM.
>
>Hmm, I’m not sure how to handle this right now.  It seems a little chicken and egg case, since we don’t have support for qcom,name.


>
>>> +
>>>> +Optional properties
>>>> +
>>>> +- qcom,saw2-avs-limit: The AVS limit register
>>>
>>> same comments above (as qcom,saw2-spm-ctl)
>>>
>>>> +- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
>>>> + between AVS controller requests
>>>
>>> is this a delay value, list of values, a register offset? not clear.
>>>
>>>> +- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
>>>> + index to send the PMIC data to
>>>
>>> what is FTS?
>> Fast Transient Switch of the PMIC regulator
>
>In general with acronyms, please do something like FTS (Fast Transient Switch) for the first reference.
Will fix it.
>
>> +- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
>>>> + voltage
>>>> +- qcom,phase-port: The PVC port used for changing the number of phases
>>>> +- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
>>>> +- qcom,saw2-spm-cmd-wfi: The WFI command sequence
>>>> +- qcom,saw2-spm-cmd-ret: The Retention command sequence
>>>> +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
>>>> +- qcom,saw2-spm-cmd-pc-no-rpm: The Power Collapse command sequence where APPS
>>>> + proc won't inform the RPM.
>>>> +- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
>>>> +- qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence
>>>
>>> GDHS?
>> Globally Distributed Head Switch. Even though it doesnt match the state
>> correctly, it has been a convention (internally) to use GDHS
>> to indicate that the L2 memory would be retained while the control logic
>> would be powered down as opposed to retention where both the memory and
>> the control logic would be on.
>
>Some comment about acronyms
>
>>>
>>>> +- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
>>>> + Depricated: Replaced with cpu-vctl-list when cpu phandles are available.
>>>
>>> if deprecated, remove it.
>> Will evaluate the need of this for 8064 and remove this.
>
>We should fixup 8064 if need by to not require this if that is feasible.
Okay.

>
>- 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] 24+ messages in thread

* [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets
  2014-08-08  4:00 [RFC] [PATCH 00/13] QCOM: 8087 CPUIDLE driver Lina Iyer
@ 2014-08-08  4:00 ` Lina Iyer
  0 siblings, 0 replies; 24+ messages in thread
From: Lina Iyer @ 2014-08-08  4:00 UTC (permalink / raw)
  To: daniel.lezcano, khilman, amit.kucheria, sboyd, davidb, galak,
	linux-arm-msm, linux-arm-kernel
  Cc: msivasub, bryanh, Lina Iyer, Praveen Chidamabram, Murali Nalajala

Qualcomm chipsets use an separate h/w block to control the logic around
the processor cores (cpu and L2). The SPM h/w block regulates power to
the cores and controls the power when the core enter low power modes.

Each core has its own instance of SPM. The SPM has the following key
functions
	- Configure the h/w dependencies when entering low power modes
	- Wait for interrupt and wake up on interrupt
	- Ensure the dependencies are ready before bringing the core out
	  of sleep
	- Regulating voltage to the core, interfacing with the PMIC.
	- Optimize power based on runtime recommendations.

The driver identifies and configures the SPMs, by reading the nodes and
the register values from the devicetree. The SPMs need to be configured
to allow the processor to be idled in a low power state.

Signed-off-by: Praveen Chidamabram <pchidamb@codeaurora.org>
Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
---
 .../devicetree/bindings/arm/msm/spm-v2.txt         | 104 +++
 drivers/soc/qcom/Makefile                          |   2 +
 drivers/soc/qcom/spm.c                             | 559 ++++++++++++++++
 drivers/soc/qcom/spm_devices.c                     | 709 +++++++++++++++++++++
 drivers/soc/qcom/spm_driver.h                      | 118 ++++
 include/soc/qcom/spm.h                             | 106 +++
 6 files changed, 1598 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/spm-v2.txt
 create mode 100644 drivers/soc/qcom/spm.c
 create mode 100644 drivers/soc/qcom/spm_devices.c
 create mode 100644 drivers/soc/qcom/spm_driver.h
 create mode 100644 include/soc/qcom/spm.h

diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
new file mode 100644
index 0000000..ea0fc7a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -0,0 +1,104 @@
+* MSM Subsystem Power Manager (spm-v2)
+
+S4 generation of MSMs 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 SAW hardware block handles SPM and
+AVS functionality for the cores.
+
+The devicetree representation of the SPM block should be:
+
+Required properties
+
+- compatible: "qcom,spm-v2"
+- 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. On targets
+	that dont support CPU phandles the driver would support qcom,core-id.
+	This field is required on only for SPMs that control the CPU.
+- qcom, core-id: This property will be deprecated once all targets start
+	supporting CPU phandles. This field will be used to identify SPMs
+	that control the CPU.
+	{0..n} for cores {0..n}
+- qcom,saw2-ver-reg: The location of the version register
+- qcom,saw2-cfg: SAW2 configuration register
+- qcom,saw2-avs-ctl: The AVS control register
+- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS
+	controller requests
+- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM
+	sequence
+- qcom,saw2-spm-ctl: The SPM control register
+- qcom,vctl-timeout-us: The timeout value in us to wait for voltage to change
+	after sending the voltage command to the PMIC
+- qcom,name: The name with which a SPM device is identified by the power
+management code.
+
+Optional properties
+
+- qcom,saw2-avs-limit: The AVS limit register
+- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values
+	between AVS controller requests
+- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
+	index to send the PMIC data to
+- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+	voltage
+- qcom,phase-port: The PVC port used for changing the number of phases
+- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
+- qcom,saw2-spm-cmd-wfi: The WFI command sequence
+- qcom,saw2-spm-cmd-ret: The Retention command sequence
+- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
+- qcom,saw2-spm-cmd-pc-no-rpm: The Power Collapse command sequence where APPS
+	proc won't inform the RPM.
+- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
+- qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence
+- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
+	Depricated: Replaced with cpu-vctl-list when cpu phandles are available.
+- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
+	can control.
+
+Example 1:
+	qcom,spm@f9089000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,core-id = <0>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x1b>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x20000400>;
+		qcom,saw2-spm-ctl = <0x1>;
+		qcom,cpu-vctl-mask = <0xf>;
+		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];
+		qcom,saw2-spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92
+				a0 b0 82 10 30 02 22 30 0f];
+	};
+
+Example 2:
+	qcom,spm@f9089000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9089000 0x1000>;
+		qcom,core-id = <0>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x1b>;
+		qcom,saw2-avs-ctl = <0>;
+		qcom,saw2-avs-hysteresis = <0>;
+		qcom,saw2-avs-limit = <0>;
+		qcom,saw2-avs-dly= <0>;
+		qcom,saw2-spm-dly= <0x20000400>;
+		qcom,saw2-spm-ctl = <0x1>;
+		qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
+		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];
+		qcom,saw2-spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92
+				a0 b0 82 10 30 02 22 30 0f];
+	};
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 70d52ed..3de7ed9 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,3 +1,5 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
+obj-$(CONFIG_QCOM_PM) +=	spm_devices.o 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..984aca6
--- /dev/null
+++ b/drivers/soc/qcom/spm.c
@@ -0,0 +1,559 @@
+/* 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 "spm_driver.h"
+
+#define MSM_SPM_PMIC_STATE_IDLE  0
+
+enum {
+	MSM_SPM_DEBUG_SHADOW = 1U << 0,
+	MSM_SPM_DEBUG_VCTL = 1U << 1,
+};
+
+static int msm_spm_debug_mask;
+module_param_named(
+	debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+struct saw2_data {
+	const char *ver_name;
+	uint32_t major;
+	uint32_t minor;
+	uint32_t *spm_reg_offset_ptr;
+};
+
+static uint32_t msm_spm_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,
+};
+
+static uint32_t msm_spm_reg_offsets_saw2_v3_0[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_STS2]			= 0x38,
+	[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]		= 0x400,
+	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
+};
+
+static struct saw2_data saw2_info[] = {
+	[0] = {
+		"SAW2_v2.1",
+		0x2,
+		0x1,
+		msm_spm_reg_offsets_saw2_v2_1,
+	},
+	[1] = {
+		"SAW2_v3.0",
+		0x3,
+		0x0,
+		msm_spm_reg_offsets_saw2_v3_0,
+	},
+};
+
+static uint32_t num_pmic_data;
+
+static inline uint32_t msm_spm_drv_get_num_spm_entry(
+		struct msm_spm_driver_data *dev)
+{
+	return 32;
+}
+
+static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
+		unsigned int reg_index)
+{
+	__raw_writel(dev->reg_shadow[reg_index],
+		dev->reg_base_addr + dev->reg_offsets[reg_index]);
+}
+
+static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
+		unsigned int reg_index)
+{
+	dev->reg_shadow[reg_index] =
+		__raw_readl(dev->reg_base_addr +
+				dev->reg_offsets[reg_index]);
+}
+
+static inline void msm_spm_drv_set_start_addr(
+		struct msm_spm_driver_data *dev, uint32_t addr, bool pc_mode)
+{
+	addr &= 0x7F;
+	addr <<= 4;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr;
+
+	if (dev->major != 0x3)
+		return;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFEFFFF;
+	if (pc_mode)
+		dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= 0x00010000;
+}
+
+static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID);
+	return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1;
+}
+
+static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
+		uint32_t vlevel)
+{
+	unsigned int pmic_data = 0;
+
+	/**
+	 * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
+	 * Ensure that vctl_port is always set to 0.
+	 */
+	WARN_ON(dev->vctl_port);
+
+	pmic_data |= vlevel;
+	pmic_data |= (dev->vctl_port & 0x7) << 16;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] &= ~0x700FF;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] |= pmic_data;
+
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_3);
+}
+
+static inline uint32_t msm_spm_drv_get_num_pmic_data(
+		struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID);
+	mb();
+	return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 4) & 0x7;
+}
+
+static inline uint32_t msm_spm_drv_get_sts_pmic_state(
+		struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
+	return (dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] >> 16) &
+				0x03;
+}
+
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+		struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
+	return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] & 0xFF;
+}
+
+static inline void msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev,
+		uint32_t *major, uint32_t *minor)
+{
+	uint32_t val = 0;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION] =
+			__raw_readl(dev->reg_base_addr + dev->ver_reg);
+
+	val = dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION];
+
+	*major = (val >> 28) & 0xF;
+	*minor = (val >> 16) & 0xFFF;
+}
+
+inline int msm_spm_drv_set_spm_enable(
+		struct msm_spm_driver_data *dev, bool enable)
+{
+	uint32_t value = enable ? 0x01 : 0x00;
+
+	if (!dev)
+		return -EINVAL;
+
+	if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) {
+
+		dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1;
+		dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value;
+
+		msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
+		wmb();
+	}
+	return 0;
+}
+void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
+{
+	int i;
+	int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
+
+	if (!dev) {
+		__WARN();
+		return;
+	}
+
+	for (i = 0; i < num_spm_entry; i++) {
+		__raw_writel(dev->reg_seq_entry_shadow[i],
+			dev->reg_base_addr
+			+ dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY]
+			+ 4 * i);
+	}
+	mb();
+}
+
+void dump_regs(struct msm_spm_driver_data *dev, int cpu)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_STS);
+	mb();
+	pr_err("CPU%d: spm register MSM_SPM_REG_SAW2_SPM_STS: 0x%x\n", cpu,
+			dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_STS]);
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
+	mb();
+	pr_err("CPU%d: spm register MSM_SPM_REG_SAW2_SPM_CTL: 0x%x\n", cpu,
+			dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL]);
+}
+
+int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
+		uint8_t *cmd, uint32_t *offset)
+{
+	uint32_t cmd_w;
+	uint32_t offset_w = *offset / 4;
+	uint8_t last_cmd;
+
+	if (!cmd)
+		return -EINVAL;
+
+	while (1) {
+		int i;
+
+		cmd_w = 0;
+		last_cmd = 0;
+		cmd_w = dev->reg_seq_entry_shadow[offset_w];
+
+		for (i = (*offset % 4); i < 4; i++) {
+			last_cmd = *(cmd++);
+			cmd_w |=  last_cmd << (i * 8);
+			(*offset)++;
+			if (last_cmd == 0x0f)
+				break;
+		}
+
+		dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
+		if (last_cmd == 0x0f)
+			break;
+	}
+
+	return 0;
+}
+
+int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
+		uint32_t addr, bool pc_mode)
+{
+
+	/* SPM is configured to reset start address to zero after end of Program
+	 */
+	if (!dev)
+		return -EINVAL;
+
+	msm_spm_drv_set_start_addr(dev, addr, pc_mode);
+
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
+	wmb();
+
+	if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
+		int i;
+
+		for (i = 0; i < MSM_SPM_REG_NR; i++)
+			pr_info("%s: reg %02x = 0x%08x\n", __func__,
+				dev->reg_offsets[i], dev->reg_shadow[i]);
+	}
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_STS);
+
+	return 0;
+}
+
+#ifdef CONFIG_MSM_AVS_HW
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+	return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(0);
+}
+
+static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~BIT(27);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+}
+
+static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev)
+{
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= BIT(27);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+}
+
+static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
+		unsigned int vlevel)
+{
+	vlevel &= 0x3f;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~0x7efc00;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= ((vlevel - 4) << 10);
+	dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= (vlevel << 17);
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+}
+
+#else
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+	return false;
+}
+
+static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
+
+static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev) { }
+
+static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
+		unsigned int vlevel) { }
+#endif
+
+int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
+{
+	uint32_t timeout_us, new_level;
+	bool avs_enabled;
+
+	if (!dev)
+		return -EINVAL;
+
+	avs_enabled  = msm_spm_drv_is_avs_enabled(dev);
+
+	if (!msm_spm_pmic_arb_present(dev))
+		return -ENOSYS;
+
+	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
+		pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
+
+	if (avs_enabled)
+		msm_spm_drv_disable_avs(dev);
+
+	/* Kick the state machine back to idle */
+	dev->reg_shadow[MSM_SPM_REG_SAW2_RST] = 1;
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_RST);
+
+	msm_spm_drv_set_vctl2(dev, vlevel);
+
+	timeout_us = dev->vctl_timeout_us;
+	/* Confirm the voltage we set was what hardware sent */
+	do {
+		new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
+		if (new_level == vlevel)
+			break;
+		udelay(1);
+	} while (--timeout_us);
+	if (!timeout_us) {
+		pr_info("Wrong level %#x\n", new_level);
+		goto set_vdd_bail;
+	}
+
+	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
+		pr_info("%s: done, remaining timeout %u us\n",
+			__func__, timeout_us);
+
+	/* Set AVS min/max */
+	if (avs_enabled) {
+		msm_spm_drv_set_avs_vlevel(dev, vlevel);
+		msm_spm_drv_enable_avs(dev);
+	}
+
+	return 0;
+
+set_vdd_bail:
+	if (avs_enabled)
+		msm_spm_drv_enable_avs(dev);
+
+	pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
+		__func__, vlevel, timeout_us, new_level);
+	return -EIO;
+}
+
+static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port)
+{
+	int index = -1;
+
+	switch (port) {
+	case MSM_SPM_PMIC_VCTL_PORT:
+		index = dev->vctl_port;
+		break;
+	case MSM_SPM_PMIC_PHASE_PORT:
+		index = dev->phase_port;
+		break;
+	case MSM_SPM_PMIC_PFM_PORT:
+		index = dev->pfm_port;
+		break;
+	default:
+		break;
+	}
+
+	return index;
+}
+
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port, unsigned int data)
+{
+	unsigned int pmic_data = 0;
+	unsigned int timeout_us = 0;
+	int index = 0;
+
+	if (!msm_spm_pmic_arb_present(dev))
+		return -ENOSYS;
+
+	index = msm_spm_drv_get_pmic_port(dev, port);
+	if (index < 0)
+		return -ENODEV;
+
+	pmic_data |= data & 0xFF;
+	pmic_data |= (index & 0x7) << 16;
+
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
+	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
+	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
+	mb();
+
+	timeout_us = dev->vctl_timeout_us;
+	/**
+	 * Confirm the pmic data set was what hardware sent by
+	 * checking the PMIC FSM state.
+	 * We cannot use the sts_pmic_data and check it against
+	 * the value like we do fot set_vdd, since the PMIC_STS
+	 * is only updated for SAW_VCTL sent with port index 0.
+	 */
+	do {
+		if (msm_spm_drv_get_sts_pmic_state(dev) ==
+				MSM_SPM_PMIC_STATE_IDLE)
+			break;
+		udelay(1);
+	} while (--timeout_us);
+
+	if (!timeout_us) {
+		pr_err("%s: failed, remaining timeout %u us, data %d\n",
+				__func__, timeout_us, data);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
+{
+	int i;
+
+	msm_spm_drv_flush_seq_entry(dev);
+	for (i = 0; i < MSM_SPM_REG_SAW2_PMIC_DATA_0 + num_pmic_data; i++)
+		msm_spm_drv_flush_shadow(dev, i);
+
+	mb();
+
+	for (i = MSM_SPM_REG_NR_INITIALIZE + 1; i < MSM_SPM_REG_NR; i++)
+		msm_spm_drv_load_shadow(dev, i);
+}
+
+int msm_spm_drv_init(struct msm_spm_driver_data *dev,
+		struct msm_spm_platform_data *data)
+{
+	int i;
+	int num_spm_entry;
+	bool found = false;
+
+	BUG_ON(!dev || !data);
+
+	dev->vctl_port = data->vctl_port;
+	dev->phase_port = data->phase_port;
+	dev->pfm_port = data->pfm_port;
+	dev->reg_base_addr = data->reg_base_addr;
+	memcpy(dev->reg_shadow, data->reg_init_values,
+			sizeof(data->reg_init_values));
+
+	dev->vctl_timeout_us = data->vctl_timeout_us;
+
+	msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor);
+
+	for (i = 0; i < ARRAY_SIZE(saw2_info); i++)
+		if (dev->major == saw2_info[i].major &&
+			dev->minor == saw2_info[i].minor) {
+			pr_debug("%s: Version found\n",
+					saw2_info[i].ver_name);
+			dev->reg_offsets = saw2_info[i].spm_reg_offset_ptr;
+			found = true;
+			break;
+		}
+
+	if (!found) {
+		pr_err("%s: No SAW2 version found\n", __func__);
+		BUG_ON(!found);
+	}
+
+	if (!num_pmic_data)
+		num_pmic_data = msm_spm_drv_get_num_pmic_data(dev);
+
+	num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
+
+	dev->reg_seq_entry_shadow =
+		kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry,
+				GFP_KERNEL);
+
+	if (!dev->reg_seq_entry_shadow)
+		return -ENOMEM;
+
+	return 0;
+}
diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c
new file mode 100644
index 0000000..755c165
--- /dev/null
+++ b/drivers/soc/qcom/spm_devices.c
@@ -0,0 +1,709 @@
+/* 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 <linux/err.h>
+
+#include <soc/qcom/spm.h>
+
+#include "spm_driver.h"
+
+#define VDD_DEFAULT 0xDEADF00D
+
+struct msm_spm_power_modes {
+	uint32_t mode;
+	bool notify_rpm;
+	uint32_t start_addr;
+};
+
+struct msm_spm_device {
+	struct list_head list;
+	bool initialized;
+	const char *name;
+	struct msm_spm_driver_data reg_data;
+	struct msm_spm_power_modes *modes;
+	uint32_t num_modes;
+	uint32_t cpu_vdd;
+	struct cpumask mask;
+	void __iomem *q2s_reg;
+};
+
+struct msm_spm_vdd_info {
+	struct msm_spm_device *vctl_dev;
+	uint32_t vlevel;
+	int err;
+};
+
+static LIST_HEAD(spm_list);
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
+static DEFINE_PER_CPU(struct msm_spm_device *, cpu_vctl_device);
+
+static void msm_spm_smp_set_vdd(void *data)
+{
+	struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
+	struct msm_spm_device *dev = info->vctl_dev;
+
+	dev->cpu_vdd = info->vlevel;
+	info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
+}
+
+/**
+ * msm_spm_probe_done(): Verify and return the status of the cpu(s) and l2
+ * probe.
+ * Return: 0 if all spm devices have been probed, else return -EPROBE_DEFER.
+ * if probe failed, then return the err number for that failure.
+ */
+int msm_spm_probe_done(void)
+{
+	struct msm_spm_device *dev;
+	int cpu;
+	int ret = 0;
+
+	for_each_possible_cpu(cpu) {
+		dev = per_cpu(cpu_vctl_device, cpu);
+		if (!dev)
+			return -EPROBE_DEFER;
+
+		ret = IS_ERR(dev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_spm_probe_done);
+
+void msm_spm_dump_regs(unsigned int cpu)
+{
+	dump_regs(&per_cpu(msm_cpu_spm_device, cpu).reg_data, cpu);
+}
+
+/**
+ * msm_spm_set_vdd(): Set core voltage
+ * @cpu: core id
+ * @vlevel: Encoded PMIC data.
+ *
+ * Return: 0 on success or -(ERRNO) on failure.
+ */
+int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
+{
+	struct msm_spm_vdd_info info;
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+	int ret;
+
+	if (!dev)
+		return -EPROBE_DEFER;
+
+	ret = IS_ERR(dev);
+	if (ret)
+		return ret;
+
+	info.vctl_dev = dev;
+	info.vlevel = vlevel;
+
+	ret = smp_call_function_any(&dev->mask, msm_spm_smp_set_vdd, &info,
+					true);
+	if (ret)
+		return ret;
+
+	return info.err;
+}
+EXPORT_SYMBOL(msm_spm_set_vdd);
+
+/**
+ * msm_spm_get_vdd(): Get core voltage
+ * @cpu: core id
+ * @return: Returns encoded PMIC data.
+ */
+unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+	int ret;
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	if (!dev)
+		return -EPROBE_DEFER;
+
+	ret = IS_ERR(dev);
+	if (ret)
+		return ret;
+
+	return dev->cpu_vdd;
+}
+EXPORT_SYMBOL(msm_spm_get_vdd);
+
+static void msm_spm_config_q2s(struct msm_spm_device *dev, unsigned int mode)
+{
+	uint32_t spm_legacy_mode = 0;
+	uint32_t qchannel_ignore = 0;
+	uint32_t val = 0;
+
+	if (!dev->q2s_reg)
+		return;
+
+	switch (mode) {
+	case MSM_SPM_MODE_DISABLED:
+	case MSM_SPM_MODE_CLOCK_GATING:
+		qchannel_ignore = 1;
+		spm_legacy_mode = 0;
+		break;
+	case MSM_SPM_MODE_RETENTION:
+		qchannel_ignore = 0;
+		spm_legacy_mode = 0;
+		break;
+	case MSM_SPM_MODE_GDHS:
+	case MSM_SPM_MODE_POWER_COLLAPSE:
+		qchannel_ignore = 0;
+		spm_legacy_mode = 1;
+		break;
+	default:
+		break;
+	}
+
+	val = spm_legacy_mode << 2 | qchannel_ignore << 1;
+	__raw_writel(val, dev->q2s_reg);
+	mb();
+}
+
+static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	uint32_t i;
+	uint32_t start_addr = 0;
+	int ret = -EINVAL;
+	bool pc_mode = false;
+
+	if (!dev->initialized)
+		return -ENXIO;
+
+	if ((mode == MSM_SPM_MODE_POWER_COLLAPSE)
+			|| (mode == MSM_SPM_MODE_GDHS))
+		pc_mode = true;
+
+	if (mode == MSM_SPM_MODE_DISABLED) {
+		ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
+	} else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
+		for (i = 0; i < dev->num_modes; i++) {
+			if ((dev->modes[i].mode == mode) &&
+				(dev->modes[i].notify_rpm == notify_rpm)) {
+				start_addr = dev->modes[i].start_addr;
+				break;
+			}
+		}
+		ret = msm_spm_drv_set_low_power_mode(&dev->reg_data,
+					start_addr, pc_mode);
+	}
+
+	msm_spm_config_q2s(dev, mode);
+
+	return ret;
+}
+
+static int msm_spm_dev_init(struct msm_spm_device *dev,
+		struct msm_spm_platform_data *data)
+{
+	int i, ret = -ENOMEM;
+	uint32_t offset = 0;
+
+	dev->cpu_vdd = VDD_DEFAULT;
+	dev->num_modes = data->num_modes;
+	dev->modes = kmalloc(
+			sizeof(struct msm_spm_power_modes) * dev->num_modes,
+			GFP_KERNEL);
+
+	if (!dev->modes)
+		goto spm_failed_malloc;
+
+	dev->reg_data.ver_reg = data->ver_reg;
+	ret = msm_spm_drv_init(&dev->reg_data, data);
+
+	if (ret)
+		goto spm_failed_init;
+
+	for (i = 0; i < dev->num_modes; i++) {
+
+		/* Default offset is 0 and gets updated as we write more
+		 * sequences into SPM
+		 */
+		dev->modes[i].start_addr = offset;
+		ret = msm_spm_drv_write_seq_data(&dev->reg_data,
+						data->modes[i].cmd, &offset);
+		if (ret < 0)
+			goto spm_failed_init;
+
+		dev->modes[i].mode = data->modes[i].mode;
+		dev->modes[i].notify_rpm = data->modes[i].notify_rpm;
+	}
+	msm_spm_drv_reinit(&dev->reg_data);
+	dev->initialized = true;
+	return 0;
+
+spm_failed_init:
+	kfree(dev->modes);
+spm_failed_malloc:
+	return ret;
+}
+
+/**
+ * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core
+ * @base: The SAW VCTL register which would set the voltage up.
+ * @val: The value to be set on the rail
+ * @cpu: The cpu for this with rail is being powered on
+ */
+int msm_spm_turn_on_cpu_rail(void __iomem *base, unsigned int val, int cpu)
+{
+	uint32_t timeout = 2000; /* delay for voltage to settle on the core */
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	/*
+	 * If clock drivers have already set up the voltage,
+	 * do not overwrite that value.
+	 */
+	if (dev && (dev->cpu_vdd != VDD_DEFAULT))
+		return 0;
+
+	/* Set the CPU supply regulator voltage */
+	val = (val & 0xFF);
+	writel_relaxed(val, base);
+	mb();
+	udelay(timeout);
+
+	/* Enable the CPU supply regulator*/
+	val = 0x30080;
+	writel_relaxed(val, base);
+	mb();
+	udelay(timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
+
+void msm_spm_reinit(void)
+{
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu)
+		msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu));
+}
+EXPORT_SYMBOL(msm_spm_reinit);
+
+/*
+ * msm_spm_is_mode_avail() - Specifies if a mode is available for the cpu
+ * It should only be used to decide a mode before lpm driver is probed.
+ * @mode: SPM LPM mode to be selected
+ */
+bool msm_spm_is_mode_avail(unsigned int mode)
+{
+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
+	int i;
+
+	for (i = 0; i < dev->num_modes; i++) {
+		if (dev->modes[i].mode == mode)
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ * @notify_rpm: Notify RPM in this mode
+ */
+int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
+{
+	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
+
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+}
+EXPORT_SYMBOL(msm_spm_set_low_power_mode);
+
+/**
+ * msm_spm_init(): Board initalization function
+ * @data: platform specific SPM register configuration data
+ * @nr_devs: Number of SPM devices being initialized
+ */
+int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
+{
+	unsigned int cpu;
+	int ret = 0;
+
+	BUG_ON((nr_devs < num_possible_cpus()) || !data);
+
+	for_each_possible_cpu(cpu) {
+		struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
+
+		ret = msm_spm_dev_init(dev, &data[cpu]);
+		if (ret < 0) {
+			pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
+					cpu, ret);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+struct msm_spm_device *msm_spm_get_device_by_name(const char *name)
+{
+	struct list_head *list;
+
+	list_for_each(list, &spm_list) {
+		struct msm_spm_device *dev
+			= list_entry(list, typeof(*dev), list);
+		if (dev->name && !strcmp(dev->name, name))
+			return dev;
+	}
+	return ERR_PTR(-ENODEV);
+}
+
+int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+}
+#ifdef CONFIG_MSM_L2_SPM
+
+/**
+ * msm_spm_apcs_set_phase(): Set number of SMPS phases.
+ * @cpu: cpu which is requesting the change in number of phases.
+ * @phase_cnt: Number of phases to be set active
+ */
+int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
+{
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	if (!dev)
+		return -ENXIO;
+
+	return msm_spm_drv_set_pmic_data(&dev->reg_data,
+			MSM_SPM_PMIC_PHASE_PORT, phase_cnt);
+}
+EXPORT_SYMBOL(msm_spm_apcs_set_phase);
+
+/** msm_spm_enable_fts_lpm() : Enable FTS to switch to low power
+ *                             when the cores are in low power modes
+ * @cpu: cpu that is entering low power mode.
+ * @mode: The mode configuration for FTS
+ */
+int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
+{
+	struct msm_spm_device *dev = per_cpu(cpu_vctl_device, cpu);
+
+	if (!dev)
+		return -ENXIO;
+
+	return msm_spm_drv_set_pmic_data(&dev->reg_data,
+			MSM_SPM_PMIC_PFM_PORT, mode);
+}
+EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
+
+#endif
+
+static int get_cpu_id(struct device_node *node)
+{
+	struct device_node *cpu_node;
+	u32 cpu;
+	int ret = -EINVAL;
+	char *key = "qcom,cpu";
+
+	cpu_node = of_parse_phandle(node, key, 0);
+	if (cpu_node) {
+		for_each_possible_cpu(cpu) {
+			if (of_get_cpu_node(cpu, NULL) == cpu_node)
+				return cpu;
+		}
+	} else {
+		char *key = "qcom,core-id";
+
+		ret = of_property_read_u32(node, key, &cpu);
+		if (!ret)
+			return cpu;
+	}
+	return ret;
+}
+
+static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev)
+{
+	struct msm_spm_device *dev = NULL;
+	const char *val = NULL;
+	char *key = "qcom,name";
+	int cpu = get_cpu_id(pdev->dev.of_node);
+
+	if ((cpu >= 0) && cpu < num_possible_cpus())
+		dev = &per_cpu(msm_cpu_spm_device, cpu);
+	else if ((cpu == 0xffff) || (cpu < 0))
+		dev = devm_kzalloc(&pdev->dev, sizeof(struct msm_spm_device),
+					GFP_KERNEL);
+
+	if (!dev)
+		return NULL;
+
+	if (of_property_read_string(pdev->dev.of_node, key, &val)) {
+		pr_err("%s(): Cannot find a required node key:%s\n",
+				__func__, key);
+		return NULL;
+	}
+	dev->name = val;
+	list_add(&dev->list, &spm_list);
+
+	return dev;
+}
+
+static void get_cpumask(struct device_node *node, struct cpumask *mask)
+{
+	unsigned long vctl_mask = 0;
+	unsigned c = 0;
+	int idx = 0;
+	struct device_node *cpu_node = NULL;
+	int ret = 0;
+	char *key = "qcom,cpu-vctl-list";
+	bool found = false;
+
+	cpu_node = of_parse_phandle(node, key, idx++);
+	while (cpu_node) {
+		found = true;
+		for_each_possible_cpu(c) {
+			if (of_get_cpu_node(c, NULL) == cpu_node)
+				cpumask_set_cpu(c, mask);
+		}
+		cpu_node = of_parse_phandle(node, key, idx++);
+	};
+
+	if (found)
+		return;
+
+	key = "qcom,cpu-vctl-mask";
+	ret = of_property_read_u32(node, key, (u32 *) &vctl_mask);
+	if (!ret) {
+		for_each_set_bit(c, &vctl_mask, num_possible_cpus()) {
+			cpumask_set_cpu(c, mask);
+		}
+	}
+}
+
+static int msm_spm_dev_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int cpu = 0;
+	int i = 0;
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_spm_platform_data spm_data;
+	char *key = NULL;
+	uint32_t val = 0;
+	struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
+	int len = 0;
+	struct msm_spm_device *dev = NULL;
+	struct resource *res = NULL;
+	uint32_t mode_count = 0;
+
+	struct spm_of {
+		char *key;
+		uint32_t id;
+	};
+
+	struct spm_of spm_of_data[] = {
+		{"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG},
+		{"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL},
+		{"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS},
+		{"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT},
+		{"qcom,saw2-avs-dly", MSM_SPM_REG_SAW2_AVS_DLY},
+		{"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY},
+		{"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL},
+		{"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0},
+		{"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1},
+		{"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2},
+		{"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3},
+		{"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4},
+		{"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5},
+		{"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6},
+		{"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7},
+	};
+
+	struct mode_of {
+		char *key;
+		uint32_t id;
+		uint32_t notify_rpm;
+	};
+
+	struct mode_of mode_of_data[] = {
+		{"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0},
+		{"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_RETENTION, 0},
+		{"qcom,saw2-spm-cmd-gdhs", MSM_SPM_MODE_GDHS, 1},
+		{"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0},
+		{"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1},
+	};
+
+	dev = msm_spm_get_device(pdev);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	get_cpumask(node, &dev->mask);
+
+	memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
+	memset(&modes, 0,
+		(MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
+
+	key = "qcom,saw2-ver-reg";
+	ret = of_property_read_u32(node, key, &val);
+	if (ret)
+		goto fail;
+	spm_data.ver_reg = val;
+
+	key = "qcom,vctl-timeout-us";
+	ret = of_property_read_u32(node, key, &val);
+	if (!ret)
+		spm_data.vctl_timeout_us = val;
+
+	/* SAW start address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!spm_data.reg_base_addr) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	spm_data.vctl_port = -1;
+	spm_data.phase_port = -1;
+	spm_data.pfm_port = -1;
+
+	key = "qcom,vctl-port";
+	of_property_read_u32(node, key, &spm_data.vctl_port);
+
+	key = "qcom,phase-port";
+	of_property_read_u32(node, key, &spm_data.phase_port);
+
+	key = "qcom,pfm-port";
+	of_property_read_u32(node, key, &spm_data.pfm_port);
+
+	/* Q2S (QChannel-2-SPM) register */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		dev->q2s_reg = devm_ioremap(&pdev->dev, res->start,
+						resource_size(res));
+		if (!dev->q2s_reg) {
+			pr_err("%s(): Unable to iomap Q2S register\n",
+					__func__);
+			ret = -EADDRNOTAVAIL;
+			goto fail;
+		}
+	}
+	/*
+	 * At system boot, cpus and or clusters can remain in reset. CCI SPM
+	 * will not be triggered unless SPM_LEGACY_MODE bit is set for the
+	 * cluster in reset. Initialize q2s registers and set the
+	 * SPM_LEGACY_MODE bit.
+	 */
+	msm_spm_config_q2s(dev, MSM_SPM_MODE_POWER_COLLAPSE);
+
+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+		ret = of_property_read_u32(node, spm_of_data[i].key, &val);
+		if (ret)
+			continue;
+		spm_data.reg_init_values[spm_of_data[i].id] = val;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) {
+		key = mode_of_data[i].key;
+		modes[mode_count].cmd =
+			(uint8_t *)of_get_property(node, key, &len);
+		if (!modes[mode_count].cmd)
+			continue;
+		modes[mode_count].mode = mode_of_data[i].id;
+		modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm;
+		pr_debug("%s(): dev: %s cmd:%s, mode:%d rpm:%d\n", __func__,
+				dev->name, key, modes[mode_count].mode,
+				modes[mode_count].notify_rpm);
+		mode_count++;
+	}
+
+	spm_data.modes = modes;
+	spm_data.num_modes = mode_count;
+
+	ret = msm_spm_dev_init(dev, &spm_data);
+	if (ret)
+		goto fail;
+
+	platform_set_drvdata(pdev, dev);
+
+	for_each_cpu(cpu, &dev->mask)
+		per_cpu(cpu_vctl_device, cpu) = dev;
+
+	return ret;
+
+fail:
+	cpu = get_cpu_id(pdev->dev.of_node);
+	if (dev && (cpu >= num_possible_cpus() || (cpu < 0))) {
+		for_each_cpu(cpu, &dev->mask)
+			per_cpu(cpu_vctl_device, cpu) = ERR_PTR(ret);
+	}
+
+	pr_err("%s: CPU%d SPM device probe failed: %d\n", __func__, cpu, ret);
+
+	return ret;
+}
+
+static int msm_spm_dev_remove(struct platform_device *pdev)
+{
+	struct msm_spm_device *dev = platform_get_drvdata(pdev);
+
+	list_del(&dev->list);
+
+	return 0;
+}
+
+static struct of_device_id msm_spm_match_table[] = {
+	{.compatible = "qcom,spm-v2"},
+	{},
+};
+
+static struct platform_driver msm_spm_device_driver = {
+	.probe = msm_spm_dev_probe,
+	.remove = msm_spm_dev_remove,
+	.driver = {
+		.name = "spm-v2",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_spm_match_table,
+	},
+};
+
+/**
+ * msm_spm_device_init(): Device tree initialization function
+ */
+int __init msm_spm_device_init(void)
+{
+	static bool registered;
+
+	if (registered)
+		return 0;
+
+	registered = true;
+
+	return platform_driver_register(&msm_spm_device_driver);
+}
+device_initcall(msm_spm_device_init);
diff --git a/drivers/soc/qcom/spm_driver.h b/drivers/soc/qcom/spm_driver.h
new file mode 100644
index 0000000..49cd16a
--- /dev/null
+++ b/drivers/soc/qcom/spm_driver.h
@@ -0,0 +1,118 @@
+/* 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.
+ */
+#ifndef __ARCH_ARM_MACH_MSM_SPM_DRIVER_H
+#define __ARCH_ARM_MACH_MSM_SPM_DRIVER_H
+
+#include <soc/qcom/spm.h>
+
+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,
+};
+
+struct msm_spm_seq_entry {
+	uint32_t mode;
+	uint8_t *cmd;
+	bool  notify_rpm;
+};
+
+struct msm_spm_platform_data {
+	void __iomem *reg_base_addr;
+	uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE];
+
+	uint32_t ver_reg;
+	uint32_t vctl_port;
+	uint32_t phase_port;
+	uint32_t pfm_port;
+
+	uint8_t awake_vlevel;
+	uint32_t vctl_timeout_us;
+	uint32_t avs_timeout_us;
+
+	uint32_t num_modes;
+	struct msm_spm_seq_entry *modes;
+};
+
+enum msm_spm_pmic_port {
+	MSM_SPM_PMIC_VCTL_PORT,
+	MSM_SPM_PMIC_PHASE_PORT,
+	MSM_SPM_PMIC_PFM_PORT,
+};
+
+struct msm_spm_driver_data {
+	uint32_t major;
+	uint32_t minor;
+	uint32_t ver_reg;
+	uint32_t vctl_port;
+	uint32_t phase_port;
+	uint32_t pfm_port;
+	void __iomem *reg_base_addr;
+	uint32_t vctl_timeout_us;
+	uint32_t avs_timeout_us;
+	uint32_t reg_shadow[MSM_SPM_REG_NR];
+	uint32_t *reg_seq_entry_shadow;
+	uint32_t *reg_offsets;
+};
+
+int msm_spm_drv_init(struct msm_spm_driver_data *dev,
+		struct msm_spm_platform_data *data);
+void msm_spm_drv_reinit(struct msm_spm_driver_data *dev);
+int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
+		uint32_t addr, bool pc_mode);
+int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev,
+		unsigned int vlevel);
+void dump_regs(struct msm_spm_driver_data *dev, int cpu);
+uint32_t msm_spm_drv_get_sts_curr_pmic_data(
+		struct msm_spm_driver_data *dev);
+int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
+		uint8_t *cmd, uint32_t *offset);
+void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
+int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
+		bool enable);
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port, unsigned int data);
+
+void msm_spm_reinit(void);
+int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs);
+
+#endif
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
new file mode 100644
index 0000000..185ddaf
--- /dev/null
+++ b/include/soc/qcom/spm.h
@@ -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.
+ */
+
+#ifndef __MSM_SPM_H
+#define __MSM_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(unsigned int mode, bool notify_rpm);
+int msm_spm_probe_done(void);
+int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
+unsigned int msm_spm_get_vdd(unsigned int cpu);
+int msm_spm_turn_on_cpu_rail(void __iomem *base, unsigned int val, int cpu);
+struct msm_spm_device *msm_spm_get_device_by_name(const char *name);
+int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm);
+int msm_spm_device_init(void);
+bool msm_spm_is_mode_avail(unsigned int mode);
+void msm_spm_dump_regs(unsigned int cpu);
+int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt);
+int msm_spm_enable_fts_lpm(int cpu, uint32_t mode);
+
+#else /* defined(CONFIG_QCOM_PM) */
+static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_probe_done(void)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
+{
+	return -ENOSYS;
+}
+
+static inline unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+	return 0;
+}
+
+static inline int msm_spm_turn_on_cpu_rail(void __iomem *base,
+		unsigned int val, int cpu)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_device_init(void)
+{
+	return -ENOSYS;
+}
+
+static void msm_spm_dump_regs(unsigned int cpu)
+{
+}
+
+static inline int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	return -ENODEV;
+}
+static inline struct msm_spm_device *msm_spm_get_device_by_name(
+		const char *name)
+{
+	return NULL;
+}
+
+static inline bool msm_spm_is_mode_avail(unsigned int mode)
+{
+	return false;
+}
+
+static inline int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
+{
+	return -ENOSYS;
+}
+
+static inline int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
+{
+	return -ENOSYS;
+}
+
+#endif  /* defined (CONFIG_QCOM_PM) */
+#endif  /* __MSM_SPM_H */
-- 
1.9.1

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

end of thread, other threads:[~2014-08-11 20:40 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-08  4:05 [RFC] [PATCH 00/13] QCOM: 8074 CPUIDLE driver Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 01/13] msm: scm: Move scm-boot files to drivers/soc and include/soc Lina Iyer
2014-08-08  7:42   ` Pramod Gurav
2014-08-08 14:39     ` Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 02/13] msm: scm: Add SCM warmboot flags for quad core targets Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets Lina Iyer
2014-08-08  9:18   ` Pramod Gurav
2014-08-08 10:07     ` Pramod Gurav
2014-08-08 14:31       ` Lina Iyer
2014-08-08 16:16   ` Kumar Gala
2014-08-08 21:53     ` Lina Iyer
2014-08-11 19:54       ` Kumar Gala
2014-08-11 20:40         ` Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 04/13] arm: dts: qcom: Add SPM device bindings for 8974 Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 05/13] qcom: msm-pm: Add cpu low power mode functions Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 06/13] qcom: msm-pm: Add support for hotplug and secondary startup Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 07/13] qcom: sleep-status: Add ability to recognize cpu power down state Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 08/13] arm: dts: qcom: Add device binding for sleep status Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 09/13] soc: qcom: Add QCOM Power management config Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 10/13] qcom: platsmp: Enable deeper idle states for hotplug Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 11/13] qcom: cpuidle: Add cpuidle driver for QCOM cpus Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 12/13] qcom: cpuidle: Add cpuidle device nodes for 8974 chipset Lina Iyer
2014-08-08  4:05 ` [RFC] [PATCH 13/13] qcom: cpuidle: Config option to enable QCOM cpuidle driver Lina Iyer
  -- strict thread matches above, loose matches on Subject: below --
2014-08-08  4:00 [RFC] [PATCH 00/13] QCOM: 8087 CPUIDLE driver Lina Iyer
2014-08-08  4:00 ` [RFC] [PATCH 03/13] qcom: spm: Add Subsystem Power Manager driver for QCOM chipsets 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.