blob: 426d6e64dcb51deaf306440fd3cc0462707de408 [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>
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>
31#ifdef CONFIG_HAS_WAKELOCK
32#include <linux/wakelock.h>
33#endif
34#include <mach/msm_iomap.h>
35#include <mach/system.h>
36#ifdef CONFIG_CPU_V7
37#include <asm/pgtable.h>
38#include <asm/pgalloc.h>
39#endif
40#ifdef CONFIG_CACHE_L2X0
41#include <asm/hardware/cache-l2x0.h>
42#endif
43#ifdef CONFIG_VFP
44#include <asm/vfp.h>
45#endif
46
47#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
48#include <mach/msm_migrate_pages.h>
49#endif
Murali Nalajala41786ab2012-03-06 10:47:32 +053050#include <mach/socinfo.h>
Anji jonnala1f2377c2012-03-27 14:35:55 +053051#include <asm/smp_scu.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"
Murali Nalajala19d33a22012-05-18 14:11:19 +053066#include "devices-msm7x2xa.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067
68/******************************************************************************
69 * Debug Definitions
70 *****************************************************************************/
71
72enum {
Murali Nalajalaa7efba12012-02-23 18:13:52 +053073 MSM_PM_DEBUG_SUSPEND = BIT(0),
74 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
75 MSM_PM_DEBUG_STATE = BIT(2),
76 MSM_PM_DEBUG_CLOCK = BIT(3),
77 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
78 MSM_PM_DEBUG_SMSM_STATE = BIT(5),
79 MSM_PM_DEBUG_IDLE = BIT(6),
80 MSM_PM_DEBUG_HOTPLUG = BIT(7),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081};
82
83static int msm_pm_debug_mask;
Taniya Dase30a6b22012-03-20 11:37:45 +053084int power_collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085module_param_named(
86 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
87);
88
89#define MSM_PM_DPRINTK(mask, level, message, ...) \
90 do { \
91 if ((mask) & msm_pm_debug_mask) \
92 printk(level message, ## __VA_ARGS__); \
93 } while (0)
94
95#define MSM_PM_DEBUG_PRINT_STATE(tag) \
96 do { \
97 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
98 KERN_INFO, "%s: " \
99 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
100 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
101 "SMSM_APPS_DEM %x\n", \
102 tag, \
103 __raw_readl(APPS_CLK_SLEEP_EN), \
104 __raw_readl(APPS_PWRDOWN), \
105 smsm_get_state(SMSM_POWER_MASTER_DEM), \
106 smsm_get_state(SMSM_MODEM_STATE), \
107 smsm_get_state(SMSM_APPS_DEM)); \
108 } while (0)
109
110#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
111 do { \
112 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
113 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
114 msm_pm_smem_data->resources_used, \
115 msm_pm_smem_data->irq_mask, \
116 msm_pm_smem_data->wakeup_reason, \
117 msm_pm_smem_data->pending_irqs); \
118 } while (0)
119
120
121/******************************************************************************
122 * Sleep Modes and Parameters
123 *****************************************************************************/
124
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
126module_param_named(
127 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
128 int, S_IRUGO | S_IWUSR | S_IWGRP
129);
130
131enum {
132 MSM_PM_MODE_ATTR_SUSPEND,
133 MSM_PM_MODE_ATTR_IDLE,
134 MSM_PM_MODE_ATTR_LATENCY,
135 MSM_PM_MODE_ATTR_RESIDENCY,
136 MSM_PM_MODE_ATTR_NR,
137};
138
139static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
140 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
141 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
142 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
143 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
144};
145
146static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
147 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
148 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
150 "ramp_down_and_wfi",
151 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
152 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
153 "power_collapse_no_xo_shutdown",
154 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
155 "standalone_power_collapse",
156};
157
158static struct msm_pm_platform_data *msm_pm_modes;
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530159static struct msm_pm_irq_calls *msm_pm_irq_extns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530161struct msm_pm_kobj_attribute {
162 unsigned int cpu;
163 struct kobj_attribute ka;
164};
165
166#define GET_CPU_OF_ATTR(attr) \
167 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
168
169struct msm_pm_sysfs_sleep_mode {
170 struct kobject *kobj;
171 struct attribute_group attr_group;
172 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
173 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
174};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175
176/*
177 * Write out the attribute.
178 */
179static ssize_t msm_pm_mode_attr_show(
180 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
181{
182 int ret = -EINVAL;
183 int i;
184
185 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
186 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530187 unsigned int cpu;
188 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189
190 if (msm_pm_sleep_mode_labels[i] == NULL)
191 continue;
192
193 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
194 continue;
195
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530196 cpu = GET_CPU_OF_ATTR(attr);
197 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
198
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 if (!strcmp(attr->attr.name,
200 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530201 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 kp.arg = &arg;
203 ret = param_get_ulong(buf, &kp);
204 } else if (!strcmp(attr->attr.name,
205 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530206 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 kp.arg = &arg;
208 ret = param_get_ulong(buf, &kp);
209 } else if (!strcmp(attr->attr.name,
210 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530211 u32 arg = mode->latency;
212 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 ret = param_get_ulong(buf, &kp);
214 } else if (!strcmp(attr->attr.name,
215 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530216 u32 arg = mode->residency;
217 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218 ret = param_get_ulong(buf, &kp);
219 }
220
221 break;
222 }
223
224 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530225 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 ret++;
227 }
228
229 return ret;
230}
231
232/*
233 * Read in the new attribute value.
234 */
235static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
236 struct kobj_attribute *attr, const char *buf, size_t count)
237{
238 int ret = -EINVAL;
239 int i;
240
241 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
242 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530243 unsigned int cpu;
244 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700245
246 if (msm_pm_sleep_mode_labels[i] == NULL)
247 continue;
248
249 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
250 continue;
251
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530252 cpu = GET_CPU_OF_ATTR(attr);
253 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
254
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 if (!strcmp(attr->attr.name,
256 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530257 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 ret = param_set_byte(buf, &kp);
259 } else if (!strcmp(attr->attr.name,
260 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530261 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262 ret = param_set_byte(buf, &kp);
263 } else if (!strcmp(attr->attr.name,
264 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530265 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266 ret = param_set_ulong(buf, &kp);
267 } else if (!strcmp(attr->attr.name,
268 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530269 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270 ret = param_set_ulong(buf, &kp);
271 }
272
273 break;
274 }
275
276 return ret ? ret : count;
277}
278
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530279 /* Add sysfs entries for one cpu. */
280static int __init msm_pm_mode_sysfs_add_cpu(
281 unsigned int cpu, struct kobject *modes_kobj)
282{
283 char cpu_name[8];
284 struct kobject *cpu_kobj;
285 struct msm_pm_sysfs_sleep_mode *mode = NULL;
286 int i, j, k;
287 int ret;
288
289 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
290 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
291 if (!cpu_kobj) {
292 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
293 ret = -ENOMEM;
294 goto mode_sysfs_add_cpu_exit;
295 }
296
297 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
298 int idx = MSM_PM_MODE(cpu, i);
299
300 if ((!msm_pm_modes[idx].suspend_supported) &&
301 (!msm_pm_modes[idx].idle_supported))
302 continue;
303
304 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
305 if (!mode) {
306 pr_err("%s: cannot allocate memory for attributes\n",
307 __func__);
308 ret = -ENOMEM;
309 goto mode_sysfs_add_cpu_exit;
310 }
311
312 mode->kobj = kobject_create_and_add(
313 msm_pm_sleep_mode_labels[i], cpu_kobj);
314 if (!mode->kobj) {
315 pr_err("%s: cannot create kobject\n", __func__);
316 ret = -ENOMEM;
317 goto mode_sysfs_add_cpu_exit;
318 }
319
320 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
321 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
322 !msm_pm_modes[idx].idle_supported)
323 continue;
324 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
325 !msm_pm_modes[idx].suspend_supported)
326 continue;
327 mode->kas[j].cpu = cpu;
328 mode->kas[j].ka.attr.mode = 0644;
329 mode->kas[j].ka.show = msm_pm_mode_attr_show;
330 mode->kas[j].ka.store = msm_pm_mode_attr_store;
331 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
332 mode->attrs[j] = &mode->kas[j].ka.attr;
333 j++;
334 }
335 mode->attrs[j] = NULL;
336
337 mode->attr_group.attrs = mode->attrs;
338 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
339 if (ret) {
340 printk(KERN_ERR
341 "%s: cannot create kobject attribute group\n",
342 __func__);
343 goto mode_sysfs_add_cpu_exit;
344 }
345 }
346
347 ret = 0;
348
349mode_sysfs_add_cpu_exit:
350 if (ret) {
351 if (mode && mode->kobj)
352 kobject_del(mode->kobj);
353 kfree(mode);
354 }
355
356 return ret;
357}
358
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359/*
360 * Add sysfs entries for the sleep modes.
361 */
362static int __init msm_pm_mode_sysfs_add(void)
363{
364 struct kobject *module_kobj = NULL;
365 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530366 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367 int ret;
368
369 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
370 if (!module_kobj) {
371 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
372 __func__, KBUILD_MODNAME);
373 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530374 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375 }
376
377 modes_kobj = kobject_create_and_add("modes", module_kobj);
378 if (!modes_kobj) {
379 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
380 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530381 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 }
383
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530384 for_each_possible_cpu(cpu) {
385 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
386 if (ret)
387 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 }
389
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530390 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530392mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393 return ret;
394}
395
396void __init msm_pm_set_platform_data(
397 struct msm_pm_platform_data *data, int count)
398{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530399 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400 msm_pm_modes = data;
401}
402
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530403void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls)
404{
405 /* sanity check */
406 BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL ||
407 irq_calls->idle_sleep_allowed == NULL ||
408 irq_calls->enter_sleep1 == NULL ||
409 irq_calls->enter_sleep2 == NULL ||
410 irq_calls->exit_sleep1 == NULL ||
411 irq_calls->exit_sleep2 == NULL ||
412 irq_calls->exit_sleep3 == NULL);
413
414 msm_pm_irq_extns = irq_calls;
415}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416
417/******************************************************************************
418 * Sleep Limitations
419 *****************************************************************************/
420enum {
421 SLEEP_LIMIT_NONE = 0,
422 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
423 SLEEP_LIMIT_MASK = 0x03,
424};
425
Praveen Chidambaram3895bde2012-05-14 19:42:40 +0530426static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
428enum {
429 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
430 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
431};
432#endif
433
434
435/******************************************************************************
436 * Configure Hardware for Power Down/Up
437 *****************************************************************************/
438
439#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530440#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
441#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530443#define APPS_STANDBY_CTL NULL
444#else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
446#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
447#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530448#define APPS_SECOP NULL
449#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450
451/*
452 * Configure hardware registers in preparation for Apps power down.
453 */
454static void msm_pm_config_hw_before_power_down(void)
455{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530456 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530457 __raw_writel(4, APPS_SECOP);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530458 } else if (cpu_is_msm7x27()) {
459 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530460 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530461 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
462 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530463 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530464 } else if (cpu_is_qsd8x50()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530465 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
466 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530467 __raw_writel(0, APPS_STANDBY_CTL);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530468 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530469 mb();
470 __raw_writel(1, APPS_PWRDOWN);
471 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472}
473
474/*
Anji jonnala1f2377c2012-03-27 14:35:55 +0530475 * Program the top csr from core0 context to put the
476 * core1 into GDFS, as core1 is not running yet.
477 */
478static void configure_top_csr(void)
479{
480 void __iomem *base_ptr;
481 unsigned int value = 0;
482
Murali Nalajala19d33a22012-05-18 14:11:19 +0530483 base_ptr = core1_reset_base();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530484 if (!base_ptr)
485 return;
486
487 /* bring the core1 out of reset */
488 __raw_writel(0x3, base_ptr);
489 mb();
490 /*
491 * override DBGNOPOWERDN and program the GDFS
492 * count val
493 */
494
495 __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38));
496 mb();
497
498 /* Initialize the SPM0 and SPM1 registers */
499 msm_spm_reinit();
500
501 /* enable TCSR for core1 */
502 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
503 value |= BIT(22);
504 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
505 mb();
506
507 /* set reset bit for SPM1 */
508 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
509 value |= BIT(20);
510 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
511 mb();
512
513 /* set CLK_OFF bit */
514 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
515 value |= BIT(18);
516 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
517 mb();
518
519 /* set clamps bit */
520 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
521 value |= BIT(21);
522 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
523 mb();
524
525 /* set power_up bit */
526 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
527 value |= BIT(19);
528 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
529 mb();
530
531 /* Disable TSCR for core0 */
532 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
533 value &= ~BIT(22);
534 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
535 mb();
536 __raw_writel(0x0, base_ptr);
537 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530538}
539
540/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 * Clear hardware registers after Apps powers up.
542 */
543static void msm_pm_config_hw_after_power_up(void)
544{
Anji jonnala1f2377c2012-03-27 14:35:55 +0530545
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530546 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
547 __raw_writel(0, APPS_SECOP);
548 mb();
549 __raw_writel(0, APPS_PWRDOWN);
550 mb();
551 msm_spm_reinit();
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530552 } else if (cpu_is_msm8625()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530553 __raw_writel(0, APPS_PWRDOWN);
554 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530555
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530556 if (power_collapsed) {
Anji jonnala1f2377c2012-03-27 14:35:55 +0530557 /*
558 * enable the SCU while coming out of power
559 * collapse.
560 */
561 scu_enable(MSM_SCU_BASE);
562 /*
563 * Program the top csr to put the core1 into GDFS.
564 */
565 configure_top_csr();
566 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530567 } else {
568 __raw_writel(0, APPS_PWRDOWN);
569 mb();
570 __raw_writel(0, APPS_CLK_SLEEP_EN);
571 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530572 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573}
574
575/*
576 * Configure hardware registers in preparation for SWFI.
577 */
578static void msm_pm_config_hw_before_swfi(void)
579{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530580 if (cpu_is_qsd8x50()) {
581 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
582 mb();
583 } else if (cpu_is_msm7x27()) {
584 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
585 mb();
586 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530587 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
588 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530589 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
590 mb();
591 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592}
593
594/*
595 * Respond to timing out waiting for Modem
596 *
597 * NOTE: The function never returns.
598 */
599static void msm_pm_timeout(void)
600{
601#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
602 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
603 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
604#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
605 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
606 msm_proc_comm_reset_modem_now();
607#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
608 printk(KERN_EMERG "%s(): halting\n", __func__);
609#endif
610 for (;;)
611 ;
612}
613
614
615/******************************************************************************
616 * State Polling Definitions
617 *****************************************************************************/
618
619struct msm_pm_polled_group {
620 uint32_t group_id;
621
622 uint32_t bits_all_set;
623 uint32_t bits_all_clear;
624 uint32_t bits_any_set;
625 uint32_t bits_any_clear;
626
627 uint32_t value_read;
628};
629
630/*
631 * Return true if all bits indicated by flag are set in source.
632 */
633static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
634{
635 return (source & flag) == flag;
636}
637
638/*
639 * Return true if any bit indicated by flag are set in source.
640 */
641static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
642{
643 return !flag || (source & flag);
644}
645
646/*
647 * Return true if all bits indicated by flag are cleared in source.
648 */
649static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
650{
651 return (~source & flag) == flag;
652}
653
654/*
655 * Return true if any bit indicated by flag are cleared in source.
656 */
657static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
658{
659 return !flag || (~source & flag);
660}
661
662/*
663 * Poll the shared memory states as indicated by the poll groups.
664 *
665 * nr_grps: number of groups in the array
666 * grps: array of groups
667 *
668 * The function returns when conditions specified by any of the poll
669 * groups become true. The conditions specified by a poll group are
670 * deemed true when 1) at least one bit from bits_any_set is set OR one
671 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
672 * are set; and 3) all bits in bits_all_clear are cleared.
673 *
674 * Return value:
675 * >=0: index of the poll group whose conditions have become true
676 * -ETIMEDOUT: timed out
677 */
678static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
679{
680 int i, k;
681
682 for (i = 0; i < 50000; i++) {
683 for (k = 0; k < nr_grps; k++) {
684 bool all_set, all_clear;
685 bool any_set, any_clear;
686
687 grps[k].value_read = smsm_get_state(grps[k].group_id);
688
689 all_set = msm_pm_all_set(grps[k].value_read,
690 grps[k].bits_all_set);
691 all_clear = msm_pm_all_clear(grps[k].value_read,
692 grps[k].bits_all_clear);
693 any_set = msm_pm_any_set(grps[k].value_read,
694 grps[k].bits_any_set);
695 any_clear = msm_pm_any_clear(grps[k].value_read,
696 grps[k].bits_any_clear);
697
698 if (all_set && all_clear && (any_set || any_clear))
699 return k;
700 }
701 udelay(50);
702 }
703
704 printk(KERN_ERR "%s failed:\n", __func__);
705 for (k = 0; k < nr_grps; k++)
706 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
707 grps[k].bits_all_set, grps[k].bits_all_clear,
708 grps[k].bits_any_set, grps[k].bits_any_clear,
709 grps[k].value_read);
710
711 return -ETIMEDOUT;
712}
713
714
715/******************************************************************************
716 * Suspend Max Sleep Time
717 *****************************************************************************/
718
719#define SCLK_HZ (32768)
720#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
721
722#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
723static int msm_pm_sleep_time_override;
724module_param_named(sleep_time_override,
725 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
726#endif
727
728static uint32_t msm_pm_max_sleep_time;
729
730/*
731 * Convert time from nanoseconds to slow clock ticks, then cap it to the
732 * specified limit
733 */
734static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
735{
736 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
737 return (time_ns > limit) ? limit : time_ns;
738}
739
740/*
741 * Set the sleep time for suspend. 0 means infinite sleep time.
742 */
743void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
744{
745 unsigned long flags;
746
747 local_irq_save(flags);
748 if (max_sleep_time_ns == 0) {
749 msm_pm_max_sleep_time = 0;
750 } else {
751 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
752 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
753
754 if (msm_pm_max_sleep_time == 0)
755 msm_pm_max_sleep_time = 1;
756 }
757
758 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
759 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
760 max_sleep_time_ns, msm_pm_max_sleep_time);
761 local_irq_restore(flags);
762}
763EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
764
765
766/******************************************************************************
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700767 * Shared Memory Bits
768 *****************************************************************************/
769
770#define DEM_MASTER_BITS_PER_CPU 6
771
772/* Power Master State Bits - Per CPU */
773#define DEM_MASTER_SMSM_RUN \
774 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
775#define DEM_MASTER_SMSM_RSA \
776 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
777#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
778 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
779#define DEM_MASTER_SMSM_SLEEP_EXIT \
780 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
781#define DEM_MASTER_SMSM_READY \
782 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
783#define DEM_MASTER_SMSM_SLEEP \
784 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
785
786/* Power Slave State Bits */
787#define DEM_SLAVE_SMSM_RUN (0x0001)
788#define DEM_SLAVE_SMSM_PWRC (0x0002)
789#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
790#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
791#define DEM_SLAVE_SMSM_WFPI (0x0010)
792#define DEM_SLAVE_SMSM_SLEEP (0x0020)
793#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
794#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
795#define DEM_SLAVE_SMSM_RESET (0x0100)
796#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
797
798
799/******************************************************************************
800 * Shared Memory Data
801 *****************************************************************************/
802
803#define DEM_MAX_PORT_NAME_LEN (20)
804
805struct msm_pm_smem_t {
806 uint32_t sleep_time;
807 uint32_t irq_mask;
808 uint32_t resources_used;
809 uint32_t reserved1;
810
811 uint32_t wakeup_reason;
812 uint32_t pending_irqs;
813 uint32_t rpc_prog;
814 uint32_t rpc_proc;
815 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
816 uint32_t reserved2;
817};
818
819
820/******************************************************************************
821 *
822 *****************************************************************************/
823static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700824static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
825
826static int msm_pm_modem_busy(void)
827{
828 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
829 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
830 KERN_INFO, "%s(): master not ready\n", __func__);
831 return -EBUSY;
832 }
833
834 return 0;
835}
836
837/*
838 * Power collapse the Apps processor. This function executes the handshake
839 * protocol with Modem.
840 *
841 * Return value:
842 * -EAGAIN: modem reset occurred or early exit from power collapse
843 * -EBUSY: modem not ready for our power collapse -- no power loss
844 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
845 * 0: success
846 */
847static int msm_pm_power_collapse
848 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
849{
850 struct msm_pm_polled_group state_grps[2];
851 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 int collapsed = 0;
853 int ret;
Murali Nalajala07b04022012-04-10 16:00:49 +0530854 int val;
855 int modem_early_exit = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700856
857 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
858 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
859 (int)from_idle, sleep_delay, sleep_limit);
860
861 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
862 MSM_PM_DPRINTK(
863 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
864 KERN_INFO, "%s(): master not ready\n", __func__);
865 ret = -EBUSY;
866 goto power_collapse_bail;
867 }
868
869 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
870
Murali Nalajala41786ab2012-03-06 10:47:32 +0530871 if (cpu_is_msm8625()) {
872 /* Program the SPM */
873 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
874 false);
875 WARN_ON(ret);
876 }
877
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530878 msm_pm_irq_extns->enter_sleep1(true, from_idle,
879 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880 msm_sirc_enter_sleep();
881 msm_gpio_enter_sleep(from_idle);
882
883 msm_pm_smem_data->sleep_time = sleep_delay;
884 msm_pm_smem_data->resources_used = sleep_limit;
885
886 /* Enter PWRC/PWRC_SUSPEND */
887
888 if (from_idle)
889 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
890 DEM_SLAVE_SMSM_PWRC);
891 else
892 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
893 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
894
895 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
896 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
897
898 memset(state_grps, 0, sizeof(state_grps));
899 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
900 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
901 state_grps[1].group_id = SMSM_MODEM_STATE;
902 state_grps[1].bits_all_set = SMSM_RESET;
903
904 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
905
906 if (ret < 0) {
907 printk(KERN_EMERG "%s(): power collapse entry "
908 "timed out waiting for Modem's response\n", __func__);
909 msm_pm_timeout();
910 }
911
912 if (ret == 1) {
913 MSM_PM_DPRINTK(
914 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
915 KERN_INFO,
916 "%s(): msm_pm_poll_state detected Modem reset\n",
917 __func__);
918 goto power_collapse_early_exit;
919 }
920
921 /* DEM Master in RSA */
922
923 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
924
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530925 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700926 if (ret < 0) {
927 MSM_PM_DPRINTK(
928 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
929 KERN_INFO,
930 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
931 ret);
932 goto power_collapse_early_exit;
933 }
934
935 msm_pm_config_hw_before_power_down();
936 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
937
938 saved_acpuclk_rate = acpuclk_power_collapse();
939 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
940 "%s(): change clock rate (old rate = %lu)\n", __func__,
941 saved_acpuclk_rate);
942
943 if (saved_acpuclk_rate == 0) {
944 msm_pm_config_hw_after_power_up();
945 goto power_collapse_early_exit;
946 }
947
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600948 msm_pm_boot_config_before_pc(smp_processor_id(),
949 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950
951#ifdef CONFIG_VFP
952 if (from_idle)
953 vfp_flush_context();
954#endif
955
956#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530957 if (!cpu_is_msm8625())
958 l2cc_suspend();
959 else
960 apps_power_collapse = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961#endif
962
963 collapsed = msm_pm_collapse();
Murali Nalajala07b04022012-04-10 16:00:49 +0530964
965 /*
966 * TBD: Currently recognise the MODEM early exit
967 * path by reading the MPA5_GDFS_CNT_VAL register.
968 */
969 if (cpu_is_msm8625()) {
970 /*
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530971 * on system reset, default value of MPA5_GDFS_CNT_VAL
972 * is = 0x0, later modem reprogram this value to
973 * 0x00030004. Once APPS did a power collapse and
974 * coming out of it expected value of this register
975 * always be 0x00030004. Incase if APPS sees the value
976 * as 0x00030002 consider this case as a modem early
977 * exit.
Murali Nalajala07b04022012-04-10 16:00:49 +0530978 */
979 val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530980 if (val != 0x00030002)
Murali Nalajala07b04022012-04-10 16:00:49 +0530981 power_collapsed = 1;
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530982 else
983 modem_early_exit = 1;
Murali Nalajala07b04022012-04-10 16:00:49 +0530984 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700985
986#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530987 if (!cpu_is_msm8625())
988 l2cc_resume();
989 else
990 apps_power_collapse = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991#endif
992
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600993 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700994
995 if (collapsed) {
996#ifdef CONFIG_VFP
997 if (from_idle)
998 vfp_reinit();
999#endif
1000 cpu_init();
1001 local_fiq_enable();
1002 }
1003
1004 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1005 KERN_INFO,
1006 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1007
1008 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1009 "%s(): restore clock rate to %lu\n", __func__,
1010 saved_acpuclk_rate);
1011 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1012 SETRATE_PC) < 0)
1013 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1014 __func__, saved_acpuclk_rate);
1015
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301016 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001017 msm_pm_smem_data->wakeup_reason,
1018 msm_pm_smem_data->pending_irqs);
1019
1020 msm_pm_config_hw_after_power_up();
1021 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1022
1023 memset(state_grps, 0, sizeof(state_grps));
1024 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1025 state_grps[0].bits_any_set =
1026 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1027 state_grps[1].group_id = SMSM_MODEM_STATE;
1028 state_grps[1].bits_all_set = SMSM_RESET;
1029
1030 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1031
1032 if (ret < 0) {
1033 printk(KERN_EMERG "%s(): power collapse exit "
1034 "timed out waiting for Modem's response\n", __func__);
1035 msm_pm_timeout();
1036 }
1037
1038 if (ret == 1) {
1039 MSM_PM_DPRINTK(
1040 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1041 KERN_INFO,
1042 "%s(): msm_pm_poll_state detected Modem reset\n",
1043 __func__);
1044 goto power_collapse_early_exit;
1045 }
1046
1047 /* Sanity check */
Murali Nalajala07b04022012-04-10 16:00:49 +05301048 if (collapsed && !modem_early_exit) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001049 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1050 } else {
1051 BUG_ON(!(state_grps[0].value_read &
1052 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1053 goto power_collapse_early_exit;
1054 }
1055
1056 /* Enter WFPI */
1057
1058 smsm_change_state(SMSM_APPS_DEM,
1059 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1060 DEM_SLAVE_SMSM_WFPI);
1061
1062 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1063
1064 memset(state_grps, 0, sizeof(state_grps));
1065 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1066 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1067 state_grps[1].group_id = SMSM_MODEM_STATE;
1068 state_grps[1].bits_all_set = SMSM_RESET;
1069
1070 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1071
1072 if (ret < 0) {
1073 printk(KERN_EMERG "%s(): power collapse WFPI "
1074 "timed out waiting for Modem's response\n", __func__);
1075 msm_pm_timeout();
1076 }
1077
1078 if (ret == 1) {
1079 MSM_PM_DPRINTK(
1080 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1081 KERN_INFO,
1082 "%s(): msm_pm_poll_state detected Modem reset\n",
1083 __func__);
1084 ret = -EAGAIN;
1085 goto power_collapse_restore_gpio_bail;
1086 }
1087
1088 /* DEM Master == RUN */
1089
1090 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1091 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1092
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301093 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001094 msm_pm_smem_data->wakeup_reason,
1095 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301096 msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001097 msm_pm_smem_data->wakeup_reason,
1098 msm_pm_smem_data->pending_irqs);
1099 msm_gpio_exit_sleep();
1100 msm_sirc_exit_sleep();
1101
1102 smsm_change_state(SMSM_APPS_DEM,
1103 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1104
1105 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1106
1107 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301108
1109 if (cpu_is_msm8625()) {
1110 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1111 false);
1112 WARN_ON(ret);
1113 }
1114
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001115 return 0;
1116
1117power_collapse_early_exit:
1118 /* Enter PWRC_EARLY_EXIT */
1119
1120 smsm_change_state(SMSM_APPS_DEM,
1121 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1122 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1123
1124 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1125
1126 memset(state_grps, 0, sizeof(state_grps));
1127 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1128 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1129 state_grps[1].group_id = SMSM_MODEM_STATE;
1130 state_grps[1].bits_all_set = SMSM_RESET;
1131
1132 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1133 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1134
1135 if (ret < 0) {
1136 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1137 "timed out waiting for Modem's response\n", __func__);
1138 msm_pm_timeout();
1139 }
1140
1141 if (ret == 1) {
1142 MSM_PM_DPRINTK(
1143 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1144 KERN_INFO,
1145 "%s(): msm_pm_poll_state detected Modem reset\n",
1146 __func__);
1147 }
1148
1149 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1150
1151 ret = -EAGAIN;
1152
1153power_collapse_restore_gpio_bail:
1154 msm_gpio_exit_sleep();
1155 msm_sirc_exit_sleep();
1156
1157 /* Enter RUN */
1158 smsm_change_state(SMSM_APPS_DEM,
1159 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1160 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1161
1162 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1163
1164 if (collapsed)
1165 smd_sleep_exit();
1166
1167power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301168 if (cpu_is_msm8625()) {
1169 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1170 false);
1171 WARN_ON(ret);
1172 }
1173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001174 return ret;
1175}
1176
1177/*
1178 * Power collapse the Apps processor without involving Modem.
1179 *
1180 * Return value:
1181 * 0: success
1182 */
Stephen Boydb29750d2012-02-21 01:21:32 -08001183static int __ref msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001184{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185 int collapsed = 0;
1186 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301187 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001188
1189 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1190 KERN_INFO, "%s()\n", __func__);
1191
1192 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1193 WARN_ON(ret);
1194
Murali Nalajala41786ab2012-03-06 10:47:32 +05301195 entry = (!smp_processor_id() || from_idle) ?
1196 msm_pm_collapse_exit : msm_secondary_startup;
1197
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001198 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301199 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001200
1201#ifdef CONFIG_VFP
1202 vfp_flush_context();
1203#endif
1204
1205#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301206 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301207 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001208#endif
1209
1210 collapsed = msm_pm_collapse();
1211
1212#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301213 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301214 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001215#endif
1216
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001217 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001218
1219 if (collapsed) {
1220#ifdef CONFIG_VFP
1221 vfp_reinit();
1222#endif
1223 cpu_init();
1224 local_fiq_enable();
1225 }
1226
1227 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1228 KERN_INFO,
1229 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1230
1231 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1232 WARN_ON(ret);
1233
Anji jonnalac6816222012-03-31 10:55:14 +05301234 return !collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001235}
1236
1237/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001238 * Bring the Apps processor to SWFI.
1239 *
1240 * Return value:
1241 * -EIO: could not ramp Apps processor clock
1242 * 0: success
1243 */
1244static int msm_pm_swfi(bool ramp_acpu)
1245{
1246 unsigned long saved_acpuclk_rate = 0;
1247
1248 if (ramp_acpu) {
1249 saved_acpuclk_rate = acpuclk_wait_for_irq();
1250 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1251 "%s(): change clock rate (old rate = %lu)\n", __func__,
1252 saved_acpuclk_rate);
1253
1254 if (!saved_acpuclk_rate)
1255 return -EIO;
1256 }
1257
Murali Nalajala41786ab2012-03-06 10:47:32 +05301258 if (!cpu_is_msm8625())
1259 msm_pm_config_hw_before_swfi();
1260
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001261 msm_arch_idle();
1262
1263 if (ramp_acpu) {
1264 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1265 "%s(): restore clock rate to %lu\n", __func__,
1266 saved_acpuclk_rate);
1267 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1268 SETRATE_SWFI) < 0)
1269 printk(KERN_ERR
1270 "%s(): failed to restore clock rate(%lu)\n",
1271 __func__, saved_acpuclk_rate);
1272 }
1273
1274 return 0;
1275}
1276
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301277static int64_t msm_pm_timer_enter_suspend(int64_t *period)
1278{
1279 int time = 0;
1280
1281 time = msm_timer_get_sclk_time(period);
1282 if (!time)
1283 pr_err("%s: Unable to read sclk.\n", __func__);
1284 return time;
1285}
1286
1287static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period)
1288{
1289
1290 if (time != 0) {
1291 int64_t end_time = msm_timer_get_sclk_time(NULL);
1292 if (end_time != 0) {
1293 time = end_time - time;
1294 if (time < 0)
1295 time += period;
1296 } else
1297 time = 0;
1298 }
1299 return time;
1300}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001301
1302/******************************************************************************
1303 * External Idle/Suspend Functions
1304 *****************************************************************************/
1305
1306/*
1307 * Put CPU in low power mode.
1308 */
1309void arch_idle(void)
1310{
1311 bool allow[MSM_PM_SLEEP_MODE_NR];
1312 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1313
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001314 int64_t timer_expiration;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301315 int latency_qos;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001316 int ret;
1317 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301318 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001319 int64_t t1;
Murali Nalajalab86f3702012-03-30 17:54:57 +05301320 static DEFINE_PER_CPU(int64_t, t2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001321 int exit_stat;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001322
1323 if (!atomic_read(&msm_pm_init_done))
1324 return;
1325
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301326 cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001327 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301328 /* get the next timer expiration */
1329 timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001330
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001331 t1 = ktime_to_ns(ktime_get());
Murali Nalajalab86f3702012-03-30 17:54:57 +05301332 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001333 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301334 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001335
1336 for (i = 0; i < ARRAY_SIZE(allow); i++)
1337 allow[i] = true;
1338
Murali Nalajala41786ab2012-03-06 10:47:32 +05301339 if (num_online_cpus() > 1 ||
1340 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001341#ifdef CONFIG_HAS_WAKELOCK
1342 has_wake_lock(WAKE_LOCK_IDLE) ||
1343#endif
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301344 !msm_pm_irq_extns->idle_sleep_allowed()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001345 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1346 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001347 }
1348
1349 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301350 struct msm_pm_platform_data *mode =
1351 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001352 if (!mode->idle_supported || !mode->idle_enabled ||
1353 mode->latency >= latency_qos ||
1354 mode->residency * 1000ULL >= timer_expiration)
1355 allow[i] = false;
1356 }
1357
1358 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1359 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1360 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1361 while (msm_pm_modem_busy() && wait_us) {
1362 if (wait_us > 100) {
1363 udelay(100);
1364 wait_us -= 100;
1365 } else {
1366 udelay(wait_us);
1367 wait_us = 0;
1368 }
1369 }
1370
1371 if (msm_pm_modem_busy()) {
1372 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1373 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1374 = false;
1375 }
1376 }
1377
1378 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1379 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1380 __func__, latency_qos, timer_expiration, sleep_limit);
1381
1382 for (i = 0; i < ARRAY_SIZE(allow); i++)
1383 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1384 "%s(): allow %s: %d\n", __func__,
1385 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1386
1387 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1388 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Murali Nalajala8fda4492012-03-19 18:22:59 +05301389 /* Sync the timer with SCLK, it is needed only for modem
1390 * assissted pollapse case.
1391 */
1392 int64_t next_timer_exp = msm_timer_enter_idle();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001393 uint32_t sleep_delay;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301394 bool low_power = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001395
1396 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
Murali Nalajala8fda4492012-03-19 18:22:59 +05301397 next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT);
1398
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001399 if (sleep_delay == 0) /* 0 would mean infinite time */
1400 sleep_delay = 1;
1401
1402 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1403 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1404
1405#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1406 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1407#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1408 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1409#endif
1410
1411 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1412 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301413 msm_timer_exit_idle(low_power);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001414
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001415 if (ret)
1416 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1417 else {
1418 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1419 msm_pm_sleep_limit = sleep_limit;
1420 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001421 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301422 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001423 exit_stat = ret ?
1424 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1425 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001426 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1427 ret = msm_pm_swfi(true);
1428 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301429 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001430 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001431 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001432 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1433 msm_pm_swfi(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001434 exit_stat = MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001435 } else {
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301436 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001437 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001438 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001439 }
1440
Murali Nalajalab86f3702012-03-30 17:54:57 +05301441 __get_cpu_var(t2) = ktime_to_ns(ktime_get());
1442 msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001443}
1444
1445/*
1446 * Suspend the Apps processor.
1447 *
1448 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301449 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001450 * -EAGAIN: modem reset occurred or early exit from suspend
1451 * -EBUSY: modem not ready for our suspend
1452 * -EINVAL: invalid sleep mode
1453 * -EIO: could not ramp Apps processor clock
1454 * -ETIMEDOUT: timed out waiting for modem's handshake
1455 * 0: success
1456 */
1457static int msm_pm_enter(suspend_state_t state)
1458{
1459 bool allow[MSM_PM_SLEEP_MODE_NR];
1460 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301461 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001462 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001463 int64_t period = 0;
1464 int64_t time = 0;
1465
Murali Nalajala41786ab2012-03-06 10:47:32 +05301466 /* Must executed by CORE0 */
1467 if (smp_processor_id()) {
1468 __WARN();
1469 goto suspend_exit;
1470 }
1471
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301472 time = msm_pm_timer_enter_suspend(&period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001473
1474 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1475 "%s(): sleep limit %u\n", __func__, sleep_limit);
1476
1477 for (i = 0; i < ARRAY_SIZE(allow); i++)
1478 allow[i] = true;
1479
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001480 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301481 struct msm_pm_platform_data *mode;
1482 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001483 if (!mode->suspend_supported || !mode->suspend_enabled)
1484 allow[i] = false;
1485 }
1486
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001487 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1488 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001489 enum msm_pm_time_stats_id id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001490
1491 clock_debug_print_enabled();
1492
1493#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1494 if (msm_pm_sleep_time_override > 0) {
1495 int64_t ns;
1496 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1497 msm_pm_set_max_sleep_time(ns);
1498 msm_pm_sleep_time_override = 0;
1499 }
1500#endif
1501 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1502 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1503
1504#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1505 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1506#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1507 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1508#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1509 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1510 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1511#endif
1512
1513 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1514 udelay(500);
1515
1516 ret = msm_pm_power_collapse(
1517 false, msm_pm_max_sleep_time, sleep_limit);
1518
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001519 if (ret)
1520 id = MSM_PM_STAT_FAILED_SUSPEND;
1521 else {
1522 id = MSM_PM_STAT_SUSPEND;
1523 msm_pm_sleep_limit = sleep_limit;
1524 }
1525
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301526 time = msm_pm_timer_exit_suspend(time, period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001527 msm_pm_add_stat(id, time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001528 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301529 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001530 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1531 ret = msm_pm_swfi(true);
1532 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301533 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001534 udelay(1);
1535 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1536 msm_pm_swfi(false);
1537 }
1538
Murali Nalajala41786ab2012-03-06 10:47:32 +05301539suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001540 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1541 "%s(): return %d\n", __func__, ret);
1542
1543 return ret;
1544}
1545
1546static struct platform_suspend_ops msm_pm_ops = {
1547 .enter = msm_pm_enter,
1548 .valid = suspend_valid_only_mem,
1549};
1550
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301551/* Hotplug the "non boot" CPU's and put
1552 * the cores into low power mode
1553 */
1554void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1555{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301556 bool allow[MSM_PM_SLEEP_MODE_NR];
1557 int i;
1558
1559 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1560 struct msm_pm_platform_data *mode;
1561
1562 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1563 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1564 }
1565
1566 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1567 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1568
1569 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1570 msm_pm_power_collapse_standalone(false);
1571 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1572 msm_pm_swfi(false);
1573 } else {
1574 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1575 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1576 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301577}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001578
1579/******************************************************************************
1580 * Restart Definitions
1581 *****************************************************************************/
1582
1583static uint32_t restart_reason = 0x776655AA;
1584
1585static void msm_pm_power_off(void)
1586{
1587 msm_rpcrouter_close();
1588 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1589 for (;;)
1590 ;
1591}
1592
1593static void msm_pm_restart(char str, const char *cmd)
1594{
1595 msm_rpcrouter_close();
1596 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1597
1598 for (;;)
1599 ;
1600}
1601
1602static int msm_reboot_call
1603 (struct notifier_block *this, unsigned long code, void *_cmd)
1604{
1605 if ((code == SYS_RESTART) && _cmd) {
1606 char *cmd = _cmd;
1607 if (!strcmp(cmd, "bootloader")) {
1608 restart_reason = 0x77665500;
1609 } else if (!strcmp(cmd, "recovery")) {
1610 restart_reason = 0x77665502;
1611 } else if (!strcmp(cmd, "eraseflash")) {
1612 restart_reason = 0x776655EF;
1613 } else if (!strncmp(cmd, "oem-", 4)) {
1614 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1615 restart_reason = 0x6f656d00 | code;
1616 } else {
1617 restart_reason = 0x77665501;
1618 }
1619 }
1620 return NOTIFY_DONE;
1621}
1622
1623static struct notifier_block msm_reboot_notifier = {
1624 .notifier_call = msm_reboot_call,
1625};
1626
1627
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001628/*
1629 * Initialize the power management subsystem.
1630 *
1631 * Return value:
1632 * -ENODEV: initialization failed
1633 * 0: success
1634 */
1635static int __init msm_pm_init(void)
1636{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001637 int ret;
Murali Nalajala93f29992012-03-21 15:59:27 +05301638 int val;
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301639 enum msm_pm_time_stats_id enable_stats[] = {
1640 MSM_PM_STAT_REQUESTED_IDLE,
1641 MSM_PM_STAT_IDLE_SPIN,
1642 MSM_PM_STAT_IDLE_WFI,
1643 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
1644 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
1645 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
1646 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
1647 MSM_PM_STAT_SUSPEND,
1648 MSM_PM_STAT_FAILED_SUSPEND,
1649 MSM_PM_STAT_NOT_IDLE,
1650 };
1651
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001652#ifdef CONFIG_CPU_V7
1653 pgd_t *pc_pgd;
1654 pmd_t *pmd;
1655 unsigned long pmdval;
1656
1657 /* Page table for cores to come back up safely. */
1658 pc_pgd = pgd_alloc(&init_mm);
1659 if (!pc_pgd)
1660 return -ENOMEM;
1661 pmd = pmd_offset(pc_pgd +
1662 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1663 virt_to_phys(msm_pm_collapse_exit));
1664 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1665 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1666 pmd[0] = __pmd(pmdval);
1667 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1668
Steve Mucklefcece052012-02-18 20:09:58 -08001669 msm_saved_state_phys =
1670 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1671 num_possible_cpus(), 4);
1672 if (!msm_saved_state_phys)
1673 return -ENOMEM;
1674 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1675 CPU_SAVED_STATE_SIZE *
1676 num_possible_cpus());
1677 if (!msm_saved_state)
1678 return -ENOMEM;
1679
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001680 /* It is remotely possible that the code in msm_pm_collapse_exit()
1681 * which turns on the MMU with this mapping is in the
1682 * next even-numbered megabyte beyond the
1683 * start of msm_pm_collapse_exit().
1684 * Map this megabyte in as well.
1685 */
1686 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1687 flush_pmd_entry(pmd);
1688 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001689 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1690 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001691#endif
1692
1693 pm_power_off = msm_pm_power_off;
1694 arm_pm_restart = msm_pm_restart;
1695 register_reboot_notifier(&msm_reboot_notifier);
1696
1697 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1698 sizeof(*msm_pm_smem_data));
1699 if (msm_pm_smem_data == NULL) {
1700 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1701 return -ENODEV;
1702 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001703
1704 ret = msm_timer_init_time_sync(msm_pm_timeout);
1705 if (ret)
1706 return ret;
1707
1708 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1709 if (ret) {
1710 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1711 __func__, ret);
1712 return ret;
1713 }
1714
Murali Nalajala93f29992012-03-21 15:59:27 +05301715 if (cpu_is_msm8625()) {
1716 target_type = TARGET_IS_8625;
1717 clean_caches((unsigned long)&target_type, sizeof(target_type),
1718 virt_to_phys(&target_type));
1719
Anji jonnalae644f8e2012-05-09 19:52:18 +05301720 /*
1721 * Configure the MPA5_GDFS_CNT_VAL register for
1722 * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the
1723 * DBGNOPOWERDN for each cpu.
1724 * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for
1725 * GDFS control.
Murali Nalajala93f29992012-03-21 15:59:27 +05301726 */
Anji jonnalae644f8e2012-05-09 19:52:18 +05301727 val = 0x00030002;
Murali Nalajala93f29992012-03-21 15:59:27 +05301728 __raw_writel(val, (MSM_CFG_CTL_BASE + 0x38));
Murali Nalajala73c13332012-05-15 11:30:59 +05301729
1730 l2x0_base_addr = MSM_L2CC_BASE;
Murali Nalajala93f29992012-03-21 15:59:27 +05301731 }
1732
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001733#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1734 /* The wakeup_reason field is overloaded during initialization time
1735 to signal Modem that Apps will control the low power modes of
1736 the memory.
1737 */
1738 msm_pm_smem_data->wakeup_reason = 1;
1739 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1740#endif
1741
1742 BUG_ON(msm_pm_modes == NULL);
1743
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001744 suspend_set_ops(&msm_pm_ops);
1745
1746 msm_pm_mode_sysfs_add();
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301747 msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
Murali Nalajala558c0ce2012-03-29 19:42:08 +05301748
1749 atomic_set(&msm_pm_init_done, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001750 return 0;
1751}
1752
1753late_initcall_sync(msm_pm_init);