blob: 6b026ace8974ca41b713085df9d56c59e90f25fd [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;
83module_param_named(
84 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
85);
86
87#define MSM_PM_DPRINTK(mask, level, message, ...) \
88 do { \
89 if ((mask) & msm_pm_debug_mask) \
90 printk(level message, ## __VA_ARGS__); \
91 } while (0)
92
93#define MSM_PM_DEBUG_PRINT_STATE(tag) \
94 do { \
95 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
96 KERN_INFO, "%s: " \
97 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
98 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
99 "SMSM_APPS_DEM %x\n", \
100 tag, \
101 __raw_readl(APPS_CLK_SLEEP_EN), \
102 __raw_readl(APPS_PWRDOWN), \
103 smsm_get_state(SMSM_POWER_MASTER_DEM), \
104 smsm_get_state(SMSM_MODEM_STATE), \
105 smsm_get_state(SMSM_APPS_DEM)); \
106 } while (0)
107
108#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
109 do { \
110 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
111 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
112 msm_pm_smem_data->resources_used, \
113 msm_pm_smem_data->irq_mask, \
114 msm_pm_smem_data->wakeup_reason, \
115 msm_pm_smem_data->pending_irqs); \
116 } while (0)
117
118
119/******************************************************************************
120 * Sleep Modes and Parameters
121 *****************************************************************************/
122
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
124module_param_named(
125 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
126 int, S_IRUGO | S_IWUSR | S_IWGRP
127);
128
129enum {
130 MSM_PM_MODE_ATTR_SUSPEND,
131 MSM_PM_MODE_ATTR_IDLE,
132 MSM_PM_MODE_ATTR_LATENCY,
133 MSM_PM_MODE_ATTR_RESIDENCY,
134 MSM_PM_MODE_ATTR_NR,
135};
136
137static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
138 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
139 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
140 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
141 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
142};
143
144static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
145 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
146 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
148 "ramp_down_and_wfi",
149 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
150 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
151 "power_collapse_no_xo_shutdown",
152 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
153 "standalone_power_collapse",
154};
155
156static struct msm_pm_platform_data *msm_pm_modes;
157
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530158struct msm_pm_kobj_attribute {
159 unsigned int cpu;
160 struct kobj_attribute ka;
161};
162
163#define GET_CPU_OF_ATTR(attr) \
164 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
165
166struct msm_pm_sysfs_sleep_mode {
167 struct kobject *kobj;
168 struct attribute_group attr_group;
169 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
170 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
171};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172
173/*
174 * Write out the attribute.
175 */
176static ssize_t msm_pm_mode_attr_show(
177 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
178{
179 int ret = -EINVAL;
180 int i;
181
182 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
183 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530184 unsigned int cpu;
185 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186
187 if (msm_pm_sleep_mode_labels[i] == NULL)
188 continue;
189
190 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
191 continue;
192
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530193 cpu = GET_CPU_OF_ATTR(attr);
194 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
195
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 if (!strcmp(attr->attr.name,
197 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530198 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 kp.arg = &arg;
200 ret = param_get_ulong(buf, &kp);
201 } else if (!strcmp(attr->attr.name,
202 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530203 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 kp.arg = &arg;
205 ret = param_get_ulong(buf, &kp);
206 } else if (!strcmp(attr->attr.name,
207 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530208 u32 arg = mode->latency;
209 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 ret = param_get_ulong(buf, &kp);
211 } else if (!strcmp(attr->attr.name,
212 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530213 u32 arg = mode->residency;
214 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 ret = param_get_ulong(buf, &kp);
216 }
217
218 break;
219 }
220
221 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530222 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 ret++;
224 }
225
226 return ret;
227}
228
229/*
230 * Read in the new attribute value.
231 */
232static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
233 struct kobj_attribute *attr, const char *buf, size_t count)
234{
235 int ret = -EINVAL;
236 int i;
237
238 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
239 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530240 unsigned int cpu;
241 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242
243 if (msm_pm_sleep_mode_labels[i] == NULL)
244 continue;
245
246 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
247 continue;
248
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530249 cpu = GET_CPU_OF_ATTR(attr);
250 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
251
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252 if (!strcmp(attr->attr.name,
253 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530254 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 ret = param_set_byte(buf, &kp);
256 } else if (!strcmp(attr->attr.name,
257 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530258 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259 ret = param_set_byte(buf, &kp);
260 } else if (!strcmp(attr->attr.name,
261 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530262 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 ret = param_set_ulong(buf, &kp);
264 } else if (!strcmp(attr->attr.name,
265 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530266 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 ret = param_set_ulong(buf, &kp);
268 }
269
270 break;
271 }
272
273 return ret ? ret : count;
274}
275
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530276 /* Add sysfs entries for one cpu. */
277static int __init msm_pm_mode_sysfs_add_cpu(
278 unsigned int cpu, struct kobject *modes_kobj)
279{
280 char cpu_name[8];
281 struct kobject *cpu_kobj;
282 struct msm_pm_sysfs_sleep_mode *mode = NULL;
283 int i, j, k;
284 int ret;
285
286 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
287 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
288 if (!cpu_kobj) {
289 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
290 ret = -ENOMEM;
291 goto mode_sysfs_add_cpu_exit;
292 }
293
294 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
295 int idx = MSM_PM_MODE(cpu, i);
296
297 if ((!msm_pm_modes[idx].suspend_supported) &&
298 (!msm_pm_modes[idx].idle_supported))
299 continue;
300
301 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
302 if (!mode) {
303 pr_err("%s: cannot allocate memory for attributes\n",
304 __func__);
305 ret = -ENOMEM;
306 goto mode_sysfs_add_cpu_exit;
307 }
308
309 mode->kobj = kobject_create_and_add(
310 msm_pm_sleep_mode_labels[i], cpu_kobj);
311 if (!mode->kobj) {
312 pr_err("%s: cannot create kobject\n", __func__);
313 ret = -ENOMEM;
314 goto mode_sysfs_add_cpu_exit;
315 }
316
317 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
318 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
319 !msm_pm_modes[idx].idle_supported)
320 continue;
321 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
322 !msm_pm_modes[idx].suspend_supported)
323 continue;
324 mode->kas[j].cpu = cpu;
325 mode->kas[j].ka.attr.mode = 0644;
326 mode->kas[j].ka.show = msm_pm_mode_attr_show;
327 mode->kas[j].ka.store = msm_pm_mode_attr_store;
328 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
329 mode->attrs[j] = &mode->kas[j].ka.attr;
330 j++;
331 }
332 mode->attrs[j] = NULL;
333
334 mode->attr_group.attrs = mode->attrs;
335 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
336 if (ret) {
337 printk(KERN_ERR
338 "%s: cannot create kobject attribute group\n",
339 __func__);
340 goto mode_sysfs_add_cpu_exit;
341 }
342 }
343
344 ret = 0;
345
346mode_sysfs_add_cpu_exit:
347 if (ret) {
348 if (mode && mode->kobj)
349 kobject_del(mode->kobj);
350 kfree(mode);
351 }
352
353 return ret;
354}
355
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356/*
357 * Add sysfs entries for the sleep modes.
358 */
359static int __init msm_pm_mode_sysfs_add(void)
360{
361 struct kobject *module_kobj = NULL;
362 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530363 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 int ret;
365
366 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
367 if (!module_kobj) {
368 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
369 __func__, KBUILD_MODNAME);
370 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530371 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 }
373
374 modes_kobj = kobject_create_and_add("modes", module_kobj);
375 if (!modes_kobj) {
376 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
377 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530378 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 }
380
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530381 for_each_possible_cpu(cpu) {
382 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
383 if (ret)
384 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 }
386
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530387 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530389mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 return ret;
391}
392
393void __init msm_pm_set_platform_data(
394 struct msm_pm_platform_data *data, int count)
395{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530396 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397 msm_pm_modes = data;
398}
399
400
401/******************************************************************************
402 * Sleep Limitations
403 *****************************************************************************/
404enum {
405 SLEEP_LIMIT_NONE = 0,
406 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
407 SLEEP_LIMIT_MASK = 0x03,
408};
409
410#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
411enum {
412 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
413 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
414};
415#endif
416
417
418/******************************************************************************
419 * Configure Hardware for Power Down/Up
420 *****************************************************************************/
421
422#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530423#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
424#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
426#else /* defined(CONFIG_ARCH_MSM7X30) */
427#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
428#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
429#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
430#endif /* defined(CONFIG_ARCH_MSM7X30) */
431
432/*
433 * Configure hardware registers in preparation for Apps power down.
434 */
435static void msm_pm_config_hw_before_power_down(void)
436{
437#if defined(CONFIG_ARCH_MSM7X30)
438 __raw_writel(1, APPS_PWRDOWN);
439 mb();
440 __raw_writel(4, APPS_SECOP);
441 mb();
442#elif defined(CONFIG_ARCH_MSM7X27)
443 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
444 mb();
445 __raw_writel(1, APPS_PWRDOWN);
446 mb();
447#elif defined(CONFIG_ARCH_MSM7x27A)
448 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
449 mb();
450 __raw_writel(1, APPS_PWRDOWN);
451 mb();
452#else
453 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
454 mb();
455 __raw_writel(1, APPS_PWRDOWN);
456 mb();
457 __raw_writel(0, APPS_STANDBY_CTL);
458 mb();
459#endif
460}
461
462/*
463 * Clear hardware registers after Apps powers up.
464 */
465static void msm_pm_config_hw_after_power_up(void)
466{
467#if defined(CONFIG_ARCH_MSM7X30)
468 __raw_writel(0, APPS_SECOP);
469 mb();
470 __raw_writel(0, APPS_PWRDOWN);
471 mb();
472 msm_spm_reinit();
473#elif defined(CONFIG_ARCH_MSM7x27A)
474 __raw_writel(0, APPS_PWRDOWN);
475 mb();
476 __raw_writel(0, APPS_CLK_SLEEP_EN);
477 mb();
478#else
479 __raw_writel(0, APPS_PWRDOWN);
480 mb();
481 __raw_writel(0, APPS_CLK_SLEEP_EN);
482 mb();
483#endif
484}
485
486/*
487 * Configure hardware registers in preparation for SWFI.
488 */
489static void msm_pm_config_hw_before_swfi(void)
490{
491#if defined(CONFIG_ARCH_QSD8X50)
492 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
493 mb();
494#elif defined(CONFIG_ARCH_MSM7X27)
495 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
496 mb();
497#elif defined(CONFIG_ARCH_MSM7X27A)
498 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
499 mb();
500#endif
501}
502
503/*
504 * Respond to timing out waiting for Modem
505 *
506 * NOTE: The function never returns.
507 */
508static void msm_pm_timeout(void)
509{
510#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
511 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
512 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
513#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
514 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
515 msm_proc_comm_reset_modem_now();
516#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
517 printk(KERN_EMERG "%s(): halting\n", __func__);
518#endif
519 for (;;)
520 ;
521}
522
523
524/******************************************************************************
525 * State Polling Definitions
526 *****************************************************************************/
527
528struct msm_pm_polled_group {
529 uint32_t group_id;
530
531 uint32_t bits_all_set;
532 uint32_t bits_all_clear;
533 uint32_t bits_any_set;
534 uint32_t bits_any_clear;
535
536 uint32_t value_read;
537};
538
539/*
540 * Return true if all bits indicated by flag are set in source.
541 */
542static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
543{
544 return (source & flag) == flag;
545}
546
547/*
548 * Return true if any bit indicated by flag are set in source.
549 */
550static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
551{
552 return !flag || (source & flag);
553}
554
555/*
556 * Return true if all bits indicated by flag are cleared in source.
557 */
558static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
559{
560 return (~source & flag) == flag;
561}
562
563/*
564 * Return true if any bit indicated by flag are cleared in source.
565 */
566static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
567{
568 return !flag || (~source & flag);
569}
570
571/*
572 * Poll the shared memory states as indicated by the poll groups.
573 *
574 * nr_grps: number of groups in the array
575 * grps: array of groups
576 *
577 * The function returns when conditions specified by any of the poll
578 * groups become true. The conditions specified by a poll group are
579 * deemed true when 1) at least one bit from bits_any_set is set OR one
580 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
581 * are set; and 3) all bits in bits_all_clear are cleared.
582 *
583 * Return value:
584 * >=0: index of the poll group whose conditions have become true
585 * -ETIMEDOUT: timed out
586 */
587static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
588{
589 int i, k;
590
591 for (i = 0; i < 50000; i++) {
592 for (k = 0; k < nr_grps; k++) {
593 bool all_set, all_clear;
594 bool any_set, any_clear;
595
596 grps[k].value_read = smsm_get_state(grps[k].group_id);
597
598 all_set = msm_pm_all_set(grps[k].value_read,
599 grps[k].bits_all_set);
600 all_clear = msm_pm_all_clear(grps[k].value_read,
601 grps[k].bits_all_clear);
602 any_set = msm_pm_any_set(grps[k].value_read,
603 grps[k].bits_any_set);
604 any_clear = msm_pm_any_clear(grps[k].value_read,
605 grps[k].bits_any_clear);
606
607 if (all_set && all_clear && (any_set || any_clear))
608 return k;
609 }
610 udelay(50);
611 }
612
613 printk(KERN_ERR "%s failed:\n", __func__);
614 for (k = 0; k < nr_grps; k++)
615 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
616 grps[k].bits_all_set, grps[k].bits_all_clear,
617 grps[k].bits_any_set, grps[k].bits_any_clear,
618 grps[k].value_read);
619
620 return -ETIMEDOUT;
621}
622
623
624/******************************************************************************
625 * Suspend Max Sleep Time
626 *****************************************************************************/
627
628#define SCLK_HZ (32768)
629#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
630
631#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
632static int msm_pm_sleep_time_override;
633module_param_named(sleep_time_override,
634 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
635#endif
636
637static uint32_t msm_pm_max_sleep_time;
638
639/*
640 * Convert time from nanoseconds to slow clock ticks, then cap it to the
641 * specified limit
642 */
643static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
644{
645 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
646 return (time_ns > limit) ? limit : time_ns;
647}
648
649/*
650 * Set the sleep time for suspend. 0 means infinite sleep time.
651 */
652void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
653{
654 unsigned long flags;
655
656 local_irq_save(flags);
657 if (max_sleep_time_ns == 0) {
658 msm_pm_max_sleep_time = 0;
659 } else {
660 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
661 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
662
663 if (msm_pm_max_sleep_time == 0)
664 msm_pm_max_sleep_time = 1;
665 }
666
667 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
668 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
669 max_sleep_time_ns, msm_pm_max_sleep_time);
670 local_irq_restore(flags);
671}
672EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
673
674
675/******************************************************************************
676 * CONFIG_MSM_IDLE_STATS
677 *****************************************************************************/
678
679#ifdef CONFIG_MSM_IDLE_STATS
680enum msm_pm_time_stats_id {
681 MSM_PM_STAT_REQUESTED_IDLE,
682 MSM_PM_STAT_IDLE_SPIN,
683 MSM_PM_STAT_IDLE_WFI,
684 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
685 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
687 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
688 MSM_PM_STAT_SUSPEND,
689 MSM_PM_STAT_FAILED_SUSPEND,
690 MSM_PM_STAT_NOT_IDLE,
691 MSM_PM_STAT_COUNT
692};
693
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530694struct msm_pm_time_stats {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 const char *name;
696 int64_t first_bucket_time;
697 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
698 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
699 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
700 int count;
701 int64_t total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702};
703
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530704struct msm_pm_cpu_time_stats {
705 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
706};
707
708static DEFINE_PER_CPU_SHARED_ALIGNED(
709 struct msm_pm_cpu_time_stats, msm_pm_stats);
710
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
712
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530713static DEFINE_SPINLOCK(msm_pm_stats_lock);
714
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715/*
716 * Add the given time data to the statistics collection.
717 */
718static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
719{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530720 unsigned long flags;
721 struct msm_pm_time_stats *stats;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700722 int i;
723 int64_t bt;
724
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530725 spin_lock_irqsave(&msm_pm_stats_lock, flags);
726 stats = __get_cpu_var(msm_pm_stats).stats;
727
728 stats[id].total_time += t;
729 stats[id].count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700730
731 bt = t;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530732 do_div(bt, stats[id].first_bucket_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733
734 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
735 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
736 i = DIV_ROUND_UP(fls((uint32_t)bt),
737 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
738 else
739 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
740
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530741 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
742 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700743
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530744 stats[id].bucket[i]++;
745
746 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
747 stats[id].min_time[i] = t;
748 if (t > stats[id].max_time[i])
749 stats[id].max_time[i] = t;
750
751 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752}
753
754/*
755 * Helper function of snprintf where buf is auto-incremented, size is auto-
756 * decremented, and there is no return value.
757 *
758 * NOTE: buf and size must be l-values (e.g. variables)
759 */
760#define SNPRINTF(buf, size, format, ...) \
761 do { \
762 if (size > 0) { \
763 int ret; \
764 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
765 if (ret > size) { \
766 buf += size; \
767 size = 0; \
768 } else { \
769 buf += ret; \
770 size -= ret; \
771 } \
772 } \
773 } while (0)
774
775/*
776 * Write out the power management statistics.
777 */
778static int msm_pm_read_proc
779 (char *page, char **start, off_t off, int count, int *eof, void *data)
780{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530781 unsigned int cpu = off / MSM_PM_STAT_COUNT;
782 int id = off % MSM_PM_STAT_COUNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700783 char *p = page;
784
785 if (count < 1024) {
786 *start = (char *) 0;
787 *eof = 0;
788 return 0;
789 }
790
791 if (!off) {
792 SNPRINTF(p, count, "Last power collapse voted ");
793 if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
794 SLEEP_LIMIT_NONE)
795 SNPRINTF(p, count, "for TCXO shutdown\n\n");
796 else
797 SNPRINTF(p, count, "against TCXO shutdown\n\n");
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530798 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700799
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530800 if (cpu < num_possible_cpus()) {
801 unsigned long flags;
802 struct msm_pm_time_stats *stats;
803 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700804 int64_t bucket_time;
805 int64_t s;
806 uint32_t ns;
807
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530808 spin_lock_irqsave(&msm_pm_stats_lock, flags);
809 stats = per_cpu(msm_pm_stats, cpu).stats;
810
811 s = stats[id].total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812 ns = do_div(s, NSEC_PER_SEC);
813 SNPRINTF(p, count,
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530814 "[cpu %u] %s:\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700815 " count: %7d\n"
816 " total_time: %lld.%09u\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530817 cpu, stats[id].name,
818 stats[id].count,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819 s, ns);
820
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530821 bucket_time = stats[id].first_bucket_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
823 s = bucket_time;
824 ns = do_div(s, NSEC_PER_SEC);
825 SNPRINTF(p, count,
826 " <%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530827 s, ns, stats[id].bucket[i],
828 stats[id].min_time[i],
829 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700830
831 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
832 }
833
834 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530835 s, ns, stats[id].bucket[i],
836 stats[id].min_time[i],
837 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838
839 *start = (char *) 1;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530840 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
841
842 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700843 }
844
845 return p - page;
846}
847#undef SNPRINTF
848
849#define MSM_PM_STATS_RESET "reset"
850
851/*
852 * Reset the power management statistics values.
853 */
854static int msm_pm_write_proc(struct file *file, const char __user *buffer,
855 unsigned long count, void *data)
856{
857 char buf[sizeof(MSM_PM_STATS_RESET)];
858 int ret;
859 unsigned long flags;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530860 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861
862 if (count < strlen(MSM_PM_STATS_RESET)) {
863 ret = -EINVAL;
864 goto write_proc_failed;
865 }
866
867 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
868 ret = -EFAULT;
869 goto write_proc_failed;
870 }
871
872 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
873 ret = -EINVAL;
874 goto write_proc_failed;
875 }
876
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530877 spin_lock_irqsave(&msm_pm_stats_lock, flags);
878 for_each_possible_cpu(cpu) {
879 struct msm_pm_time_stats *stats;
880 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700881
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530882 stats = per_cpu(msm_pm_stats, cpu).stats;
883 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
884 memset(stats[i].bucket,
885 0, sizeof(stats[i].bucket));
886 memset(stats[i].min_time,
887 0, sizeof(stats[i].min_time));
888 memset(stats[i].max_time,
889 0, sizeof(stats[i].max_time));
890 stats[i].count = 0;
891 stats[i].total_time = 0;
892 }
893 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700894 msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530895 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896
897 return count;
898
899write_proc_failed:
900 return ret;
901}
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530902
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700903#undef MSM_PM_STATS_RESET
904#endif /* CONFIG_MSM_IDLE_STATS */
905
906
907/******************************************************************************
908 * Shared Memory Bits
909 *****************************************************************************/
910
911#define DEM_MASTER_BITS_PER_CPU 6
912
913/* Power Master State Bits - Per CPU */
914#define DEM_MASTER_SMSM_RUN \
915 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
916#define DEM_MASTER_SMSM_RSA \
917 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
918#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
919 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
920#define DEM_MASTER_SMSM_SLEEP_EXIT \
921 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
922#define DEM_MASTER_SMSM_READY \
923 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
924#define DEM_MASTER_SMSM_SLEEP \
925 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
926
927/* Power Slave State Bits */
928#define DEM_SLAVE_SMSM_RUN (0x0001)
929#define DEM_SLAVE_SMSM_PWRC (0x0002)
930#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
931#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
932#define DEM_SLAVE_SMSM_WFPI (0x0010)
933#define DEM_SLAVE_SMSM_SLEEP (0x0020)
934#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
935#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
936#define DEM_SLAVE_SMSM_RESET (0x0100)
937#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
938
939
940/******************************************************************************
941 * Shared Memory Data
942 *****************************************************************************/
943
944#define DEM_MAX_PORT_NAME_LEN (20)
945
946struct msm_pm_smem_t {
947 uint32_t sleep_time;
948 uint32_t irq_mask;
949 uint32_t resources_used;
950 uint32_t reserved1;
951
952 uint32_t wakeup_reason;
953 uint32_t pending_irqs;
954 uint32_t rpc_prog;
955 uint32_t rpc_proc;
956 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
957 uint32_t reserved2;
958};
959
960
961/******************************************************************************
962 *
963 *****************************************************************************/
964static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
966
967static int msm_pm_modem_busy(void)
968{
969 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
970 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
971 KERN_INFO, "%s(): master not ready\n", __func__);
972 return -EBUSY;
973 }
974
975 return 0;
976}
977
978/*
979 * Power collapse the Apps processor. This function executes the handshake
980 * protocol with Modem.
981 *
982 * Return value:
983 * -EAGAIN: modem reset occurred or early exit from power collapse
984 * -EBUSY: modem not ready for our power collapse -- no power loss
985 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
986 * 0: success
987 */
988static int msm_pm_power_collapse
989 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
990{
991 struct msm_pm_polled_group state_grps[2];
992 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993 int collapsed = 0;
994 int ret;
995
996 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
997 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
998 (int)from_idle, sleep_delay, sleep_limit);
999
1000 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1001 MSM_PM_DPRINTK(
1002 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1003 KERN_INFO, "%s(): master not ready\n", __func__);
1004 ret = -EBUSY;
1005 goto power_collapse_bail;
1006 }
1007
1008 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
1009
Murali Nalajala41786ab2012-03-06 10:47:32 +05301010 if (cpu_is_msm8625()) {
1011 /* Program the SPM */
1012 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
1013 false);
1014 WARN_ON(ret);
1015 }
1016
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001017 msm_irq_enter_sleep1(true, from_idle, &msm_pm_smem_data->irq_mask);
1018 msm_sirc_enter_sleep();
1019 msm_gpio_enter_sleep(from_idle);
1020
1021 msm_pm_smem_data->sleep_time = sleep_delay;
1022 msm_pm_smem_data->resources_used = sleep_limit;
1023
1024 /* Enter PWRC/PWRC_SUSPEND */
1025
1026 if (from_idle)
1027 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1028 DEM_SLAVE_SMSM_PWRC);
1029 else
1030 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1031 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
1032
1033 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
1034 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1035
1036 memset(state_grps, 0, sizeof(state_grps));
1037 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1038 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
1039 state_grps[1].group_id = SMSM_MODEM_STATE;
1040 state_grps[1].bits_all_set = SMSM_RESET;
1041
1042 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1043
1044 if (ret < 0) {
1045 printk(KERN_EMERG "%s(): power collapse entry "
1046 "timed out waiting for Modem's response\n", __func__);
1047 msm_pm_timeout();
1048 }
1049
1050 if (ret == 1) {
1051 MSM_PM_DPRINTK(
1052 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1053 KERN_INFO,
1054 "%s(): msm_pm_poll_state detected Modem reset\n",
1055 __func__);
1056 goto power_collapse_early_exit;
1057 }
1058
1059 /* DEM Master in RSA */
1060
1061 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
1062
1063 ret = msm_irq_enter_sleep2(true, from_idle);
1064 if (ret < 0) {
1065 MSM_PM_DPRINTK(
1066 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1067 KERN_INFO,
1068 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
1069 ret);
1070 goto power_collapse_early_exit;
1071 }
1072
1073 msm_pm_config_hw_before_power_down();
1074 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
1075
1076 saved_acpuclk_rate = acpuclk_power_collapse();
1077 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1078 "%s(): change clock rate (old rate = %lu)\n", __func__,
1079 saved_acpuclk_rate);
1080
1081 if (saved_acpuclk_rate == 0) {
1082 msm_pm_config_hw_after_power_up();
1083 goto power_collapse_early_exit;
1084 }
1085
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001086 msm_pm_boot_config_before_pc(smp_processor_id(),
1087 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001088
1089#ifdef CONFIG_VFP
1090 if (from_idle)
1091 vfp_flush_context();
1092#endif
1093
1094#ifdef CONFIG_CACHE_L2X0
1095 l2x0_suspend();
1096#endif
1097
1098 collapsed = msm_pm_collapse();
1099
1100#ifdef CONFIG_CACHE_L2X0
1101 l2x0_resume(collapsed);
1102#endif
1103
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001104 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001105
1106 if (collapsed) {
1107#ifdef CONFIG_VFP
1108 if (from_idle)
1109 vfp_reinit();
1110#endif
1111 cpu_init();
1112 local_fiq_enable();
1113 }
1114
1115 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1116 KERN_INFO,
1117 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1118
1119 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1120 "%s(): restore clock rate to %lu\n", __func__,
1121 saved_acpuclk_rate);
1122 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1123 SETRATE_PC) < 0)
1124 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1125 __func__, saved_acpuclk_rate);
1126
1127 msm_irq_exit_sleep1(msm_pm_smem_data->irq_mask,
1128 msm_pm_smem_data->wakeup_reason,
1129 msm_pm_smem_data->pending_irqs);
1130
1131 msm_pm_config_hw_after_power_up();
1132 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1133
1134 memset(state_grps, 0, sizeof(state_grps));
1135 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1136 state_grps[0].bits_any_set =
1137 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1138 state_grps[1].group_id = SMSM_MODEM_STATE;
1139 state_grps[1].bits_all_set = SMSM_RESET;
1140
1141 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1142
1143 if (ret < 0) {
1144 printk(KERN_EMERG "%s(): power collapse exit "
1145 "timed out waiting for Modem's response\n", __func__);
1146 msm_pm_timeout();
1147 }
1148
1149 if (ret == 1) {
1150 MSM_PM_DPRINTK(
1151 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1152 KERN_INFO,
1153 "%s(): msm_pm_poll_state detected Modem reset\n",
1154 __func__);
1155 goto power_collapse_early_exit;
1156 }
1157
1158 /* Sanity check */
1159 if (collapsed) {
1160 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1161 } else {
1162 BUG_ON(!(state_grps[0].value_read &
1163 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1164 goto power_collapse_early_exit;
1165 }
1166
1167 /* Enter WFPI */
1168
1169 smsm_change_state(SMSM_APPS_DEM,
1170 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1171 DEM_SLAVE_SMSM_WFPI);
1172
1173 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1174
1175 memset(state_grps, 0, sizeof(state_grps));
1176 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1177 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1178 state_grps[1].group_id = SMSM_MODEM_STATE;
1179 state_grps[1].bits_all_set = SMSM_RESET;
1180
1181 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1182
1183 if (ret < 0) {
1184 printk(KERN_EMERG "%s(): power collapse WFPI "
1185 "timed out waiting for Modem's response\n", __func__);
1186 msm_pm_timeout();
1187 }
1188
1189 if (ret == 1) {
1190 MSM_PM_DPRINTK(
1191 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1192 KERN_INFO,
1193 "%s(): msm_pm_poll_state detected Modem reset\n",
1194 __func__);
1195 ret = -EAGAIN;
1196 goto power_collapse_restore_gpio_bail;
1197 }
1198
1199 /* DEM Master == RUN */
1200
1201 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1202 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1203
1204 msm_irq_exit_sleep2(msm_pm_smem_data->irq_mask,
1205 msm_pm_smem_data->wakeup_reason,
1206 msm_pm_smem_data->pending_irqs);
1207 msm_irq_exit_sleep3(msm_pm_smem_data->irq_mask,
1208 msm_pm_smem_data->wakeup_reason,
1209 msm_pm_smem_data->pending_irqs);
1210 msm_gpio_exit_sleep();
1211 msm_sirc_exit_sleep();
1212
1213 smsm_change_state(SMSM_APPS_DEM,
1214 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1215
1216 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1217
1218 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301219
1220 if (cpu_is_msm8625()) {
1221 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1222 false);
1223 WARN_ON(ret);
1224 }
1225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001226 return 0;
1227
1228power_collapse_early_exit:
1229 /* Enter PWRC_EARLY_EXIT */
1230
1231 smsm_change_state(SMSM_APPS_DEM,
1232 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1233 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1234
1235 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1236
1237 memset(state_grps, 0, sizeof(state_grps));
1238 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1239 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1240 state_grps[1].group_id = SMSM_MODEM_STATE;
1241 state_grps[1].bits_all_set = SMSM_RESET;
1242
1243 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1244 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1245
1246 if (ret < 0) {
1247 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1248 "timed out waiting for Modem's response\n", __func__);
1249 msm_pm_timeout();
1250 }
1251
1252 if (ret == 1) {
1253 MSM_PM_DPRINTK(
1254 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1255 KERN_INFO,
1256 "%s(): msm_pm_poll_state detected Modem reset\n",
1257 __func__);
1258 }
1259
1260 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1261
1262 ret = -EAGAIN;
1263
1264power_collapse_restore_gpio_bail:
1265 msm_gpio_exit_sleep();
1266 msm_sirc_exit_sleep();
1267
1268 /* Enter RUN */
1269 smsm_change_state(SMSM_APPS_DEM,
1270 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1271 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1272
1273 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1274
1275 if (collapsed)
1276 smd_sleep_exit();
1277
1278power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301279 if (cpu_is_msm8625()) {
1280 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1281 false);
1282 WARN_ON(ret);
1283 }
1284
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001285 return ret;
1286}
1287
1288/*
1289 * Power collapse the Apps processor without involving Modem.
1290 *
1291 * Return value:
1292 * 0: success
1293 */
Murali Nalajala41786ab2012-03-06 10:47:32 +05301294static int msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001295{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001296 int collapsed = 0;
1297 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301298 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001299
1300 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1301 KERN_INFO, "%s()\n", __func__);
1302
1303 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1304 WARN_ON(ret);
1305
Murali Nalajala41786ab2012-03-06 10:47:32 +05301306 entry = (!smp_processor_id() || from_idle) ?
1307 msm_pm_collapse_exit : msm_secondary_startup;
1308
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001309 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301310 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001311
1312#ifdef CONFIG_VFP
1313 vfp_flush_context();
1314#endif
1315
1316#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301317 if (!cpu_is_msm8625())
1318 l2x0_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001319#endif
1320
1321 collapsed = msm_pm_collapse();
1322
1323#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301324 if (!cpu_is_msm8625())
1325 l2x0_resume(collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001326#endif
1327
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001328 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001329
1330 if (collapsed) {
1331#ifdef CONFIG_VFP
1332 vfp_reinit();
1333#endif
1334 cpu_init();
1335 local_fiq_enable();
1336 }
1337
1338 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1339 KERN_INFO,
1340 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1341
1342 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1343 WARN_ON(ret);
1344
1345 return 0;
1346}
1347
1348/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001349 * Bring the Apps processor to SWFI.
1350 *
1351 * Return value:
1352 * -EIO: could not ramp Apps processor clock
1353 * 0: success
1354 */
1355static int msm_pm_swfi(bool ramp_acpu)
1356{
1357 unsigned long saved_acpuclk_rate = 0;
1358
1359 if (ramp_acpu) {
1360 saved_acpuclk_rate = acpuclk_wait_for_irq();
1361 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1362 "%s(): change clock rate (old rate = %lu)\n", __func__,
1363 saved_acpuclk_rate);
1364
1365 if (!saved_acpuclk_rate)
1366 return -EIO;
1367 }
1368
Murali Nalajala41786ab2012-03-06 10:47:32 +05301369 if (!cpu_is_msm8625())
1370 msm_pm_config_hw_before_swfi();
1371
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001372 msm_arch_idle();
1373
1374 if (ramp_acpu) {
1375 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1376 "%s(): restore clock rate to %lu\n", __func__,
1377 saved_acpuclk_rate);
1378 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1379 SETRATE_SWFI) < 0)
1380 printk(KERN_ERR
1381 "%s(): failed to restore clock rate(%lu)\n",
1382 __func__, saved_acpuclk_rate);
1383 }
1384
1385 return 0;
1386}
1387
1388
1389/******************************************************************************
1390 * External Idle/Suspend Functions
1391 *****************************************************************************/
1392
1393/*
1394 * Put CPU in low power mode.
1395 */
1396void arch_idle(void)
1397{
1398 bool allow[MSM_PM_SLEEP_MODE_NR];
1399 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1400
1401 int latency_qos;
1402 int64_t timer_expiration;
1403
1404 int low_power;
1405 int ret;
1406 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301407 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001408
1409#ifdef CONFIG_MSM_IDLE_STATS
1410 int64_t t1;
1411 static int64_t t2;
1412 int exit_stat;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301413 #endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001414
1415 if (!atomic_read(&msm_pm_init_done))
1416 return;
1417
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301418 cpu = smp_processor_id();
1419
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001420 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
1421 timer_expiration = msm_timer_enter_idle();
1422
1423#ifdef CONFIG_MSM_IDLE_STATS
1424 t1 = ktime_to_ns(ktime_get());
1425 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
1426 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301427
1428 exit_stat = MSM_PM_STAT_IDLE_SPIN;
1429 low_power = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301430#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001431
1432 for (i = 0; i < ARRAY_SIZE(allow); i++)
1433 allow[i] = true;
1434
Murali Nalajala41786ab2012-03-06 10:47:32 +05301435 if (num_online_cpus() > 1 ||
1436 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001437#ifdef CONFIG_HAS_WAKELOCK
1438 has_wake_lock(WAKE_LOCK_IDLE) ||
1439#endif
1440 !msm_irq_idle_sleep_allowed()) {
1441 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1442 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001443 }
1444
1445 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301446 struct msm_pm_platform_data *mode =
1447 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001448 if (!mode->idle_supported || !mode->idle_enabled ||
1449 mode->latency >= latency_qos ||
1450 mode->residency * 1000ULL >= timer_expiration)
1451 allow[i] = false;
1452 }
1453
1454 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1455 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1456 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1457 while (msm_pm_modem_busy() && wait_us) {
1458 if (wait_us > 100) {
1459 udelay(100);
1460 wait_us -= 100;
1461 } else {
1462 udelay(wait_us);
1463 wait_us = 0;
1464 }
1465 }
1466
1467 if (msm_pm_modem_busy()) {
1468 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1469 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1470 = false;
1471 }
1472 }
1473
1474 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1475 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1476 __func__, latency_qos, timer_expiration, sleep_limit);
1477
1478 for (i = 0; i < ARRAY_SIZE(allow); i++)
1479 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1480 "%s(): allow %s: %d\n", __func__,
1481 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1482
1483 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1484 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1485 uint32_t sleep_delay;
1486
1487 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
1488 timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
1489 if (sleep_delay == 0) /* 0 would mean infinite time */
1490 sleep_delay = 1;
1491
1492 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1493 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1494
1495#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1496 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1497#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1498 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1499#endif
1500
1501 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1502 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
1503
1504#ifdef CONFIG_MSM_IDLE_STATS
1505 if (ret)
1506 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1507 else {
1508 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1509 msm_pm_sleep_limit = sleep_limit;
1510 }
Murali Nalajala41786ab2012-03-06 10:47:32 +05301511#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001512 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301513 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001514 low_power = 0;
1515#ifdef CONFIG_MSM_IDLE_STATS
1516 exit_stat = ret ?
1517 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1518 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301519#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001520 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1521 ret = msm_pm_swfi(true);
1522 if (ret)
1523 while (!msm_irq_pending())
1524 udelay(1);
1525 low_power = 0;
1526#ifdef CONFIG_MSM_IDLE_STATS
1527 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301528#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1530 msm_pm_swfi(false);
1531 low_power = 0;
1532#ifdef CONFIG_MSM_IDLE_STATS
1533 exit_stat = MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301534#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535 } else {
1536 while (!msm_irq_pending())
1537 udelay(1);
1538 low_power = 0;
1539#ifdef CONFIG_MSM_IDLE_STATS
1540 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301541#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001542 }
1543
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001544 msm_timer_exit_idle(low_power);
1545
1546#ifdef CONFIG_MSM_IDLE_STATS
1547 t2 = ktime_to_ns(ktime_get());
1548 msm_pm_add_stat(exit_stat, t2 - t1);
Murali Nalajala41786ab2012-03-06 10:47:32 +05301549#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001550}
1551
1552/*
1553 * Suspend the Apps processor.
1554 *
1555 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301556 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001557 * -EAGAIN: modem reset occurred or early exit from suspend
1558 * -EBUSY: modem not ready for our suspend
1559 * -EINVAL: invalid sleep mode
1560 * -EIO: could not ramp Apps processor clock
1561 * -ETIMEDOUT: timed out waiting for modem's handshake
1562 * 0: success
1563 */
1564static int msm_pm_enter(suspend_state_t state)
1565{
1566 bool allow[MSM_PM_SLEEP_MODE_NR];
1567 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301568 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001569 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001570#ifdef CONFIG_MSM_IDLE_STATS
1571 int64_t period = 0;
1572 int64_t time = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301573#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001574
Murali Nalajala41786ab2012-03-06 10:47:32 +05301575 /* Must executed by CORE0 */
1576 if (smp_processor_id()) {
1577 __WARN();
1578 goto suspend_exit;
1579 }
1580
1581#ifdef CONFIG_MSM_IDLE_STATS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001582 time = msm_timer_get_sclk_time(&period);
1583#endif
1584
1585 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1586 "%s(): sleep limit %u\n", __func__, sleep_limit);
1587
1588 for (i = 0; i < ARRAY_SIZE(allow); i++)
1589 allow[i] = true;
1590
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001591 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301592 struct msm_pm_platform_data *mode;
1593 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001594 if (!mode->suspend_supported || !mode->suspend_enabled)
1595 allow[i] = false;
1596 }
1597
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001598 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1599 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1600#ifdef CONFIG_MSM_IDLE_STATS
1601 enum msm_pm_time_stats_id id;
1602 int64_t end_time;
1603#endif
1604
1605 clock_debug_print_enabled();
1606
1607#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1608 if (msm_pm_sleep_time_override > 0) {
1609 int64_t ns;
1610 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1611 msm_pm_set_max_sleep_time(ns);
1612 msm_pm_sleep_time_override = 0;
1613 }
1614#endif
1615 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1616 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1617
1618#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1619 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1620#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1621 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1622#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1623 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1624 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1625#endif
1626
1627 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1628 udelay(500);
1629
1630 ret = msm_pm_power_collapse(
1631 false, msm_pm_max_sleep_time, sleep_limit);
1632
1633#ifdef CONFIG_MSM_IDLE_STATS
1634 if (ret)
1635 id = MSM_PM_STAT_FAILED_SUSPEND;
1636 else {
1637 id = MSM_PM_STAT_SUSPEND;
1638 msm_pm_sleep_limit = sleep_limit;
1639 }
1640
1641 if (time != 0) {
1642 end_time = msm_timer_get_sclk_time(NULL);
1643 if (end_time != 0) {
1644 time = end_time - time;
1645 if (time < 0)
1646 time += period;
1647 } else
1648 time = 0;
1649 }
1650
1651 msm_pm_add_stat(id, time);
1652#endif
1653 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301654 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001655 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1656 ret = msm_pm_swfi(true);
1657 if (ret)
1658 while (!msm_irq_pending())
1659 udelay(1);
1660 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1661 msm_pm_swfi(false);
1662 }
1663
Murali Nalajala41786ab2012-03-06 10:47:32 +05301664suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001665 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1666 "%s(): return %d\n", __func__, ret);
1667
1668 return ret;
1669}
1670
1671static struct platform_suspend_ops msm_pm_ops = {
1672 .enter = msm_pm_enter,
1673 .valid = suspend_valid_only_mem,
1674};
1675
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301676/* Hotplug the "non boot" CPU's and put
1677 * the cores into low power mode
1678 */
1679void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1680{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301681 bool allow[MSM_PM_SLEEP_MODE_NR];
1682 int i;
1683
1684 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1685 struct msm_pm_platform_data *mode;
1686
1687 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1688 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1689 }
1690
1691 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1692 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1693
1694 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1695 msm_pm_power_collapse_standalone(false);
1696 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1697 msm_pm_swfi(false);
1698 } else {
1699 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1700 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1701 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301702}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001703
1704/******************************************************************************
1705 * Restart Definitions
1706 *****************************************************************************/
1707
1708static uint32_t restart_reason = 0x776655AA;
1709
1710static void msm_pm_power_off(void)
1711{
1712 msm_rpcrouter_close();
1713 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1714 for (;;)
1715 ;
1716}
1717
1718static void msm_pm_restart(char str, const char *cmd)
1719{
1720 msm_rpcrouter_close();
1721 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1722
1723 for (;;)
1724 ;
1725}
1726
1727static int msm_reboot_call
1728 (struct notifier_block *this, unsigned long code, void *_cmd)
1729{
1730 if ((code == SYS_RESTART) && _cmd) {
1731 char *cmd = _cmd;
1732 if (!strcmp(cmd, "bootloader")) {
1733 restart_reason = 0x77665500;
1734 } else if (!strcmp(cmd, "recovery")) {
1735 restart_reason = 0x77665502;
1736 } else if (!strcmp(cmd, "eraseflash")) {
1737 restart_reason = 0x776655EF;
1738 } else if (!strncmp(cmd, "oem-", 4)) {
1739 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1740 restart_reason = 0x6f656d00 | code;
1741 } else {
1742 restart_reason = 0x77665501;
1743 }
1744 }
1745 return NOTIFY_DONE;
1746}
1747
1748static struct notifier_block msm_reboot_notifier = {
1749 .notifier_call = msm_reboot_call,
1750};
1751
1752
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001753/*
1754 * Initialize the power management subsystem.
1755 *
1756 * Return value:
1757 * -ENODEV: initialization failed
1758 * 0: success
1759 */
1760static int __init msm_pm_init(void)
1761{
1762#ifdef CONFIG_MSM_IDLE_STATS
1763 struct proc_dir_entry *d_entry;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301764 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001765#endif
1766 int ret;
1767#ifdef CONFIG_CPU_V7
1768 pgd_t *pc_pgd;
1769 pmd_t *pmd;
1770 unsigned long pmdval;
1771
Aparna Mallavarapuced47282012-03-08 12:29:41 +05301772 if (cpu_is_msm8625())
1773 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001774 /* Page table for cores to come back up safely. */
1775 pc_pgd = pgd_alloc(&init_mm);
1776 if (!pc_pgd)
1777 return -ENOMEM;
1778 pmd = pmd_offset(pc_pgd +
1779 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1780 virt_to_phys(msm_pm_collapse_exit));
1781 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1782 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1783 pmd[0] = __pmd(pmdval);
1784 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1785
1786 /* It is remotely possible that the code in msm_pm_collapse_exit()
1787 * which turns on the MMU with this mapping is in the
1788 * next even-numbered megabyte beyond the
1789 * start of msm_pm_collapse_exit().
1790 * Map this megabyte in as well.
1791 */
1792 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1793 flush_pmd_entry(pmd);
1794 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
1795#endif
1796
1797 pm_power_off = msm_pm_power_off;
1798 arm_pm_restart = msm_pm_restart;
1799 register_reboot_notifier(&msm_reboot_notifier);
1800
1801 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1802 sizeof(*msm_pm_smem_data));
1803 if (msm_pm_smem_data == NULL) {
1804 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1805 return -ENODEV;
1806 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001807
1808 ret = msm_timer_init_time_sync(msm_pm_timeout);
1809 if (ret)
1810 return ret;
1811
1812 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1813 if (ret) {
1814 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1815 __func__, ret);
1816 return ret;
1817 }
1818
1819#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1820 /* The wakeup_reason field is overloaded during initialization time
1821 to signal Modem that Apps will control the low power modes of
1822 the memory.
1823 */
1824 msm_pm_smem_data->wakeup_reason = 1;
1825 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1826#endif
1827
1828 BUG_ON(msm_pm_modes == NULL);
1829
1830 atomic_set(&msm_pm_init_done, 1);
1831 suspend_set_ops(&msm_pm_ops);
1832
1833 msm_pm_mode_sysfs_add();
1834#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301835 for_each_possible_cpu(cpu) {
1836 struct msm_pm_time_stats *stats =
1837 per_cpu(msm_pm_stats, cpu).stats;
1838
1839 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1840 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1841 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1842
1843 stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin";
1844 stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
1845 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1846
1847 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1848 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1849 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1850
1851 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1852 "idle-standalone-power-collapse";
1853 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1854 first_bucket_time =
1855 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1856
1857 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
1858 "idle-failed-standalone-power-collapse";
1859 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].
1860 first_bucket_time =
1861 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1862
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301863 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
1864 "idle-power-collapse";
1865 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
1866 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1867
1868 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
1869 "idle-failed-power-collapse";
1870 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].
1871 first_bucket_time =
1872 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1873
1874 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
1875 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
1876 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
1877
1878 stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend";
1879 stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
1880 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1881
1882 stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle";
1883 stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
1884 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1885 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001886 d_entry = create_proc_entry("msm_pm_stats",
1887 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
1888 if (d_entry) {
1889 d_entry->read_proc = msm_pm_read_proc;
1890 d_entry->write_proc = msm_pm_write_proc;
1891 d_entry->data = NULL;
1892 }
1893#endif
1894
1895 return 0;
1896}
1897
1898late_initcall_sync(msm_pm_init);