blob: ff7a9303c7122030f9c1d1782e7bfdf352d04ca2 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/pm2.c
2 *
3 * MSM Power Management Routines
4 *
5 * Copyright (C) 2007 Google, Inc.
Murali Nalajala0df9fee2012-01-12 15:26:09 +05306 * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07007 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/clk.h>
22#include <linux/delay.h>
23#include <linux/init.h>
24#include <linux/pm.h>
25#include <linux/pm_qos_params.h>
26#include <linux/proc_fs.h>
27#include <linux/suspend.h>
28#include <linux/reboot.h>
29#include <linux/uaccess.h>
30#include <linux/io.h>
31#include <linux/memory.h>
32#ifdef CONFIG_HAS_WAKELOCK
33#include <linux/wakelock.h>
34#endif
35#include <mach/msm_iomap.h>
36#include <mach/system.h>
37#ifdef CONFIG_CPU_V7
38#include <asm/pgtable.h>
39#include <asm/pgalloc.h>
40#endif
41#ifdef CONFIG_CACHE_L2X0
42#include <asm/hardware/cache-l2x0.h>
43#endif
44#ifdef CONFIG_VFP
45#include <asm/vfp.h>
46#endif
47
48#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
49#include <mach/msm_migrate_pages.h>
50#endif
Murali Nalajala41786ab2012-03-06 10:47:32 +053051#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052
53#include "smd_private.h"
54#include "smd_rpcrouter.h"
55#include "acpuclock.h"
56#include "clock.h"
57#include "proc_comm.h"
58#include "idle.h"
59#include "irq.h"
60#include "gpio.h"
61#include "timer.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080062#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063#include "spm.h"
64#include "sirc.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060065#include "pm-boot.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066
67/******************************************************************************
68 * Debug Definitions
69 *****************************************************************************/
70
71enum {
72 MSM_PM_DEBUG_SUSPEND = 1U << 0,
73 MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1,
74 MSM_PM_DEBUG_STATE = 1U << 2,
75 MSM_PM_DEBUG_CLOCK = 1U << 3,
76 MSM_PM_DEBUG_RESET_VECTOR = 1U << 4,
77 MSM_PM_DEBUG_SMSM_STATE = 1U << 5,
78 MSM_PM_DEBUG_IDLE = 1U << 6,
79};
80
81static int msm_pm_debug_mask;
82module_param_named(
83 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
84);
85
86#define MSM_PM_DPRINTK(mask, level, message, ...) \
87 do { \
88 if ((mask) & msm_pm_debug_mask) \
89 printk(level message, ## __VA_ARGS__); \
90 } while (0)
91
92#define MSM_PM_DEBUG_PRINT_STATE(tag) \
93 do { \
94 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
95 KERN_INFO, "%s: " \
96 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
97 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
98 "SMSM_APPS_DEM %x\n", \
99 tag, \
100 __raw_readl(APPS_CLK_SLEEP_EN), \
101 __raw_readl(APPS_PWRDOWN), \
102 smsm_get_state(SMSM_POWER_MASTER_DEM), \
103 smsm_get_state(SMSM_MODEM_STATE), \
104 smsm_get_state(SMSM_APPS_DEM)); \
105 } while (0)
106
107#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
108 do { \
109 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
110 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
111 msm_pm_smem_data->resources_used, \
112 msm_pm_smem_data->irq_mask, \
113 msm_pm_smem_data->wakeup_reason, \
114 msm_pm_smem_data->pending_irqs); \
115 } while (0)
116
117
118/******************************************************************************
119 * Sleep Modes and Parameters
120 *****************************************************************************/
121
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
123module_param_named(
124 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
125 int, S_IRUGO | S_IWUSR | S_IWGRP
126);
127
128enum {
129 MSM_PM_MODE_ATTR_SUSPEND,
130 MSM_PM_MODE_ATTR_IDLE,
131 MSM_PM_MODE_ATTR_LATENCY,
132 MSM_PM_MODE_ATTR_RESIDENCY,
133 MSM_PM_MODE_ATTR_NR,
134};
135
136static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
137 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
138 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
139 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
140 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
141};
142
143static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
144 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
145 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
147 "ramp_down_and_wfi",
148 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
149 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
150 "power_collapse_no_xo_shutdown",
151 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
152 "standalone_power_collapse",
153};
154
155static struct msm_pm_platform_data *msm_pm_modes;
156
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530157struct msm_pm_kobj_attribute {
158 unsigned int cpu;
159 struct kobj_attribute ka;
160};
161
162#define GET_CPU_OF_ATTR(attr) \
163 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
164
165struct msm_pm_sysfs_sleep_mode {
166 struct kobject *kobj;
167 struct attribute_group attr_group;
168 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
169 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
170};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171
172/*
173 * Write out the attribute.
174 */
175static ssize_t msm_pm_mode_attr_show(
176 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
177{
178 int ret = -EINVAL;
179 int i;
180
181 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
182 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530183 unsigned int cpu;
184 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185
186 if (msm_pm_sleep_mode_labels[i] == NULL)
187 continue;
188
189 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
190 continue;
191
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530192 cpu = GET_CPU_OF_ATTR(attr);
193 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
194
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 if (!strcmp(attr->attr.name,
196 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530197 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 kp.arg = &arg;
199 ret = param_get_ulong(buf, &kp);
200 } else if (!strcmp(attr->attr.name,
201 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530202 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203 kp.arg = &arg;
204 ret = param_get_ulong(buf, &kp);
205 } else if (!strcmp(attr->attr.name,
206 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530207 u32 arg = mode->latency;
208 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 ret = param_get_ulong(buf, &kp);
210 } else if (!strcmp(attr->attr.name,
211 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530212 u32 arg = mode->residency;
213 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 ret = param_get_ulong(buf, &kp);
215 }
216
217 break;
218 }
219
220 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530221 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 ret++;
223 }
224
225 return ret;
226}
227
228/*
229 * Read in the new attribute value.
230 */
231static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
232 struct kobj_attribute *attr, const char *buf, size_t count)
233{
234 int ret = -EINVAL;
235 int i;
236
237 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
238 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530239 unsigned int cpu;
240 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241
242 if (msm_pm_sleep_mode_labels[i] == NULL)
243 continue;
244
245 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
246 continue;
247
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530248 cpu = GET_CPU_OF_ATTR(attr);
249 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
250
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251 if (!strcmp(attr->attr.name,
252 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530253 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 ret = param_set_byte(buf, &kp);
255 } else if (!strcmp(attr->attr.name,
256 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530257 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 ret = param_set_byte(buf, &kp);
259 } else if (!strcmp(attr->attr.name,
260 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530261 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262 ret = param_set_ulong(buf, &kp);
263 } else if (!strcmp(attr->attr.name,
264 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530265 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266 ret = param_set_ulong(buf, &kp);
267 }
268
269 break;
270 }
271
272 return ret ? ret : count;
273}
274
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530275 /* Add sysfs entries for one cpu. */
276static int __init msm_pm_mode_sysfs_add_cpu(
277 unsigned int cpu, struct kobject *modes_kobj)
278{
279 char cpu_name[8];
280 struct kobject *cpu_kobj;
281 struct msm_pm_sysfs_sleep_mode *mode = NULL;
282 int i, j, k;
283 int ret;
284
285 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
286 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
287 if (!cpu_kobj) {
288 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
289 ret = -ENOMEM;
290 goto mode_sysfs_add_cpu_exit;
291 }
292
293 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
294 int idx = MSM_PM_MODE(cpu, i);
295
296 if ((!msm_pm_modes[idx].suspend_supported) &&
297 (!msm_pm_modes[idx].idle_supported))
298 continue;
299
300 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
301 if (!mode) {
302 pr_err("%s: cannot allocate memory for attributes\n",
303 __func__);
304 ret = -ENOMEM;
305 goto mode_sysfs_add_cpu_exit;
306 }
307
308 mode->kobj = kobject_create_and_add(
309 msm_pm_sleep_mode_labels[i], cpu_kobj);
310 if (!mode->kobj) {
311 pr_err("%s: cannot create kobject\n", __func__);
312 ret = -ENOMEM;
313 goto mode_sysfs_add_cpu_exit;
314 }
315
316 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
317 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
318 !msm_pm_modes[idx].idle_supported)
319 continue;
320 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
321 !msm_pm_modes[idx].suspend_supported)
322 continue;
323 mode->kas[j].cpu = cpu;
324 mode->kas[j].ka.attr.mode = 0644;
325 mode->kas[j].ka.show = msm_pm_mode_attr_show;
326 mode->kas[j].ka.store = msm_pm_mode_attr_store;
327 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
328 mode->attrs[j] = &mode->kas[j].ka.attr;
329 j++;
330 }
331 mode->attrs[j] = NULL;
332
333 mode->attr_group.attrs = mode->attrs;
334 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
335 if (ret) {
336 printk(KERN_ERR
337 "%s: cannot create kobject attribute group\n",
338 __func__);
339 goto mode_sysfs_add_cpu_exit;
340 }
341 }
342
343 ret = 0;
344
345mode_sysfs_add_cpu_exit:
346 if (ret) {
347 if (mode && mode->kobj)
348 kobject_del(mode->kobj);
349 kfree(mode);
350 }
351
352 return ret;
353}
354
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355/*
356 * Add sysfs entries for the sleep modes.
357 */
358static int __init msm_pm_mode_sysfs_add(void)
359{
360 struct kobject *module_kobj = NULL;
361 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530362 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363 int ret;
364
365 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
366 if (!module_kobj) {
367 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
368 __func__, KBUILD_MODNAME);
369 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530370 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371 }
372
373 modes_kobj = kobject_create_and_add("modes", module_kobj);
374 if (!modes_kobj) {
375 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
376 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530377 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 }
379
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530380 for_each_possible_cpu(cpu) {
381 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
382 if (ret)
383 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 }
385
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530386 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530388mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 return ret;
390}
391
392void __init msm_pm_set_platform_data(
393 struct msm_pm_platform_data *data, int count)
394{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530395 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 msm_pm_modes = data;
397}
398
399
400/******************************************************************************
401 * Sleep Limitations
402 *****************************************************************************/
403enum {
404 SLEEP_LIMIT_NONE = 0,
405 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
406 SLEEP_LIMIT_MASK = 0x03,
407};
408
409#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
410enum {
411 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
412 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
413};
414#endif
415
416
417/******************************************************************************
418 * Configure Hardware for Power Down/Up
419 *****************************************************************************/
420
421#if defined(CONFIG_ARCH_MSM7X30)
422#define APPS_CLK_SLEEP_EN (MSM_GCC_BASE + 0x020)
423#define APPS_PWRDOWN (MSM_ACC_BASE + 0x01c)
424#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
425#else /* defined(CONFIG_ARCH_MSM7X30) */
426#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
427#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
428#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
429#endif /* defined(CONFIG_ARCH_MSM7X30) */
430
431/*
432 * Configure hardware registers in preparation for Apps power down.
433 */
434static void msm_pm_config_hw_before_power_down(void)
435{
436#if defined(CONFIG_ARCH_MSM7X30)
437 __raw_writel(1, APPS_PWRDOWN);
438 mb();
439 __raw_writel(4, APPS_SECOP);
440 mb();
441#elif defined(CONFIG_ARCH_MSM7X27)
442 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
443 mb();
444 __raw_writel(1, APPS_PWRDOWN);
445 mb();
446#elif defined(CONFIG_ARCH_MSM7x27A)
447 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
448 mb();
449 __raw_writel(1, APPS_PWRDOWN);
450 mb();
451#else
452 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
453 mb();
454 __raw_writel(1, APPS_PWRDOWN);
455 mb();
456 __raw_writel(0, APPS_STANDBY_CTL);
457 mb();
458#endif
459}
460
461/*
462 * Clear hardware registers after Apps powers up.
463 */
464static void msm_pm_config_hw_after_power_up(void)
465{
466#if defined(CONFIG_ARCH_MSM7X30)
467 __raw_writel(0, APPS_SECOP);
468 mb();
469 __raw_writel(0, APPS_PWRDOWN);
470 mb();
471 msm_spm_reinit();
472#elif defined(CONFIG_ARCH_MSM7x27A)
473 __raw_writel(0, APPS_PWRDOWN);
474 mb();
475 __raw_writel(0, APPS_CLK_SLEEP_EN);
476 mb();
477#else
478 __raw_writel(0, APPS_PWRDOWN);
479 mb();
480 __raw_writel(0, APPS_CLK_SLEEP_EN);
481 mb();
482#endif
483}
484
485/*
486 * Configure hardware registers in preparation for SWFI.
487 */
488static void msm_pm_config_hw_before_swfi(void)
489{
490#if defined(CONFIG_ARCH_QSD8X50)
491 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
492 mb();
493#elif defined(CONFIG_ARCH_MSM7X27)
494 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
495 mb();
496#elif defined(CONFIG_ARCH_MSM7X27A)
497 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
498 mb();
499#endif
500}
501
502/*
503 * Respond to timing out waiting for Modem
504 *
505 * NOTE: The function never returns.
506 */
507static void msm_pm_timeout(void)
508{
509#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
510 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
511 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
512#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
513 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
514 msm_proc_comm_reset_modem_now();
515#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
516 printk(KERN_EMERG "%s(): halting\n", __func__);
517#endif
518 for (;;)
519 ;
520}
521
522
523/******************************************************************************
524 * State Polling Definitions
525 *****************************************************************************/
526
527struct msm_pm_polled_group {
528 uint32_t group_id;
529
530 uint32_t bits_all_set;
531 uint32_t bits_all_clear;
532 uint32_t bits_any_set;
533 uint32_t bits_any_clear;
534
535 uint32_t value_read;
536};
537
538/*
539 * Return true if all bits indicated by flag are set in source.
540 */
541static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
542{
543 return (source & flag) == flag;
544}
545
546/*
547 * Return true if any bit indicated by flag are set in source.
548 */
549static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
550{
551 return !flag || (source & flag);
552}
553
554/*
555 * Return true if all bits indicated by flag are cleared in source.
556 */
557static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
558{
559 return (~source & flag) == flag;
560}
561
562/*
563 * Return true if any bit indicated by flag are cleared in source.
564 */
565static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
566{
567 return !flag || (~source & flag);
568}
569
570/*
571 * Poll the shared memory states as indicated by the poll groups.
572 *
573 * nr_grps: number of groups in the array
574 * grps: array of groups
575 *
576 * The function returns when conditions specified by any of the poll
577 * groups become true. The conditions specified by a poll group are
578 * deemed true when 1) at least one bit from bits_any_set is set OR one
579 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
580 * are set; and 3) all bits in bits_all_clear are cleared.
581 *
582 * Return value:
583 * >=0: index of the poll group whose conditions have become true
584 * -ETIMEDOUT: timed out
585 */
586static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
587{
588 int i, k;
589
590 for (i = 0; i < 50000; i++) {
591 for (k = 0; k < nr_grps; k++) {
592 bool all_set, all_clear;
593 bool any_set, any_clear;
594
595 grps[k].value_read = smsm_get_state(grps[k].group_id);
596
597 all_set = msm_pm_all_set(grps[k].value_read,
598 grps[k].bits_all_set);
599 all_clear = msm_pm_all_clear(grps[k].value_read,
600 grps[k].bits_all_clear);
601 any_set = msm_pm_any_set(grps[k].value_read,
602 grps[k].bits_any_set);
603 any_clear = msm_pm_any_clear(grps[k].value_read,
604 grps[k].bits_any_clear);
605
606 if (all_set && all_clear && (any_set || any_clear))
607 return k;
608 }
609 udelay(50);
610 }
611
612 printk(KERN_ERR "%s failed:\n", __func__);
613 for (k = 0; k < nr_grps; k++)
614 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
615 grps[k].bits_all_set, grps[k].bits_all_clear,
616 grps[k].bits_any_set, grps[k].bits_any_clear,
617 grps[k].value_read);
618
619 return -ETIMEDOUT;
620}
621
622
623/******************************************************************************
624 * Suspend Max Sleep Time
625 *****************************************************************************/
626
627#define SCLK_HZ (32768)
628#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
629
630#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
631static int msm_pm_sleep_time_override;
632module_param_named(sleep_time_override,
633 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
634#endif
635
636static uint32_t msm_pm_max_sleep_time;
637
638/*
639 * Convert time from nanoseconds to slow clock ticks, then cap it to the
640 * specified limit
641 */
642static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
643{
644 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
645 return (time_ns > limit) ? limit : time_ns;
646}
647
648/*
649 * Set the sleep time for suspend. 0 means infinite sleep time.
650 */
651void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
652{
653 unsigned long flags;
654
655 local_irq_save(flags);
656 if (max_sleep_time_ns == 0) {
657 msm_pm_max_sleep_time = 0;
658 } else {
659 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
660 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
661
662 if (msm_pm_max_sleep_time == 0)
663 msm_pm_max_sleep_time = 1;
664 }
665
666 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
667 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
668 max_sleep_time_ns, msm_pm_max_sleep_time);
669 local_irq_restore(flags);
670}
671EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
672
673
674/******************************************************************************
675 * CONFIG_MSM_IDLE_STATS
676 *****************************************************************************/
677
678#ifdef CONFIG_MSM_IDLE_STATS
679enum msm_pm_time_stats_id {
680 MSM_PM_STAT_REQUESTED_IDLE,
681 MSM_PM_STAT_IDLE_SPIN,
682 MSM_PM_STAT_IDLE_WFI,
683 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
684 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700685 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
686 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
687 MSM_PM_STAT_SUSPEND,
688 MSM_PM_STAT_FAILED_SUSPEND,
689 MSM_PM_STAT_NOT_IDLE,
690 MSM_PM_STAT_COUNT
691};
692
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530693struct msm_pm_time_stats {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700694 const char *name;
695 int64_t first_bucket_time;
696 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
697 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
698 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
699 int count;
700 int64_t total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701};
702
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530703struct msm_pm_cpu_time_stats {
704 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
705};
706
707static DEFINE_PER_CPU_SHARED_ALIGNED(
708 struct msm_pm_cpu_time_stats, msm_pm_stats);
709
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700710static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
711
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530712static DEFINE_SPINLOCK(msm_pm_stats_lock);
713
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714/*
715 * Add the given time data to the statistics collection.
716 */
717static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
718{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530719 unsigned long flags;
720 struct msm_pm_time_stats *stats;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721 int i;
722 int64_t bt;
723
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530724 spin_lock_irqsave(&msm_pm_stats_lock, flags);
725 stats = __get_cpu_var(msm_pm_stats).stats;
726
727 stats[id].total_time += t;
728 stats[id].count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729
730 bt = t;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530731 do_div(bt, stats[id].first_bucket_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732
733 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
734 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
735 i = DIV_ROUND_UP(fls((uint32_t)bt),
736 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
737 else
738 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
739
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530740 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
741 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700742
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530743 stats[id].bucket[i]++;
744
745 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
746 stats[id].min_time[i] = t;
747 if (t > stats[id].max_time[i])
748 stats[id].max_time[i] = t;
749
750 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700751}
752
753/*
754 * Helper function of snprintf where buf is auto-incremented, size is auto-
755 * decremented, and there is no return value.
756 *
757 * NOTE: buf and size must be l-values (e.g. variables)
758 */
759#define SNPRINTF(buf, size, format, ...) \
760 do { \
761 if (size > 0) { \
762 int ret; \
763 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
764 if (ret > size) { \
765 buf += size; \
766 size = 0; \
767 } else { \
768 buf += ret; \
769 size -= ret; \
770 } \
771 } \
772 } while (0)
773
774/*
775 * Write out the power management statistics.
776 */
777static int msm_pm_read_proc
778 (char *page, char **start, off_t off, int count, int *eof, void *data)
779{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530780 unsigned int cpu = off / MSM_PM_STAT_COUNT;
781 int id = off % MSM_PM_STAT_COUNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700782 char *p = page;
783
784 if (count < 1024) {
785 *start = (char *) 0;
786 *eof = 0;
787 return 0;
788 }
789
790 if (!off) {
791 SNPRINTF(p, count, "Last power collapse voted ");
792 if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
793 SLEEP_LIMIT_NONE)
794 SNPRINTF(p, count, "for TCXO shutdown\n\n");
795 else
796 SNPRINTF(p, count, "against TCXO shutdown\n\n");
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530797 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700798
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530799 if (cpu < num_possible_cpus()) {
800 unsigned long flags;
801 struct msm_pm_time_stats *stats;
802 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 int64_t bucket_time;
804 int64_t s;
805 uint32_t ns;
806
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530807 spin_lock_irqsave(&msm_pm_stats_lock, flags);
808 stats = per_cpu(msm_pm_stats, cpu).stats;
809
810 s = stats[id].total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700811 ns = do_div(s, NSEC_PER_SEC);
812 SNPRINTF(p, count,
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530813 "[cpu %u] %s:\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700814 " count: %7d\n"
815 " total_time: %lld.%09u\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530816 cpu, stats[id].name,
817 stats[id].count,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700818 s, ns);
819
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530820 bucket_time = stats[id].first_bucket_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700821 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
822 s = bucket_time;
823 ns = do_div(s, NSEC_PER_SEC);
824 SNPRINTF(p, count,
825 " <%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530826 s, ns, stats[id].bucket[i],
827 stats[id].min_time[i],
828 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829
830 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
831 }
832
833 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530834 s, ns, stats[id].bucket[i],
835 stats[id].min_time[i],
836 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700837
838 *start = (char *) 1;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530839 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
840
841 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700842 }
843
844 return p - page;
845}
846#undef SNPRINTF
847
848#define MSM_PM_STATS_RESET "reset"
849
850/*
851 * Reset the power management statistics values.
852 */
853static int msm_pm_write_proc(struct file *file, const char __user *buffer,
854 unsigned long count, void *data)
855{
856 char buf[sizeof(MSM_PM_STATS_RESET)];
857 int ret;
858 unsigned long flags;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530859 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860
861 if (count < strlen(MSM_PM_STATS_RESET)) {
862 ret = -EINVAL;
863 goto write_proc_failed;
864 }
865
866 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
867 ret = -EFAULT;
868 goto write_proc_failed;
869 }
870
871 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
872 ret = -EINVAL;
873 goto write_proc_failed;
874 }
875
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530876 spin_lock_irqsave(&msm_pm_stats_lock, flags);
877 for_each_possible_cpu(cpu) {
878 struct msm_pm_time_stats *stats;
879 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530881 stats = per_cpu(msm_pm_stats, cpu).stats;
882 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
883 memset(stats[i].bucket,
884 0, sizeof(stats[i].bucket));
885 memset(stats[i].min_time,
886 0, sizeof(stats[i].min_time));
887 memset(stats[i].max_time,
888 0, sizeof(stats[i].max_time));
889 stats[i].count = 0;
890 stats[i].total_time = 0;
891 }
892 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700893 msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530894 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895
896 return count;
897
898write_proc_failed:
899 return ret;
900}
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530901
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902#undef MSM_PM_STATS_RESET
903#endif /* CONFIG_MSM_IDLE_STATS */
904
905
906/******************************************************************************
907 * Shared Memory Bits
908 *****************************************************************************/
909
910#define DEM_MASTER_BITS_PER_CPU 6
911
912/* Power Master State Bits - Per CPU */
913#define DEM_MASTER_SMSM_RUN \
914 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
915#define DEM_MASTER_SMSM_RSA \
916 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
917#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
918 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
919#define DEM_MASTER_SMSM_SLEEP_EXIT \
920 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
921#define DEM_MASTER_SMSM_READY \
922 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
923#define DEM_MASTER_SMSM_SLEEP \
924 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
925
926/* Power Slave State Bits */
927#define DEM_SLAVE_SMSM_RUN (0x0001)
928#define DEM_SLAVE_SMSM_PWRC (0x0002)
929#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
930#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
931#define DEM_SLAVE_SMSM_WFPI (0x0010)
932#define DEM_SLAVE_SMSM_SLEEP (0x0020)
933#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
934#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
935#define DEM_SLAVE_SMSM_RESET (0x0100)
936#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
937
938
939/******************************************************************************
940 * Shared Memory Data
941 *****************************************************************************/
942
943#define DEM_MAX_PORT_NAME_LEN (20)
944
945struct msm_pm_smem_t {
946 uint32_t sleep_time;
947 uint32_t irq_mask;
948 uint32_t resources_used;
949 uint32_t reserved1;
950
951 uint32_t wakeup_reason;
952 uint32_t pending_irqs;
953 uint32_t rpc_prog;
954 uint32_t rpc_proc;
955 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
956 uint32_t reserved2;
957};
958
959
960/******************************************************************************
961 *
962 *****************************************************************************/
963static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700964static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
965
966static int msm_pm_modem_busy(void)
967{
968 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
969 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
970 KERN_INFO, "%s(): master not ready\n", __func__);
971 return -EBUSY;
972 }
973
974 return 0;
975}
976
977/*
978 * Power collapse the Apps processor. This function executes the handshake
979 * protocol with Modem.
980 *
981 * Return value:
982 * -EAGAIN: modem reset occurred or early exit from power collapse
983 * -EBUSY: modem not ready for our power collapse -- no power loss
984 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
985 * 0: success
986 */
987static int msm_pm_power_collapse
988 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
989{
990 struct msm_pm_polled_group state_grps[2];
991 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700992 int collapsed = 0;
993 int ret;
994
995 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
996 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
997 (int)from_idle, sleep_delay, sleep_limit);
998
999 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1000 MSM_PM_DPRINTK(
1001 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1002 KERN_INFO, "%s(): master not ready\n", __func__);
1003 ret = -EBUSY;
1004 goto power_collapse_bail;
1005 }
1006
1007 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
1008
Murali Nalajala41786ab2012-03-06 10:47:32 +05301009 if (cpu_is_msm8625()) {
1010 /* Program the SPM */
1011 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
1012 false);
1013 WARN_ON(ret);
1014 }
1015
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001016 msm_irq_enter_sleep1(true, from_idle, &msm_pm_smem_data->irq_mask);
1017 msm_sirc_enter_sleep();
1018 msm_gpio_enter_sleep(from_idle);
1019
1020 msm_pm_smem_data->sleep_time = sleep_delay;
1021 msm_pm_smem_data->resources_used = sleep_limit;
1022
1023 /* Enter PWRC/PWRC_SUSPEND */
1024
1025 if (from_idle)
1026 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1027 DEM_SLAVE_SMSM_PWRC);
1028 else
1029 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1030 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
1031
1032 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
1033 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1034
1035 memset(state_grps, 0, sizeof(state_grps));
1036 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1037 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
1038 state_grps[1].group_id = SMSM_MODEM_STATE;
1039 state_grps[1].bits_all_set = SMSM_RESET;
1040
1041 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1042
1043 if (ret < 0) {
1044 printk(KERN_EMERG "%s(): power collapse entry "
1045 "timed out waiting for Modem's response\n", __func__);
1046 msm_pm_timeout();
1047 }
1048
1049 if (ret == 1) {
1050 MSM_PM_DPRINTK(
1051 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1052 KERN_INFO,
1053 "%s(): msm_pm_poll_state detected Modem reset\n",
1054 __func__);
1055 goto power_collapse_early_exit;
1056 }
1057
1058 /* DEM Master in RSA */
1059
1060 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
1061
1062 ret = msm_irq_enter_sleep2(true, from_idle);
1063 if (ret < 0) {
1064 MSM_PM_DPRINTK(
1065 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1066 KERN_INFO,
1067 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
1068 ret);
1069 goto power_collapse_early_exit;
1070 }
1071
1072 msm_pm_config_hw_before_power_down();
1073 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
1074
1075 saved_acpuclk_rate = acpuclk_power_collapse();
1076 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1077 "%s(): change clock rate (old rate = %lu)\n", __func__,
1078 saved_acpuclk_rate);
1079
1080 if (saved_acpuclk_rate == 0) {
1081 msm_pm_config_hw_after_power_up();
1082 goto power_collapse_early_exit;
1083 }
1084
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001085 msm_pm_boot_config_before_pc(smp_processor_id(),
1086 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001087
1088#ifdef CONFIG_VFP
1089 if (from_idle)
1090 vfp_flush_context();
1091#endif
1092
1093#ifdef CONFIG_CACHE_L2X0
1094 l2x0_suspend();
1095#endif
1096
1097 collapsed = msm_pm_collapse();
1098
1099#ifdef CONFIG_CACHE_L2X0
1100 l2x0_resume(collapsed);
1101#endif
1102
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001103 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001104
1105 if (collapsed) {
1106#ifdef CONFIG_VFP
1107 if (from_idle)
1108 vfp_reinit();
1109#endif
1110 cpu_init();
1111 local_fiq_enable();
1112 }
1113
1114 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1115 KERN_INFO,
1116 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1117
1118 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1119 "%s(): restore clock rate to %lu\n", __func__,
1120 saved_acpuclk_rate);
1121 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1122 SETRATE_PC) < 0)
1123 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1124 __func__, saved_acpuclk_rate);
1125
1126 msm_irq_exit_sleep1(msm_pm_smem_data->irq_mask,
1127 msm_pm_smem_data->wakeup_reason,
1128 msm_pm_smem_data->pending_irqs);
1129
1130 msm_pm_config_hw_after_power_up();
1131 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1132
1133 memset(state_grps, 0, sizeof(state_grps));
1134 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1135 state_grps[0].bits_any_set =
1136 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1137 state_grps[1].group_id = SMSM_MODEM_STATE;
1138 state_grps[1].bits_all_set = SMSM_RESET;
1139
1140 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1141
1142 if (ret < 0) {
1143 printk(KERN_EMERG "%s(): power collapse exit "
1144 "timed out waiting for Modem's response\n", __func__);
1145 msm_pm_timeout();
1146 }
1147
1148 if (ret == 1) {
1149 MSM_PM_DPRINTK(
1150 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1151 KERN_INFO,
1152 "%s(): msm_pm_poll_state detected Modem reset\n",
1153 __func__);
1154 goto power_collapse_early_exit;
1155 }
1156
1157 /* Sanity check */
1158 if (collapsed) {
1159 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1160 } else {
1161 BUG_ON(!(state_grps[0].value_read &
1162 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1163 goto power_collapse_early_exit;
1164 }
1165
1166 /* Enter WFPI */
1167
1168 smsm_change_state(SMSM_APPS_DEM,
1169 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1170 DEM_SLAVE_SMSM_WFPI);
1171
1172 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1173
1174 memset(state_grps, 0, sizeof(state_grps));
1175 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1176 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1177 state_grps[1].group_id = SMSM_MODEM_STATE;
1178 state_grps[1].bits_all_set = SMSM_RESET;
1179
1180 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1181
1182 if (ret < 0) {
1183 printk(KERN_EMERG "%s(): power collapse WFPI "
1184 "timed out waiting for Modem's response\n", __func__);
1185 msm_pm_timeout();
1186 }
1187
1188 if (ret == 1) {
1189 MSM_PM_DPRINTK(
1190 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1191 KERN_INFO,
1192 "%s(): msm_pm_poll_state detected Modem reset\n",
1193 __func__);
1194 ret = -EAGAIN;
1195 goto power_collapse_restore_gpio_bail;
1196 }
1197
1198 /* DEM Master == RUN */
1199
1200 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1201 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1202
1203 msm_irq_exit_sleep2(msm_pm_smem_data->irq_mask,
1204 msm_pm_smem_data->wakeup_reason,
1205 msm_pm_smem_data->pending_irqs);
1206 msm_irq_exit_sleep3(msm_pm_smem_data->irq_mask,
1207 msm_pm_smem_data->wakeup_reason,
1208 msm_pm_smem_data->pending_irqs);
1209 msm_gpio_exit_sleep();
1210 msm_sirc_exit_sleep();
1211
1212 smsm_change_state(SMSM_APPS_DEM,
1213 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1214
1215 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1216
1217 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301218
1219 if (cpu_is_msm8625()) {
1220 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1221 false);
1222 WARN_ON(ret);
1223 }
1224
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001225 return 0;
1226
1227power_collapse_early_exit:
1228 /* Enter PWRC_EARLY_EXIT */
1229
1230 smsm_change_state(SMSM_APPS_DEM,
1231 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1232 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1233
1234 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1235
1236 memset(state_grps, 0, sizeof(state_grps));
1237 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1238 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1239 state_grps[1].group_id = SMSM_MODEM_STATE;
1240 state_grps[1].bits_all_set = SMSM_RESET;
1241
1242 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1243 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1244
1245 if (ret < 0) {
1246 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1247 "timed out waiting for Modem's response\n", __func__);
1248 msm_pm_timeout();
1249 }
1250
1251 if (ret == 1) {
1252 MSM_PM_DPRINTK(
1253 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1254 KERN_INFO,
1255 "%s(): msm_pm_poll_state detected Modem reset\n",
1256 __func__);
1257 }
1258
1259 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1260
1261 ret = -EAGAIN;
1262
1263power_collapse_restore_gpio_bail:
1264 msm_gpio_exit_sleep();
1265 msm_sirc_exit_sleep();
1266
1267 /* Enter RUN */
1268 smsm_change_state(SMSM_APPS_DEM,
1269 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1270 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1271
1272 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1273
1274 if (collapsed)
1275 smd_sleep_exit();
1276
1277power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301278 if (cpu_is_msm8625()) {
1279 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1280 false);
1281 WARN_ON(ret);
1282 }
1283
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001284 return ret;
1285}
1286
1287/*
1288 * Power collapse the Apps processor without involving Modem.
1289 *
1290 * Return value:
1291 * 0: success
1292 */
Murali Nalajala41786ab2012-03-06 10:47:32 +05301293static int msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001294{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001295 int collapsed = 0;
1296 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301297 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001298
1299 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1300 KERN_INFO, "%s()\n", __func__);
1301
1302 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1303 WARN_ON(ret);
1304
Murali Nalajala41786ab2012-03-06 10:47:32 +05301305 entry = (!smp_processor_id() || from_idle) ?
1306 msm_pm_collapse_exit : msm_secondary_startup;
1307
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001308 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301309 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001310
1311#ifdef CONFIG_VFP
1312 vfp_flush_context();
1313#endif
1314
1315#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301316 if (!cpu_is_msm8625())
1317 l2x0_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001318#endif
1319
1320 collapsed = msm_pm_collapse();
1321
1322#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301323 if (!cpu_is_msm8625())
1324 l2x0_resume(collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001325#endif
1326
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001327 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001328
1329 if (collapsed) {
1330#ifdef CONFIG_VFP
1331 vfp_reinit();
1332#endif
1333 cpu_init();
1334 local_fiq_enable();
1335 }
1336
1337 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1338 KERN_INFO,
1339 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1340
1341 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1342 WARN_ON(ret);
1343
1344 return 0;
1345}
1346
1347/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001348 * Bring the Apps processor to SWFI.
1349 *
1350 * Return value:
1351 * -EIO: could not ramp Apps processor clock
1352 * 0: success
1353 */
1354static int msm_pm_swfi(bool ramp_acpu)
1355{
1356 unsigned long saved_acpuclk_rate = 0;
1357
1358 if (ramp_acpu) {
1359 saved_acpuclk_rate = acpuclk_wait_for_irq();
1360 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1361 "%s(): change clock rate (old rate = %lu)\n", __func__,
1362 saved_acpuclk_rate);
1363
1364 if (!saved_acpuclk_rate)
1365 return -EIO;
1366 }
1367
Murali Nalajala41786ab2012-03-06 10:47:32 +05301368 if (!cpu_is_msm8625())
1369 msm_pm_config_hw_before_swfi();
1370
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001371 msm_arch_idle();
1372
1373 if (ramp_acpu) {
1374 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1375 "%s(): restore clock rate to %lu\n", __func__,
1376 saved_acpuclk_rate);
1377 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1378 SETRATE_SWFI) < 0)
1379 printk(KERN_ERR
1380 "%s(): failed to restore clock rate(%lu)\n",
1381 __func__, saved_acpuclk_rate);
1382 }
1383
1384 return 0;
1385}
1386
1387
1388/******************************************************************************
1389 * External Idle/Suspend Functions
1390 *****************************************************************************/
1391
1392/*
1393 * Put CPU in low power mode.
1394 */
1395void arch_idle(void)
1396{
1397 bool allow[MSM_PM_SLEEP_MODE_NR];
1398 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1399
1400 int latency_qos;
1401 int64_t timer_expiration;
1402
1403 int low_power;
1404 int ret;
1405 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301406 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001407
1408#ifdef CONFIG_MSM_IDLE_STATS
1409 int64_t t1;
1410 static int64_t t2;
1411 int exit_stat;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301412 #endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001413
1414 if (!atomic_read(&msm_pm_init_done))
1415 return;
1416
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301417 cpu = smp_processor_id();
1418
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001419 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
1420 timer_expiration = msm_timer_enter_idle();
1421
1422#ifdef CONFIG_MSM_IDLE_STATS
1423 t1 = ktime_to_ns(ktime_get());
1424 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
1425 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301426
1427 exit_stat = MSM_PM_STAT_IDLE_SPIN;
1428 low_power = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301429#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001430
1431 for (i = 0; i < ARRAY_SIZE(allow); i++)
1432 allow[i] = true;
1433
Murali Nalajala41786ab2012-03-06 10:47:32 +05301434 if (num_online_cpus() > 1 ||
1435 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001436#ifdef CONFIG_HAS_WAKELOCK
1437 has_wake_lock(WAKE_LOCK_IDLE) ||
1438#endif
1439 !msm_irq_idle_sleep_allowed()) {
1440 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1441 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001442 }
1443
1444 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301445 struct msm_pm_platform_data *mode =
1446 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001447 if (!mode->idle_supported || !mode->idle_enabled ||
1448 mode->latency >= latency_qos ||
1449 mode->residency * 1000ULL >= timer_expiration)
1450 allow[i] = false;
1451 }
1452
1453 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1454 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1455 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1456 while (msm_pm_modem_busy() && wait_us) {
1457 if (wait_us > 100) {
1458 udelay(100);
1459 wait_us -= 100;
1460 } else {
1461 udelay(wait_us);
1462 wait_us = 0;
1463 }
1464 }
1465
1466 if (msm_pm_modem_busy()) {
1467 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1468 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1469 = false;
1470 }
1471 }
1472
1473 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1474 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1475 __func__, latency_qos, timer_expiration, sleep_limit);
1476
1477 for (i = 0; i < ARRAY_SIZE(allow); i++)
1478 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1479 "%s(): allow %s: %d\n", __func__,
1480 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1481
1482 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1483 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1484 uint32_t sleep_delay;
1485
1486 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
1487 timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
1488 if (sleep_delay == 0) /* 0 would mean infinite time */
1489 sleep_delay = 1;
1490
1491 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1492 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1493
1494#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1495 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1496#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1497 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1498#endif
1499
1500 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1501 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
1502
1503#ifdef CONFIG_MSM_IDLE_STATS
1504 if (ret)
1505 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1506 else {
1507 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1508 msm_pm_sleep_limit = sleep_limit;
1509 }
Murali Nalajala41786ab2012-03-06 10:47:32 +05301510#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001511 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301512 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001513 low_power = 0;
1514#ifdef CONFIG_MSM_IDLE_STATS
1515 exit_stat = ret ?
1516 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1517 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301518#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001519 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1520 ret = msm_pm_swfi(true);
1521 if (ret)
1522 while (!msm_irq_pending())
1523 udelay(1);
1524 low_power = 0;
1525#ifdef CONFIG_MSM_IDLE_STATS
1526 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301527#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001528 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1529 msm_pm_swfi(false);
1530 low_power = 0;
1531#ifdef CONFIG_MSM_IDLE_STATS
1532 exit_stat = MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301533#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001534 } else {
1535 while (!msm_irq_pending())
1536 udelay(1);
1537 low_power = 0;
1538#ifdef CONFIG_MSM_IDLE_STATS
1539 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301540#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001541 }
1542
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001543 msm_timer_exit_idle(low_power);
1544
1545#ifdef CONFIG_MSM_IDLE_STATS
1546 t2 = ktime_to_ns(ktime_get());
1547 msm_pm_add_stat(exit_stat, t2 - t1);
Murali Nalajala41786ab2012-03-06 10:47:32 +05301548#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001549}
1550
1551/*
1552 * Suspend the Apps processor.
1553 *
1554 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301555 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001556 * -EAGAIN: modem reset occurred or early exit from suspend
1557 * -EBUSY: modem not ready for our suspend
1558 * -EINVAL: invalid sleep mode
1559 * -EIO: could not ramp Apps processor clock
1560 * -ETIMEDOUT: timed out waiting for modem's handshake
1561 * 0: success
1562 */
1563static int msm_pm_enter(suspend_state_t state)
1564{
1565 bool allow[MSM_PM_SLEEP_MODE_NR];
1566 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301567 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001568 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001569#ifdef CONFIG_MSM_IDLE_STATS
1570 int64_t period = 0;
1571 int64_t time = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301572#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001573
Murali Nalajala41786ab2012-03-06 10:47:32 +05301574 /* Must executed by CORE0 */
1575 if (smp_processor_id()) {
1576 __WARN();
1577 goto suspend_exit;
1578 }
1579
1580#ifdef CONFIG_MSM_IDLE_STATS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001581 time = msm_timer_get_sclk_time(&period);
1582#endif
1583
1584 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1585 "%s(): sleep limit %u\n", __func__, sleep_limit);
1586
1587 for (i = 0; i < ARRAY_SIZE(allow); i++)
1588 allow[i] = true;
1589
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001590 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301591 struct msm_pm_platform_data *mode;
1592 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001593 if (!mode->suspend_supported || !mode->suspend_enabled)
1594 allow[i] = false;
1595 }
1596
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001597 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1598 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1599#ifdef CONFIG_MSM_IDLE_STATS
1600 enum msm_pm_time_stats_id id;
1601 int64_t end_time;
1602#endif
1603
1604 clock_debug_print_enabled();
1605
1606#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1607 if (msm_pm_sleep_time_override > 0) {
1608 int64_t ns;
1609 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1610 msm_pm_set_max_sleep_time(ns);
1611 msm_pm_sleep_time_override = 0;
1612 }
1613#endif
1614 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1615 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1616
1617#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1618 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1619#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1620 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1621#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1622 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1623 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1624#endif
1625
1626 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1627 udelay(500);
1628
1629 ret = msm_pm_power_collapse(
1630 false, msm_pm_max_sleep_time, sleep_limit);
1631
1632#ifdef CONFIG_MSM_IDLE_STATS
1633 if (ret)
1634 id = MSM_PM_STAT_FAILED_SUSPEND;
1635 else {
1636 id = MSM_PM_STAT_SUSPEND;
1637 msm_pm_sleep_limit = sleep_limit;
1638 }
1639
1640 if (time != 0) {
1641 end_time = msm_timer_get_sclk_time(NULL);
1642 if (end_time != 0) {
1643 time = end_time - time;
1644 if (time < 0)
1645 time += period;
1646 } else
1647 time = 0;
1648 }
1649
1650 msm_pm_add_stat(id, time);
1651#endif
1652 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301653 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001654 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1655 ret = msm_pm_swfi(true);
1656 if (ret)
1657 while (!msm_irq_pending())
1658 udelay(1);
1659 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1660 msm_pm_swfi(false);
1661 }
1662
Murali Nalajala41786ab2012-03-06 10:47:32 +05301663suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001664 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1665 "%s(): return %d\n", __func__, ret);
1666
1667 return ret;
1668}
1669
1670static struct platform_suspend_ops msm_pm_ops = {
1671 .enter = msm_pm_enter,
1672 .valid = suspend_valid_only_mem,
1673};
1674
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301675/* Hotplug the "non boot" CPU's and put
1676 * the cores into low power mode
1677 */
1678void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1679{
1680 return;
1681}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001682
1683/******************************************************************************
1684 * Restart Definitions
1685 *****************************************************************************/
1686
1687static uint32_t restart_reason = 0x776655AA;
1688
1689static void msm_pm_power_off(void)
1690{
1691 msm_rpcrouter_close();
1692 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1693 for (;;)
1694 ;
1695}
1696
1697static void msm_pm_restart(char str, const char *cmd)
1698{
1699 msm_rpcrouter_close();
1700 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1701
1702 for (;;)
1703 ;
1704}
1705
1706static int msm_reboot_call
1707 (struct notifier_block *this, unsigned long code, void *_cmd)
1708{
1709 if ((code == SYS_RESTART) && _cmd) {
1710 char *cmd = _cmd;
1711 if (!strcmp(cmd, "bootloader")) {
1712 restart_reason = 0x77665500;
1713 } else if (!strcmp(cmd, "recovery")) {
1714 restart_reason = 0x77665502;
1715 } else if (!strcmp(cmd, "eraseflash")) {
1716 restart_reason = 0x776655EF;
1717 } else if (!strncmp(cmd, "oem-", 4)) {
1718 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1719 restart_reason = 0x6f656d00 | code;
1720 } else {
1721 restart_reason = 0x77665501;
1722 }
1723 }
1724 return NOTIFY_DONE;
1725}
1726
1727static struct notifier_block msm_reboot_notifier = {
1728 .notifier_call = msm_reboot_call,
1729};
1730
1731
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001732/*
1733 * Initialize the power management subsystem.
1734 *
1735 * Return value:
1736 * -ENODEV: initialization failed
1737 * 0: success
1738 */
1739static int __init msm_pm_init(void)
1740{
1741#ifdef CONFIG_MSM_IDLE_STATS
1742 struct proc_dir_entry *d_entry;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301743 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001744#endif
1745 int ret;
1746#ifdef CONFIG_CPU_V7
1747 pgd_t *pc_pgd;
1748 pmd_t *pmd;
1749 unsigned long pmdval;
1750
1751 /* Page table for cores to come back up safely. */
1752 pc_pgd = pgd_alloc(&init_mm);
1753 if (!pc_pgd)
1754 return -ENOMEM;
1755 pmd = pmd_offset(pc_pgd +
1756 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1757 virt_to_phys(msm_pm_collapse_exit));
1758 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1759 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1760 pmd[0] = __pmd(pmdval);
1761 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1762
1763 /* It is remotely possible that the code in msm_pm_collapse_exit()
1764 * which turns on the MMU with this mapping is in the
1765 * next even-numbered megabyte beyond the
1766 * start of msm_pm_collapse_exit().
1767 * Map this megabyte in as well.
1768 */
1769 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1770 flush_pmd_entry(pmd);
1771 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
1772#endif
1773
1774 pm_power_off = msm_pm_power_off;
1775 arm_pm_restart = msm_pm_restart;
1776 register_reboot_notifier(&msm_reboot_notifier);
1777
1778 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1779 sizeof(*msm_pm_smem_data));
1780 if (msm_pm_smem_data == NULL) {
1781 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1782 return -ENODEV;
1783 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001784
1785 ret = msm_timer_init_time_sync(msm_pm_timeout);
1786 if (ret)
1787 return ret;
1788
1789 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1790 if (ret) {
1791 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1792 __func__, ret);
1793 return ret;
1794 }
1795
1796#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1797 /* The wakeup_reason field is overloaded during initialization time
1798 to signal Modem that Apps will control the low power modes of
1799 the memory.
1800 */
1801 msm_pm_smem_data->wakeup_reason = 1;
1802 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1803#endif
1804
1805 BUG_ON(msm_pm_modes == NULL);
1806
1807 atomic_set(&msm_pm_init_done, 1);
1808 suspend_set_ops(&msm_pm_ops);
1809
1810 msm_pm_mode_sysfs_add();
1811#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301812 for_each_possible_cpu(cpu) {
1813 struct msm_pm_time_stats *stats =
1814 per_cpu(msm_pm_stats, cpu).stats;
1815
1816 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1817 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1818 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1819
1820 stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin";
1821 stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
1822 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1823
1824 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1825 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1826 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1827
1828 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1829 "idle-standalone-power-collapse";
1830 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1831 first_bucket_time =
1832 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1833
1834 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
1835 "idle-failed-standalone-power-collapse";
1836 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].
1837 first_bucket_time =
1838 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1839
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301840 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
1841 "idle-power-collapse";
1842 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
1843 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1844
1845 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
1846 "idle-failed-power-collapse";
1847 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].
1848 first_bucket_time =
1849 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1850
1851 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
1852 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
1853 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
1854
1855 stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend";
1856 stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
1857 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1858
1859 stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle";
1860 stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
1861 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1862 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001863 d_entry = create_proc_entry("msm_pm_stats",
1864 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
1865 if (d_entry) {
1866 d_entry->read_proc = msm_pm_read_proc;
1867 d_entry->write_proc = msm_pm_write_proc;
1868 d_entry->data = NULL;
1869 }
1870#endif
1871
1872 return 0;
1873}
1874
1875late_initcall_sync(msm_pm_init);