blob: 7a8e4c3fa8f9211d457e063408018aecc634b34a [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>
Steve Mucklef132c6c2012-06-06 18:30:57 -070025#include <linux/pm_qos.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <linux/suspend.h>
27#include <linux/reboot.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include <linux/io.h>
Murali Nalajala8fda4492012-03-19 18:22:59 +053029#include <linux/tick.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include <linux/memory.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031#include <mach/msm_iomap.h>
32#include <mach/system.h>
33#ifdef CONFIG_CPU_V7
34#include <asm/pgtable.h>
35#include <asm/pgalloc.h>
36#endif
Steve Mucklef132c6c2012-06-06 18:30:57 -070037#include <asm/system_misc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#ifdef CONFIG_CACHE_L2X0
39#include <asm/hardware/cache-l2x0.h>
40#endif
41#ifdef CONFIG_VFP
42#include <asm/vfp.h>
43#endif
44
45#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
46#include <mach/msm_migrate_pages.h>
47#endif
Murali Nalajala41786ab2012-03-06 10:47:32 +053048#include <mach/socinfo.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070049#include <mach/proc_comm.h>
Anji jonnala1f2377c2012-03-27 14:35:55 +053050#include <asm/smp_scu.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
52#include "smd_private.h"
53#include "smd_rpcrouter.h"
54#include "acpuclock.h"
55#include "clock.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056#include "idle.h"
57#include "irq.h"
58#include "gpio.h"
59#include "timer.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080060#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061#include "spm.h"
62#include "sirc.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060063#include "pm-boot.h"
Murali Nalajala19d33a22012-05-18 14:11:19 +053064#include "devices-msm7x2xa.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
66/******************************************************************************
67 * Debug Definitions
68 *****************************************************************************/
69
70enum {
Murali Nalajalaa7efba12012-02-23 18:13:52 +053071 MSM_PM_DEBUG_SUSPEND = BIT(0),
72 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
73 MSM_PM_DEBUG_STATE = BIT(2),
74 MSM_PM_DEBUG_CLOCK = BIT(3),
75 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
76 MSM_PM_DEBUG_SMSM_STATE = BIT(5),
77 MSM_PM_DEBUG_IDLE = BIT(6),
78 MSM_PM_DEBUG_HOTPLUG = BIT(7),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079};
80
81static int msm_pm_debug_mask;
Taniya Dase30a6b22012-03-20 11:37:45 +053082int power_collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083module_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;
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530157static struct msm_pm_irq_calls *msm_pm_irq_extns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530159struct msm_pm_kobj_attribute {
160 unsigned int cpu;
161 struct kobj_attribute ka;
162};
163
164#define GET_CPU_OF_ATTR(attr) \
165 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
166
167struct msm_pm_sysfs_sleep_mode {
168 struct kobject *kobj;
169 struct attribute_group attr_group;
170 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
171 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
172};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173
174/*
175 * Write out the attribute.
176 */
177static ssize_t msm_pm_mode_attr_show(
178 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
179{
180 int ret = -EINVAL;
181 int i;
182
183 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
184 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530185 unsigned int cpu;
186 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187
188 if (msm_pm_sleep_mode_labels[i] == NULL)
189 continue;
190
191 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
192 continue;
193
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530194 cpu = GET_CPU_OF_ATTR(attr);
195 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 if (!strcmp(attr->attr.name,
198 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530199 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 kp.arg = &arg;
201 ret = param_get_ulong(buf, &kp);
202 } else if (!strcmp(attr->attr.name,
203 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530204 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 kp.arg = &arg;
206 ret = param_get_ulong(buf, &kp);
207 } else if (!strcmp(attr->attr.name,
208 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530209 u32 arg = mode->latency;
210 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 ret = param_get_ulong(buf, &kp);
212 } else if (!strcmp(attr->attr.name,
213 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530214 u32 arg = mode->residency;
215 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 ret = param_get_ulong(buf, &kp);
217 }
218
219 break;
220 }
221
222 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530223 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224 ret++;
225 }
226
227 return ret;
228}
229
230/*
231 * Read in the new attribute value.
232 */
233static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
234 struct kobj_attribute *attr, const char *buf, size_t count)
235{
236 int ret = -EINVAL;
237 int i;
238
239 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
240 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530241 unsigned int cpu;
242 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243
244 if (msm_pm_sleep_mode_labels[i] == NULL)
245 continue;
246
247 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
248 continue;
249
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530250 cpu = GET_CPU_OF_ATTR(attr);
251 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
252
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 if (!strcmp(attr->attr.name,
254 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530255 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 ret = param_set_byte(buf, &kp);
257 } else if (!strcmp(attr->attr.name,
258 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530259 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 ret = param_set_byte(buf, &kp);
261 } else if (!strcmp(attr->attr.name,
262 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530263 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 ret = param_set_ulong(buf, &kp);
265 } else if (!strcmp(attr->attr.name,
266 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530267 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 ret = param_set_ulong(buf, &kp);
269 }
270
271 break;
272 }
273
274 return ret ? ret : count;
275}
276
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530277 /* Add sysfs entries for one cpu. */
278static int __init msm_pm_mode_sysfs_add_cpu(
279 unsigned int cpu, struct kobject *modes_kobj)
280{
281 char cpu_name[8];
282 struct kobject *cpu_kobj;
283 struct msm_pm_sysfs_sleep_mode *mode = NULL;
284 int i, j, k;
285 int ret;
286
287 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
288 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
289 if (!cpu_kobj) {
290 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
291 ret = -ENOMEM;
292 goto mode_sysfs_add_cpu_exit;
293 }
294
295 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
296 int idx = MSM_PM_MODE(cpu, i);
297
298 if ((!msm_pm_modes[idx].suspend_supported) &&
299 (!msm_pm_modes[idx].idle_supported))
300 continue;
301
302 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
303 if (!mode) {
304 pr_err("%s: cannot allocate memory for attributes\n",
305 __func__);
306 ret = -ENOMEM;
307 goto mode_sysfs_add_cpu_exit;
308 }
309
310 mode->kobj = kobject_create_and_add(
311 msm_pm_sleep_mode_labels[i], cpu_kobj);
312 if (!mode->kobj) {
313 pr_err("%s: cannot create kobject\n", __func__);
314 ret = -ENOMEM;
315 goto mode_sysfs_add_cpu_exit;
316 }
317
318 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
319 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
320 !msm_pm_modes[idx].idle_supported)
321 continue;
322 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
323 !msm_pm_modes[idx].suspend_supported)
324 continue;
325 mode->kas[j].cpu = cpu;
326 mode->kas[j].ka.attr.mode = 0644;
327 mode->kas[j].ka.show = msm_pm_mode_attr_show;
328 mode->kas[j].ka.store = msm_pm_mode_attr_store;
329 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
330 mode->attrs[j] = &mode->kas[j].ka.attr;
331 j++;
332 }
333 mode->attrs[j] = NULL;
334
335 mode->attr_group.attrs = mode->attrs;
336 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
337 if (ret) {
338 printk(KERN_ERR
339 "%s: cannot create kobject attribute group\n",
340 __func__);
341 goto mode_sysfs_add_cpu_exit;
342 }
343 }
344
345 ret = 0;
346
347mode_sysfs_add_cpu_exit:
348 if (ret) {
349 if (mode && mode->kobj)
350 kobject_del(mode->kobj);
351 kfree(mode);
352 }
353
354 return ret;
355}
356
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357/*
358 * Add sysfs entries for the sleep modes.
359 */
360static int __init msm_pm_mode_sysfs_add(void)
361{
362 struct kobject *module_kobj = NULL;
363 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530364 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 int ret;
366
367 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
368 if (!module_kobj) {
369 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
370 __func__, KBUILD_MODNAME);
371 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530372 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 }
374
375 modes_kobj = kobject_create_and_add("modes", module_kobj);
376 if (!modes_kobj) {
377 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
378 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530379 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 }
381
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530382 for_each_possible_cpu(cpu) {
383 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
384 if (ret)
385 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700386 }
387
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530388 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530390mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391 return ret;
392}
393
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700394s32 msm_cpuidle_get_deep_idle_latency(void)
395{
396 int i = MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN);
397 return msm_pm_modes[i].latency - 1;
398}
399
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400void __init msm_pm_set_platform_data(
401 struct msm_pm_platform_data *data, int count)
402{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530403 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404 msm_pm_modes = data;
405}
406
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530407void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls)
408{
409 /* sanity check */
410 BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL ||
411 irq_calls->idle_sleep_allowed == NULL ||
412 irq_calls->enter_sleep1 == NULL ||
413 irq_calls->enter_sleep2 == NULL ||
414 irq_calls->exit_sleep1 == NULL ||
415 irq_calls->exit_sleep2 == NULL ||
416 irq_calls->exit_sleep3 == NULL);
417
418 msm_pm_irq_extns = irq_calls;
419}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420
421/******************************************************************************
422 * Sleep Limitations
423 *****************************************************************************/
424enum {
425 SLEEP_LIMIT_NONE = 0,
426 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
427 SLEEP_LIMIT_MASK = 0x03,
428};
429
Praveen Chidambaram3895bde2012-05-14 19:42:40 +0530430static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
432enum {
433 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
434 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
435};
436#endif
437
438
439/******************************************************************************
440 * Configure Hardware for Power Down/Up
441 *****************************************************************************/
442
443#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530444#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
445#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530447#define APPS_STANDBY_CTL NULL
448#else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
450#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
451#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530452#define APPS_SECOP NULL
453#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454
455/*
456 * Configure hardware registers in preparation for Apps power down.
457 */
458static void msm_pm_config_hw_before_power_down(void)
459{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530460 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530461 __raw_writel(4, APPS_SECOP);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530462 } else if (cpu_is_msm7x27()) {
463 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530464 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530465 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
466 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530467 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530468 } else if (cpu_is_qsd8x50()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530469 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
470 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530471 __raw_writel(0, APPS_STANDBY_CTL);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530472 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530473 mb();
474 __raw_writel(1, APPS_PWRDOWN);
475 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476}
477
478/*
Anji jonnala1f2377c2012-03-27 14:35:55 +0530479 * Program the top csr from core0 context to put the
480 * core1 into GDFS, as core1 is not running yet.
481 */
482static void configure_top_csr(void)
483{
484 void __iomem *base_ptr;
485 unsigned int value = 0;
486
Murali Nalajala19d33a22012-05-18 14:11:19 +0530487 base_ptr = core1_reset_base();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530488 if (!base_ptr)
489 return;
490
491 /* bring the core1 out of reset */
492 __raw_writel(0x3, base_ptr);
493 mb();
494 /*
495 * override DBGNOPOWERDN and program the GDFS
496 * count val
497 */
498
499 __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38));
500 mb();
501
502 /* Initialize the SPM0 and SPM1 registers */
503 msm_spm_reinit();
504
505 /* enable TCSR for core1 */
506 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
507 value |= BIT(22);
508 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
509 mb();
510
511 /* set reset bit for SPM1 */
512 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
513 value |= BIT(20);
514 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
515 mb();
516
517 /* set CLK_OFF bit */
518 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
519 value |= BIT(18);
520 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
521 mb();
522
523 /* set clamps bit */
524 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
525 value |= BIT(21);
526 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
527 mb();
528
529 /* set power_up bit */
530 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
531 value |= BIT(19);
532 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
533 mb();
534
535 /* Disable TSCR for core0 */
536 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
537 value &= ~BIT(22);
538 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
539 mb();
540 __raw_writel(0x0, base_ptr);
541 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530542}
543
544/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545 * Clear hardware registers after Apps powers up.
546 */
547static void msm_pm_config_hw_after_power_up(void)
548{
Anji jonnala1f2377c2012-03-27 14:35:55 +0530549
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530550 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
551 __raw_writel(0, APPS_SECOP);
552 mb();
553 __raw_writel(0, APPS_PWRDOWN);
554 mb();
555 msm_spm_reinit();
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530556 } else if (cpu_is_msm8625()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530557 __raw_writel(0, APPS_PWRDOWN);
558 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530559
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530560 if (power_collapsed) {
Anji jonnala1f2377c2012-03-27 14:35:55 +0530561 /*
562 * enable the SCU while coming out of power
563 * collapse.
564 */
565 scu_enable(MSM_SCU_BASE);
566 /*
567 * Program the top csr to put the core1 into GDFS.
568 */
569 configure_top_csr();
570 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530571 } else {
572 __raw_writel(0, APPS_PWRDOWN);
573 mb();
574 __raw_writel(0, APPS_CLK_SLEEP_EN);
575 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530576 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577}
578
579/*
580 * Configure hardware registers in preparation for SWFI.
581 */
582static void msm_pm_config_hw_before_swfi(void)
583{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530584 if (cpu_is_qsd8x50()) {
585 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
586 mb();
587 } else if (cpu_is_msm7x27()) {
588 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
589 mb();
590 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530591 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
592 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530593 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
594 mb();
595 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596}
597
598/*
599 * Respond to timing out waiting for Modem
600 *
601 * NOTE: The function never returns.
602 */
603static void msm_pm_timeout(void)
604{
605#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
606 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
607 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
608#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
609 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
610 msm_proc_comm_reset_modem_now();
611#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
612 printk(KERN_EMERG "%s(): halting\n", __func__);
613#endif
614 for (;;)
615 ;
616}
617
618
619/******************************************************************************
620 * State Polling Definitions
621 *****************************************************************************/
622
623struct msm_pm_polled_group {
624 uint32_t group_id;
625
626 uint32_t bits_all_set;
627 uint32_t bits_all_clear;
628 uint32_t bits_any_set;
629 uint32_t bits_any_clear;
630
631 uint32_t value_read;
632};
633
634/*
635 * Return true if all bits indicated by flag are set in source.
636 */
637static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
638{
639 return (source & flag) == flag;
640}
641
642/*
643 * Return true if any bit indicated by flag are set in source.
644 */
645static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
646{
647 return !flag || (source & flag);
648}
649
650/*
651 * Return true if all bits indicated by flag are cleared in source.
652 */
653static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
654{
655 return (~source & flag) == flag;
656}
657
658/*
659 * Return true if any bit indicated by flag are cleared in source.
660 */
661static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
662{
663 return !flag || (~source & flag);
664}
665
666/*
667 * Poll the shared memory states as indicated by the poll groups.
668 *
669 * nr_grps: number of groups in the array
670 * grps: array of groups
671 *
672 * The function returns when conditions specified by any of the poll
673 * groups become true. The conditions specified by a poll group are
674 * deemed true when 1) at least one bit from bits_any_set is set OR one
675 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
676 * are set; and 3) all bits in bits_all_clear are cleared.
677 *
678 * Return value:
679 * >=0: index of the poll group whose conditions have become true
680 * -ETIMEDOUT: timed out
681 */
682static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
683{
684 int i, k;
685
686 for (i = 0; i < 50000; i++) {
687 for (k = 0; k < nr_grps; k++) {
688 bool all_set, all_clear;
689 bool any_set, any_clear;
690
691 grps[k].value_read = smsm_get_state(grps[k].group_id);
692
693 all_set = msm_pm_all_set(grps[k].value_read,
694 grps[k].bits_all_set);
695 all_clear = msm_pm_all_clear(grps[k].value_read,
696 grps[k].bits_all_clear);
697 any_set = msm_pm_any_set(grps[k].value_read,
698 grps[k].bits_any_set);
699 any_clear = msm_pm_any_clear(grps[k].value_read,
700 grps[k].bits_any_clear);
701
702 if (all_set && all_clear && (any_set || any_clear))
703 return k;
704 }
705 udelay(50);
706 }
707
708 printk(KERN_ERR "%s failed:\n", __func__);
709 for (k = 0; k < nr_grps; k++)
710 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
711 grps[k].bits_all_set, grps[k].bits_all_clear,
712 grps[k].bits_any_set, grps[k].bits_any_clear,
713 grps[k].value_read);
714
715 return -ETIMEDOUT;
716}
717
718
719/******************************************************************************
720 * Suspend Max Sleep Time
721 *****************************************************************************/
722
723#define SCLK_HZ (32768)
724#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
725
726#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
727static int msm_pm_sleep_time_override;
728module_param_named(sleep_time_override,
729 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
730#endif
731
732static uint32_t msm_pm_max_sleep_time;
733
734/*
735 * Convert time from nanoseconds to slow clock ticks, then cap it to the
736 * specified limit
737 */
738static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
739{
740 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
741 return (time_ns > limit) ? limit : time_ns;
742}
743
744/*
745 * Set the sleep time for suspend. 0 means infinite sleep time.
746 */
747void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
748{
749 unsigned long flags;
750
751 local_irq_save(flags);
752 if (max_sleep_time_ns == 0) {
753 msm_pm_max_sleep_time = 0;
754 } else {
755 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
756 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
757
758 if (msm_pm_max_sleep_time == 0)
759 msm_pm_max_sleep_time = 1;
760 }
761
762 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
763 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
764 max_sleep_time_ns, msm_pm_max_sleep_time);
765 local_irq_restore(flags);
766}
767EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
768
769
770/******************************************************************************
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700771 * Shared Memory Bits
772 *****************************************************************************/
773
774#define DEM_MASTER_BITS_PER_CPU 6
775
776/* Power Master State Bits - Per CPU */
777#define DEM_MASTER_SMSM_RUN \
778 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
779#define DEM_MASTER_SMSM_RSA \
780 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
781#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
782 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
783#define DEM_MASTER_SMSM_SLEEP_EXIT \
784 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
785#define DEM_MASTER_SMSM_READY \
786 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
787#define DEM_MASTER_SMSM_SLEEP \
788 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
789
790/* Power Slave State Bits */
791#define DEM_SLAVE_SMSM_RUN (0x0001)
792#define DEM_SLAVE_SMSM_PWRC (0x0002)
793#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
794#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
795#define DEM_SLAVE_SMSM_WFPI (0x0010)
796#define DEM_SLAVE_SMSM_SLEEP (0x0020)
797#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
798#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
799#define DEM_SLAVE_SMSM_RESET (0x0100)
800#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
801
802
803/******************************************************************************
804 * Shared Memory Data
805 *****************************************************************************/
806
807#define DEM_MAX_PORT_NAME_LEN (20)
808
809struct msm_pm_smem_t {
810 uint32_t sleep_time;
811 uint32_t irq_mask;
812 uint32_t resources_used;
813 uint32_t reserved1;
814
815 uint32_t wakeup_reason;
816 uint32_t pending_irqs;
817 uint32_t rpc_prog;
818 uint32_t rpc_proc;
819 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
820 uint32_t reserved2;
821};
822
823
824/******************************************************************************
825 *
826 *****************************************************************************/
827static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700828static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
829
830static int msm_pm_modem_busy(void)
831{
832 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
833 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
834 KERN_INFO, "%s(): master not ready\n", __func__);
835 return -EBUSY;
836 }
837
838 return 0;
839}
840
841/*
842 * Power collapse the Apps processor. This function executes the handshake
843 * protocol with Modem.
844 *
845 * Return value:
846 * -EAGAIN: modem reset occurred or early exit from power collapse
847 * -EBUSY: modem not ready for our power collapse -- no power loss
848 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
849 * 0: success
850 */
851static int msm_pm_power_collapse
852 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
853{
854 struct msm_pm_polled_group state_grps[2];
855 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700856 int collapsed = 0;
857 int ret;
Murali Nalajala07b04022012-04-10 16:00:49 +0530858 int val;
859 int modem_early_exit = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860
861 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
862 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
863 (int)from_idle, sleep_delay, sleep_limit);
864
865 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
866 MSM_PM_DPRINTK(
867 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
868 KERN_INFO, "%s(): master not ready\n", __func__);
869 ret = -EBUSY;
870 goto power_collapse_bail;
871 }
872
873 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
874
Murali Nalajala41786ab2012-03-06 10:47:32 +0530875 if (cpu_is_msm8625()) {
876 /* Program the SPM */
877 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
878 false);
879 WARN_ON(ret);
880 }
881
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530882 msm_pm_irq_extns->enter_sleep1(true, from_idle,
883 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700884 msm_sirc_enter_sleep();
885 msm_gpio_enter_sleep(from_idle);
886
887 msm_pm_smem_data->sleep_time = sleep_delay;
888 msm_pm_smem_data->resources_used = sleep_limit;
889
890 /* Enter PWRC/PWRC_SUSPEND */
891
892 if (from_idle)
893 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
894 DEM_SLAVE_SMSM_PWRC);
895 else
896 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
897 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
898
899 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
900 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
901
902 memset(state_grps, 0, sizeof(state_grps));
903 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
904 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
905 state_grps[1].group_id = SMSM_MODEM_STATE;
906 state_grps[1].bits_all_set = SMSM_RESET;
907
908 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
909
910 if (ret < 0) {
911 printk(KERN_EMERG "%s(): power collapse entry "
912 "timed out waiting for Modem's response\n", __func__);
913 msm_pm_timeout();
914 }
915
916 if (ret == 1) {
917 MSM_PM_DPRINTK(
918 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
919 KERN_INFO,
920 "%s(): msm_pm_poll_state detected Modem reset\n",
921 __func__);
922 goto power_collapse_early_exit;
923 }
924
925 /* DEM Master in RSA */
926
927 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
928
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530929 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930 if (ret < 0) {
931 MSM_PM_DPRINTK(
932 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
933 KERN_INFO,
934 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
935 ret);
936 goto power_collapse_early_exit;
937 }
938
939 msm_pm_config_hw_before_power_down();
940 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
941
942 saved_acpuclk_rate = acpuclk_power_collapse();
943 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
944 "%s(): change clock rate (old rate = %lu)\n", __func__,
945 saved_acpuclk_rate);
946
947 if (saved_acpuclk_rate == 0) {
948 msm_pm_config_hw_after_power_up();
949 goto power_collapse_early_exit;
950 }
951
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600952 msm_pm_boot_config_before_pc(smp_processor_id(),
953 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954
955#ifdef CONFIG_VFP
956 if (from_idle)
Steve Mucklef132c6c2012-06-06 18:30:57 -0700957 vfp_pm_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700958#endif
959
960#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530961 if (!cpu_is_msm8625())
962 l2cc_suspend();
963 else
964 apps_power_collapse = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965#endif
966
967 collapsed = msm_pm_collapse();
Murali Nalajala07b04022012-04-10 16:00:49 +0530968
969 /*
970 * TBD: Currently recognise the MODEM early exit
971 * path by reading the MPA5_GDFS_CNT_VAL register.
972 */
973 if (cpu_is_msm8625()) {
974 /*
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530975 * on system reset, default value of MPA5_GDFS_CNT_VAL
976 * is = 0x0, later modem reprogram this value to
977 * 0x00030004. Once APPS did a power collapse and
978 * coming out of it expected value of this register
979 * always be 0x00030004. Incase if APPS sees the value
980 * as 0x00030002 consider this case as a modem early
981 * exit.
Murali Nalajala07b04022012-04-10 16:00:49 +0530982 */
983 val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530984 if (val != 0x00030002)
Murali Nalajala07b04022012-04-10 16:00:49 +0530985 power_collapsed = 1;
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530986 else
987 modem_early_exit = 1;
Murali Nalajala07b04022012-04-10 16:00:49 +0530988 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989
990#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530991 if (!cpu_is_msm8625())
992 l2cc_resume();
993 else
994 apps_power_collapse = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995#endif
996
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600997 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998
999 if (collapsed) {
1000#ifdef CONFIG_VFP
1001 if (from_idle)
Steve Mucklef132c6c2012-06-06 18:30:57 -07001002 vfp_pm_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001003#endif
1004 cpu_init();
1005 local_fiq_enable();
1006 }
1007
1008 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1009 KERN_INFO,
1010 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1011
1012 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1013 "%s(): restore clock rate to %lu\n", __func__,
1014 saved_acpuclk_rate);
1015 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1016 SETRATE_PC) < 0)
1017 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1018 __func__, saved_acpuclk_rate);
1019
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301020 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001021 msm_pm_smem_data->wakeup_reason,
1022 msm_pm_smem_data->pending_irqs);
1023
1024 msm_pm_config_hw_after_power_up();
1025 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1026
1027 memset(state_grps, 0, sizeof(state_grps));
1028 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1029 state_grps[0].bits_any_set =
1030 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1031 state_grps[1].group_id = SMSM_MODEM_STATE;
1032 state_grps[1].bits_all_set = SMSM_RESET;
1033
1034 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1035
1036 if (ret < 0) {
1037 printk(KERN_EMERG "%s(): power collapse exit "
1038 "timed out waiting for Modem's response\n", __func__);
1039 msm_pm_timeout();
1040 }
1041
1042 if (ret == 1) {
1043 MSM_PM_DPRINTK(
1044 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1045 KERN_INFO,
1046 "%s(): msm_pm_poll_state detected Modem reset\n",
1047 __func__);
1048 goto power_collapse_early_exit;
1049 }
1050
1051 /* Sanity check */
Murali Nalajala07b04022012-04-10 16:00:49 +05301052 if (collapsed && !modem_early_exit) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001053 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1054 } else {
1055 BUG_ON(!(state_grps[0].value_read &
1056 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1057 goto power_collapse_early_exit;
1058 }
1059
1060 /* Enter WFPI */
1061
1062 smsm_change_state(SMSM_APPS_DEM,
1063 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1064 DEM_SLAVE_SMSM_WFPI);
1065
1066 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1067
1068 memset(state_grps, 0, sizeof(state_grps));
1069 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1070 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1071 state_grps[1].group_id = SMSM_MODEM_STATE;
1072 state_grps[1].bits_all_set = SMSM_RESET;
1073
1074 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1075
1076 if (ret < 0) {
1077 printk(KERN_EMERG "%s(): power collapse WFPI "
1078 "timed out waiting for Modem's response\n", __func__);
1079 msm_pm_timeout();
1080 }
1081
1082 if (ret == 1) {
1083 MSM_PM_DPRINTK(
1084 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1085 KERN_INFO,
1086 "%s(): msm_pm_poll_state detected Modem reset\n",
1087 __func__);
1088 ret = -EAGAIN;
1089 goto power_collapse_restore_gpio_bail;
1090 }
1091
1092 /* DEM Master == RUN */
1093
1094 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1095 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1096
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301097 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098 msm_pm_smem_data->wakeup_reason,
1099 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301100 msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001101 msm_pm_smem_data->wakeup_reason,
1102 msm_pm_smem_data->pending_irqs);
1103 msm_gpio_exit_sleep();
1104 msm_sirc_exit_sleep();
1105
1106 smsm_change_state(SMSM_APPS_DEM,
1107 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1108
1109 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1110
1111 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301112
1113 if (cpu_is_msm8625()) {
1114 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1115 false);
1116 WARN_ON(ret);
1117 }
1118
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001119 return 0;
1120
1121power_collapse_early_exit:
1122 /* Enter PWRC_EARLY_EXIT */
1123
1124 smsm_change_state(SMSM_APPS_DEM,
1125 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1126 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1127
1128 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1129
1130 memset(state_grps, 0, sizeof(state_grps));
1131 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1132 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1133 state_grps[1].group_id = SMSM_MODEM_STATE;
1134 state_grps[1].bits_all_set = SMSM_RESET;
1135
1136 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1137 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1138
1139 if (ret < 0) {
1140 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1141 "timed out waiting for Modem's response\n", __func__);
1142 msm_pm_timeout();
1143 }
1144
1145 if (ret == 1) {
1146 MSM_PM_DPRINTK(
1147 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1148 KERN_INFO,
1149 "%s(): msm_pm_poll_state detected Modem reset\n",
1150 __func__);
1151 }
1152
1153 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1154
1155 ret = -EAGAIN;
1156
1157power_collapse_restore_gpio_bail:
1158 msm_gpio_exit_sleep();
1159 msm_sirc_exit_sleep();
1160
1161 /* Enter RUN */
1162 smsm_change_state(SMSM_APPS_DEM,
1163 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1164 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1165
1166 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1167
1168 if (collapsed)
1169 smd_sleep_exit();
1170
1171power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301172 if (cpu_is_msm8625()) {
1173 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1174 false);
1175 WARN_ON(ret);
1176 }
1177
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001178 return ret;
1179}
1180
1181/*
1182 * Power collapse the Apps processor without involving Modem.
1183 *
1184 * Return value:
1185 * 0: success
1186 */
Stephen Boydb29750d2012-02-21 01:21:32 -08001187static int __ref msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001188{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001189 int collapsed = 0;
1190 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301191 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001192
1193 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1194 KERN_INFO, "%s()\n", __func__);
1195
1196 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1197 WARN_ON(ret);
1198
Murali Nalajala41786ab2012-03-06 10:47:32 +05301199 entry = (!smp_processor_id() || from_idle) ?
1200 msm_pm_collapse_exit : msm_secondary_startup;
1201
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001202 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301203 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001204
1205#ifdef CONFIG_VFP
Steve Mucklef132c6c2012-06-06 18:30:57 -07001206 vfp_pm_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001207#endif
1208
1209#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301210 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301211 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001212#endif
1213
1214 collapsed = msm_pm_collapse();
1215
1216#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301217 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301218 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001219#endif
1220
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001221 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001222
1223 if (collapsed) {
1224#ifdef CONFIG_VFP
Steve Mucklef132c6c2012-06-06 18:30:57 -07001225 vfp_pm_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001226#endif
1227 cpu_init();
1228 local_fiq_enable();
1229 }
1230
1231 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1232 KERN_INFO,
1233 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1234
1235 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1236 WARN_ON(ret);
1237
Anji jonnalac6816222012-03-31 10:55:14 +05301238 return !collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001239}
1240
1241/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001242 * Bring the Apps processor to SWFI.
1243 *
1244 * Return value:
1245 * -EIO: could not ramp Apps processor clock
1246 * 0: success
1247 */
1248static int msm_pm_swfi(bool ramp_acpu)
1249{
1250 unsigned long saved_acpuclk_rate = 0;
1251
1252 if (ramp_acpu) {
1253 saved_acpuclk_rate = acpuclk_wait_for_irq();
1254 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1255 "%s(): change clock rate (old rate = %lu)\n", __func__,
1256 saved_acpuclk_rate);
1257
1258 if (!saved_acpuclk_rate)
1259 return -EIO;
1260 }
1261
Murali Nalajala41786ab2012-03-06 10:47:32 +05301262 if (!cpu_is_msm8625())
1263 msm_pm_config_hw_before_swfi();
1264
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001265 msm_arch_idle();
1266
1267 if (ramp_acpu) {
1268 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1269 "%s(): restore clock rate to %lu\n", __func__,
1270 saved_acpuclk_rate);
1271 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1272 SETRATE_SWFI) < 0)
1273 printk(KERN_ERR
1274 "%s(): failed to restore clock rate(%lu)\n",
1275 __func__, saved_acpuclk_rate);
1276 }
1277
1278 return 0;
1279}
1280
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301281static int64_t msm_pm_timer_enter_suspend(int64_t *period)
1282{
Anji Jonnalac02367a2012-07-01 02:56:11 +05301283 int64_t time = 0;
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301284
1285 time = msm_timer_get_sclk_time(period);
1286 if (!time)
1287 pr_err("%s: Unable to read sclk.\n", __func__);
1288 return time;
1289}
1290
1291static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period)
1292{
1293
1294 if (time != 0) {
1295 int64_t end_time = msm_timer_get_sclk_time(NULL);
1296 if (end_time != 0) {
1297 time = end_time - time;
1298 if (time < 0)
1299 time += period;
1300 } else
1301 time = 0;
1302 }
1303 return time;
1304}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001305
1306/******************************************************************************
1307 * External Idle/Suspend Functions
1308 *****************************************************************************/
1309
1310/*
1311 * Put CPU in low power mode.
1312 */
1313void arch_idle(void)
1314{
1315 bool allow[MSM_PM_SLEEP_MODE_NR];
1316 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1317
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001318 int64_t timer_expiration;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301319 int latency_qos;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001320 int ret;
1321 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301322 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001323 int64_t t1;
Murali Nalajalab86f3702012-03-30 17:54:57 +05301324 static DEFINE_PER_CPU(int64_t, t2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001325 int exit_stat;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001326
1327 if (!atomic_read(&msm_pm_init_done))
1328 return;
1329
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301330 cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001331 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301332 /* get the next timer expiration */
1333 timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001334
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001335 t1 = ktime_to_ns(ktime_get());
Murali Nalajalab86f3702012-03-30 17:54:57 +05301336 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001337 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301338 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001339
1340 for (i = 0; i < ARRAY_SIZE(allow); i++)
1341 allow[i] = true;
1342
Murali Nalajala41786ab2012-03-06 10:47:32 +05301343 if (num_online_cpus() > 1 ||
1344 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301345 !msm_pm_irq_extns->idle_sleep_allowed()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001346 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1347 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001348 }
1349
1350 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301351 struct msm_pm_platform_data *mode =
1352 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001353 if (!mode->idle_supported || !mode->idle_enabled ||
1354 mode->latency >= latency_qos ||
1355 mode->residency * 1000ULL >= timer_expiration)
1356 allow[i] = false;
1357 }
1358
1359 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1360 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1361 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1362 while (msm_pm_modem_busy() && wait_us) {
1363 if (wait_us > 100) {
1364 udelay(100);
1365 wait_us -= 100;
1366 } else {
1367 udelay(wait_us);
1368 wait_us = 0;
1369 }
1370 }
1371
1372 if (msm_pm_modem_busy()) {
1373 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1374 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1375 = false;
1376 }
1377 }
1378
1379 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1380 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1381 __func__, latency_qos, timer_expiration, sleep_limit);
1382
1383 for (i = 0; i < ARRAY_SIZE(allow); i++)
1384 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1385 "%s(): allow %s: %d\n", __func__,
1386 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1387
1388 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1389 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Murali Nalajala8fda4492012-03-19 18:22:59 +05301390 /* Sync the timer with SCLK, it is needed only for modem
1391 * assissted pollapse case.
1392 */
1393 int64_t next_timer_exp = msm_timer_enter_idle();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001394 uint32_t sleep_delay;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301395 bool low_power = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001396
1397 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
Murali Nalajala8fda4492012-03-19 18:22:59 +05301398 next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT);
1399
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001400 if (sleep_delay == 0) /* 0 would mean infinite time */
1401 sleep_delay = 1;
1402
1403 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1404 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1405
1406#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1407 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1408#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1409 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1410#endif
1411
1412 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1413 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301414 msm_timer_exit_idle(low_power);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001415
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001416 if (ret)
1417 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1418 else {
1419 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1420 msm_pm_sleep_limit = sleep_limit;
1421 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001422 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301423 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001424 exit_stat = ret ?
1425 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1426 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001427 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1428 ret = msm_pm_swfi(true);
1429 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301430 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001431 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001432 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001433 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1434 msm_pm_swfi(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001435 exit_stat = MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001436 } else {
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301437 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001438 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001439 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001440 }
1441
Murali Nalajalab86f3702012-03-30 17:54:57 +05301442 __get_cpu_var(t2) = ktime_to_ns(ktime_get());
1443 msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001444}
1445
1446/*
1447 * Suspend the Apps processor.
1448 *
1449 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301450 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001451 * -EAGAIN: modem reset occurred or early exit from suspend
1452 * -EBUSY: modem not ready for our suspend
1453 * -EINVAL: invalid sleep mode
1454 * -EIO: could not ramp Apps processor clock
1455 * -ETIMEDOUT: timed out waiting for modem's handshake
1456 * 0: success
1457 */
1458static int msm_pm_enter(suspend_state_t state)
1459{
1460 bool allow[MSM_PM_SLEEP_MODE_NR];
1461 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301462 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001463 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001464 int64_t period = 0;
1465 int64_t time = 0;
1466
Murali Nalajala41786ab2012-03-06 10:47:32 +05301467 /* Must executed by CORE0 */
1468 if (smp_processor_id()) {
1469 __WARN();
1470 goto suspend_exit;
1471 }
1472
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301473 time = msm_pm_timer_enter_suspend(&period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001474
1475 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1476 "%s(): sleep limit %u\n", __func__, sleep_limit);
1477
1478 for (i = 0; i < ARRAY_SIZE(allow); i++)
1479 allow[i] = true;
1480
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001481 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301482 struct msm_pm_platform_data *mode;
1483 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001484 if (!mode->suspend_supported || !mode->suspend_enabled)
1485 allow[i] = false;
1486 }
1487
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001488 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1489 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001490 enum msm_pm_time_stats_id id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001491
1492 clock_debug_print_enabled();
1493
1494#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1495 if (msm_pm_sleep_time_override > 0) {
1496 int64_t ns;
1497 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1498 msm_pm_set_max_sleep_time(ns);
1499 msm_pm_sleep_time_override = 0;
1500 }
1501#endif
1502 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1503 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1504
1505#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1506 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1507#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1508 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1509#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1510 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1511 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1512#endif
1513
1514 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1515 udelay(500);
1516
1517 ret = msm_pm_power_collapse(
1518 false, msm_pm_max_sleep_time, sleep_limit);
1519
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001520 if (ret)
1521 id = MSM_PM_STAT_FAILED_SUSPEND;
1522 else {
1523 id = MSM_PM_STAT_SUSPEND;
1524 msm_pm_sleep_limit = sleep_limit;
1525 }
1526
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301527 time = msm_pm_timer_exit_suspend(time, period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001528 msm_pm_add_stat(id, time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301530 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001531 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1532 ret = msm_pm_swfi(true);
1533 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301534 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535 udelay(1);
1536 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1537 msm_pm_swfi(false);
1538 }
1539
Murali Nalajala41786ab2012-03-06 10:47:32 +05301540suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001541 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1542 "%s(): return %d\n", __func__, ret);
1543
1544 return ret;
1545}
1546
1547static struct platform_suspend_ops msm_pm_ops = {
1548 .enter = msm_pm_enter,
1549 .valid = suspend_valid_only_mem,
1550};
1551
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301552/* Hotplug the "non boot" CPU's and put
1553 * the cores into low power mode
1554 */
1555void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1556{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301557 bool allow[MSM_PM_SLEEP_MODE_NR];
1558 int i;
1559
1560 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1561 struct msm_pm_platform_data *mode;
1562
1563 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1564 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1565 }
1566
1567 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1568 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1569
1570 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1571 msm_pm_power_collapse_standalone(false);
1572 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1573 msm_pm_swfi(false);
1574 } else {
1575 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1576 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1577 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301578}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001579
1580/******************************************************************************
1581 * Restart Definitions
1582 *****************************************************************************/
1583
1584static uint32_t restart_reason = 0x776655AA;
1585
1586static void msm_pm_power_off(void)
1587{
1588 msm_rpcrouter_close();
1589 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1590 for (;;)
1591 ;
1592}
1593
1594static void msm_pm_restart(char str, const char *cmd)
1595{
1596 msm_rpcrouter_close();
1597 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1598
1599 for (;;)
1600 ;
1601}
1602
1603static int msm_reboot_call
1604 (struct notifier_block *this, unsigned long code, void *_cmd)
1605{
1606 if ((code == SYS_RESTART) && _cmd) {
1607 char *cmd = _cmd;
1608 if (!strcmp(cmd, "bootloader")) {
1609 restart_reason = 0x77665500;
1610 } else if (!strcmp(cmd, "recovery")) {
1611 restart_reason = 0x77665502;
1612 } else if (!strcmp(cmd, "eraseflash")) {
1613 restart_reason = 0x776655EF;
1614 } else if (!strncmp(cmd, "oem-", 4)) {
1615 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1616 restart_reason = 0x6f656d00 | code;
1617 } else {
1618 restart_reason = 0x77665501;
1619 }
1620 }
1621 return NOTIFY_DONE;
1622}
1623
1624static struct notifier_block msm_reboot_notifier = {
1625 .notifier_call = msm_reboot_call,
1626};
1627
1628
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001629/*
1630 * Initialize the power management subsystem.
1631 *
1632 * Return value:
1633 * -ENODEV: initialization failed
1634 * 0: success
1635 */
1636static int __init msm_pm_init(void)
1637{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001638 int ret;
Murali Nalajala93f29992012-03-21 15:59:27 +05301639 int val;
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301640 enum msm_pm_time_stats_id enable_stats[] = {
1641 MSM_PM_STAT_REQUESTED_IDLE,
1642 MSM_PM_STAT_IDLE_SPIN,
1643 MSM_PM_STAT_IDLE_WFI,
1644 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
1645 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
1646 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
1647 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
1648 MSM_PM_STAT_SUSPEND,
1649 MSM_PM_STAT_FAILED_SUSPEND,
1650 MSM_PM_STAT_NOT_IDLE,
1651 };
1652
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001653#ifdef CONFIG_CPU_V7
1654 pgd_t *pc_pgd;
1655 pmd_t *pmd;
1656 unsigned long pmdval;
Steve Mucklef132c6c2012-06-06 18:30:57 -07001657 unsigned long exit_phys;
1658
1659 exit_phys = virt_to_phys(msm_pm_collapse_exit);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001660
1661 /* Page table for cores to come back up safely. */
1662 pc_pgd = pgd_alloc(&init_mm);
1663 if (!pc_pgd)
1664 return -ENOMEM;
Steve Mucklef132c6c2012-06-06 18:30:57 -07001665 pmd = pmd_offset(pud_offset(pc_pgd + pgd_index(exit_phys), exit_phys),
1666 exit_phys);
1667 pmdval = (exit_phys & PGDIR_MASK) |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001668 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1669 pmd[0] = __pmd(pmdval);
1670 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1671
Steve Mucklefcece052012-02-18 20:09:58 -08001672 msm_saved_state_phys =
1673 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1674 num_possible_cpus(), 4);
1675 if (!msm_saved_state_phys)
1676 return -ENOMEM;
1677 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1678 CPU_SAVED_STATE_SIZE *
1679 num_possible_cpus());
1680 if (!msm_saved_state)
1681 return -ENOMEM;
1682
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001683 /* It is remotely possible that the code in msm_pm_collapse_exit()
1684 * which turns on the MMU with this mapping is in the
1685 * next even-numbered megabyte beyond the
1686 * start of msm_pm_collapse_exit().
1687 * Map this megabyte in as well.
1688 */
1689 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1690 flush_pmd_entry(pmd);
1691 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001692 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1693 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001694#endif
1695
1696 pm_power_off = msm_pm_power_off;
1697 arm_pm_restart = msm_pm_restart;
1698 register_reboot_notifier(&msm_reboot_notifier);
1699
1700 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1701 sizeof(*msm_pm_smem_data));
1702 if (msm_pm_smem_data == NULL) {
1703 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1704 return -ENODEV;
1705 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001706
1707 ret = msm_timer_init_time_sync(msm_pm_timeout);
1708 if (ret)
1709 return ret;
1710
1711 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1712 if (ret) {
1713 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1714 __func__, ret);
1715 return ret;
1716 }
1717
Murali Nalajala93f29992012-03-21 15:59:27 +05301718 if (cpu_is_msm8625()) {
1719 target_type = TARGET_IS_8625;
1720 clean_caches((unsigned long)&target_type, sizeof(target_type),
1721 virt_to_phys(&target_type));
1722
Anji jonnalae644f8e2012-05-09 19:52:18 +05301723 /*
1724 * Configure the MPA5_GDFS_CNT_VAL register for
1725 * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the
1726 * DBGNOPOWERDN for each cpu.
1727 * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for
1728 * GDFS control.
Murali Nalajala93f29992012-03-21 15:59:27 +05301729 */
Anji jonnalae644f8e2012-05-09 19:52:18 +05301730 val = 0x00030002;
Murali Nalajala93f29992012-03-21 15:59:27 +05301731 __raw_writel(val, (MSM_CFG_CTL_BASE + 0x38));
Murali Nalajala73c13332012-05-15 11:30:59 +05301732
1733 l2x0_base_addr = MSM_L2CC_BASE;
Murali Nalajala93f29992012-03-21 15:59:27 +05301734 }
1735
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001736#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1737 /* The wakeup_reason field is overloaded during initialization time
1738 to signal Modem that Apps will control the low power modes of
1739 the memory.
1740 */
1741 msm_pm_smem_data->wakeup_reason = 1;
1742 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1743#endif
1744
1745 BUG_ON(msm_pm_modes == NULL);
1746
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001747 suspend_set_ops(&msm_pm_ops);
1748
1749 msm_pm_mode_sysfs_add();
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301750 msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
Murali Nalajala558c0ce2012-03-29 19:42:08 +05301751
1752 atomic_set(&msm_pm_init_done, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001753 return 0;
1754}
1755
1756late_initcall_sync(msm_pm_init);