blob: 9ea4fe2b043bef7dcc1e7c6bdb4d5a9d28fcfaf3 [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;
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530158static struct msm_pm_irq_calls *msm_pm_irq_extns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530160struct msm_pm_kobj_attribute {
161 unsigned int cpu;
162 struct kobj_attribute ka;
163};
164
165#define GET_CPU_OF_ATTR(attr) \
166 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
167
168struct msm_pm_sysfs_sleep_mode {
169 struct kobject *kobj;
170 struct attribute_group attr_group;
171 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
172 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
173};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174
175/*
176 * Write out the attribute.
177 */
178static ssize_t msm_pm_mode_attr_show(
179 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
180{
181 int ret = -EINVAL;
182 int i;
183
184 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
185 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530186 unsigned int cpu;
187 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188
189 if (msm_pm_sleep_mode_labels[i] == NULL)
190 continue;
191
192 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
193 continue;
194
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530195 cpu = GET_CPU_OF_ATTR(attr);
196 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
197
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 if (!strcmp(attr->attr.name,
199 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530200 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 kp.arg = &arg;
202 ret = param_get_ulong(buf, &kp);
203 } else if (!strcmp(attr->attr.name,
204 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530205 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 kp.arg = &arg;
207 ret = param_get_ulong(buf, &kp);
208 } else if (!strcmp(attr->attr.name,
209 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530210 u32 arg = mode->latency;
211 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 ret = param_get_ulong(buf, &kp);
213 } else if (!strcmp(attr->attr.name,
214 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530215 u32 arg = mode->residency;
216 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 ret = param_get_ulong(buf, &kp);
218 }
219
220 break;
221 }
222
223 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530224 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 ret++;
226 }
227
228 return ret;
229}
230
231/*
232 * Read in the new attribute value.
233 */
234static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
235 struct kobj_attribute *attr, const char *buf, size_t count)
236{
237 int ret = -EINVAL;
238 int i;
239
240 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
241 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530242 unsigned int cpu;
243 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244
245 if (msm_pm_sleep_mode_labels[i] == NULL)
246 continue;
247
248 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
249 continue;
250
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530251 cpu = GET_CPU_OF_ATTR(attr);
252 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
253
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 if (!strcmp(attr->attr.name,
255 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530256 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 ret = param_set_byte(buf, &kp);
258 } else if (!strcmp(attr->attr.name,
259 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530260 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261 ret = param_set_byte(buf, &kp);
262 } else if (!strcmp(attr->attr.name,
263 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530264 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 ret = param_set_ulong(buf, &kp);
266 } else if (!strcmp(attr->attr.name,
267 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530268 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 ret = param_set_ulong(buf, &kp);
270 }
271
272 break;
273 }
274
275 return ret ? ret : count;
276}
277
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530278 /* Add sysfs entries for one cpu. */
279static int __init msm_pm_mode_sysfs_add_cpu(
280 unsigned int cpu, struct kobject *modes_kobj)
281{
282 char cpu_name[8];
283 struct kobject *cpu_kobj;
284 struct msm_pm_sysfs_sleep_mode *mode = NULL;
285 int i, j, k;
286 int ret;
287
288 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
289 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
290 if (!cpu_kobj) {
291 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
292 ret = -ENOMEM;
293 goto mode_sysfs_add_cpu_exit;
294 }
295
296 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
297 int idx = MSM_PM_MODE(cpu, i);
298
299 if ((!msm_pm_modes[idx].suspend_supported) &&
300 (!msm_pm_modes[idx].idle_supported))
301 continue;
302
303 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
304 if (!mode) {
305 pr_err("%s: cannot allocate memory for attributes\n",
306 __func__);
307 ret = -ENOMEM;
308 goto mode_sysfs_add_cpu_exit;
309 }
310
311 mode->kobj = kobject_create_and_add(
312 msm_pm_sleep_mode_labels[i], cpu_kobj);
313 if (!mode->kobj) {
314 pr_err("%s: cannot create kobject\n", __func__);
315 ret = -ENOMEM;
316 goto mode_sysfs_add_cpu_exit;
317 }
318
319 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
320 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
321 !msm_pm_modes[idx].idle_supported)
322 continue;
323 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
324 !msm_pm_modes[idx].suspend_supported)
325 continue;
326 mode->kas[j].cpu = cpu;
327 mode->kas[j].ka.attr.mode = 0644;
328 mode->kas[j].ka.show = msm_pm_mode_attr_show;
329 mode->kas[j].ka.store = msm_pm_mode_attr_store;
330 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
331 mode->attrs[j] = &mode->kas[j].ka.attr;
332 j++;
333 }
334 mode->attrs[j] = NULL;
335
336 mode->attr_group.attrs = mode->attrs;
337 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
338 if (ret) {
339 printk(KERN_ERR
340 "%s: cannot create kobject attribute group\n",
341 __func__);
342 goto mode_sysfs_add_cpu_exit;
343 }
344 }
345
346 ret = 0;
347
348mode_sysfs_add_cpu_exit:
349 if (ret) {
350 if (mode && mode->kobj)
351 kobject_del(mode->kobj);
352 kfree(mode);
353 }
354
355 return ret;
356}
357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358/*
359 * Add sysfs entries for the sleep modes.
360 */
361static int __init msm_pm_mode_sysfs_add(void)
362{
363 struct kobject *module_kobj = NULL;
364 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530365 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700366 int ret;
367
368 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
369 if (!module_kobj) {
370 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
371 __func__, KBUILD_MODNAME);
372 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530373 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374 }
375
376 modes_kobj = kobject_create_and_add("modes", module_kobj);
377 if (!modes_kobj) {
378 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
379 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530380 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381 }
382
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530383 for_each_possible_cpu(cpu) {
384 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
385 if (ret)
386 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 }
388
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530389 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530391mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 return ret;
393}
394
395void __init msm_pm_set_platform_data(
396 struct msm_pm_platform_data *data, int count)
397{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530398 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 msm_pm_modes = data;
400}
401
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530402void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls)
403{
404 /* sanity check */
405 BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL ||
406 irq_calls->idle_sleep_allowed == NULL ||
407 irq_calls->enter_sleep1 == NULL ||
408 irq_calls->enter_sleep2 == NULL ||
409 irq_calls->exit_sleep1 == NULL ||
410 irq_calls->exit_sleep2 == NULL ||
411 irq_calls->exit_sleep3 == NULL);
412
413 msm_pm_irq_extns = irq_calls;
414}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415
416/******************************************************************************
417 * Sleep Limitations
418 *****************************************************************************/
419enum {
420 SLEEP_LIMIT_NONE = 0,
421 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
422 SLEEP_LIMIT_MASK = 0x03,
423};
424
425#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
426enum {
427 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
428 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
429};
430#endif
431
432
433/******************************************************************************
434 * Configure Hardware for Power Down/Up
435 *****************************************************************************/
436
437#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530438#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
439#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530441#define APPS_STANDBY_CTL NULL
442#else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
444#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
445#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530446#define APPS_SECOP NULL
447#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448
449/*
450 * Configure hardware registers in preparation for Apps power down.
451 */
452static void msm_pm_config_hw_before_power_down(void)
453{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530454 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
455 __raw_writel(1, APPS_PWRDOWN);
456 mb();
457 __raw_writel(4, APPS_SECOP);
458 mb();
459 } else if (cpu_is_msm7x27()) {
460 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
461 mb();
462 __raw_writel(1, APPS_PWRDOWN);
463 mb();
464 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
465 cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
466 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
467 mb();
468 __raw_writel(1, APPS_PWRDOWN);
469 mb();
470 } else {
471 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
472 mb();
473 __raw_writel(1, APPS_PWRDOWN);
474 mb();
475 __raw_writel(0, APPS_STANDBY_CTL);
476 mb();
477 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478}
479
480/*
481 * Clear hardware registers after Apps powers up.
482 */
483static void msm_pm_config_hw_after_power_up(void)
484{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530485 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
486 __raw_writel(0, APPS_SECOP);
487 mb();
488 __raw_writel(0, APPS_PWRDOWN);
489 mb();
490 msm_spm_reinit();
491 } else {
492 __raw_writel(0, APPS_PWRDOWN);
493 mb();
494 __raw_writel(0, APPS_CLK_SLEEP_EN);
495 mb();
496 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700497}
498
499/*
500 * Configure hardware registers in preparation for SWFI.
501 */
502static void msm_pm_config_hw_before_swfi(void)
503{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530504 if (cpu_is_qsd8x50()) {
505 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
506 mb();
507 } else if (cpu_is_msm7x27()) {
508 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
509 mb();
510 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
511 cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
512 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
513 mb();
514 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515}
516
517/*
518 * Respond to timing out waiting for Modem
519 *
520 * NOTE: The function never returns.
521 */
522static void msm_pm_timeout(void)
523{
524#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
525 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
526 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
527#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
528 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
529 msm_proc_comm_reset_modem_now();
530#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
531 printk(KERN_EMERG "%s(): halting\n", __func__);
532#endif
533 for (;;)
534 ;
535}
536
537
538/******************************************************************************
539 * State Polling Definitions
540 *****************************************************************************/
541
542struct msm_pm_polled_group {
543 uint32_t group_id;
544
545 uint32_t bits_all_set;
546 uint32_t bits_all_clear;
547 uint32_t bits_any_set;
548 uint32_t bits_any_clear;
549
550 uint32_t value_read;
551};
552
553/*
554 * Return true if all bits indicated by flag are set in source.
555 */
556static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
557{
558 return (source & flag) == flag;
559}
560
561/*
562 * Return true if any bit indicated by flag are set in source.
563 */
564static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
565{
566 return !flag || (source & flag);
567}
568
569/*
570 * Return true if all bits indicated by flag are cleared in source.
571 */
572static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
573{
574 return (~source & flag) == flag;
575}
576
577/*
578 * Return true if any bit indicated by flag are cleared in source.
579 */
580static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
581{
582 return !flag || (~source & flag);
583}
584
585/*
586 * Poll the shared memory states as indicated by the poll groups.
587 *
588 * nr_grps: number of groups in the array
589 * grps: array of groups
590 *
591 * The function returns when conditions specified by any of the poll
592 * groups become true. The conditions specified by a poll group are
593 * deemed true when 1) at least one bit from bits_any_set is set OR one
594 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
595 * are set; and 3) all bits in bits_all_clear are cleared.
596 *
597 * Return value:
598 * >=0: index of the poll group whose conditions have become true
599 * -ETIMEDOUT: timed out
600 */
601static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
602{
603 int i, k;
604
605 for (i = 0; i < 50000; i++) {
606 for (k = 0; k < nr_grps; k++) {
607 bool all_set, all_clear;
608 bool any_set, any_clear;
609
610 grps[k].value_read = smsm_get_state(grps[k].group_id);
611
612 all_set = msm_pm_all_set(grps[k].value_read,
613 grps[k].bits_all_set);
614 all_clear = msm_pm_all_clear(grps[k].value_read,
615 grps[k].bits_all_clear);
616 any_set = msm_pm_any_set(grps[k].value_read,
617 grps[k].bits_any_set);
618 any_clear = msm_pm_any_clear(grps[k].value_read,
619 grps[k].bits_any_clear);
620
621 if (all_set && all_clear && (any_set || any_clear))
622 return k;
623 }
624 udelay(50);
625 }
626
627 printk(KERN_ERR "%s failed:\n", __func__);
628 for (k = 0; k < nr_grps; k++)
629 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
630 grps[k].bits_all_set, grps[k].bits_all_clear,
631 grps[k].bits_any_set, grps[k].bits_any_clear,
632 grps[k].value_read);
633
634 return -ETIMEDOUT;
635}
636
637
638/******************************************************************************
639 * Suspend Max Sleep Time
640 *****************************************************************************/
641
642#define SCLK_HZ (32768)
643#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
644
645#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
646static int msm_pm_sleep_time_override;
647module_param_named(sleep_time_override,
648 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
649#endif
650
651static uint32_t msm_pm_max_sleep_time;
652
653/*
654 * Convert time from nanoseconds to slow clock ticks, then cap it to the
655 * specified limit
656 */
657static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
658{
659 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
660 return (time_ns > limit) ? limit : time_ns;
661}
662
663/*
664 * Set the sleep time for suspend. 0 means infinite sleep time.
665 */
666void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
667{
668 unsigned long flags;
669
670 local_irq_save(flags);
671 if (max_sleep_time_ns == 0) {
672 msm_pm_max_sleep_time = 0;
673 } else {
674 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
675 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
676
677 if (msm_pm_max_sleep_time == 0)
678 msm_pm_max_sleep_time = 1;
679 }
680
681 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
682 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
683 max_sleep_time_ns, msm_pm_max_sleep_time);
684 local_irq_restore(flags);
685}
686EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
687
688
689/******************************************************************************
690 * CONFIG_MSM_IDLE_STATS
691 *****************************************************************************/
692
693#ifdef CONFIG_MSM_IDLE_STATS
694enum msm_pm_time_stats_id {
695 MSM_PM_STAT_REQUESTED_IDLE,
696 MSM_PM_STAT_IDLE_SPIN,
697 MSM_PM_STAT_IDLE_WFI,
698 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
699 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700700 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
701 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
702 MSM_PM_STAT_SUSPEND,
703 MSM_PM_STAT_FAILED_SUSPEND,
704 MSM_PM_STAT_NOT_IDLE,
705 MSM_PM_STAT_COUNT
706};
707
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530708struct msm_pm_time_stats {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700709 const char *name;
710 int64_t first_bucket_time;
711 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
712 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
713 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
714 int count;
715 int64_t total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716};
717
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530718struct msm_pm_cpu_time_stats {
719 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
720};
721
722static DEFINE_PER_CPU_SHARED_ALIGNED(
723 struct msm_pm_cpu_time_stats, msm_pm_stats);
724
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
726
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530727static DEFINE_SPINLOCK(msm_pm_stats_lock);
728
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729/*
730 * Add the given time data to the statistics collection.
731 */
732static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
733{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530734 unsigned long flags;
735 struct msm_pm_time_stats *stats;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736 int i;
737 int64_t bt;
738
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530739 spin_lock_irqsave(&msm_pm_stats_lock, flags);
740 stats = __get_cpu_var(msm_pm_stats).stats;
741
742 stats[id].total_time += t;
743 stats[id].count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744
745 bt = t;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530746 do_div(bt, stats[id].first_bucket_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747
748 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
749 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
750 i = DIV_ROUND_UP(fls((uint32_t)bt),
751 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
752 else
753 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
754
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530755 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
756 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700757
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530758 stats[id].bucket[i]++;
759
760 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
761 stats[id].min_time[i] = t;
762 if (t > stats[id].max_time[i])
763 stats[id].max_time[i] = t;
764
765 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766}
767
768/*
769 * Helper function of snprintf where buf is auto-incremented, size is auto-
770 * decremented, and there is no return value.
771 *
772 * NOTE: buf and size must be l-values (e.g. variables)
773 */
774#define SNPRINTF(buf, size, format, ...) \
775 do { \
776 if (size > 0) { \
777 int ret; \
778 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
779 if (ret > size) { \
780 buf += size; \
781 size = 0; \
782 } else { \
783 buf += ret; \
784 size -= ret; \
785 } \
786 } \
787 } while (0)
788
789/*
790 * Write out the power management statistics.
791 */
792static int msm_pm_read_proc
793 (char *page, char **start, off_t off, int count, int *eof, void *data)
794{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530795 unsigned int cpu = off / MSM_PM_STAT_COUNT;
796 int id = off % MSM_PM_STAT_COUNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700797 char *p = page;
798
799 if (count < 1024) {
800 *start = (char *) 0;
801 *eof = 0;
802 return 0;
803 }
804
805 if (!off) {
806 SNPRINTF(p, count, "Last power collapse voted ");
807 if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
808 SLEEP_LIMIT_NONE)
809 SNPRINTF(p, count, "for TCXO shutdown\n\n");
810 else
811 SNPRINTF(p, count, "against TCXO shutdown\n\n");
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530812 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530814 if (cpu < num_possible_cpus()) {
815 unsigned long flags;
816 struct msm_pm_time_stats *stats;
817 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700818 int64_t bucket_time;
819 int64_t s;
820 uint32_t ns;
821
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530822 spin_lock_irqsave(&msm_pm_stats_lock, flags);
823 stats = per_cpu(msm_pm_stats, cpu).stats;
824
825 s = stats[id].total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826 ns = do_div(s, NSEC_PER_SEC);
827 SNPRINTF(p, count,
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530828 "[cpu %u] %s:\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829 " count: %7d\n"
830 " total_time: %lld.%09u\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530831 cpu, stats[id].name,
832 stats[id].count,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700833 s, ns);
834
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530835 bucket_time = stats[id].first_bucket_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700836 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
837 s = bucket_time;
838 ns = do_div(s, NSEC_PER_SEC);
839 SNPRINTF(p, count,
840 " <%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530841 s, ns, stats[id].bucket[i],
842 stats[id].min_time[i],
843 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844
845 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
846 }
847
848 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530849 s, ns, stats[id].bucket[i],
850 stats[id].min_time[i],
851 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852
853 *start = (char *) 1;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530854 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
855
856 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857 }
858
859 return p - page;
860}
861#undef SNPRINTF
862
863#define MSM_PM_STATS_RESET "reset"
864
865/*
866 * Reset the power management statistics values.
867 */
868static int msm_pm_write_proc(struct file *file, const char __user *buffer,
869 unsigned long count, void *data)
870{
871 char buf[sizeof(MSM_PM_STATS_RESET)];
872 int ret;
873 unsigned long flags;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530874 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875
876 if (count < strlen(MSM_PM_STATS_RESET)) {
877 ret = -EINVAL;
878 goto write_proc_failed;
879 }
880
881 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
882 ret = -EFAULT;
883 goto write_proc_failed;
884 }
885
886 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
887 ret = -EINVAL;
888 goto write_proc_failed;
889 }
890
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530891 spin_lock_irqsave(&msm_pm_stats_lock, flags);
892 for_each_possible_cpu(cpu) {
893 struct msm_pm_time_stats *stats;
894 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530896 stats = per_cpu(msm_pm_stats, cpu).stats;
897 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
898 memset(stats[i].bucket,
899 0, sizeof(stats[i].bucket));
900 memset(stats[i].min_time,
901 0, sizeof(stats[i].min_time));
902 memset(stats[i].max_time,
903 0, sizeof(stats[i].max_time));
904 stats[i].count = 0;
905 stats[i].total_time = 0;
906 }
907 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700908 msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530909 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700910
911 return count;
912
913write_proc_failed:
914 return ret;
915}
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530916
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700917#undef MSM_PM_STATS_RESET
918#endif /* CONFIG_MSM_IDLE_STATS */
919
920
921/******************************************************************************
922 * Shared Memory Bits
923 *****************************************************************************/
924
925#define DEM_MASTER_BITS_PER_CPU 6
926
927/* Power Master State Bits - Per CPU */
928#define DEM_MASTER_SMSM_RUN \
929 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
930#define DEM_MASTER_SMSM_RSA \
931 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
932#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
933 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
934#define DEM_MASTER_SMSM_SLEEP_EXIT \
935 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
936#define DEM_MASTER_SMSM_READY \
937 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
938#define DEM_MASTER_SMSM_SLEEP \
939 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
940
941/* Power Slave State Bits */
942#define DEM_SLAVE_SMSM_RUN (0x0001)
943#define DEM_SLAVE_SMSM_PWRC (0x0002)
944#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
945#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
946#define DEM_SLAVE_SMSM_WFPI (0x0010)
947#define DEM_SLAVE_SMSM_SLEEP (0x0020)
948#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
949#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
950#define DEM_SLAVE_SMSM_RESET (0x0100)
951#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
952
953
954/******************************************************************************
955 * Shared Memory Data
956 *****************************************************************************/
957
958#define DEM_MAX_PORT_NAME_LEN (20)
959
960struct msm_pm_smem_t {
961 uint32_t sleep_time;
962 uint32_t irq_mask;
963 uint32_t resources_used;
964 uint32_t reserved1;
965
966 uint32_t wakeup_reason;
967 uint32_t pending_irqs;
968 uint32_t rpc_prog;
969 uint32_t rpc_proc;
970 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
971 uint32_t reserved2;
972};
973
974
975/******************************************************************************
976 *
977 *****************************************************************************/
978static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
980
981static int msm_pm_modem_busy(void)
982{
983 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
984 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
985 KERN_INFO, "%s(): master not ready\n", __func__);
986 return -EBUSY;
987 }
988
989 return 0;
990}
991
992/*
993 * Power collapse the Apps processor. This function executes the handshake
994 * protocol with Modem.
995 *
996 * Return value:
997 * -EAGAIN: modem reset occurred or early exit from power collapse
998 * -EBUSY: modem not ready for our power collapse -- no power loss
999 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
1000 * 0: success
1001 */
1002static int msm_pm_power_collapse
1003 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
1004{
1005 struct msm_pm_polled_group state_grps[2];
1006 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001007 int collapsed = 0;
1008 int ret;
1009
1010 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1011 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
1012 (int)from_idle, sleep_delay, sleep_limit);
1013
1014 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1015 MSM_PM_DPRINTK(
1016 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1017 KERN_INFO, "%s(): master not ready\n", __func__);
1018 ret = -EBUSY;
1019 goto power_collapse_bail;
1020 }
1021
1022 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
1023
Murali Nalajala41786ab2012-03-06 10:47:32 +05301024 if (cpu_is_msm8625()) {
1025 /* Program the SPM */
1026 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
1027 false);
1028 WARN_ON(ret);
1029 }
1030
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301031 msm_pm_irq_extns->enter_sleep1(true, from_idle,
1032 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001033 msm_sirc_enter_sleep();
1034 msm_gpio_enter_sleep(from_idle);
1035
1036 msm_pm_smem_data->sleep_time = sleep_delay;
1037 msm_pm_smem_data->resources_used = sleep_limit;
1038
1039 /* Enter PWRC/PWRC_SUSPEND */
1040
1041 if (from_idle)
1042 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1043 DEM_SLAVE_SMSM_PWRC);
1044 else
1045 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1046 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
1047
1048 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
1049 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1050
1051 memset(state_grps, 0, sizeof(state_grps));
1052 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1053 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
1054 state_grps[1].group_id = SMSM_MODEM_STATE;
1055 state_grps[1].bits_all_set = SMSM_RESET;
1056
1057 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1058
1059 if (ret < 0) {
1060 printk(KERN_EMERG "%s(): power collapse entry "
1061 "timed out waiting for Modem's response\n", __func__);
1062 msm_pm_timeout();
1063 }
1064
1065 if (ret == 1) {
1066 MSM_PM_DPRINTK(
1067 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1068 KERN_INFO,
1069 "%s(): msm_pm_poll_state detected Modem reset\n",
1070 __func__);
1071 goto power_collapse_early_exit;
1072 }
1073
1074 /* DEM Master in RSA */
1075
1076 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
1077
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301078 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001079 if (ret < 0) {
1080 MSM_PM_DPRINTK(
1081 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1082 KERN_INFO,
1083 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
1084 ret);
1085 goto power_collapse_early_exit;
1086 }
1087
1088 msm_pm_config_hw_before_power_down();
1089 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
1090
1091 saved_acpuclk_rate = acpuclk_power_collapse();
1092 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1093 "%s(): change clock rate (old rate = %lu)\n", __func__,
1094 saved_acpuclk_rate);
1095
1096 if (saved_acpuclk_rate == 0) {
1097 msm_pm_config_hw_after_power_up();
1098 goto power_collapse_early_exit;
1099 }
1100
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001101 msm_pm_boot_config_before_pc(smp_processor_id(),
1102 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001103
1104#ifdef CONFIG_VFP
1105 if (from_idle)
1106 vfp_flush_context();
1107#endif
1108
1109#ifdef CONFIG_CACHE_L2X0
1110 l2x0_suspend();
1111#endif
1112
1113 collapsed = msm_pm_collapse();
1114
1115#ifdef CONFIG_CACHE_L2X0
1116 l2x0_resume(collapsed);
1117#endif
1118
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001119 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001120
1121 if (collapsed) {
1122#ifdef CONFIG_VFP
1123 if (from_idle)
1124 vfp_reinit();
1125#endif
1126 cpu_init();
1127 local_fiq_enable();
1128 }
1129
1130 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1131 KERN_INFO,
1132 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1133
1134 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1135 "%s(): restore clock rate to %lu\n", __func__,
1136 saved_acpuclk_rate);
1137 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1138 SETRATE_PC) < 0)
1139 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1140 __func__, saved_acpuclk_rate);
1141
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301142 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001143 msm_pm_smem_data->wakeup_reason,
1144 msm_pm_smem_data->pending_irqs);
1145
1146 msm_pm_config_hw_after_power_up();
1147 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1148
1149 memset(state_grps, 0, sizeof(state_grps));
1150 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1151 state_grps[0].bits_any_set =
1152 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1153 state_grps[1].group_id = SMSM_MODEM_STATE;
1154 state_grps[1].bits_all_set = SMSM_RESET;
1155
1156 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1157
1158 if (ret < 0) {
1159 printk(KERN_EMERG "%s(): power collapse exit "
1160 "timed out waiting for Modem's response\n", __func__);
1161 msm_pm_timeout();
1162 }
1163
1164 if (ret == 1) {
1165 MSM_PM_DPRINTK(
1166 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1167 KERN_INFO,
1168 "%s(): msm_pm_poll_state detected Modem reset\n",
1169 __func__);
1170 goto power_collapse_early_exit;
1171 }
1172
1173 /* Sanity check */
1174 if (collapsed) {
1175 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1176 } else {
1177 BUG_ON(!(state_grps[0].value_read &
1178 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1179 goto power_collapse_early_exit;
1180 }
1181
1182 /* Enter WFPI */
1183
1184 smsm_change_state(SMSM_APPS_DEM,
1185 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1186 DEM_SLAVE_SMSM_WFPI);
1187
1188 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1189
1190 memset(state_grps, 0, sizeof(state_grps));
1191 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1192 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1193 state_grps[1].group_id = SMSM_MODEM_STATE;
1194 state_grps[1].bits_all_set = SMSM_RESET;
1195
1196 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1197
1198 if (ret < 0) {
1199 printk(KERN_EMERG "%s(): power collapse WFPI "
1200 "timed out waiting for Modem's response\n", __func__);
1201 msm_pm_timeout();
1202 }
1203
1204 if (ret == 1) {
1205 MSM_PM_DPRINTK(
1206 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1207 KERN_INFO,
1208 "%s(): msm_pm_poll_state detected Modem reset\n",
1209 __func__);
1210 ret = -EAGAIN;
1211 goto power_collapse_restore_gpio_bail;
1212 }
1213
1214 /* DEM Master == RUN */
1215
1216 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1217 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1218
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301219 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001220 msm_pm_smem_data->wakeup_reason,
1221 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301222 msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001223 msm_pm_smem_data->wakeup_reason,
1224 msm_pm_smem_data->pending_irqs);
1225 msm_gpio_exit_sleep();
1226 msm_sirc_exit_sleep();
1227
1228 smsm_change_state(SMSM_APPS_DEM,
1229 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1230
1231 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1232
1233 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301234
1235 if (cpu_is_msm8625()) {
1236 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1237 false);
1238 WARN_ON(ret);
1239 }
1240
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001241 return 0;
1242
1243power_collapse_early_exit:
1244 /* Enter PWRC_EARLY_EXIT */
1245
1246 smsm_change_state(SMSM_APPS_DEM,
1247 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1248 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1249
1250 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1251
1252 memset(state_grps, 0, sizeof(state_grps));
1253 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1254 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1255 state_grps[1].group_id = SMSM_MODEM_STATE;
1256 state_grps[1].bits_all_set = SMSM_RESET;
1257
1258 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1259 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1260
1261 if (ret < 0) {
1262 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1263 "timed out waiting for Modem's response\n", __func__);
1264 msm_pm_timeout();
1265 }
1266
1267 if (ret == 1) {
1268 MSM_PM_DPRINTK(
1269 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1270 KERN_INFO,
1271 "%s(): msm_pm_poll_state detected Modem reset\n",
1272 __func__);
1273 }
1274
1275 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1276
1277 ret = -EAGAIN;
1278
1279power_collapse_restore_gpio_bail:
1280 msm_gpio_exit_sleep();
1281 msm_sirc_exit_sleep();
1282
1283 /* Enter RUN */
1284 smsm_change_state(SMSM_APPS_DEM,
1285 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1286 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1287
1288 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1289
1290 if (collapsed)
1291 smd_sleep_exit();
1292
1293power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301294 if (cpu_is_msm8625()) {
1295 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1296 false);
1297 WARN_ON(ret);
1298 }
1299
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001300 return ret;
1301}
1302
1303/*
1304 * Power collapse the Apps processor without involving Modem.
1305 *
1306 * Return value:
1307 * 0: success
1308 */
Murali Nalajala41786ab2012-03-06 10:47:32 +05301309static int msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001310{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001311 int collapsed = 0;
1312 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301313 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001314
1315 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1316 KERN_INFO, "%s()\n", __func__);
1317
1318 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1319 WARN_ON(ret);
1320
Murali Nalajala41786ab2012-03-06 10:47:32 +05301321 entry = (!smp_processor_id() || from_idle) ?
1322 msm_pm_collapse_exit : msm_secondary_startup;
1323
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001324 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301325 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001326
1327#ifdef CONFIG_VFP
1328 vfp_flush_context();
1329#endif
1330
1331#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301332 if (!cpu_is_msm8625())
1333 l2x0_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001334#endif
1335
1336 collapsed = msm_pm_collapse();
1337
1338#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301339 if (!cpu_is_msm8625())
1340 l2x0_resume(collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001341#endif
1342
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001343 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001344
1345 if (collapsed) {
1346#ifdef CONFIG_VFP
1347 vfp_reinit();
1348#endif
1349 cpu_init();
1350 local_fiq_enable();
1351 }
1352
1353 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1354 KERN_INFO,
1355 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1356
1357 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1358 WARN_ON(ret);
1359
1360 return 0;
1361}
1362
1363/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001364 * Bring the Apps processor to SWFI.
1365 *
1366 * Return value:
1367 * -EIO: could not ramp Apps processor clock
1368 * 0: success
1369 */
1370static int msm_pm_swfi(bool ramp_acpu)
1371{
1372 unsigned long saved_acpuclk_rate = 0;
1373
1374 if (ramp_acpu) {
1375 saved_acpuclk_rate = acpuclk_wait_for_irq();
1376 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1377 "%s(): change clock rate (old rate = %lu)\n", __func__,
1378 saved_acpuclk_rate);
1379
1380 if (!saved_acpuclk_rate)
1381 return -EIO;
1382 }
1383
Murali Nalajala41786ab2012-03-06 10:47:32 +05301384 if (!cpu_is_msm8625())
1385 msm_pm_config_hw_before_swfi();
1386
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001387 msm_arch_idle();
1388
1389 if (ramp_acpu) {
1390 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1391 "%s(): restore clock rate to %lu\n", __func__,
1392 saved_acpuclk_rate);
1393 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1394 SETRATE_SWFI) < 0)
1395 printk(KERN_ERR
1396 "%s(): failed to restore clock rate(%lu)\n",
1397 __func__, saved_acpuclk_rate);
1398 }
1399
1400 return 0;
1401}
1402
1403
1404/******************************************************************************
1405 * External Idle/Suspend Functions
1406 *****************************************************************************/
1407
1408/*
1409 * Put CPU in low power mode.
1410 */
1411void arch_idle(void)
1412{
1413 bool allow[MSM_PM_SLEEP_MODE_NR];
1414 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1415
1416 int latency_qos;
1417 int64_t timer_expiration;
1418
1419 int low_power;
1420 int ret;
1421 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301422 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001423
1424#ifdef CONFIG_MSM_IDLE_STATS
1425 int64_t t1;
1426 static int64_t t2;
1427 int exit_stat;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301428 #endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001429
1430 if (!atomic_read(&msm_pm_init_done))
1431 return;
1432
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301433 cpu = smp_processor_id();
1434
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001435 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
1436 timer_expiration = msm_timer_enter_idle();
1437
1438#ifdef CONFIG_MSM_IDLE_STATS
1439 t1 = ktime_to_ns(ktime_get());
1440 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
1441 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301442
1443 exit_stat = MSM_PM_STAT_IDLE_SPIN;
1444 low_power = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301445#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001446
1447 for (i = 0; i < ARRAY_SIZE(allow); i++)
1448 allow[i] = true;
1449
Murali Nalajala41786ab2012-03-06 10:47:32 +05301450 if (num_online_cpus() > 1 ||
1451 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001452#ifdef CONFIG_HAS_WAKELOCK
1453 has_wake_lock(WAKE_LOCK_IDLE) ||
1454#endif
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301455 !msm_pm_irq_extns->idle_sleep_allowed()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001456 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1457 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001458 }
1459
1460 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301461 struct msm_pm_platform_data *mode =
1462 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001463 if (!mode->idle_supported || !mode->idle_enabled ||
1464 mode->latency >= latency_qos ||
1465 mode->residency * 1000ULL >= timer_expiration)
1466 allow[i] = false;
1467 }
1468
1469 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1470 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1471 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1472 while (msm_pm_modem_busy() && wait_us) {
1473 if (wait_us > 100) {
1474 udelay(100);
1475 wait_us -= 100;
1476 } else {
1477 udelay(wait_us);
1478 wait_us = 0;
1479 }
1480 }
1481
1482 if (msm_pm_modem_busy()) {
1483 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1484 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1485 = false;
1486 }
1487 }
1488
1489 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1490 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1491 __func__, latency_qos, timer_expiration, sleep_limit);
1492
1493 for (i = 0; i < ARRAY_SIZE(allow); i++)
1494 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1495 "%s(): allow %s: %d\n", __func__,
1496 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1497
1498 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1499 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1500 uint32_t sleep_delay;
1501
1502 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
1503 timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
1504 if (sleep_delay == 0) /* 0 would mean infinite time */
1505 sleep_delay = 1;
1506
1507 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1508 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1509
1510#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1511 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1512#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1513 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1514#endif
1515
1516 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1517 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
1518
1519#ifdef CONFIG_MSM_IDLE_STATS
1520 if (ret)
1521 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1522 else {
1523 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1524 msm_pm_sleep_limit = sleep_limit;
1525 }
Murali Nalajala41786ab2012-03-06 10:47:32 +05301526#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001527 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301528 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529 low_power = 0;
1530#ifdef CONFIG_MSM_IDLE_STATS
1531 exit_stat = ret ?
1532 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1533 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301534#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1536 ret = msm_pm_swfi(true);
1537 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301538 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001539 udelay(1);
1540 low_power = 0;
1541#ifdef CONFIG_MSM_IDLE_STATS
1542 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301543#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001544 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1545 msm_pm_swfi(false);
1546 low_power = 0;
1547#ifdef CONFIG_MSM_IDLE_STATS
1548 exit_stat = MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301549#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001550 } else {
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301551 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001552 udelay(1);
1553 low_power = 0;
1554#ifdef CONFIG_MSM_IDLE_STATS
1555 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301556#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001557 }
1558
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001559 msm_timer_exit_idle(low_power);
1560
1561#ifdef CONFIG_MSM_IDLE_STATS
1562 t2 = ktime_to_ns(ktime_get());
1563 msm_pm_add_stat(exit_stat, t2 - t1);
Murali Nalajala41786ab2012-03-06 10:47:32 +05301564#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001565}
1566
1567/*
1568 * Suspend the Apps processor.
1569 *
1570 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301571 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001572 * -EAGAIN: modem reset occurred or early exit from suspend
1573 * -EBUSY: modem not ready for our suspend
1574 * -EINVAL: invalid sleep mode
1575 * -EIO: could not ramp Apps processor clock
1576 * -ETIMEDOUT: timed out waiting for modem's handshake
1577 * 0: success
1578 */
1579static int msm_pm_enter(suspend_state_t state)
1580{
1581 bool allow[MSM_PM_SLEEP_MODE_NR];
1582 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301583 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001584 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001585#ifdef CONFIG_MSM_IDLE_STATS
1586 int64_t period = 0;
1587 int64_t time = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301588#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001589
Murali Nalajala41786ab2012-03-06 10:47:32 +05301590 /* Must executed by CORE0 */
1591 if (smp_processor_id()) {
1592 __WARN();
1593 goto suspend_exit;
1594 }
1595
1596#ifdef CONFIG_MSM_IDLE_STATS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001597 time = msm_timer_get_sclk_time(&period);
1598#endif
1599
1600 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1601 "%s(): sleep limit %u\n", __func__, sleep_limit);
1602
1603 for (i = 0; i < ARRAY_SIZE(allow); i++)
1604 allow[i] = true;
1605
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001606 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301607 struct msm_pm_platform_data *mode;
1608 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001609 if (!mode->suspend_supported || !mode->suspend_enabled)
1610 allow[i] = false;
1611 }
1612
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001613 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1614 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1615#ifdef CONFIG_MSM_IDLE_STATS
1616 enum msm_pm_time_stats_id id;
1617 int64_t end_time;
1618#endif
1619
1620 clock_debug_print_enabled();
1621
1622#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1623 if (msm_pm_sleep_time_override > 0) {
1624 int64_t ns;
1625 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1626 msm_pm_set_max_sleep_time(ns);
1627 msm_pm_sleep_time_override = 0;
1628 }
1629#endif
1630 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1631 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1632
1633#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1634 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1635#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1636 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1637#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1638 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1639 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1640#endif
1641
1642 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1643 udelay(500);
1644
1645 ret = msm_pm_power_collapse(
1646 false, msm_pm_max_sleep_time, sleep_limit);
1647
1648#ifdef CONFIG_MSM_IDLE_STATS
1649 if (ret)
1650 id = MSM_PM_STAT_FAILED_SUSPEND;
1651 else {
1652 id = MSM_PM_STAT_SUSPEND;
1653 msm_pm_sleep_limit = sleep_limit;
1654 }
1655
1656 if (time != 0) {
1657 end_time = msm_timer_get_sclk_time(NULL);
1658 if (end_time != 0) {
1659 time = end_time - time;
1660 if (time < 0)
1661 time += period;
1662 } else
1663 time = 0;
1664 }
1665
1666 msm_pm_add_stat(id, time);
1667#endif
1668 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301669 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001670 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1671 ret = msm_pm_swfi(true);
1672 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301673 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001674 udelay(1);
1675 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1676 msm_pm_swfi(false);
1677 }
1678
Murali Nalajala41786ab2012-03-06 10:47:32 +05301679suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001680 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1681 "%s(): return %d\n", __func__, ret);
1682
1683 return ret;
1684}
1685
1686static struct platform_suspend_ops msm_pm_ops = {
1687 .enter = msm_pm_enter,
1688 .valid = suspend_valid_only_mem,
1689};
1690
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301691/* Hotplug the "non boot" CPU's and put
1692 * the cores into low power mode
1693 */
1694void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1695{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301696 bool allow[MSM_PM_SLEEP_MODE_NR];
1697 int i;
1698
1699 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1700 struct msm_pm_platform_data *mode;
1701
1702 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1703 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1704 }
1705
1706 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1707 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1708
1709 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1710 msm_pm_power_collapse_standalone(false);
1711 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1712 msm_pm_swfi(false);
1713 } else {
1714 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1715 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1716 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301717}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001718
1719/******************************************************************************
1720 * Restart Definitions
1721 *****************************************************************************/
1722
1723static uint32_t restart_reason = 0x776655AA;
1724
1725static void msm_pm_power_off(void)
1726{
1727 msm_rpcrouter_close();
1728 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1729 for (;;)
1730 ;
1731}
1732
1733static void msm_pm_restart(char str, const char *cmd)
1734{
1735 msm_rpcrouter_close();
1736 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1737
1738 for (;;)
1739 ;
1740}
1741
1742static int msm_reboot_call
1743 (struct notifier_block *this, unsigned long code, void *_cmd)
1744{
1745 if ((code == SYS_RESTART) && _cmd) {
1746 char *cmd = _cmd;
1747 if (!strcmp(cmd, "bootloader")) {
1748 restart_reason = 0x77665500;
1749 } else if (!strcmp(cmd, "recovery")) {
1750 restart_reason = 0x77665502;
1751 } else if (!strcmp(cmd, "eraseflash")) {
1752 restart_reason = 0x776655EF;
1753 } else if (!strncmp(cmd, "oem-", 4)) {
1754 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1755 restart_reason = 0x6f656d00 | code;
1756 } else {
1757 restart_reason = 0x77665501;
1758 }
1759 }
1760 return NOTIFY_DONE;
1761}
1762
1763static struct notifier_block msm_reboot_notifier = {
1764 .notifier_call = msm_reboot_call,
1765};
1766
1767
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001768/*
1769 * Initialize the power management subsystem.
1770 *
1771 * Return value:
1772 * -ENODEV: initialization failed
1773 * 0: success
1774 */
1775static int __init msm_pm_init(void)
1776{
1777#ifdef CONFIG_MSM_IDLE_STATS
1778 struct proc_dir_entry *d_entry;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301779 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001780#endif
1781 int ret;
1782#ifdef CONFIG_CPU_V7
1783 pgd_t *pc_pgd;
1784 pmd_t *pmd;
1785 unsigned long pmdval;
1786
Aparna Mallavarapuced47282012-03-08 12:29:41 +05301787 if (cpu_is_msm8625())
1788 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001789 /* Page table for cores to come back up safely. */
1790 pc_pgd = pgd_alloc(&init_mm);
1791 if (!pc_pgd)
1792 return -ENOMEM;
1793 pmd = pmd_offset(pc_pgd +
1794 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1795 virt_to_phys(msm_pm_collapse_exit));
1796 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1797 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1798 pmd[0] = __pmd(pmdval);
1799 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1800
1801 /* It is remotely possible that the code in msm_pm_collapse_exit()
1802 * which turns on the MMU with this mapping is in the
1803 * next even-numbered megabyte beyond the
1804 * start of msm_pm_collapse_exit().
1805 * Map this megabyte in as well.
1806 */
1807 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1808 flush_pmd_entry(pmd);
1809 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
1810#endif
1811
1812 pm_power_off = msm_pm_power_off;
1813 arm_pm_restart = msm_pm_restart;
1814 register_reboot_notifier(&msm_reboot_notifier);
1815
1816 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1817 sizeof(*msm_pm_smem_data));
1818 if (msm_pm_smem_data == NULL) {
1819 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1820 return -ENODEV;
1821 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001822
1823 ret = msm_timer_init_time_sync(msm_pm_timeout);
1824 if (ret)
1825 return ret;
1826
1827 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1828 if (ret) {
1829 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1830 __func__, ret);
1831 return ret;
1832 }
1833
1834#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1835 /* The wakeup_reason field is overloaded during initialization time
1836 to signal Modem that Apps will control the low power modes of
1837 the memory.
1838 */
1839 msm_pm_smem_data->wakeup_reason = 1;
1840 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1841#endif
1842
1843 BUG_ON(msm_pm_modes == NULL);
1844
1845 atomic_set(&msm_pm_init_done, 1);
1846 suspend_set_ops(&msm_pm_ops);
1847
1848 msm_pm_mode_sysfs_add();
1849#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301850 for_each_possible_cpu(cpu) {
1851 struct msm_pm_time_stats *stats =
1852 per_cpu(msm_pm_stats, cpu).stats;
1853
1854 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1855 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1856 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1857
1858 stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin";
1859 stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
1860 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1861
1862 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1863 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1864 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1865
1866 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1867 "idle-standalone-power-collapse";
1868 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1869 first_bucket_time =
1870 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1871
1872 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
1873 "idle-failed-standalone-power-collapse";
1874 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].
1875 first_bucket_time =
1876 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1877
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301878 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
1879 "idle-power-collapse";
1880 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
1881 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1882
1883 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
1884 "idle-failed-power-collapse";
1885 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].
1886 first_bucket_time =
1887 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1888
1889 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
1890 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
1891 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
1892
1893 stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend";
1894 stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
1895 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1896
1897 stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle";
1898 stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
1899 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1900 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001901 d_entry = create_proc_entry("msm_pm_stats",
1902 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
1903 if (d_entry) {
1904 d_entry->read_proc = msm_pm_read_proc;
1905 d_entry->write_proc = msm_pm_write_proc;
1906 d_entry->data = NULL;
1907 }
1908#endif
1909
1910 return 0;
1911}
1912
1913late_initcall_sync(msm_pm_init);