Implement retention sleep mode
/arch/arm/mach-msm/pm-8x60.c
blob:bef0080970d072cb5449dcbcf33f68c8b39c3d2a -> blob:aa16c8f404812193a2ea5c156d9a2a969d0a9bd3
--- arch/arm/mach-msm/pm-8x60.c
+++ arch/arm/mach-msm/pm-8x60.c
@@ -26,10 +26,11 @@
#include <linux/suspend.h>
#include <linux/tick.h>
#include <linux/uaccess.h>
-#include <linux/wakelock.h>
#include <linux/delay.h>
#include <mach/msm_iomap.h>
#include <mach/system.h>
+#include <mach/scm.h>
+#include <mach/socinfo.h>
#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
#include <asm/pgtable.h>
@@ -72,11 +73,11 @@ enum {
MSM_PM_DEBUG_HOTPLUG = BIT(8),
};
-static int msm_pm_debug_mask = 1;
+static int msm_pm_debug_mask = 319;
module_param_named(
debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
);
-
+static int msm_pm_retention_tz_call;
/******************************************************************************
* Sleep Modes and Parameters
@@ -104,6 +105,9 @@ enum {
MSM_PM_MODE_ATTR_NR,
};
+#define SCM_L2_RETENTION (0x2)
+#define SCM_CMD_TERMINATE_PC (0x2)
+
static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
[MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
[MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
@@ -127,6 +131,7 @@ struct msm_pm_sysfs_sleep_mode {
static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
+ [MSM_PM_SLEEP_MODE_RETENTION] = "retention",
[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
"standalone_power_collapse",
};
@@ -349,8 +354,9 @@ mode_sysfs_add_exit:
#ifdef CONFIG_MSM_IDLE_STATS
enum msm_pm_time_stats_id {
- MSM_PM_STAT_REQUESTED_IDLE,
+ MSM_PM_STAT_REQUESTED_IDLE = 0,
MSM_PM_STAT_IDLE_WFI,
+ MSM_PM_STAT_RETENTION,
MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
MSM_PM_STAT_IDLE_POWER_COLLAPSE,
MSM_PM_STAT_SUSPEND,
@@ -584,6 +590,22 @@ static void msm_pm_config_hw_before_swfi
return;
}
+/*
+ * Configure/Restore hardware registers in preparation for Retention.
+ */
+
+static void msm_pm_config_hw_after_retention(void)
+{
+ int ret;
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+ WARN_ON(ret);
+}
+
+static void msm_pm_config_hw_before_retention(void)
+{
+ return;
+}
+
/******************************************************************************
* Suspend Max Sleep Time
@@ -645,6 +667,23 @@ static void msm_pm_swfi(void)
msm_arch_idle();
}
+static void msm_pm_retention(void)
+{
+ int ret = 0;
+
+ msm_pm_config_hw_before_retention();
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_RETENTION, false);
+ WARN_ON(ret);
+
+ if (msm_pm_retention_tz_call)
+ scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
+ SCM_L2_RETENTION);
+ else
+ msm_arch_idle();
+
+ msm_pm_config_hw_after_retention();
+}
+
#ifdef CONFIG_CACHE_L2X0
static inline bool msm_pm_l2x0_power_collapse(void)
{
@@ -763,12 +802,28 @@ static bool msm_pm_power_collapse(bool f
collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
- if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
- pr_info("CPU%u: %s: restore clock rate to %lu\n",
- cpu, __func__, saved_acpuclk_rate);
- if (acpuclk_set_rate(cpu, saved_acpuclk_rate, SETRATE_PC) < 0)
- pr_err("CPU%u: %s: failed to restore clock rate(%lu)\n",
- cpu, __func__, saved_acpuclk_rate);
+ if (cpu_online(cpu)) {
+ if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: restore clock rate to %lu\n",
+ cpu, __func__, saved_acpuclk_rate);
+ if (acpuclk_set_rate(cpu, saved_acpuclk_rate, SETRATE_PC) < 0)
+ pr_err("CPU%u: %s: failed to restore clock rate(%lu)\n",
+ cpu, __func__, saved_acpuclk_rate);
+ } else {
+ unsigned int gic_dist_enabled;
+ unsigned int gic_dist_pending;
+ gic_dist_enabled = readl_relaxed(
+ MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_CLEAR);
+ gic_dist_pending = readl_relaxed(
+ MSM_QGIC_DIST_BASE + GIC_DIST_PENDING_SET);
+ mb();
+ gic_dist_pending &= gic_dist_enabled;
+
+ if (gic_dist_pending)
+ pr_err("CPU %d interrupted during hotplug.Pending int 0x%x\n",
+ cpu, gic_dist_pending);
+ }
+
avs_reset_delays(avsdscr_setting);
msm_pm_config_hw_after_power_up();
@@ -823,19 +878,21 @@ int msm_pm_idle_prepare(struct cpuidle_d
switch (mode) {
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
- if (!allow)
- break;
-
if (num_online_cpus() > 1) {
allow = false;
break;
}
-#ifdef CONFIG_HAS_WAKELOCK
- if (has_wake_lock(WAKE_LOCK_IDLE)) {
+ /* fall through */
+
+ case MSM_PM_SLEEP_MODE_RETENTION:
+ if (!allow)
+ break;
+
+ if (msm_pm_retention_tz_call &&
+ num_online_cpus() > 1) {
allow = false;
break;
}
-#endif
/* fall through */
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
@@ -923,6 +980,13 @@ int msm_pm_idle_enter(enum msm_pm_sleep_
#endif
break;
+ case MSM_PM_SLEEP_MODE_RETENTION:
+ msm_pm_retention();
+#ifdef CONFIG_MSM_IDLE_STATS
+ exit_stat = MSM_PM_STAT_RETENTION;
+#endif
+ break;
+
case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
msm_pm_power_collapse_standalone(true);
#ifdef CONFIG_MSM_IDLE_STATS
@@ -971,6 +1035,7 @@ int msm_pm_idle_enter(enum msm_pm_sleep_
time = ktime_to_ns(ktime_get()) - time;
#ifdef CONFIG_MSM_IDLE_STATS
+ msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, time);
msm_pm_add_stat(exit_stat, time);
#endif
@@ -1021,9 +1086,13 @@ void msm_pm_cpu_enter_lowpower(unsigned
per_cpu(msm_pm_last_slp_mode, cpu)
= MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
msm_pm_power_collapse_standalone(false);
+ } else if (allow[MSM_PM_SLEEP_MODE_RETENTION]) {
+ per_cpu(msm_pm_last_slp_mode, cpu)
+ = MSM_PM_SLEEP_MODE_RETENTION;
+ msm_pm_retention();
} else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
per_cpu(msm_pm_last_slp_mode, cpu)
- = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
+ = MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT;
msm_pm_swfi();
} else
per_cpu(msm_pm_last_slp_mode, cpu) = MSM_PM_SLEEP_MODE_NR;
@@ -1112,10 +1181,11 @@ static int msm_pm_enter(suspend_state_t
if ((MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) &&
rs_limits)
pr_info("%s: limit %p: pxo %d, l2_cache %d, "
- "vdd_mem %d, vdd_dig %d\n",
+ "vdd_mem %d, vdd_dig %d, power %d\n",
__func__, rs_limits,
rs_limits->pxo, rs_limits->l2_cache,
- rs_limits->vdd_mem, rs_limits->vdd_dig);
+ rs_limits->vdd_mem, rs_limits->vdd_dig,
+ rs_limits->power[smp_processor_id()]);
if (rs_limits) {
ret = msm_rpmrs_enter_sleep(
@@ -1147,6 +1217,10 @@ static int msm_pm_enter(suspend_state_t
if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
pr_info("%s: standalone power collapse\n", __func__);
msm_pm_power_collapse_standalone(false);
+ } else if (allow[MSM_PM_SLEEP_MODE_RETENTION]) {
+ if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
+ pr_info("%s: retention\n", __func__);
+ msm_pm_retention();
} else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
pr_info("%s: swfi\n", __func__);
@@ -1169,12 +1243,42 @@ static struct platform_suspend_ops msm_p
/******************************************************************************
* Initialization routine
*****************************************************************************/
+/*
+ * For speeding up boot time:
+ * During booting up, disable entering arch_idle() by disable_hlt()
+ * Enable it after booting up BOOT_LOCK_TIMEOUT sec.
+*/
+
+#define BOOT_LOCK_TIMEOUT_NORMAL (60 * HZ)
+
+static void do_expire_boot_lock(struct work_struct *work)
+{
+ enable_hlt();
+ pr_info("Release 'boot-time' no_halt_lock\n");
+}
+static DECLARE_DELAYED_WORK(work_expire_boot_lock, do_expire_boot_lock);
+
+static void __init boot_lock_nohalt(void)
+{
+ int nohalt_timeout;
+
+ nohalt_timeout = BOOT_LOCK_TIMEOUT_NORMAL;
+
+ disable_hlt();
+ schedule_delayed_work(&work_expire_boot_lock, nohalt_timeout);
+ pr_info("Acquire 'boot-time' no_halt_lock %ds\n", nohalt_timeout / HZ);
+}
void __init msm_pm_init_sleep_status_data(
struct msm_pm_sleep_status_data *data)
{
msm_pm_slp_sts = data;
}
+void __init msm_pm_set_tz_retention_flag(unsigned int flag)
+{
+ msm_pm_retention_tz_call = flag;
+}
+
static int __init msm_pm_init(void)
{
pgd_t *pc_pgd;
@@ -1238,6 +1342,10 @@ static int __init msm_pm_init(void)
stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
+ stats[MSM_PM_STAT_RETENTION].name = "retention";
+ stats[MSM_PM_STAT_RETENTION].first_bucket_time =
+ CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
+
stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
"idle-standalone-power-collapse";
stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
@@ -1266,6 +1374,7 @@ static int __init msm_pm_init(void)
msm_spm_allow_x_cpu_set_vdd(false);
suspend_set_ops(&msm_pm_ops);
+ boot_lock_nohalt();
msm_cpuidle_init();
return 0;