msm: pm: Rearchitect power management code
The existing PM code relies on a levels table to determine the various
sleep modes including the system sleep states. Splitting them into cpu
and system/cluster specific levels is a more appropriate representation
of the existing system. As a part of this rearchitecture, the decision
to enter a low power mode is made within lpm_levels module. Unlike
earlier, the decision was split between pm-8x60 and lpm_levels.
To this effect, the lpm_levels directly registers with the cpuidle
driver instead of relying on a glue msm-cpuidle driver. The
functionality of the cpuidle driver was limited to calling into a PM
driver and without providing any additional functionality.
While at it, also remove any references to unused enums and
functionality.
Change-Id: I474cc85c6220473fdaa87e14e3b0a199e95d7eb7
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/msm-pm.c b/arch/arm/mach-msm/msm-pm.c
index aebd61f..5f8657e 100644
--- a/arch/arm/mach-msm/msm-pm.c
+++ b/arch/arm/mach-msm/msm-pm.c
@@ -11,61 +11,37 @@
*
*/
-#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
-#include <linux/completion.h>
-#include <linux/cpuidle.h>
-#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ktime.h>
-#include <linux/pm.h>
-#include <linux/pm_qos.h>
#include <linux/smp.h>
-#include <linux/suspend.h>
#include <linux/tick.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
-#include <linux/regulator/krait-regulator.h>
-#include <linux/cpu.h>
-#include <mach/msm_iomap.h>
-#include <mach/socinfo.h>
-#include <mach/system.h>
-#include <mach/scm.h>
-#include <mach/socinfo.h>
-#define CREATE_TRACE_POINTS
-#include <mach/trace_msm_low_power.h>
-#include <mach/msm-krait-l2-accessors.h>
-#include <mach/msm_bus.h>
-#include <mach/mpm.h>
-#include <mach/jtag.h>
+#include <linux/cpu_pm.h>
+#include <asm/uaccess.h>
#include <asm/suspend.h>
#include <asm/cacheflush.h>
-#include <asm/hardware/gic.h>
-#include <asm/pgtable.h>
-#include <asm/pgalloc.h>
-#include <asm/barrier.h>
#include <asm/outercache.h>
-#ifdef CONFIG_VFP
-#include <asm/vfp.h>
-#endif
+#include <mach/scm.h>
+#include <mach/msm_bus.h>
+#include <mach/jtag.h>
#include "acpuclock.h"
-#include "clock.h"
#include "avs.h"
-#include <mach/cpuidle.h>
#include "idle.h"
#include "pm.h"
#include "scm-boot.h"
#include "spm.h"
-#include "timer.h"
#include "pm-boot.h"
-#include <mach/event_timer.h>
-#include <linux/cpu_pm.h>
+
+#define CREATE_TRACE_POINTS
+#include <mach/trace_msm_low_power.h>
#define SCM_CMD_TERMINATE_PC (0x2)
#define SCM_CMD_CORE_HOTPLUGGED (0x10)
@@ -82,10 +58,6 @@
debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
);
-static int msm_pm_sleep_time_override;
-module_param_named(sleep_time_override,
- msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
-
static bool use_acpuclk_apis;
enum {
@@ -138,15 +110,9 @@
"standalone_power_collapse",
};
-static struct hrtimer pm_hrtimer;
-static struct msm_pm_sleep_ops pm_sleep_ops;
static bool msm_pm_ldo_retention_enabled = true;
-static bool msm_pm_use_sync_timer;
-static struct msm_pm_cp15_save_data cp15_data;
-static bool msm_pm_retention_calls_tz;
static bool msm_no_ramp_down_pc;
static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
-static bool msm_pm_pc_reset_timer;
DEFINE_PER_CPU(struct clk *, cpu_clks);
static struct clk *l2_clk;
@@ -172,6 +138,9 @@
return msm_pm_flush_l2_flag;
}
+static cpumask_t retention_cpus;
+static DEFINE_SPINLOCK(retention_lock);
+
static int msm_pm_get_pc_mode(struct device_node *node,
const char *key, uint32_t *pc_mode_val)
{
@@ -186,13 +155,10 @@
{MSM_PM_PC_TZ_L2_EXT , "tz_l2_ext"} };
int ret;
const char *pc_mode_str;
+ *pc_mode_val = MSM_PM_PC_TZ_L2_INT;
ret = of_property_read_string(node, key, &pc_mode_str);
- if (ret) {
- pr_debug("%s: Cannot read %s,defaulting to 0", __func__, key);
- pc_mode_val = MSM_PM_PC_TZ_L2_INT;
- ret = 0;
- } else {
+ if (!ret) {
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(pc_modes); i++) {
if (!strncmp(pc_mode_str, pc_modes[i].mode_name,
@@ -202,6 +168,9 @@
break;
}
}
+ } else {
+ pr_debug("%s: Cannot read %s,defaulting to 0", __func__, key);
+ ret = 0;
}
return ret;
}
@@ -288,7 +257,7 @@
return ret ? ret : count;
}
-static int __devinit msm_pm_mode_sysfs_add_cpu(
+static int msm_pm_mode_sysfs_add_cpu(
unsigned int cpu, struct kobject *modes_kobj)
{
char cpu_name[8];
@@ -371,7 +340,7 @@
return ret;
}
-int __devinit msm_pm_mode_sysfs_add(void)
+int msm_pm_mode_sysfs_add(void)
{
struct kobject *module_kobj;
struct kobject *modes_kobj;
@@ -405,44 +374,10 @@
return ret;
}
-/*
- * Configure hardware registers in preparation for Apps power down.
- */
-static void msm_pm_config_hw_before_power_down(void)
+static inline void msm_arch_idle(void)
{
- return;
-}
-
-/*
- * Clear hardware registers after Apps powers up.
- */
-static void msm_pm_config_hw_after_power_up(void)
-{
-}
-
-/*
- * Configure hardware registers in preparation for SWFI.
- */
-static void msm_pm_config_hw_before_swfi(void)
-{
- 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;
+ mb();
+ wfi();
}
static bool msm_pm_is_L1_writeback(void)
@@ -458,76 +393,38 @@
return cache_id & BIT(31);
}
-static void msm_pm_save_cpu_reg(void)
+static enum msm_pm_time_stats_id msm_pm_swfi(bool from_idle)
{
- int i;
-
- /* Only on core0 */
- if (smp_processor_id())
- return;
-
- /**
- * On some targets, L2 PC will turn off may reset the core
- * configuration for the mux and the default may not make the core
- * happy when it resumes.
- * Save the active vdd, and set the core vdd to QSB max vdd, so that
- * when the core resumes, it is capable of supporting the current QSB
- * rate. Then restore the active vdd before switching the acpuclk rate.
- */
- if (msm_pm_get_l2_flush_flag() == MSM_SCM_L2_OFF) {
- cp15_data.active_vdd = msm_spm_get_vdd(0);
- for (i = 0; i < cp15_data.reg_saved_state_size; i++)
- cp15_data.reg_val[i] =
- get_l2_indirect_reg(
- cp15_data.reg_data[i]);
- msm_spm_set_vdd(0, cp15_data.qsb_pc_vdd);
- }
-}
-
-static void msm_pm_restore_cpu_reg(void)
-{
- int i;
-
- /* Only on core0 */
- if (smp_processor_id())
- return;
-
- if (msm_pm_get_l2_flush_flag() == MSM_SCM_L2_OFF) {
- for (i = 0; i < cp15_data.reg_saved_state_size; i++)
- set_l2_indirect_reg(
- cp15_data.reg_data[i],
- cp15_data.reg_val[i]);
- msm_spm_set_vdd(0, cp15_data.active_vdd);
- }
-}
-
-static inline void msm_arch_idle(void)
-{
- mb();
- wfi();
-}
-
-static void msm_pm_swfi(void)
-{
- msm_pm_config_hw_before_swfi();
msm_arch_idle();
+ return MSM_PM_STAT_IDLE_WFI;
}
-static void msm_pm_retention(void)
+static enum msm_pm_time_stats_id msm_pm_retention(bool from_idle)
{
int ret = 0;
+ int cpu = smp_processor_id();
- msm_pm_config_hw_before_retention();
+ spin_lock(&retention_lock);
+
+ if (!msm_pm_ldo_retention_enabled)
+ goto bailout;
+
+ cpumask_set_cpu(cpu, &retention_cpus);
+ spin_unlock(&retention_lock);
+
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_RETENTION, false);
WARN_ON(ret);
- if (msm_pm_retention_calls_tz)
- scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
- MSM_SCM_L2_RET);
- else
- msm_arch_idle();
+ msm_arch_idle();
+ spin_lock(&retention_lock);
+ cpumask_clear_cpu(cpu, &retention_cpus);
+bailout:
+ spin_unlock(&retention_lock);
- msm_pm_config_hw_after_retention();
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+ WARN_ON(ret);
+
+ return MSM_PM_STAT_RETENTION;
}
static inline void msm_pc_inc_debug_count(uint32_t cpu,
@@ -594,16 +491,12 @@
bool collapsed = 0;
int ret;
bool save_cpu_regs = !cpu || from_idle;
- unsigned int saved_gic_cpu_ctrl;
-
- saved_gic_cpu_ctrl = readl_relaxed(MSM_QGIC_CPU_BASE + GIC_CPU_CTRL);
- mb();
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: notify_rpm %d\n",
cpu, __func__, (int) notify_rpm);
- if (from_idle == true)
+ if (from_idle)
cpu_pm_enter();
ret = msm_spm_set_low_power_mode(
@@ -617,30 +510,23 @@
if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
pr_info("CPU%u: %s: program vector to %p\n",
cpu, __func__, entry);
- if (from_idle && msm_pm_pc_reset_timer)
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
msm_jtag_save_state();
+
collapsed = save_cpu_regs ?
!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
+
msm_jtag_restore_state();
- if (from_idle && msm_pm_pc_reset_timer)
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
+ if (collapsed) {
+ cpu_init();
+ local_fiq_enable();
+ }
msm_pm_boot_config_after_pc(cpu);
- if (collapsed) {
-#ifdef CONFIG_VFP
- vfp_pm_resume();
-#endif
- cpu_init();
- writel(0xF0, MSM_QGIC_CPU_BASE + GIC_CPU_PRIMASK);
- writel_relaxed(saved_gic_cpu_ctrl,
- MSM_QGIC_CPU_BASE + GIC_CPU_CTRL);
- mb();
- local_fiq_enable();
- }
+ if (from_idle)
+ cpu_pm_exit();
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
@@ -648,14 +534,11 @@
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
WARN_ON(ret);
-
- if (from_idle == true)
- cpu_pm_exit();
-
return collapsed;
}
-static bool msm_pm_power_collapse_standalone(bool from_idle)
+static enum msm_pm_time_stats_id msm_pm_power_collapse_standalone(
+ bool from_idle)
{
unsigned int cpu = smp_processor_id();
unsigned int avsdscr;
@@ -670,7 +553,8 @@
avs_set_avsdscr(avsdscr);
avs_set_avscsr(avscsr);
- return collapsed;
+ return collapsed ? MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE :
+ MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE;
}
static int ramp_down_last_cpu(int cpu)
@@ -725,7 +609,7 @@
return rc;
}
-static bool msm_pm_power_collapse(bool from_idle)
+static enum msm_pm_time_stats_id msm_pm_power_collapse(bool from_idle)
{
unsigned int cpu = smp_processor_id();
unsigned long saved_acpuclk_rate = 0;
@@ -737,7 +621,6 @@
pr_info("CPU%u: %s: idle %d\n",
cpu, __func__, (int)from_idle);
- msm_pm_config_hw_before_power_down();
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
@@ -748,120 +631,22 @@
if (cpu_online(cpu) && !msm_no_ramp_down_pc)
saved_acpuclk_rate = ramp_down_last_cpu(cpu);
- if (cp15_data.save_cp15)
- msm_pm_save_cpu_reg();
-
collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
- if (cp15_data.save_cp15)
- msm_pm_restore_cpu_reg();
-
- if (cpu_online(cpu) && !msm_no_ramp_down_pc) {
+ if (cpu_online(cpu) && !msm_no_ramp_down_pc)
ramp_up_first_cpu(cpu, 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_set_avsdscr(avsdscr);
avs_set_avscsr(avscsr);
- msm_pm_config_hw_after_power_up();
+
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: post power up\n", cpu, __func__);
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: return\n", cpu, __func__);
- return collapsed;
+ return collapsed ? MSM_PM_STAT_IDLE_POWER_COLLAPSE :
+ MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
}
-
-static int64_t msm_pm_timer_enter_idle(void)
-{
- if (msm_pm_use_sync_timer)
- return ktime_to_ns(tick_nohz_get_sleep_length());
-
- return msm_timer_enter_idle();
-}
-
-static void msm_pm_timer_exit_idle(bool timer_halted)
-{
- if (msm_pm_use_sync_timer)
- return;
-
- msm_timer_exit_idle((int) timer_halted);
-}
-
-static int64_t msm_pm_timer_enter_suspend(int64_t *period)
-{
- int64_t time = 0;
-
- if (msm_pm_use_sync_timer) {
- struct timespec ts;
- getnstimeofday(&ts);
- return timespec_to_ns(&ts);
- }
-
- time = msm_timer_get_sclk_time(period);
- if (!time)
- pr_err("%s: Unable to read sclk.\n", __func__);
-
- return time;
-}
-
-static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period)
-{
- if (msm_pm_use_sync_timer) {
- struct timespec ts;
- getnstimeofday(&ts);
-
- return timespec_to_ns(&ts) - time;
- }
-
- if (time != 0) {
- int64_t end_time = msm_timer_get_sclk_time(NULL);
- if (end_time != 0) {
- time = end_time - time;
- if (time < 0)
- time += period;
- } else
- time = 0;
- }
-
- return time;
-}
-
-/**
- * pm_hrtimer_cb() : Callback function for hrtimer created if the
- * core needs to be awake to handle an event.
- * @hrtimer : Pointer to hrtimer
- */
-static enum hrtimer_restart pm_hrtimer_cb(struct hrtimer *hrtimer)
-{
- return HRTIMER_NORESTART;
-}
-
-/**
- * msm_pm_set_timer() : Set an hrtimer to wakeup the core in time
- * to handle an event.
- */
-static void msm_pm_set_timer(uint32_t modified_time_us)
-{
- u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
- ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
- pm_hrtimer.function = pm_hrtimer_cb;
- hrtimer_start(&pm_hrtimer, modified_ktime, HRTIMER_MODE_REL);
-}
-
/******************************************************************************
* External Idle/Suspend Functions
*****************************************************************************/
@@ -916,221 +701,66 @@
}
}
-static int msm_pm_idle_prepare(struct cpuidle_device *dev,
- struct cpuidle_driver *drv, int index,
- void **msm_pm_idle_rs_limits)
+static enum msm_pm_time_stats_id (*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,
+};
+
+bool msm_cpu_pm_check_mode(unsigned int cpu, enum msm_pm_sleep_mode mode,
+ bool from_idle)
{
- int i;
- unsigned int power_usage = -1;
- int ret = MSM_PM_SLEEP_MODE_NOT_SELECTED;
- uint32_t modified_time_us = 0;
- struct msm_pm_time_params time_param;
+ int idx = MSM_PM_MODE(cpu, mode);
+ struct msm_pm_platform_data *d = &msm_pm_sleep_modes[idx];
- time_param.latency_us =
- (uint32_t) pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
- time_param.sleep_us =
- (uint32_t) (ktime_to_us(tick_nohz_get_sleep_length())
- & UINT_MAX);
- time_param.modified_time_us = 0;
+ if ((mode == MSM_PM_SLEEP_MODE_RETENTION)
+ && !msm_pm_ldo_retention_enabled)
+ return false;
- if (!dev->cpu)
- time_param.next_event_us =
- (uint32_t) (ktime_to_us(get_next_event_time())
- & UINT_MAX);
+ if (from_idle)
+ return d->idle_enabled && d->idle_supported;
else
- time_param.next_event_us = 0;
-
- for (i = 0; i < dev->state_count; i++) {
- struct cpuidle_state *state = &drv->states[i];
- struct cpuidle_state_usage *st_usage = &dev->states_usage[i];
- enum msm_pm_sleep_mode mode;
- bool allow;
- uint32_t power;
- int idx;
- void *rs_limits = NULL;
-
- mode = (enum msm_pm_sleep_mode) cpuidle_get_statedata(st_usage);
- idx = MSM_PM_MODE(dev->cpu, mode);
-
- allow = msm_pm_sleep_modes[idx].idle_enabled &&
- msm_pm_sleep_modes[idx].idle_supported;
-
- switch (mode) {
- case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
- if (num_online_cpus() > 1)
- allow = false;
- break;
- case MSM_PM_SLEEP_MODE_RETENTION:
- /*
- * The Krait BHS regulator doesn't have enough head
- * room to drive the retention voltage on LDO and so
- * has disabled retention
- */
- if (!msm_pm_ldo_retention_enabled)
- allow = false;
-
- if (msm_pm_retention_calls_tz && num_online_cpus() > 1)
- allow = false;
- break;
- case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
- case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
- break;
- default:
- allow = false;
- break;
- }
-
- if (!allow)
- continue;
-
- if (pm_sleep_ops.lowest_limits)
- rs_limits = pm_sleep_ops.lowest_limits(true,
- mode, &time_param, &power);
-
- if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
- pr_info("CPU%u:%s:%s, latency %uus, slp %uus, lim %p\n",
- dev->cpu, __func__, state->desc,
- time_param.latency_us,
- time_param.sleep_us, rs_limits);
- if (!rs_limits)
- continue;
-
- if (power < power_usage) {
- power_usage = power;
- modified_time_us = time_param.modified_time_us;
- ret = mode;
- *msm_pm_idle_rs_limits = rs_limits;
- }
-
- }
-
- if (modified_time_us && !dev->cpu)
- msm_pm_set_timer(modified_time_us);
-
- msm_pm_ftrace_lpm_enter(dev->cpu, time_param.latency_us,
- time_param.sleep_us, time_param.next_event_us,
- ret);
-
- return ret;
+ return d->suspend_enabled && d->suspend_supported;
}
-enum msm_pm_sleep_mode msm_pm_idle_enter(struct cpuidle_device *dev,
- struct cpuidle_driver *drv, int index)
+int msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
{
int64_t time;
bool collapsed = 1;
int exit_stat = -1;
- enum msm_pm_sleep_mode sleep_mode;
- void *msm_pm_idle_rs_limits = NULL;
- uint32_t sleep_delay = 1;
- int ret = -ENODEV;
- int notify_rpm = false;
- bool timer_halted = false;
-
- sleep_mode = msm_pm_idle_prepare(dev, drv, index,
- &msm_pm_idle_rs_limits);
-
- if (!msm_pm_idle_rs_limits) {
- sleep_mode = MSM_PM_SLEEP_MODE_NOT_SELECTED;
- goto cpuidle_enter_bail;
- }
if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
pr_info("CPU%u: %s: mode %d\n",
- smp_processor_id(), __func__, sleep_mode);
+ smp_processor_id(), __func__, mode);
+ if (!from_idle)
+ pr_info("CPU%u: %s mode:%d\n",
+ smp_processor_id(), __func__, mode);
- time = ktime_to_ns(ktime_get());
-
- if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
- int64_t ns = msm_pm_timer_enter_idle();
- notify_rpm = true;
- do_div(ns, NSEC_PER_SEC / SCLK_HZ);
- sleep_delay = (uint32_t)ns;
-
- if (sleep_delay == 0) /* 0 would mean infinite time */
- sleep_delay = 1;
- }
-
- if (pm_sleep_ops.enter_sleep)
- ret = pm_sleep_ops.enter_sleep(sleep_delay,
- msm_pm_idle_rs_limits, true, notify_rpm);
- if (ret)
- goto cpuidle_enter_bail;
-
- switch (sleep_mode) {
- case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
- msm_pm_swfi();
- exit_stat = MSM_PM_STAT_IDLE_WFI;
- break;
-
- case MSM_PM_SLEEP_MODE_RETENTION:
- msm_pm_retention();
- exit_stat = MSM_PM_STAT_RETENTION;
- break;
-
- case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
- collapsed = msm_pm_power_collapse_standalone(true);
- if (collapsed)
- exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
- else
- exit_stat
- = MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE;
- break;
-
- case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
- if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
- clock_debug_print_enabled();
-
- collapsed = msm_pm_power_collapse(true);
- timer_halted = true;
-
- if (collapsed)
- exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
- else
- exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
-
- msm_pm_timer_exit_idle(timer_halted);
- break;
-
- case MSM_PM_SLEEP_MODE_NOT_SELECTED:
- goto cpuidle_enter_bail;
- break;
-
- default:
- __WARN();
- goto cpuidle_enter_bail;
- break;
- }
-
- if (pm_sleep_ops.exit_sleep)
- pm_sleep_ops.exit_sleep(msm_pm_idle_rs_limits, true,
- notify_rpm, collapsed);
-
- time = ktime_to_ns(ktime_get()) - time;
- msm_pm_ftrace_lpm_exit(smp_processor_id(), sleep_mode, collapsed);
+ time = sched_clock();
+ if (execute[mode])
+ exit_stat = execute[mode](from_idle);
+ time = sched_clock() - time;
+ if (from_idle)
+ msm_pm_ftrace_lpm_exit(smp_processor_id(), mode, collapsed);
+ else
+ exit_stat = MSM_PM_STAT_SUSPEND;
if (exit_stat >= 0)
msm_pm_add_stat(exit_stat, time);
do_div(time, 1000);
- dev->last_residency = (int) time;
- return sleep_mode;
-
-cpuidle_enter_bail:
- dev->last_residency = 0;
- if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
- msm_pm_timer_exit_idle(timer_halted);
- sleep_mode = MSM_PM_SLEEP_MODE_NOT_SELECTED;
- return sleep_mode;
+ return collapsed;
}
int msm_pm_wait_cpu_shutdown(unsigned int cpu)
{
- int timeout = 0;
+ int timeout = 10;
if (!msm_pm_slp_sts)
return 0;
if (!msm_pm_slp_sts[cpu].base_addr)
return 0;
- while (1) {
+ while (timeout--) {
/*
* Check for the SPM of the core being hotplugged to set
* its sleep state.The SPM sleep state indicates that the
@@ -1141,10 +771,10 @@
if (acc_sts & msm_pm_slp_sts[cpu].mask)
return 0;
udelay(100);
- WARN(++timeout == 20, "CPU%u didn't collape within 2ms\n",
- cpu);
}
+ pr_info("%s(): Timed out waiting for CPU %u SPM to enter sleep state",
+ __func__, cpu);
return -EBUSY;
}
@@ -1168,9 +798,9 @@
else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE])
msm_pm_power_collapse_standalone(false);
else if (allow[MSM_PM_SLEEP_MODE_RETENTION])
- msm_pm_retention();
+ msm_pm_retention(false);
else
- msm_pm_swfi();
+ msm_pm_swfi(false);
}
static void msm_pm_ack_retention_disable(void *data)
@@ -1191,6 +821,7 @@
return;
msm_pm_ldo_retention_enabled = enable;
+
/*
* If retention is being disabled, wakeup all online core to ensure
* that it isn't executing retention. Offlined cores need not be woken
@@ -1199,142 +830,15 @@
*/
if (!enable) {
preempt_disable();
- smp_call_function_many(cpu_online_mask,
+ smp_call_function_many(&retention_cpus,
msm_pm_ack_retention_disable,
NULL, true);
preempt_enable();
-
-
}
}
EXPORT_SYMBOL(msm_pm_enable_retention);
-static int64_t suspend_time, suspend_period;
-static int collapsed;
-static int suspend_power_collapsed;
-
-static int msm_pm_enter(suspend_state_t state)
-{
- bool allow[MSM_PM_SLEEP_MODE_NR];
- int i;
- struct msm_pm_time_params time_param;
-
- time_param.latency_us = -1;
- time_param.sleep_us = -1;
- time_param.next_event_us = 0;
-
- if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
- pr_info("%s\n", __func__);
-
- if (smp_processor_id()) {
- __WARN();
- goto enter_exit;
- }
-
-
- for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
- struct msm_pm_platform_data *mode;
-
- mode = &msm_pm_sleep_modes[MSM_PM_MODE(0, i)];
- allow[i] = mode->suspend_supported && mode->suspend_enabled;
- }
-
- if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
- void *rs_limits = NULL;
- int ret = -ENODEV;
- uint32_t power;
- uint32_t msm_pm_max_sleep_time = 0;
-
- if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
- pr_info("%s: power collapse\n", __func__);
-
- clock_debug_print_enabled();
-
- if (msm_pm_sleep_time_override > 0) {
- int64_t ns = NSEC_PER_SEC *
- (int64_t) msm_pm_sleep_time_override;
- do_div(ns, NSEC_PER_SEC / SCLK_HZ);
- msm_pm_max_sleep_time = (uint32_t) ns;
- }
-
- if (pm_sleep_ops.lowest_limits)
- rs_limits = pm_sleep_ops.lowest_limits(false,
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE, &time_param, &power);
-
- if (rs_limits) {
- if (pm_sleep_ops.enter_sleep)
- ret = pm_sleep_ops.enter_sleep(
- msm_pm_max_sleep_time,
- rs_limits, false, true);
- if (!ret) {
- collapsed = msm_pm_power_collapse(false);
- if (pm_sleep_ops.exit_sleep) {
- pm_sleep_ops.exit_sleep(rs_limits,
- false, true, collapsed);
- }
- }
- } else {
- pr_err("%s: cannot find the lowest power limit\n",
- __func__);
- }
- suspend_power_collapsed = true;
- } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
- 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__);
- msm_pm_swfi();
- }
-
-enter_exit:
- if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
- pr_info("%s: return\n", __func__);
-
- return 0;
-}
-
-void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops)
-{
- if (ops)
- pm_sleep_ops = *ops;
-}
-
-static int msm_suspend_prepare(void)
-{
- suspend_time = msm_pm_timer_enter_suspend(&suspend_period);
- msm_mpm_suspend_prepare();
- return 0;
-}
-
-static void msm_suspend_wake(void)
-{
- msm_mpm_suspend_wake();
- if (suspend_power_collapsed) {
- suspend_time = msm_pm_timer_exit_suspend(suspend_time,
- suspend_period);
- if (collapsed)
- msm_pm_add_stat(MSM_PM_STAT_SUSPEND, suspend_time);
- else
- msm_pm_add_stat(MSM_PM_STAT_FAILED_SUSPEND,
- suspend_time);
- suspend_power_collapsed = false;
- }
-}
-
-static const struct platform_suspend_ops msm_pm_ops = {
- .enter = msm_pm_enter,
- .valid = suspend_valid_only_mem,
- .prepare_late = msm_suspend_prepare,
- .wake = msm_suspend_wake,
-};
-
-static int __devinit msm_pm_snoc_client_probe(struct platform_device *pdev)
+static int msm_pm_snoc_client_probe(struct platform_device *pdev)
{
int rc = 0;
static struct msm_bus_scale_pdata *msm_pm_bus_pdata;
@@ -1347,8 +851,7 @@
msm_bus_scale_register_client(msm_pm_bus_pdata);
if (!msm_pm_bus_client) {
- pr_err("%s: Failed to register SNOC client",
- __func__);
+ pr_err("%s: Failed to register SNOC client", __func__);
rc = -ENXIO;
goto snoc_cl_probe_done;
}
@@ -1363,7 +866,7 @@
return rc;
}
-static int __devinit msm_cpu_status_probe(struct platform_device *pdev)
+static int msm_cpu_status_probe(struct platform_device *pdev)
{
struct msm_pm_sleep_status_data *pdata;
char *key;
@@ -1372,9 +875,9 @@
if (!pdev)
return -EFAULT;
- msm_pm_slp_sts =
- kzalloc(sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
- GFP_KERNEL);
+ msm_pm_slp_sts = devm_kzalloc(&pdev->dev,
+ sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
+ GFP_KERNEL);
if (!msm_pm_slp_sts)
return -ENOMEM;
@@ -1387,34 +890,34 @@
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
- goto fail_free_mem;
+ return -ENODEV;
key = "qcom,cpu-alias-addr";
rc = of_property_read_u32(pdev->dev.of_node, key, &offset);
if (rc)
- goto fail_free_mem;
+ return -ENODEV;
key = "qcom,sleep-status-mask";
- rc = of_property_read_u32(pdev->dev.of_node, key,
- &mask);
+ rc = of_property_read_u32(pdev->dev.of_node, key, &mask);
+
if (rc)
- goto fail_free_mem;
+ return -ENODEV;
for_each_possible_cpu(cpu) {
+ phys_addr_t base_c = res->start + cpu * offset;
msm_pm_slp_sts[cpu].base_addr =
- ioremap(res->start + cpu * offset,
- resource_size(res));
+ devm_ioremap(&pdev->dev, base_c,
+ resource_size(res));
msm_pm_slp_sts[cpu].mask = mask;
if (!msm_pm_slp_sts[cpu].base_addr)
- goto failed_of_node;
+ return -ENOMEM;
}
-
} else {
pdata = pdev->dev.platform_data;
if (!pdev->dev.platform_data)
- goto fail_free_mem;
+ return -EINVAL;
for_each_possible_cpu(cpu) {
msm_pm_slp_sts[cpu].base_addr =
@@ -1424,17 +927,6 @@
}
return 0;
-
-failed_of_node:
- pr_info("%s(): Failed to key=%s\n", __func__, key);
- for_each_possible_cpu(cpu) {
- if (msm_pm_slp_sts[cpu].base_addr)
- iounmap(msm_pm_slp_sts[cpu].base_addr);
- }
-fail_free_mem:
- kfree(msm_pm_slp_sts);
- return -EINVAL;
-
};
static struct of_device_id msm_slp_sts_match_tbl[] = {
@@ -1465,55 +957,24 @@
},
};
-static void setup_broadcast_timer(void *arg)
-{
- int cpu = smp_processor_id();
-
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
-}
-
-static int setup_broadcast_cpuhp_notify(struct notifier_block *n,
- unsigned long action, void *hcpu)
-{
- int cpu = (unsigned long)hcpu;
-
- switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_ONLINE:
- smp_call_function_single(cpu, setup_broadcast_timer, NULL, 1);
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block setup_broadcast_notifier = {
- .notifier_call = setup_broadcast_cpuhp_notify,
-};
-
-static int __init msm_pm_init(void)
+static int msm_pm_init(void)
{
enum msm_pm_time_stats_id enable_stats[] = {
MSM_PM_STAT_IDLE_WFI,
MSM_PM_STAT_RETENTION,
MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
MSM_PM_STAT_IDLE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
MSM_PM_STAT_SUSPEND,
};
msm_pm_mode_sysfs_add();
msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
- suspend_set_ops(&msm_pm_ops);
- hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- msm_cpuidle_init();
-
- if (msm_pm_pc_reset_timer) {
- on_each_cpu(setup_broadcast_timer, NULL, 1);
- register_cpu_notifier(&setup_broadcast_notifier);
- }
return 0;
}
-static void __devinit msm_pm_set_flush_fn(uint32_t pc_mode)
+static void msm_pm_set_flush_fn(uint32_t pc_mode)
{
msm_pm_disable_l2_fn = NULL;
msm_pm_enable_l2_fn = NULL;
@@ -1555,10 +1016,10 @@
sizeof(data->buf)-data->len,
"CPU%d\n", cpu);
- for (j = 0; j < MSM_PC_NUM_COUNTERS; j++) {
- stat = msm_pc_debug_counters_read_register(
- data->reg, cpu, j);
- data->len += scnprintf(data->buf + data->len,
+ for (j = 0; j < MSM_PC_NUM_COUNTERS; j++) {
+ stat = msm_pc_debug_counters_read_register(
+ data->reg, cpu, j);
+ data->len += scnprintf(data->buf + data->len,
sizeof(data->buf)-data->len,
"\t%s : %d\n", counter_name[j],
stat);
@@ -1579,7 +1040,7 @@
if (!data)
return -EINVAL;
- if (!bufu)
+ if (!bufu || count < 0)
return -EINVAL;
if (!access_ok(VERIFY_WRITE, bufu, count))
@@ -1673,50 +1134,42 @@
return PTR_RET(l2_clk);
}
-static int __devinit msm_pm_8x60_probe(struct platform_device *pdev)
+static int msm_cpu_pm_probe(struct platform_device *pdev)
{
char *key = NULL;
struct dentry *dent = NULL;
struct resource *res = NULL;
- int i ;
+ int i;
struct msm_pm_init_data_type pdata_local;
int ret = 0;
memset(&pdata_local, 0, sizeof(struct msm_pm_init_data_type));
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- msm_pc_debug_counters_phys = res->start;
- WARN_ON(resource_size(res) < SZ_64);
- msm_pc_debug_counters = devm_ioremap(&pdev->dev, res->start,
+ if (!res)
+ return 0;
+ msm_pc_debug_counters_phys = res->start;
+ WARN_ON(resource_size(res) < SZ_64);
+ msm_pc_debug_counters = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
- if (msm_pc_debug_counters)
- for (i = 0; i < resource_size(res)/4; i++)
- __raw_writel(0, msm_pc_debug_counters + i * 4);
+ if (msm_pc_debug_counters) {
+ for (i = 0; i < resource_size(res)/4; i++)
+ __raw_writel(0, msm_pc_debug_counters + i * 4);
- }
-
- if (!msm_pc_debug_counters) {
- msm_pc_debug_counters = 0;
- msm_pc_debug_counters_phys = 0;
- } else {
dent = debugfs_create_file("pc_debug_counter", S_IRUGO, NULL,
msm_pc_debug_counters,
&msm_pc_debug_counters_fops);
if (!dent)
pr_err("%s: ERROR debugfs_create_file failed\n",
__func__);
+ } else {
+ msm_pc_debug_counters = 0;
+ msm_pc_debug_counters_phys = 0;
}
- if (!pdev->dev.of_node) {
- struct msm_pm_init_data_type *d = pdev->dev.platform_data;
+ if (pdev->dev.of_node) {
+ enum msm_pm_pc_mode_type pc_mode;
- if (!d)
- goto pm_8x60_probe_done;
-
- memcpy(&pdata_local, d, sizeof(struct msm_pm_init_data_type));
-
- } else {
ret = msm_pm_clk_init(pdev);
if (ret) {
pr_info("msm_pm_clk_init returned error\n");
@@ -1724,52 +1177,14 @@
}
key = "qcom,pc-mode";
- ret = msm_pm_get_pc_mode(pdev->dev.of_node,
- key,
- &pdata_local.pc_mode);
+ ret = msm_pm_get_pc_mode(pdev->dev.of_node, key, &pc_mode);
if (ret) {
- pr_debug("%s: Error reading key %s",
- __func__, key);
+ pr_debug("%s: Error reading key %s", __func__, key);
return -EINVAL;
}
-
- key = "qcom,use-sync-timer";
- pdata_local.use_sync_timer =
- of_property_read_bool(pdev->dev.of_node, key);
-
- key = "qcom,saw-turns-off-pll";
- msm_no_ramp_down_pc = of_property_read_bool(pdev->dev.of_node,
- key);
-
- key = "qcom,pc-resets-timer";
- msm_pm_pc_reset_timer = of_property_read_bool(
- pdev->dev.of_node, key);
+ msm_pm_set_flush_fn(pc_mode);
}
- if (pdata_local.cp15_data.reg_data &&
- pdata_local.cp15_data.reg_saved_state_size > 0) {
- cp15_data.reg_data = kzalloc(sizeof(uint32_t) *
- pdata_local.cp15_data.reg_saved_state_size,
- GFP_KERNEL);
- if (!cp15_data.reg_data)
- return -ENOMEM;
-
- cp15_data.reg_val = kzalloc(sizeof(uint32_t) *
- pdata_local.cp15_data.reg_saved_state_size,
- GFP_KERNEL);
- if (cp15_data.reg_val)
- return -ENOMEM;
-
- memcpy(cp15_data.reg_data, pdata_local.cp15_data.reg_data,
- pdata_local.cp15_data.reg_saved_state_size *
- sizeof(uint32_t));
- }
-
- msm_pm_set_flush_fn(pdata_local.pc_mode);
- msm_pm_use_sync_timer = pdata_local.use_sync_timer;
- msm_pm_retention_calls_tz = pdata_local.retention_calls_tz;
-
-pm_8x60_probe_done:
msm_pm_init();
if (pdev->dev.of_node)
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
@@ -1777,24 +1192,26 @@
return ret;
}
-static struct of_device_id msm_pm_8x60_table[] = {
- {.compatible = "qcom,pm-8x60"},
- {},
+static struct of_device_id msm_cpu_pm_table[] = {
+ {.compatible = "qcom,pm-8x60"},
+ {},
};
-static struct platform_driver msm_pm_8x60_driver = {
- .probe = msm_pm_8x60_probe,
- .driver = {
- .name = "pm-8x60",
- .owner = THIS_MODULE,
- .of_match_table = msm_pm_8x60_table,
- },
+static struct platform_driver msm_cpu_pm_driver = {
+ .probe = msm_cpu_pm_probe,
+ .driver = {
+ .name = "pm-8x60",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cpu_pm_table,
+ },
};
-static int __init msm_pm_8x60_init(void)
+static int __init msm_cpu_pm_init(void)
{
int rc;
+ cpumask_clear(&retention_cpus);
+
rc = platform_driver_register(&msm_cpu_pm_snoc_client_driver);
if (rc) {
@@ -1803,9 +1220,9 @@
return rc;
}
- return platform_driver_register(&msm_pm_8x60_driver);
+ return platform_driver_register(&msm_cpu_pm_driver);
}
-device_initcall(msm_pm_8x60_init);
+device_initcall(msm_cpu_pm_init);
void __init msm_pm_sleep_status_init(void)
{