blob: 94bc98f21b6b85cd4e41bb0ff630824399ea61aa [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 {
Murali Nalajalaa7efba12012-02-23 18:13:52 +053072 MSM_PM_DEBUG_SUSPEND = BIT(0),
73 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
74 MSM_PM_DEBUG_STATE = BIT(2),
75 MSM_PM_DEBUG_CLOCK = BIT(3),
76 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
77 MSM_PM_DEBUG_SMSM_STATE = BIT(5),
78 MSM_PM_DEBUG_IDLE = BIT(6),
79 MSM_PM_DEBUG_HOTPLUG = BIT(7),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080};
81
82static int msm_pm_debug_mask;
Taniya Dase30a6b22012-03-20 11:37:45 +053083int power_collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084module_param_named(
85 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
86);
87
88#define MSM_PM_DPRINTK(mask, level, message, ...) \
89 do { \
90 if ((mask) & msm_pm_debug_mask) \
91 printk(level message, ## __VA_ARGS__); \
92 } while (0)
93
94#define MSM_PM_DEBUG_PRINT_STATE(tag) \
95 do { \
96 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
97 KERN_INFO, "%s: " \
98 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
99 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
100 "SMSM_APPS_DEM %x\n", \
101 tag, \
102 __raw_readl(APPS_CLK_SLEEP_EN), \
103 __raw_readl(APPS_PWRDOWN), \
104 smsm_get_state(SMSM_POWER_MASTER_DEM), \
105 smsm_get_state(SMSM_MODEM_STATE), \
106 smsm_get_state(SMSM_APPS_DEM)); \
107 } while (0)
108
109#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
110 do { \
111 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
112 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
113 msm_pm_smem_data->resources_used, \
114 msm_pm_smem_data->irq_mask, \
115 msm_pm_smem_data->wakeup_reason, \
116 msm_pm_smem_data->pending_irqs); \
117 } while (0)
118
119
120/******************************************************************************
121 * Sleep Modes and Parameters
122 *****************************************************************************/
123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
125module_param_named(
126 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
127 int, S_IRUGO | S_IWUSR | S_IWGRP
128);
129
130enum {
131 MSM_PM_MODE_ATTR_SUSPEND,
132 MSM_PM_MODE_ATTR_IDLE,
133 MSM_PM_MODE_ATTR_LATENCY,
134 MSM_PM_MODE_ATTR_RESIDENCY,
135 MSM_PM_MODE_ATTR_NR,
136};
137
138static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
139 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
140 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
141 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
142 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
143};
144
145static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
146 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
147 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
149 "ramp_down_and_wfi",
150 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
151 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
152 "power_collapse_no_xo_shutdown",
153 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
154 "standalone_power_collapse",
155};
156
157static struct msm_pm_platform_data *msm_pm_modes;
158
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530159struct msm_pm_kobj_attribute {
160 unsigned int cpu;
161 struct kobj_attribute ka;
162};
163
164#define GET_CPU_OF_ATTR(attr) \
165 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
166
167struct msm_pm_sysfs_sleep_mode {
168 struct kobject *kobj;
169 struct attribute_group attr_group;
170 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
171 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
172};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173
174/*
175 * Write out the attribute.
176 */
177static ssize_t msm_pm_mode_attr_show(
178 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
179{
180 int ret = -EINVAL;
181 int i;
182
183 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
184 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530185 unsigned int cpu;
186 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187
188 if (msm_pm_sleep_mode_labels[i] == NULL)
189 continue;
190
191 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
192 continue;
193
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530194 cpu = GET_CPU_OF_ATTR(attr);
195 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 if (!strcmp(attr->attr.name,
198 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530199 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 kp.arg = &arg;
201 ret = param_get_ulong(buf, &kp);
202 } else if (!strcmp(attr->attr.name,
203 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530204 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 kp.arg = &arg;
206 ret = param_get_ulong(buf, &kp);
207 } else if (!strcmp(attr->attr.name,
208 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530209 u32 arg = mode->latency;
210 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 ret = param_get_ulong(buf, &kp);
212 } else if (!strcmp(attr->attr.name,
213 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530214 u32 arg = mode->residency;
215 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 ret = param_get_ulong(buf, &kp);
217 }
218
219 break;
220 }
221
222 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530223 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224 ret++;
225 }
226
227 return ret;
228}
229
230/*
231 * Read in the new attribute value.
232 */
233static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
234 struct kobj_attribute *attr, const char *buf, size_t count)
235{
236 int ret = -EINVAL;
237 int i;
238
239 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
240 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530241 unsigned int cpu;
242 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243
244 if (msm_pm_sleep_mode_labels[i] == NULL)
245 continue;
246
247 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
248 continue;
249
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530250 cpu = GET_CPU_OF_ATTR(attr);
251 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
252
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 if (!strcmp(attr->attr.name,
254 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530255 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 ret = param_set_byte(buf, &kp);
257 } else if (!strcmp(attr->attr.name,
258 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530259 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 ret = param_set_byte(buf, &kp);
261 } else if (!strcmp(attr->attr.name,
262 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530263 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 ret = param_set_ulong(buf, &kp);
265 } else if (!strcmp(attr->attr.name,
266 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530267 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 ret = param_set_ulong(buf, &kp);
269 }
270
271 break;
272 }
273
274 return ret ? ret : count;
275}
276
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530277 /* Add sysfs entries for one cpu. */
278static int __init msm_pm_mode_sysfs_add_cpu(
279 unsigned int cpu, struct kobject *modes_kobj)
280{
281 char cpu_name[8];
282 struct kobject *cpu_kobj;
283 struct msm_pm_sysfs_sleep_mode *mode = NULL;
284 int i, j, k;
285 int ret;
286
287 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
288 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
289 if (!cpu_kobj) {
290 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
291 ret = -ENOMEM;
292 goto mode_sysfs_add_cpu_exit;
293 }
294
295 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
296 int idx = MSM_PM_MODE(cpu, i);
297
298 if ((!msm_pm_modes[idx].suspend_supported) &&
299 (!msm_pm_modes[idx].idle_supported))
300 continue;
301
302 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
303 if (!mode) {
304 pr_err("%s: cannot allocate memory for attributes\n",
305 __func__);
306 ret = -ENOMEM;
307 goto mode_sysfs_add_cpu_exit;
308 }
309
310 mode->kobj = kobject_create_and_add(
311 msm_pm_sleep_mode_labels[i], cpu_kobj);
312 if (!mode->kobj) {
313 pr_err("%s: cannot create kobject\n", __func__);
314 ret = -ENOMEM;
315 goto mode_sysfs_add_cpu_exit;
316 }
317
318 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
319 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
320 !msm_pm_modes[idx].idle_supported)
321 continue;
322 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
323 !msm_pm_modes[idx].suspend_supported)
324 continue;
325 mode->kas[j].cpu = cpu;
326 mode->kas[j].ka.attr.mode = 0644;
327 mode->kas[j].ka.show = msm_pm_mode_attr_show;
328 mode->kas[j].ka.store = msm_pm_mode_attr_store;
329 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
330 mode->attrs[j] = &mode->kas[j].ka.attr;
331 j++;
332 }
333 mode->attrs[j] = NULL;
334
335 mode->attr_group.attrs = mode->attrs;
336 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
337 if (ret) {
338 printk(KERN_ERR
339 "%s: cannot create kobject attribute group\n",
340 __func__);
341 goto mode_sysfs_add_cpu_exit;
342 }
343 }
344
345 ret = 0;
346
347mode_sysfs_add_cpu_exit:
348 if (ret) {
349 if (mode && mode->kobj)
350 kobject_del(mode->kobj);
351 kfree(mode);
352 }
353
354 return ret;
355}
356
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357/*
358 * Add sysfs entries for the sleep modes.
359 */
360static int __init msm_pm_mode_sysfs_add(void)
361{
362 struct kobject *module_kobj = NULL;
363 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530364 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 int ret;
366
367 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
368 if (!module_kobj) {
369 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
370 __func__, KBUILD_MODNAME);
371 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530372 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 }
374
375 modes_kobj = kobject_create_and_add("modes", module_kobj);
376 if (!modes_kobj) {
377 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
378 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530379 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 }
381
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530382 for_each_possible_cpu(cpu) {
383 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
384 if (ret)
385 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700386 }
387
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530388 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530390mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391 return ret;
392}
393
394void __init msm_pm_set_platform_data(
395 struct msm_pm_platform_data *data, int count)
396{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530397 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 msm_pm_modes = data;
399}
400
401
402/******************************************************************************
403 * Sleep Limitations
404 *****************************************************************************/
405enum {
406 SLEEP_LIMIT_NONE = 0,
407 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
408 SLEEP_LIMIT_MASK = 0x03,
409};
410
411#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
412enum {
413 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
414 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
415};
416#endif
417
418
419/******************************************************************************
420 * Configure Hardware for Power Down/Up
421 *****************************************************************************/
422
423#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530424#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
425#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
427#else /* defined(CONFIG_ARCH_MSM7X30) */
428#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
429#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
430#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
431#endif /* defined(CONFIG_ARCH_MSM7X30) */
432
433/*
434 * Configure hardware registers in preparation for Apps power down.
435 */
436static void msm_pm_config_hw_before_power_down(void)
437{
438#if defined(CONFIG_ARCH_MSM7X30)
439 __raw_writel(1, APPS_PWRDOWN);
440 mb();
441 __raw_writel(4, APPS_SECOP);
442 mb();
443#elif defined(CONFIG_ARCH_MSM7X27)
444 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
445 mb();
446 __raw_writel(1, APPS_PWRDOWN);
447 mb();
448#elif defined(CONFIG_ARCH_MSM7x27A)
449 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
450 mb();
451 __raw_writel(1, APPS_PWRDOWN);
452 mb();
453#else
454 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
455 mb();
456 __raw_writel(1, APPS_PWRDOWN);
457 mb();
458 __raw_writel(0, APPS_STANDBY_CTL);
459 mb();
460#endif
461}
462
463/*
464 * Clear hardware registers after Apps powers up.
465 */
466static void msm_pm_config_hw_after_power_up(void)
467{
468#if defined(CONFIG_ARCH_MSM7X30)
469 __raw_writel(0, APPS_SECOP);
470 mb();
471 __raw_writel(0, APPS_PWRDOWN);
472 mb();
473 msm_spm_reinit();
474#elif defined(CONFIG_ARCH_MSM7x27A)
475 __raw_writel(0, APPS_PWRDOWN);
476 mb();
477 __raw_writel(0, APPS_CLK_SLEEP_EN);
478 mb();
479#else
480 __raw_writel(0, APPS_PWRDOWN);
481 mb();
482 __raw_writel(0, APPS_CLK_SLEEP_EN);
483 mb();
484#endif
485}
486
487/*
488 * Configure hardware registers in preparation for SWFI.
489 */
490static void msm_pm_config_hw_before_swfi(void)
491{
492#if defined(CONFIG_ARCH_QSD8X50)
493 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
494 mb();
495#elif defined(CONFIG_ARCH_MSM7X27)
496 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
497 mb();
498#elif defined(CONFIG_ARCH_MSM7X27A)
499 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
500 mb();
501#endif
502}
503
504/*
505 * Respond to timing out waiting for Modem
506 *
507 * NOTE: The function never returns.
508 */
509static void msm_pm_timeout(void)
510{
511#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
512 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
513 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
514#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
515 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
516 msm_proc_comm_reset_modem_now();
517#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
518 printk(KERN_EMERG "%s(): halting\n", __func__);
519#endif
520 for (;;)
521 ;
522}
523
524
525/******************************************************************************
526 * State Polling Definitions
527 *****************************************************************************/
528
529struct msm_pm_polled_group {
530 uint32_t group_id;
531
532 uint32_t bits_all_set;
533 uint32_t bits_all_clear;
534 uint32_t bits_any_set;
535 uint32_t bits_any_clear;
536
537 uint32_t value_read;
538};
539
540/*
541 * Return true if all bits indicated by flag are set in source.
542 */
543static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
544{
545 return (source & flag) == flag;
546}
547
548/*
549 * Return true if any bit indicated by flag are set in source.
550 */
551static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
552{
553 return !flag || (source & flag);
554}
555
556/*
557 * Return true if all bits indicated by flag are cleared in source.
558 */
559static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
560{
561 return (~source & flag) == flag;
562}
563
564/*
565 * Return true if any bit indicated by flag are cleared in source.
566 */
567static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
568{
569 return !flag || (~source & flag);
570}
571
572/*
573 * Poll the shared memory states as indicated by the poll groups.
574 *
575 * nr_grps: number of groups in the array
576 * grps: array of groups
577 *
578 * The function returns when conditions specified by any of the poll
579 * groups become true. The conditions specified by a poll group are
580 * deemed true when 1) at least one bit from bits_any_set is set OR one
581 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
582 * are set; and 3) all bits in bits_all_clear are cleared.
583 *
584 * Return value:
585 * >=0: index of the poll group whose conditions have become true
586 * -ETIMEDOUT: timed out
587 */
588static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
589{
590 int i, k;
591
592 for (i = 0; i < 50000; i++) {
593 for (k = 0; k < nr_grps; k++) {
594 bool all_set, all_clear;
595 bool any_set, any_clear;
596
597 grps[k].value_read = smsm_get_state(grps[k].group_id);
598
599 all_set = msm_pm_all_set(grps[k].value_read,
600 grps[k].bits_all_set);
601 all_clear = msm_pm_all_clear(grps[k].value_read,
602 grps[k].bits_all_clear);
603 any_set = msm_pm_any_set(grps[k].value_read,
604 grps[k].bits_any_set);
605 any_clear = msm_pm_any_clear(grps[k].value_read,
606 grps[k].bits_any_clear);
607
608 if (all_set && all_clear && (any_set || any_clear))
609 return k;
610 }
611 udelay(50);
612 }
613
614 printk(KERN_ERR "%s failed:\n", __func__);
615 for (k = 0; k < nr_grps; k++)
616 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
617 grps[k].bits_all_set, grps[k].bits_all_clear,
618 grps[k].bits_any_set, grps[k].bits_any_clear,
619 grps[k].value_read);
620
621 return -ETIMEDOUT;
622}
623
624
625/******************************************************************************
626 * Suspend Max Sleep Time
627 *****************************************************************************/
628
629#define SCLK_HZ (32768)
630#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
631
632#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
633static int msm_pm_sleep_time_override;
634module_param_named(sleep_time_override,
635 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
636#endif
637
638static uint32_t msm_pm_max_sleep_time;
639
640/*
641 * Convert time from nanoseconds to slow clock ticks, then cap it to the
642 * specified limit
643 */
644static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
645{
646 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
647 return (time_ns > limit) ? limit : time_ns;
648}
649
650/*
651 * Set the sleep time for suspend. 0 means infinite sleep time.
652 */
653void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
654{
655 unsigned long flags;
656
657 local_irq_save(flags);
658 if (max_sleep_time_ns == 0) {
659 msm_pm_max_sleep_time = 0;
660 } else {
661 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
662 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
663
664 if (msm_pm_max_sleep_time == 0)
665 msm_pm_max_sleep_time = 1;
666 }
667
668 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
669 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
670 max_sleep_time_ns, msm_pm_max_sleep_time);
671 local_irq_restore(flags);
672}
673EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
674
675
676/******************************************************************************
677 * CONFIG_MSM_IDLE_STATS
678 *****************************************************************************/
679
680#ifdef CONFIG_MSM_IDLE_STATS
681enum msm_pm_time_stats_id {
682 MSM_PM_STAT_REQUESTED_IDLE,
683 MSM_PM_STAT_IDLE_SPIN,
684 MSM_PM_STAT_IDLE_WFI,
685 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
686 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
688 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
689 MSM_PM_STAT_SUSPEND,
690 MSM_PM_STAT_FAILED_SUSPEND,
691 MSM_PM_STAT_NOT_IDLE,
692 MSM_PM_STAT_COUNT
693};
694
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530695struct msm_pm_time_stats {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700696 const char *name;
697 int64_t first_bucket_time;
698 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
699 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
700 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
701 int count;
702 int64_t total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700703};
704
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530705struct msm_pm_cpu_time_stats {
706 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
707};
708
709static DEFINE_PER_CPU_SHARED_ALIGNED(
710 struct msm_pm_cpu_time_stats, msm_pm_stats);
711
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700712static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
713
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530714static DEFINE_SPINLOCK(msm_pm_stats_lock);
715
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716/*
717 * Add the given time data to the statistics collection.
718 */
719static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
720{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530721 unsigned long flags;
722 struct msm_pm_time_stats *stats;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723 int i;
724 int64_t bt;
725
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530726 spin_lock_irqsave(&msm_pm_stats_lock, flags);
727 stats = __get_cpu_var(msm_pm_stats).stats;
728
729 stats[id].total_time += t;
730 stats[id].count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731
732 bt = t;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530733 do_div(bt, stats[id].first_bucket_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700734
735 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
736 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
737 i = DIV_ROUND_UP(fls((uint32_t)bt),
738 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
739 else
740 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
741
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530742 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
743 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530745 stats[id].bucket[i]++;
746
747 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
748 stats[id].min_time[i] = t;
749 if (t > stats[id].max_time[i])
750 stats[id].max_time[i] = t;
751
752 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700753}
754
755/*
756 * Helper function of snprintf where buf is auto-incremented, size is auto-
757 * decremented, and there is no return value.
758 *
759 * NOTE: buf and size must be l-values (e.g. variables)
760 */
761#define SNPRINTF(buf, size, format, ...) \
762 do { \
763 if (size > 0) { \
764 int ret; \
765 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
766 if (ret > size) { \
767 buf += size; \
768 size = 0; \
769 } else { \
770 buf += ret; \
771 size -= ret; \
772 } \
773 } \
774 } while (0)
775
776/*
777 * Write out the power management statistics.
778 */
779static int msm_pm_read_proc
780 (char *page, char **start, off_t off, int count, int *eof, void *data)
781{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530782 unsigned int cpu = off / MSM_PM_STAT_COUNT;
783 int id = off % MSM_PM_STAT_COUNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784 char *p = page;
785
786 if (count < 1024) {
787 *start = (char *) 0;
788 *eof = 0;
789 return 0;
790 }
791
792 if (!off) {
793 SNPRINTF(p, count, "Last power collapse voted ");
794 if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
795 SLEEP_LIMIT_NONE)
796 SNPRINTF(p, count, "for TCXO shutdown\n\n");
797 else
798 SNPRINTF(p, count, "against TCXO shutdown\n\n");
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530799 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700800
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530801 if (cpu < num_possible_cpus()) {
802 unsigned long flags;
803 struct msm_pm_time_stats *stats;
804 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700805 int64_t bucket_time;
806 int64_t s;
807 uint32_t ns;
808
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530809 spin_lock_irqsave(&msm_pm_stats_lock, flags);
810 stats = per_cpu(msm_pm_stats, cpu).stats;
811
812 s = stats[id].total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813 ns = do_div(s, NSEC_PER_SEC);
814 SNPRINTF(p, count,
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530815 "[cpu %u] %s:\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816 " count: %7d\n"
817 " total_time: %lld.%09u\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530818 cpu, stats[id].name,
819 stats[id].count,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820 s, ns);
821
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530822 bucket_time = stats[id].first_bucket_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700823 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
824 s = bucket_time;
825 ns = do_div(s, NSEC_PER_SEC);
826 SNPRINTF(p, count,
827 " <%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530828 s, ns, stats[id].bucket[i],
829 stats[id].min_time[i],
830 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700831
832 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
833 }
834
835 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530836 s, ns, stats[id].bucket[i],
837 stats[id].min_time[i],
838 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700839
840 *start = (char *) 1;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530841 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
842
843 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844 }
845
846 return p - page;
847}
848#undef SNPRINTF
849
850#define MSM_PM_STATS_RESET "reset"
851
852/*
853 * Reset the power management statistics values.
854 */
855static int msm_pm_write_proc(struct file *file, const char __user *buffer,
856 unsigned long count, void *data)
857{
858 char buf[sizeof(MSM_PM_STATS_RESET)];
859 int ret;
860 unsigned long flags;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530861 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700862
863 if (count < strlen(MSM_PM_STATS_RESET)) {
864 ret = -EINVAL;
865 goto write_proc_failed;
866 }
867
868 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
869 ret = -EFAULT;
870 goto write_proc_failed;
871 }
872
873 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
874 ret = -EINVAL;
875 goto write_proc_failed;
876 }
877
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530878 spin_lock_irqsave(&msm_pm_stats_lock, flags);
879 for_each_possible_cpu(cpu) {
880 struct msm_pm_time_stats *stats;
881 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700882
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530883 stats = per_cpu(msm_pm_stats, cpu).stats;
884 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
885 memset(stats[i].bucket,
886 0, sizeof(stats[i].bucket));
887 memset(stats[i].min_time,
888 0, sizeof(stats[i].min_time));
889 memset(stats[i].max_time,
890 0, sizeof(stats[i].max_time));
891 stats[i].count = 0;
892 stats[i].total_time = 0;
893 }
894 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895 msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530896 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700897
898 return count;
899
900write_proc_failed:
901 return ret;
902}
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530903
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700904#undef MSM_PM_STATS_RESET
905#endif /* CONFIG_MSM_IDLE_STATS */
906
907
908/******************************************************************************
909 * Shared Memory Bits
910 *****************************************************************************/
911
912#define DEM_MASTER_BITS_PER_CPU 6
913
914/* Power Master State Bits - Per CPU */
915#define DEM_MASTER_SMSM_RUN \
916 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
917#define DEM_MASTER_SMSM_RSA \
918 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
919#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
920 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
921#define DEM_MASTER_SMSM_SLEEP_EXIT \
922 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
923#define DEM_MASTER_SMSM_READY \
924 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
925#define DEM_MASTER_SMSM_SLEEP \
926 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
927
928/* Power Slave State Bits */
929#define DEM_SLAVE_SMSM_RUN (0x0001)
930#define DEM_SLAVE_SMSM_PWRC (0x0002)
931#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
932#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
933#define DEM_SLAVE_SMSM_WFPI (0x0010)
934#define DEM_SLAVE_SMSM_SLEEP (0x0020)
935#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
936#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
937#define DEM_SLAVE_SMSM_RESET (0x0100)
938#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
939
940
941/******************************************************************************
942 * Shared Memory Data
943 *****************************************************************************/
944
945#define DEM_MAX_PORT_NAME_LEN (20)
946
947struct msm_pm_smem_t {
948 uint32_t sleep_time;
949 uint32_t irq_mask;
950 uint32_t resources_used;
951 uint32_t reserved1;
952
953 uint32_t wakeup_reason;
954 uint32_t pending_irqs;
955 uint32_t rpc_prog;
956 uint32_t rpc_proc;
957 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
958 uint32_t reserved2;
959};
960
961
962/******************************************************************************
963 *
964 *****************************************************************************/
965static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700966static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
967
968static int msm_pm_modem_busy(void)
969{
970 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
971 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
972 KERN_INFO, "%s(): master not ready\n", __func__);
973 return -EBUSY;
974 }
975
976 return 0;
977}
978
979/*
980 * Power collapse the Apps processor. This function executes the handshake
981 * protocol with Modem.
982 *
983 * Return value:
984 * -EAGAIN: modem reset occurred or early exit from power collapse
985 * -EBUSY: modem not ready for our power collapse -- no power loss
986 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
987 * 0: success
988 */
989static int msm_pm_power_collapse
990 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
991{
992 struct msm_pm_polled_group state_grps[2];
993 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700994 int collapsed = 0;
995 int ret;
996
997 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
998 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
999 (int)from_idle, sleep_delay, sleep_limit);
1000
1001 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1002 MSM_PM_DPRINTK(
1003 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1004 KERN_INFO, "%s(): master not ready\n", __func__);
1005 ret = -EBUSY;
1006 goto power_collapse_bail;
1007 }
1008
1009 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
1010
Murali Nalajala41786ab2012-03-06 10:47:32 +05301011 if (cpu_is_msm8625()) {
1012 /* Program the SPM */
1013 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
1014 false);
1015 WARN_ON(ret);
1016 }
1017
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018 msm_irq_enter_sleep1(true, from_idle, &msm_pm_smem_data->irq_mask);
1019 msm_sirc_enter_sleep();
1020 msm_gpio_enter_sleep(from_idle);
1021
1022 msm_pm_smem_data->sleep_time = sleep_delay;
1023 msm_pm_smem_data->resources_used = sleep_limit;
1024
1025 /* Enter PWRC/PWRC_SUSPEND */
1026
1027 if (from_idle)
1028 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1029 DEM_SLAVE_SMSM_PWRC);
1030 else
1031 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1032 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
1033
1034 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
1035 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1036
1037 memset(state_grps, 0, sizeof(state_grps));
1038 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1039 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
1040 state_grps[1].group_id = SMSM_MODEM_STATE;
1041 state_grps[1].bits_all_set = SMSM_RESET;
1042
1043 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1044
1045 if (ret < 0) {
1046 printk(KERN_EMERG "%s(): power collapse entry "
1047 "timed out waiting for Modem's response\n", __func__);
1048 msm_pm_timeout();
1049 }
1050
1051 if (ret == 1) {
1052 MSM_PM_DPRINTK(
1053 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1054 KERN_INFO,
1055 "%s(): msm_pm_poll_state detected Modem reset\n",
1056 __func__);
1057 goto power_collapse_early_exit;
1058 }
1059
1060 /* DEM Master in RSA */
1061
1062 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
1063
1064 ret = msm_irq_enter_sleep2(true, from_idle);
1065 if (ret < 0) {
1066 MSM_PM_DPRINTK(
1067 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1068 KERN_INFO,
1069 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
1070 ret);
1071 goto power_collapse_early_exit;
1072 }
1073
1074 msm_pm_config_hw_before_power_down();
1075 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
1076
1077 saved_acpuclk_rate = acpuclk_power_collapse();
1078 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1079 "%s(): change clock rate (old rate = %lu)\n", __func__,
1080 saved_acpuclk_rate);
1081
1082 if (saved_acpuclk_rate == 0) {
1083 msm_pm_config_hw_after_power_up();
1084 goto power_collapse_early_exit;
1085 }
1086
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001087 msm_pm_boot_config_before_pc(smp_processor_id(),
1088 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001089
1090#ifdef CONFIG_VFP
1091 if (from_idle)
1092 vfp_flush_context();
1093#endif
1094
1095#ifdef CONFIG_CACHE_L2X0
1096 l2x0_suspend();
1097#endif
1098
1099 collapsed = msm_pm_collapse();
1100
1101#ifdef CONFIG_CACHE_L2X0
1102 l2x0_resume(collapsed);
1103#endif
1104
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001105 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001106
1107 if (collapsed) {
1108#ifdef CONFIG_VFP
1109 if (from_idle)
1110 vfp_reinit();
1111#endif
1112 cpu_init();
1113 local_fiq_enable();
1114 }
1115
1116 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1117 KERN_INFO,
1118 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1119
1120 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1121 "%s(): restore clock rate to %lu\n", __func__,
1122 saved_acpuclk_rate);
1123 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1124 SETRATE_PC) < 0)
1125 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1126 __func__, saved_acpuclk_rate);
1127
1128 msm_irq_exit_sleep1(msm_pm_smem_data->irq_mask,
1129 msm_pm_smem_data->wakeup_reason,
1130 msm_pm_smem_data->pending_irqs);
1131
1132 msm_pm_config_hw_after_power_up();
1133 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1134
1135 memset(state_grps, 0, sizeof(state_grps));
1136 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1137 state_grps[0].bits_any_set =
1138 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1139 state_grps[1].group_id = SMSM_MODEM_STATE;
1140 state_grps[1].bits_all_set = SMSM_RESET;
1141
1142 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1143
1144 if (ret < 0) {
1145 printk(KERN_EMERG "%s(): power collapse exit "
1146 "timed out waiting for Modem's response\n", __func__);
1147 msm_pm_timeout();
1148 }
1149
1150 if (ret == 1) {
1151 MSM_PM_DPRINTK(
1152 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1153 KERN_INFO,
1154 "%s(): msm_pm_poll_state detected Modem reset\n",
1155 __func__);
1156 goto power_collapse_early_exit;
1157 }
1158
1159 /* Sanity check */
1160 if (collapsed) {
1161 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1162 } else {
1163 BUG_ON(!(state_grps[0].value_read &
1164 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1165 goto power_collapse_early_exit;
1166 }
1167
1168 /* Enter WFPI */
1169
1170 smsm_change_state(SMSM_APPS_DEM,
1171 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1172 DEM_SLAVE_SMSM_WFPI);
1173
1174 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1175
1176 memset(state_grps, 0, sizeof(state_grps));
1177 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1178 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1179 state_grps[1].group_id = SMSM_MODEM_STATE;
1180 state_grps[1].bits_all_set = SMSM_RESET;
1181
1182 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1183
1184 if (ret < 0) {
1185 printk(KERN_EMERG "%s(): power collapse WFPI "
1186 "timed out waiting for Modem's response\n", __func__);
1187 msm_pm_timeout();
1188 }
1189
1190 if (ret == 1) {
1191 MSM_PM_DPRINTK(
1192 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1193 KERN_INFO,
1194 "%s(): msm_pm_poll_state detected Modem reset\n",
1195 __func__);
1196 ret = -EAGAIN;
1197 goto power_collapse_restore_gpio_bail;
1198 }
1199
1200 /* DEM Master == RUN */
1201
1202 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1203 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1204
1205 msm_irq_exit_sleep2(msm_pm_smem_data->irq_mask,
1206 msm_pm_smem_data->wakeup_reason,
1207 msm_pm_smem_data->pending_irqs);
1208 msm_irq_exit_sleep3(msm_pm_smem_data->irq_mask,
1209 msm_pm_smem_data->wakeup_reason,
1210 msm_pm_smem_data->pending_irqs);
1211 msm_gpio_exit_sleep();
1212 msm_sirc_exit_sleep();
1213
1214 smsm_change_state(SMSM_APPS_DEM,
1215 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1216
1217 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1218
1219 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301220
1221 if (cpu_is_msm8625()) {
1222 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1223 false);
1224 WARN_ON(ret);
1225 }
1226
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001227 return 0;
1228
1229power_collapse_early_exit:
1230 /* Enter PWRC_EARLY_EXIT */
1231
1232 smsm_change_state(SMSM_APPS_DEM,
1233 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1234 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1235
1236 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1237
1238 memset(state_grps, 0, sizeof(state_grps));
1239 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1240 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1241 state_grps[1].group_id = SMSM_MODEM_STATE;
1242 state_grps[1].bits_all_set = SMSM_RESET;
1243
1244 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1245 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1246
1247 if (ret < 0) {
1248 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1249 "timed out waiting for Modem's response\n", __func__);
1250 msm_pm_timeout();
1251 }
1252
1253 if (ret == 1) {
1254 MSM_PM_DPRINTK(
1255 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1256 KERN_INFO,
1257 "%s(): msm_pm_poll_state detected Modem reset\n",
1258 __func__);
1259 }
1260
1261 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1262
1263 ret = -EAGAIN;
1264
1265power_collapse_restore_gpio_bail:
1266 msm_gpio_exit_sleep();
1267 msm_sirc_exit_sleep();
1268
1269 /* Enter RUN */
1270 smsm_change_state(SMSM_APPS_DEM,
1271 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1272 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1273
1274 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1275
1276 if (collapsed)
1277 smd_sleep_exit();
1278
1279power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301280 if (cpu_is_msm8625()) {
1281 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1282 false);
1283 WARN_ON(ret);
1284 }
1285
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001286 return ret;
1287}
1288
1289/*
1290 * Power collapse the Apps processor without involving Modem.
1291 *
1292 * Return value:
1293 * 0: success
1294 */
Murali Nalajala41786ab2012-03-06 10:47:32 +05301295static int msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001296{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001297 int collapsed = 0;
1298 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301299 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001300
1301 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1302 KERN_INFO, "%s()\n", __func__);
1303
1304 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1305 WARN_ON(ret);
1306
Murali Nalajala41786ab2012-03-06 10:47:32 +05301307 entry = (!smp_processor_id() || from_idle) ?
1308 msm_pm_collapse_exit : msm_secondary_startup;
1309
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001310 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301311 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312
1313#ifdef CONFIG_VFP
1314 vfp_flush_context();
1315#endif
1316
1317#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301318 if (!cpu_is_msm8625())
1319 l2x0_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001320#endif
1321
1322 collapsed = msm_pm_collapse();
1323
1324#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301325 if (!cpu_is_msm8625())
1326 l2x0_resume(collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001327#endif
1328
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001329 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001330
1331 if (collapsed) {
1332#ifdef CONFIG_VFP
1333 vfp_reinit();
1334#endif
1335 cpu_init();
1336 local_fiq_enable();
1337 }
1338
1339 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1340 KERN_INFO,
1341 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1342
1343 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1344 WARN_ON(ret);
1345
1346 return 0;
1347}
1348
1349/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001350 * Bring the Apps processor to SWFI.
1351 *
1352 * Return value:
1353 * -EIO: could not ramp Apps processor clock
1354 * 0: success
1355 */
1356static int msm_pm_swfi(bool ramp_acpu)
1357{
1358 unsigned long saved_acpuclk_rate = 0;
1359
1360 if (ramp_acpu) {
1361 saved_acpuclk_rate = acpuclk_wait_for_irq();
1362 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1363 "%s(): change clock rate (old rate = %lu)\n", __func__,
1364 saved_acpuclk_rate);
1365
1366 if (!saved_acpuclk_rate)
1367 return -EIO;
1368 }
1369
Murali Nalajala41786ab2012-03-06 10:47:32 +05301370 if (!cpu_is_msm8625())
1371 msm_pm_config_hw_before_swfi();
1372
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001373 msm_arch_idle();
1374
1375 if (ramp_acpu) {
1376 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1377 "%s(): restore clock rate to %lu\n", __func__,
1378 saved_acpuclk_rate);
1379 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1380 SETRATE_SWFI) < 0)
1381 printk(KERN_ERR
1382 "%s(): failed to restore clock rate(%lu)\n",
1383 __func__, saved_acpuclk_rate);
1384 }
1385
1386 return 0;
1387}
1388
1389
1390/******************************************************************************
1391 * External Idle/Suspend Functions
1392 *****************************************************************************/
1393
1394/*
1395 * Put CPU in low power mode.
1396 */
1397void arch_idle(void)
1398{
1399 bool allow[MSM_PM_SLEEP_MODE_NR];
1400 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1401
1402 int latency_qos;
1403 int64_t timer_expiration;
1404
1405 int low_power;
1406 int ret;
1407 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301408 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001409
1410#ifdef CONFIG_MSM_IDLE_STATS
1411 int64_t t1;
1412 static int64_t t2;
1413 int exit_stat;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301414 #endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001415
1416 if (!atomic_read(&msm_pm_init_done))
1417 return;
1418
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301419 cpu = smp_processor_id();
1420
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001421 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
1422 timer_expiration = msm_timer_enter_idle();
1423
1424#ifdef CONFIG_MSM_IDLE_STATS
1425 t1 = ktime_to_ns(ktime_get());
1426 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
1427 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301428
1429 exit_stat = MSM_PM_STAT_IDLE_SPIN;
1430 low_power = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301431#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001432
1433 for (i = 0; i < ARRAY_SIZE(allow); i++)
1434 allow[i] = true;
1435
Murali Nalajala41786ab2012-03-06 10:47:32 +05301436 if (num_online_cpus() > 1 ||
1437 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001438#ifdef CONFIG_HAS_WAKELOCK
1439 has_wake_lock(WAKE_LOCK_IDLE) ||
1440#endif
1441 !msm_irq_idle_sleep_allowed()) {
1442 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1443 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001444 }
1445
1446 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301447 struct msm_pm_platform_data *mode =
1448 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001449 if (!mode->idle_supported || !mode->idle_enabled ||
1450 mode->latency >= latency_qos ||
1451 mode->residency * 1000ULL >= timer_expiration)
1452 allow[i] = false;
1453 }
1454
1455 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1456 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1457 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1458 while (msm_pm_modem_busy() && wait_us) {
1459 if (wait_us > 100) {
1460 udelay(100);
1461 wait_us -= 100;
1462 } else {
1463 udelay(wait_us);
1464 wait_us = 0;
1465 }
1466 }
1467
1468 if (msm_pm_modem_busy()) {
1469 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1470 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1471 = false;
1472 }
1473 }
1474
1475 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1476 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1477 __func__, latency_qos, timer_expiration, sleep_limit);
1478
1479 for (i = 0; i < ARRAY_SIZE(allow); i++)
1480 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1481 "%s(): allow %s: %d\n", __func__,
1482 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1483
1484 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1485 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1486 uint32_t sleep_delay;
1487
1488 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
1489 timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
1490 if (sleep_delay == 0) /* 0 would mean infinite time */
1491 sleep_delay = 1;
1492
1493 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1494 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1495
1496#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1497 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1498#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1499 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1500#endif
1501
1502 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1503 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
1504
1505#ifdef CONFIG_MSM_IDLE_STATS
1506 if (ret)
1507 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1508 else {
1509 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1510 msm_pm_sleep_limit = sleep_limit;
1511 }
Murali Nalajala41786ab2012-03-06 10:47:32 +05301512#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001513 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301514 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001515 low_power = 0;
1516#ifdef CONFIG_MSM_IDLE_STATS
1517 exit_stat = ret ?
1518 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1519 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301520#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001521 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1522 ret = msm_pm_swfi(true);
1523 if (ret)
1524 while (!msm_irq_pending())
1525 udelay(1);
1526 low_power = 0;
1527#ifdef CONFIG_MSM_IDLE_STATS
1528 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301529#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001530 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1531 msm_pm_swfi(false);
1532 low_power = 0;
1533#ifdef CONFIG_MSM_IDLE_STATS
1534 exit_stat = MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301535#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001536 } else {
1537 while (!msm_irq_pending())
1538 udelay(1);
1539 low_power = 0;
1540#ifdef CONFIG_MSM_IDLE_STATS
1541 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301542#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001543 }
1544
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001545 msm_timer_exit_idle(low_power);
1546
1547#ifdef CONFIG_MSM_IDLE_STATS
1548 t2 = ktime_to_ns(ktime_get());
1549 msm_pm_add_stat(exit_stat, t2 - t1);
Murali Nalajala41786ab2012-03-06 10:47:32 +05301550#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001551}
1552
1553/*
1554 * Suspend the Apps processor.
1555 *
1556 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301557 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001558 * -EAGAIN: modem reset occurred or early exit from suspend
1559 * -EBUSY: modem not ready for our suspend
1560 * -EINVAL: invalid sleep mode
1561 * -EIO: could not ramp Apps processor clock
1562 * -ETIMEDOUT: timed out waiting for modem's handshake
1563 * 0: success
1564 */
1565static int msm_pm_enter(suspend_state_t state)
1566{
1567 bool allow[MSM_PM_SLEEP_MODE_NR];
1568 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301569 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001570 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001571#ifdef CONFIG_MSM_IDLE_STATS
1572 int64_t period = 0;
1573 int64_t time = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301574#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001575
Murali Nalajala41786ab2012-03-06 10:47:32 +05301576 /* Must executed by CORE0 */
1577 if (smp_processor_id()) {
1578 __WARN();
1579 goto suspend_exit;
1580 }
1581
1582#ifdef CONFIG_MSM_IDLE_STATS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001583 time = msm_timer_get_sclk_time(&period);
1584#endif
1585
1586 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1587 "%s(): sleep limit %u\n", __func__, sleep_limit);
1588
1589 for (i = 0; i < ARRAY_SIZE(allow); i++)
1590 allow[i] = true;
1591
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001592 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301593 struct msm_pm_platform_data *mode;
1594 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001595 if (!mode->suspend_supported || !mode->suspend_enabled)
1596 allow[i] = false;
1597 }
1598
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001599 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1600 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1601#ifdef CONFIG_MSM_IDLE_STATS
1602 enum msm_pm_time_stats_id id;
1603 int64_t end_time;
1604#endif
1605
1606 clock_debug_print_enabled();
1607
1608#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1609 if (msm_pm_sleep_time_override > 0) {
1610 int64_t ns;
1611 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1612 msm_pm_set_max_sleep_time(ns);
1613 msm_pm_sleep_time_override = 0;
1614 }
1615#endif
1616 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1617 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1618
1619#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1620 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1621#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1622 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1623#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1624 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1625 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1626#endif
1627
1628 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1629 udelay(500);
1630
1631 ret = msm_pm_power_collapse(
1632 false, msm_pm_max_sleep_time, sleep_limit);
1633
1634#ifdef CONFIG_MSM_IDLE_STATS
1635 if (ret)
1636 id = MSM_PM_STAT_FAILED_SUSPEND;
1637 else {
1638 id = MSM_PM_STAT_SUSPEND;
1639 msm_pm_sleep_limit = sleep_limit;
1640 }
1641
1642 if (time != 0) {
1643 end_time = msm_timer_get_sclk_time(NULL);
1644 if (end_time != 0) {
1645 time = end_time - time;
1646 if (time < 0)
1647 time += period;
1648 } else
1649 time = 0;
1650 }
1651
1652 msm_pm_add_stat(id, time);
1653#endif
1654 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301655 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001656 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1657 ret = msm_pm_swfi(true);
1658 if (ret)
1659 while (!msm_irq_pending())
1660 udelay(1);
1661 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1662 msm_pm_swfi(false);
1663 }
1664
Murali Nalajala41786ab2012-03-06 10:47:32 +05301665suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001666 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1667 "%s(): return %d\n", __func__, ret);
1668
1669 return ret;
1670}
1671
1672static struct platform_suspend_ops msm_pm_ops = {
1673 .enter = msm_pm_enter,
1674 .valid = suspend_valid_only_mem,
1675};
1676
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301677/* Hotplug the "non boot" CPU's and put
1678 * the cores into low power mode
1679 */
1680void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1681{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301682 bool allow[MSM_PM_SLEEP_MODE_NR];
1683 int i;
1684
1685 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1686 struct msm_pm_platform_data *mode;
1687
1688 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1689 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1690 }
1691
1692 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1693 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1694
1695 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1696 msm_pm_power_collapse_standalone(false);
1697 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1698 msm_pm_swfi(false);
1699 } else {
1700 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1701 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1702 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301703}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001704
1705/******************************************************************************
1706 * Restart Definitions
1707 *****************************************************************************/
1708
1709static uint32_t restart_reason = 0x776655AA;
1710
1711static void msm_pm_power_off(void)
1712{
1713 msm_rpcrouter_close();
1714 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1715 for (;;)
1716 ;
1717}
1718
1719static void msm_pm_restart(char str, const char *cmd)
1720{
1721 msm_rpcrouter_close();
1722 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1723
1724 for (;;)
1725 ;
1726}
1727
1728static int msm_reboot_call
1729 (struct notifier_block *this, unsigned long code, void *_cmd)
1730{
1731 if ((code == SYS_RESTART) && _cmd) {
1732 char *cmd = _cmd;
1733 if (!strcmp(cmd, "bootloader")) {
1734 restart_reason = 0x77665500;
1735 } else if (!strcmp(cmd, "recovery")) {
1736 restart_reason = 0x77665502;
1737 } else if (!strcmp(cmd, "eraseflash")) {
1738 restart_reason = 0x776655EF;
1739 } else if (!strncmp(cmd, "oem-", 4)) {
1740 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1741 restart_reason = 0x6f656d00 | code;
1742 } else {
1743 restart_reason = 0x77665501;
1744 }
1745 }
1746 return NOTIFY_DONE;
1747}
1748
1749static struct notifier_block msm_reboot_notifier = {
1750 .notifier_call = msm_reboot_call,
1751};
1752
1753
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001754/*
1755 * Initialize the power management subsystem.
1756 *
1757 * Return value:
1758 * -ENODEV: initialization failed
1759 * 0: success
1760 */
1761static int __init msm_pm_init(void)
1762{
1763#ifdef CONFIG_MSM_IDLE_STATS
1764 struct proc_dir_entry *d_entry;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301765 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001766#endif
1767 int ret;
1768#ifdef CONFIG_CPU_V7
1769 pgd_t *pc_pgd;
1770 pmd_t *pmd;
1771 unsigned long pmdval;
1772
Aparna Mallavarapuced47282012-03-08 12:29:41 +05301773 if (cpu_is_msm8625())
1774 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001775 /* Page table for cores to come back up safely. */
1776 pc_pgd = pgd_alloc(&init_mm);
1777 if (!pc_pgd)
1778 return -ENOMEM;
1779 pmd = pmd_offset(pc_pgd +
1780 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1781 virt_to_phys(msm_pm_collapse_exit));
1782 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1783 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1784 pmd[0] = __pmd(pmdval);
1785 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1786
1787 /* It is remotely possible that the code in msm_pm_collapse_exit()
1788 * which turns on the MMU with this mapping is in the
1789 * next even-numbered megabyte beyond the
1790 * start of msm_pm_collapse_exit().
1791 * Map this megabyte in as well.
1792 */
1793 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1794 flush_pmd_entry(pmd);
1795 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
1796#endif
1797
1798 pm_power_off = msm_pm_power_off;
1799 arm_pm_restart = msm_pm_restart;
1800 register_reboot_notifier(&msm_reboot_notifier);
1801
1802 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1803 sizeof(*msm_pm_smem_data));
1804 if (msm_pm_smem_data == NULL) {
1805 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1806 return -ENODEV;
1807 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001808
1809 ret = msm_timer_init_time_sync(msm_pm_timeout);
1810 if (ret)
1811 return ret;
1812
1813 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1814 if (ret) {
1815 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1816 __func__, ret);
1817 return ret;
1818 }
1819
1820#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1821 /* The wakeup_reason field is overloaded during initialization time
1822 to signal Modem that Apps will control the low power modes of
1823 the memory.
1824 */
1825 msm_pm_smem_data->wakeup_reason = 1;
1826 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1827#endif
1828
1829 BUG_ON(msm_pm_modes == NULL);
1830
1831 atomic_set(&msm_pm_init_done, 1);
1832 suspend_set_ops(&msm_pm_ops);
1833
1834 msm_pm_mode_sysfs_add();
1835#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301836 for_each_possible_cpu(cpu) {
1837 struct msm_pm_time_stats *stats =
1838 per_cpu(msm_pm_stats, cpu).stats;
1839
1840 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1841 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1842 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1843
1844 stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin";
1845 stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
1846 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1847
1848 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1849 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1850 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1851
1852 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1853 "idle-standalone-power-collapse";
1854 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1855 first_bucket_time =
1856 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1857
1858 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
1859 "idle-failed-standalone-power-collapse";
1860 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].
1861 first_bucket_time =
1862 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1863
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301864 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
1865 "idle-power-collapse";
1866 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
1867 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1868
1869 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
1870 "idle-failed-power-collapse";
1871 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].
1872 first_bucket_time =
1873 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1874
1875 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
1876 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
1877 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
1878
1879 stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend";
1880 stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
1881 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1882
1883 stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle";
1884 stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
1885 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1886 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001887 d_entry = create_proc_entry("msm_pm_stats",
1888 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
1889 if (d_entry) {
1890 d_entry->read_proc = msm_pm_read_proc;
1891 d_entry->write_proc = msm_pm_write_proc;
1892 d_entry->data = NULL;
1893 }
1894#endif
1895
1896 return 0;
1897}
1898
1899late_initcall_sync(msm_pm_init);