Merge "msm: pm: Notify secure code of L2 power mode from last core only"
diff --git a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
index 795af3b..2fbe4ca 100644
--- a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
+++ b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
@@ -12,6 +12,7 @@
The required properties for PM-8x60 are:
- compatible: "qcom,pm-8x60"
+- qcom,lpm-levels: phandle for associated lpm_levels device.
The optional properties are:
@@ -39,4 +40,5 @@
reg = <0xfe800664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
+ qcom,lpm-levels = <&lpm_levels>;
};
diff --git a/arch/arm/boot/dts/msm8226-v1-pm.dtsi b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
index d59fab3..10aff70 100644
--- a/arch/arm/boot/dts/msm8226-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* 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
@@ -105,7 +105,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -300,6 +300,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226-v2-pm.dtsi b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
index bc8fe5d..7af2c7f 100644
--- a/arch/arm/boot/dts/msm8226-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* 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
@@ -107,7 +107,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -312,6 +312,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8610-v1-pm.dtsi b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
index dc1dc8b..adc66d7 100644
--- a/arch/arm/boot/dts/msm8610-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* 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
@@ -105,7 +105,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -296,6 +296,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8610-v2-pm.dtsi b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
index 2859744..b69b061 100644
--- a/arch/arm/boot/dts/msm8610-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* 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
@@ -107,7 +107,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -308,6 +308,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index 886177d..516d068 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -130,7 +130,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_retention";
#address-cells = <1>;
@@ -311,6 +311,7 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008 {
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index 84a8c2d..cde5e5a9 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* 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
@@ -126,7 +126,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_retention";
#address-cells = <1>;
@@ -324,6 +324,7 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
qcom,pm-snoc-client {
compatible = "qcom,pm-snoc-client";
diff --git a/arch/arm/boot/dts/msm8974pro-pm.dtsi b/arch/arm/boot/dts/msm8974pro-pm.dtsi
index f735b65..0307e2a 100644
--- a/arch/arm/boot/dts/msm8974pro-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* 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
@@ -126,7 +126,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,allow-synced-levels;
qcom,default-l2-state = "l2_cache_retention";
@@ -334,6 +334,7 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
qcom,pm-snoc-client {
compatible = "qcom,pm-snoc-client";
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 1e6cdf2..ec62cd4 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -28,7 +28,7 @@
3e 0f];
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,no-l2-saw;
#address-cells = <1>;
@@ -167,6 +167,7 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_ext";
qcom,use-sync-timer;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,rpm-log@fc19dc00 {
diff --git a/arch/arm/mach-msm/msm-pm.c b/arch/arm/mach-msm/msm-pm.c
index f9a9343..080bab3 100644
--- a/arch/arm/mach-msm/msm-pm.c
+++ b/arch/arm/mach-msm/msm-pm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* 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
@@ -25,10 +25,12 @@
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/cpu_pm.h>
+#include <linux/remote_spinlock.h>
#include <asm/uaccess.h>
#include <asm/suspend.h>
#include <asm/cacheflush.h>
#include <asm/outercache.h>
+#include <mach/remote_spinlock.h>
#include <mach/scm.h>
#include <mach/msm_bus.h>
#include <mach/jtag.h>
@@ -117,6 +119,12 @@
DEFINE_PER_CPU(struct clk *, cpu_clks);
static struct clk *l2_clk;
+static int cpu_count;
+static DEFINE_SPINLOCK(cpu_cnt_lock);
+#define SCM_HANDOFF_LOCK_ID "S:7"
+static bool need_scm_handoff_lock;
+static remote_spinlock_t scm_handoff_lock;
+
static void (*msm_pm_disable_l2_fn)(void);
static void (*msm_pm_enable_l2_fn)(void);
static void (*msm_pm_flush_l2_fn)(void);
@@ -478,8 +486,30 @@
static int msm_pm_collapse(unsigned long unused)
{
uint32_t cpu = smp_processor_id();
+ enum msm_pm_l2_scm_flag flag = MSM_SCM_L2_ON;
- if (msm_pm_get_l2_flush_flag() == MSM_SCM_L2_OFF) {
+ spin_lock(&cpu_cnt_lock);
+ cpu_count++;
+ if (cpu_count == num_online_cpus())
+ flag = msm_pm_get_l2_flush_flag();
+
+ pr_debug("cpu:%d cores_in_pc:%d L2 flag: %d\n",
+ cpu, cpu_count, flag);
+
+ /*
+ * The scm_handoff_lock will be release by the secure monitor.
+ * It is used to serialize power-collapses from this point on,
+ * so that both Linux and the secure context have a consistent
+ * view regarding the number of running cpus (cpu_count).
+ *
+ * It must be acquired before releasing cpu_cnt_lock.
+ */
+ if (need_scm_handoff_lock)
+ remote_spin_lock_rlock_id(&scm_handoff_lock,
+ REMOTE_SPINLOCK_TID_START + cpu);
+ spin_unlock(&cpu_cnt_lock);
+
+ if (flag == MSM_SCM_L2_OFF) {
flush_cache_all();
if (msm_pm_flush_l2_fn)
msm_pm_flush_l2_fn();
@@ -491,8 +521,7 @@
msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
- scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
- msm_pm_get_l2_flush_flag());
+ scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
@@ -534,6 +563,12 @@
collapsed = save_cpu_regs ?
!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
+ if (save_cpu_regs) {
+ spin_lock(&cpu_cnt_lock);
+ cpu_count--;
+ BUG_ON(cpu_count > num_online_cpus());
+ spin_unlock(&cpu_cnt_lock);
+ }
msm_jtag_restore_state();
if (collapsed) {
@@ -1166,6 +1201,7 @@
struct resource *res = NULL;
int i;
struct msm_pm_init_data_type pdata_local;
+ struct device_node *lpm_node;
int ret = 0;
memset(&pdata_local, 0, sizeof(struct msm_pm_init_data_type));
@@ -1192,6 +1228,23 @@
msm_pc_debug_counters_phys = 0;
}
+ lpm_node = of_parse_phandle(pdev->dev.of_node, "qcom,lpm-levels", 0);
+ if (!lpm_node) {
+ pr_warn("Could not get qcom,lpm-levels handle\n");
+ return -EINVAL;
+ }
+ need_scm_handoff_lock = of_property_read_bool(lpm_node,
+ "qcom,allow-synced-levels");
+ if (need_scm_handoff_lock) {
+ ret = remote_spin_lock_init(&scm_handoff_lock,
+ SCM_HANDOFF_LOCK_ID);
+ if (ret) {
+ pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
if (pdev->dev.of_node) {
enum msm_pm_pc_mode_type pc_mode;