blob: bac6ef8e4bd4115bf35b7a96a477386d8960cd99 [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>
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
Steve Mucklef132c6c2012-06-06 18:30:57 -070040#include <asm/system_misc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041#ifdef CONFIG_CACHE_L2X0
42#include <asm/hardware/cache-l2x0.h>
43#endif
44#ifdef CONFIG_VFP
45#include <asm/vfp.h>
46#endif
47
48#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
49#include <mach/msm_migrate_pages.h>
50#endif
Murali Nalajala41786ab2012-03-06 10:47:32 +053051#include <mach/socinfo.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070052#include <mach/proc_comm.h>
Anji jonnala1f2377c2012-03-27 14:35:55 +053053#include <asm/smp_scu.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054
55#include "smd_private.h"
56#include "smd_rpcrouter.h"
57#include "acpuclock.h"
58#include "clock.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#include "idle.h"
60#include "irq.h"
61#include "gpio.h"
62#include "timer.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080063#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064#include "spm.h"
65#include "sirc.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060066#include "pm-boot.h"
Murali Nalajala19d33a22012-05-18 14:11:19 +053067#include "devices-msm7x2xa.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068
69/******************************************************************************
70 * Debug Definitions
71 *****************************************************************************/
72
73enum {
Murali Nalajalaa7efba12012-02-23 18:13:52 +053074 MSM_PM_DEBUG_SUSPEND = BIT(0),
75 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
76 MSM_PM_DEBUG_STATE = BIT(2),
77 MSM_PM_DEBUG_CLOCK = BIT(3),
78 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
79 MSM_PM_DEBUG_SMSM_STATE = BIT(5),
80 MSM_PM_DEBUG_IDLE = BIT(6),
81 MSM_PM_DEBUG_HOTPLUG = BIT(7),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082};
83
84static int msm_pm_debug_mask;
Taniya Dase30a6b22012-03-20 11:37:45 +053085int power_collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086module_param_named(
87 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
88);
89
90#define MSM_PM_DPRINTK(mask, level, message, ...) \
91 do { \
92 if ((mask) & msm_pm_debug_mask) \
93 printk(level message, ## __VA_ARGS__); \
94 } while (0)
95
96#define MSM_PM_DEBUG_PRINT_STATE(tag) \
97 do { \
98 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
99 KERN_INFO, "%s: " \
100 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
101 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
102 "SMSM_APPS_DEM %x\n", \
103 tag, \
104 __raw_readl(APPS_CLK_SLEEP_EN), \
105 __raw_readl(APPS_PWRDOWN), \
106 smsm_get_state(SMSM_POWER_MASTER_DEM), \
107 smsm_get_state(SMSM_MODEM_STATE), \
108 smsm_get_state(SMSM_APPS_DEM)); \
109 } while (0)
110
111#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
112 do { \
113 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
114 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
115 msm_pm_smem_data->resources_used, \
116 msm_pm_smem_data->irq_mask, \
117 msm_pm_smem_data->wakeup_reason, \
118 msm_pm_smem_data->pending_irqs); \
119 } while (0)
120
121
122/******************************************************************************
123 * Sleep Modes and Parameters
124 *****************************************************************************/
125
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
127module_param_named(
128 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
129 int, S_IRUGO | S_IWUSR | S_IWGRP
130);
131
132enum {
133 MSM_PM_MODE_ATTR_SUSPEND,
134 MSM_PM_MODE_ATTR_IDLE,
135 MSM_PM_MODE_ATTR_LATENCY,
136 MSM_PM_MODE_ATTR_RESIDENCY,
137 MSM_PM_MODE_ATTR_NR,
138};
139
140static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
141 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
142 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
143 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
144 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
145};
146
147static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
148 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
149 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
151 "ramp_down_and_wfi",
152 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
153 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
154 "power_collapse_no_xo_shutdown",
155 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
156 "standalone_power_collapse",
157};
158
159static struct msm_pm_platform_data *msm_pm_modes;
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530160static struct msm_pm_irq_calls *msm_pm_irq_extns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530162struct msm_pm_kobj_attribute {
163 unsigned int cpu;
164 struct kobj_attribute ka;
165};
166
167#define GET_CPU_OF_ATTR(attr) \
168 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
169
170struct msm_pm_sysfs_sleep_mode {
171 struct kobject *kobj;
172 struct attribute_group attr_group;
173 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
174 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
175};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176
177/*
178 * Write out the attribute.
179 */
180static ssize_t msm_pm_mode_attr_show(
181 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
182{
183 int ret = -EINVAL;
184 int i;
185
186 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
187 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530188 unsigned int cpu;
189 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190
191 if (msm_pm_sleep_mode_labels[i] == NULL)
192 continue;
193
194 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
195 continue;
196
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530197 cpu = GET_CPU_OF_ATTR(attr);
198 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
199
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 if (!strcmp(attr->attr.name,
201 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530202 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203 kp.arg = &arg;
204 ret = param_get_ulong(buf, &kp);
205 } else if (!strcmp(attr->attr.name,
206 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530207 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 kp.arg = &arg;
209 ret = param_get_ulong(buf, &kp);
210 } else if (!strcmp(attr->attr.name,
211 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530212 u32 arg = mode->latency;
213 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 ret = param_get_ulong(buf, &kp);
215 } else if (!strcmp(attr->attr.name,
216 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530217 u32 arg = mode->residency;
218 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 ret = param_get_ulong(buf, &kp);
220 }
221
222 break;
223 }
224
225 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530226 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 ret++;
228 }
229
230 return ret;
231}
232
233/*
234 * Read in the new attribute value.
235 */
236static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
237 struct kobj_attribute *attr, const char *buf, size_t count)
238{
239 int ret = -EINVAL;
240 int i;
241
242 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
243 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530244 unsigned int cpu;
245 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246
247 if (msm_pm_sleep_mode_labels[i] == NULL)
248 continue;
249
250 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
251 continue;
252
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530253 cpu = GET_CPU_OF_ATTR(attr);
254 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
255
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 if (!strcmp(attr->attr.name,
257 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530258 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259 ret = param_set_byte(buf, &kp);
260 } else if (!strcmp(attr->attr.name,
261 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530262 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 ret = param_set_byte(buf, &kp);
264 } else if (!strcmp(attr->attr.name,
265 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530266 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 ret = param_set_ulong(buf, &kp);
268 } else if (!strcmp(attr->attr.name,
269 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530270 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271 ret = param_set_ulong(buf, &kp);
272 }
273
274 break;
275 }
276
277 return ret ? ret : count;
278}
279
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530280 /* Add sysfs entries for one cpu. */
281static int __init msm_pm_mode_sysfs_add_cpu(
282 unsigned int cpu, struct kobject *modes_kobj)
283{
284 char cpu_name[8];
285 struct kobject *cpu_kobj;
286 struct msm_pm_sysfs_sleep_mode *mode = NULL;
287 int i, j, k;
288 int ret;
289
290 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
291 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
292 if (!cpu_kobj) {
293 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
294 ret = -ENOMEM;
295 goto mode_sysfs_add_cpu_exit;
296 }
297
298 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
299 int idx = MSM_PM_MODE(cpu, i);
300
301 if ((!msm_pm_modes[idx].suspend_supported) &&
302 (!msm_pm_modes[idx].idle_supported))
303 continue;
304
305 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
306 if (!mode) {
307 pr_err("%s: cannot allocate memory for attributes\n",
308 __func__);
309 ret = -ENOMEM;
310 goto mode_sysfs_add_cpu_exit;
311 }
312
313 mode->kobj = kobject_create_and_add(
314 msm_pm_sleep_mode_labels[i], cpu_kobj);
315 if (!mode->kobj) {
316 pr_err("%s: cannot create kobject\n", __func__);
317 ret = -ENOMEM;
318 goto mode_sysfs_add_cpu_exit;
319 }
320
321 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
322 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
323 !msm_pm_modes[idx].idle_supported)
324 continue;
325 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
326 !msm_pm_modes[idx].suspend_supported)
327 continue;
328 mode->kas[j].cpu = cpu;
329 mode->kas[j].ka.attr.mode = 0644;
330 mode->kas[j].ka.show = msm_pm_mode_attr_show;
331 mode->kas[j].ka.store = msm_pm_mode_attr_store;
332 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
333 mode->attrs[j] = &mode->kas[j].ka.attr;
334 j++;
335 }
336 mode->attrs[j] = NULL;
337
338 mode->attr_group.attrs = mode->attrs;
339 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
340 if (ret) {
341 printk(KERN_ERR
342 "%s: cannot create kobject attribute group\n",
343 __func__);
344 goto mode_sysfs_add_cpu_exit;
345 }
346 }
347
348 ret = 0;
349
350mode_sysfs_add_cpu_exit:
351 if (ret) {
352 if (mode && mode->kobj)
353 kobject_del(mode->kobj);
354 kfree(mode);
355 }
356
357 return ret;
358}
359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360/*
361 * Add sysfs entries for the sleep modes.
362 */
363static int __init msm_pm_mode_sysfs_add(void)
364{
365 struct kobject *module_kobj = NULL;
366 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530367 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 int ret;
369
370 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
371 if (!module_kobj) {
372 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
373 __func__, KBUILD_MODNAME);
374 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530375 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376 }
377
378 modes_kobj = kobject_create_and_add("modes", module_kobj);
379 if (!modes_kobj) {
380 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
381 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530382 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 }
384
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530385 for_each_possible_cpu(cpu) {
386 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
387 if (ret)
388 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 }
390
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530391 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530393mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 return ret;
395}
396
397void __init msm_pm_set_platform_data(
398 struct msm_pm_platform_data *data, int count)
399{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530400 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 msm_pm_modes = data;
402}
403
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530404void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls)
405{
406 /* sanity check */
407 BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL ||
408 irq_calls->idle_sleep_allowed == NULL ||
409 irq_calls->enter_sleep1 == NULL ||
410 irq_calls->enter_sleep2 == NULL ||
411 irq_calls->exit_sleep1 == NULL ||
412 irq_calls->exit_sleep2 == NULL ||
413 irq_calls->exit_sleep3 == NULL);
414
415 msm_pm_irq_extns = irq_calls;
416}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417
418/******************************************************************************
419 * Sleep Limitations
420 *****************************************************************************/
421enum {
422 SLEEP_LIMIT_NONE = 0,
423 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
424 SLEEP_LIMIT_MASK = 0x03,
425};
426
Praveen Chidambaram3895bde2012-05-14 19:42:40 +0530427static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
429enum {
430 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
431 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
432};
433#endif
434
435
436/******************************************************************************
437 * Configure Hardware for Power Down/Up
438 *****************************************************************************/
439
440#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530441#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
442#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530444#define APPS_STANDBY_CTL NULL
445#else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
447#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
448#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530449#define APPS_SECOP NULL
450#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451
452/*
453 * Configure hardware registers in preparation for Apps power down.
454 */
455static void msm_pm_config_hw_before_power_down(void)
456{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530457 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530458 __raw_writel(4, APPS_SECOP);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530459 } else if (cpu_is_msm7x27()) {
460 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530461 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530462 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
463 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530464 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530465 } else if (cpu_is_qsd8x50()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530466 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
467 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530468 __raw_writel(0, APPS_STANDBY_CTL);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530469 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530470 mb();
471 __raw_writel(1, APPS_PWRDOWN);
472 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473}
474
475/*
Anji jonnala1f2377c2012-03-27 14:35:55 +0530476 * Program the top csr from core0 context to put the
477 * core1 into GDFS, as core1 is not running yet.
478 */
479static void configure_top_csr(void)
480{
481 void __iomem *base_ptr;
482 unsigned int value = 0;
483
Murali Nalajala19d33a22012-05-18 14:11:19 +0530484 base_ptr = core1_reset_base();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530485 if (!base_ptr)
486 return;
487
488 /* bring the core1 out of reset */
489 __raw_writel(0x3, base_ptr);
490 mb();
491 /*
492 * override DBGNOPOWERDN and program the GDFS
493 * count val
494 */
495
496 __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38));
497 mb();
498
499 /* Initialize the SPM0 and SPM1 registers */
500 msm_spm_reinit();
501
502 /* enable TCSR for core1 */
503 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
504 value |= BIT(22);
505 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
506 mb();
507
508 /* set reset bit for SPM1 */
509 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
510 value |= BIT(20);
511 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
512 mb();
513
514 /* set CLK_OFF bit */
515 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
516 value |= BIT(18);
517 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
518 mb();
519
520 /* set clamps bit */
521 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
522 value |= BIT(21);
523 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
524 mb();
525
526 /* set power_up bit */
527 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
528 value |= BIT(19);
529 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
530 mb();
531
532 /* Disable TSCR for core0 */
533 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
534 value &= ~BIT(22);
535 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
536 mb();
537 __raw_writel(0x0, base_ptr);
538 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530539}
540
541/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 * Clear hardware registers after Apps powers up.
543 */
544static void msm_pm_config_hw_after_power_up(void)
545{
Anji jonnala1f2377c2012-03-27 14:35:55 +0530546
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530547 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
548 __raw_writel(0, APPS_SECOP);
549 mb();
550 __raw_writel(0, APPS_PWRDOWN);
551 mb();
552 msm_spm_reinit();
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530553 } else if (cpu_is_msm8625()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530554 __raw_writel(0, APPS_PWRDOWN);
555 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530556
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530557 if (power_collapsed) {
Anji jonnala1f2377c2012-03-27 14:35:55 +0530558 /*
559 * enable the SCU while coming out of power
560 * collapse.
561 */
562 scu_enable(MSM_SCU_BASE);
563 /*
564 * Program the top csr to put the core1 into GDFS.
565 */
566 configure_top_csr();
567 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530568 } else {
569 __raw_writel(0, APPS_PWRDOWN);
570 mb();
571 __raw_writel(0, APPS_CLK_SLEEP_EN);
572 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530573 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574}
575
576/*
577 * Configure hardware registers in preparation for SWFI.
578 */
579static void msm_pm_config_hw_before_swfi(void)
580{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530581 if (cpu_is_qsd8x50()) {
582 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
583 mb();
584 } else if (cpu_is_msm7x27()) {
585 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
586 mb();
587 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530588 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
589 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530590 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
591 mb();
592 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593}
594
595/*
596 * Respond to timing out waiting for Modem
597 *
598 * NOTE: The function never returns.
599 */
600static void msm_pm_timeout(void)
601{
602#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
603 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
604 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
605#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
606 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
607 msm_proc_comm_reset_modem_now();
608#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
609 printk(KERN_EMERG "%s(): halting\n", __func__);
610#endif
611 for (;;)
612 ;
613}
614
615
616/******************************************************************************
617 * State Polling Definitions
618 *****************************************************************************/
619
620struct msm_pm_polled_group {
621 uint32_t group_id;
622
623 uint32_t bits_all_set;
624 uint32_t bits_all_clear;
625 uint32_t bits_any_set;
626 uint32_t bits_any_clear;
627
628 uint32_t value_read;
629};
630
631/*
632 * Return true if all bits indicated by flag are set in source.
633 */
634static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
635{
636 return (source & flag) == flag;
637}
638
639/*
640 * Return true if any bit indicated by flag are set in source.
641 */
642static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
643{
644 return !flag || (source & flag);
645}
646
647/*
648 * Return true if all bits indicated by flag are cleared in source.
649 */
650static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
651{
652 return (~source & flag) == flag;
653}
654
655/*
656 * Return true if any bit indicated by flag are cleared in source.
657 */
658static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
659{
660 return !flag || (~source & flag);
661}
662
663/*
664 * Poll the shared memory states as indicated by the poll groups.
665 *
666 * nr_grps: number of groups in the array
667 * grps: array of groups
668 *
669 * The function returns when conditions specified by any of the poll
670 * groups become true. The conditions specified by a poll group are
671 * deemed true when 1) at least one bit from bits_any_set is set OR one
672 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
673 * are set; and 3) all bits in bits_all_clear are cleared.
674 *
675 * Return value:
676 * >=0: index of the poll group whose conditions have become true
677 * -ETIMEDOUT: timed out
678 */
679static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
680{
681 int i, k;
682
683 for (i = 0; i < 50000; i++) {
684 for (k = 0; k < nr_grps; k++) {
685 bool all_set, all_clear;
686 bool any_set, any_clear;
687
688 grps[k].value_read = smsm_get_state(grps[k].group_id);
689
690 all_set = msm_pm_all_set(grps[k].value_read,
691 grps[k].bits_all_set);
692 all_clear = msm_pm_all_clear(grps[k].value_read,
693 grps[k].bits_all_clear);
694 any_set = msm_pm_any_set(grps[k].value_read,
695 grps[k].bits_any_set);
696 any_clear = msm_pm_any_clear(grps[k].value_read,
697 grps[k].bits_any_clear);
698
699 if (all_set && all_clear && (any_set || any_clear))
700 return k;
701 }
702 udelay(50);
703 }
704
705 printk(KERN_ERR "%s failed:\n", __func__);
706 for (k = 0; k < nr_grps; k++)
707 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
708 grps[k].bits_all_set, grps[k].bits_all_clear,
709 grps[k].bits_any_set, grps[k].bits_any_clear,
710 grps[k].value_read);
711
712 return -ETIMEDOUT;
713}
714
715
716/******************************************************************************
717 * Suspend Max Sleep Time
718 *****************************************************************************/
719
720#define SCLK_HZ (32768)
721#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
722
723#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
724static int msm_pm_sleep_time_override;
725module_param_named(sleep_time_override,
726 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
727#endif
728
729static uint32_t msm_pm_max_sleep_time;
730
731/*
732 * Convert time from nanoseconds to slow clock ticks, then cap it to the
733 * specified limit
734 */
735static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
736{
737 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
738 return (time_ns > limit) ? limit : time_ns;
739}
740
741/*
742 * Set the sleep time for suspend. 0 means infinite sleep time.
743 */
744void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
745{
746 unsigned long flags;
747
748 local_irq_save(flags);
749 if (max_sleep_time_ns == 0) {
750 msm_pm_max_sleep_time = 0;
751 } else {
752 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
753 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
754
755 if (msm_pm_max_sleep_time == 0)
756 msm_pm_max_sleep_time = 1;
757 }
758
759 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
760 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
761 max_sleep_time_ns, msm_pm_max_sleep_time);
762 local_irq_restore(flags);
763}
764EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
765
766
767/******************************************************************************
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768 * Shared Memory Bits
769 *****************************************************************************/
770
771#define DEM_MASTER_BITS_PER_CPU 6
772
773/* Power Master State Bits - Per CPU */
774#define DEM_MASTER_SMSM_RUN \
775 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
776#define DEM_MASTER_SMSM_RSA \
777 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
778#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
779 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
780#define DEM_MASTER_SMSM_SLEEP_EXIT \
781 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
782#define DEM_MASTER_SMSM_READY \
783 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
784#define DEM_MASTER_SMSM_SLEEP \
785 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
786
787/* Power Slave State Bits */
788#define DEM_SLAVE_SMSM_RUN (0x0001)
789#define DEM_SLAVE_SMSM_PWRC (0x0002)
790#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
791#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
792#define DEM_SLAVE_SMSM_WFPI (0x0010)
793#define DEM_SLAVE_SMSM_SLEEP (0x0020)
794#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
795#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
796#define DEM_SLAVE_SMSM_RESET (0x0100)
797#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
798
799
800/******************************************************************************
801 * Shared Memory Data
802 *****************************************************************************/
803
804#define DEM_MAX_PORT_NAME_LEN (20)
805
806struct msm_pm_smem_t {
807 uint32_t sleep_time;
808 uint32_t irq_mask;
809 uint32_t resources_used;
810 uint32_t reserved1;
811
812 uint32_t wakeup_reason;
813 uint32_t pending_irqs;
814 uint32_t rpc_prog;
815 uint32_t rpc_proc;
816 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
817 uint32_t reserved2;
818};
819
820
821/******************************************************************************
822 *
823 *****************************************************************************/
824static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700825static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
826
827static int msm_pm_modem_busy(void)
828{
829 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
830 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
831 KERN_INFO, "%s(): master not ready\n", __func__);
832 return -EBUSY;
833 }
834
835 return 0;
836}
837
838/*
839 * Power collapse the Apps processor. This function executes the handshake
840 * protocol with Modem.
841 *
842 * Return value:
843 * -EAGAIN: modem reset occurred or early exit from power collapse
844 * -EBUSY: modem not ready for our power collapse -- no power loss
845 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
846 * 0: success
847 */
848static int msm_pm_power_collapse
849 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
850{
851 struct msm_pm_polled_group state_grps[2];
852 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700853 int collapsed = 0;
854 int ret;
Murali Nalajala07b04022012-04-10 16:00:49 +0530855 int val;
856 int modem_early_exit = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857
858 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
859 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
860 (int)from_idle, sleep_delay, sleep_limit);
861
862 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
863 MSM_PM_DPRINTK(
864 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
865 KERN_INFO, "%s(): master not ready\n", __func__);
866 ret = -EBUSY;
867 goto power_collapse_bail;
868 }
869
870 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
871
Murali Nalajala41786ab2012-03-06 10:47:32 +0530872 if (cpu_is_msm8625()) {
873 /* Program the SPM */
874 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
875 false);
876 WARN_ON(ret);
877 }
878
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530879 msm_pm_irq_extns->enter_sleep1(true, from_idle,
880 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700881 msm_sirc_enter_sleep();
882 msm_gpio_enter_sleep(from_idle);
883
884 msm_pm_smem_data->sleep_time = sleep_delay;
885 msm_pm_smem_data->resources_used = sleep_limit;
886
887 /* Enter PWRC/PWRC_SUSPEND */
888
889 if (from_idle)
890 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
891 DEM_SLAVE_SMSM_PWRC);
892 else
893 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
894 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
895
896 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
897 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
898
899 memset(state_grps, 0, sizeof(state_grps));
900 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
901 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
902 state_grps[1].group_id = SMSM_MODEM_STATE;
903 state_grps[1].bits_all_set = SMSM_RESET;
904
905 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
906
907 if (ret < 0) {
908 printk(KERN_EMERG "%s(): power collapse entry "
909 "timed out waiting for Modem's response\n", __func__);
910 msm_pm_timeout();
911 }
912
913 if (ret == 1) {
914 MSM_PM_DPRINTK(
915 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
916 KERN_INFO,
917 "%s(): msm_pm_poll_state detected Modem reset\n",
918 __func__);
919 goto power_collapse_early_exit;
920 }
921
922 /* DEM Master in RSA */
923
924 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
925
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530926 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700927 if (ret < 0) {
928 MSM_PM_DPRINTK(
929 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
930 KERN_INFO,
931 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
932 ret);
933 goto power_collapse_early_exit;
934 }
935
936 msm_pm_config_hw_before_power_down();
937 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
938
939 saved_acpuclk_rate = acpuclk_power_collapse();
940 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
941 "%s(): change clock rate (old rate = %lu)\n", __func__,
942 saved_acpuclk_rate);
943
944 if (saved_acpuclk_rate == 0) {
945 msm_pm_config_hw_after_power_up();
946 goto power_collapse_early_exit;
947 }
948
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600949 msm_pm_boot_config_before_pc(smp_processor_id(),
950 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700951
952#ifdef CONFIG_VFP
953 if (from_idle)
Steve Mucklef132c6c2012-06-06 18:30:57 -0700954 vfp_pm_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955#endif
956
957#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530958 if (!cpu_is_msm8625())
959 l2cc_suspend();
960 else
961 apps_power_collapse = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700962#endif
963
964 collapsed = msm_pm_collapse();
Murali Nalajala07b04022012-04-10 16:00:49 +0530965
966 /*
967 * TBD: Currently recognise the MODEM early exit
968 * path by reading the MPA5_GDFS_CNT_VAL register.
969 */
970 if (cpu_is_msm8625()) {
971 /*
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530972 * on system reset, default value of MPA5_GDFS_CNT_VAL
973 * is = 0x0, later modem reprogram this value to
974 * 0x00030004. Once APPS did a power collapse and
975 * coming out of it expected value of this register
976 * always be 0x00030004. Incase if APPS sees the value
977 * as 0x00030002 consider this case as a modem early
978 * exit.
Murali Nalajala07b04022012-04-10 16:00:49 +0530979 */
980 val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530981 if (val != 0x00030002)
Murali Nalajala07b04022012-04-10 16:00:49 +0530982 power_collapsed = 1;
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530983 else
984 modem_early_exit = 1;
Murali Nalajala07b04022012-04-10 16:00:49 +0530985 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700986
987#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530988 if (!cpu_is_msm8625())
989 l2cc_resume();
990 else
991 apps_power_collapse = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700992#endif
993
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600994 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995
996 if (collapsed) {
997#ifdef CONFIG_VFP
998 if (from_idle)
Steve Mucklef132c6c2012-06-06 18:30:57 -0700999 vfp_pm_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001000#endif
1001 cpu_init();
1002 local_fiq_enable();
1003 }
1004
1005 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1006 KERN_INFO,
1007 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1008
1009 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1010 "%s(): restore clock rate to %lu\n", __func__,
1011 saved_acpuclk_rate);
1012 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1013 SETRATE_PC) < 0)
1014 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1015 __func__, saved_acpuclk_rate);
1016
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301017 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018 msm_pm_smem_data->wakeup_reason,
1019 msm_pm_smem_data->pending_irqs);
1020
1021 msm_pm_config_hw_after_power_up();
1022 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1023
1024 memset(state_grps, 0, sizeof(state_grps));
1025 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1026 state_grps[0].bits_any_set =
1027 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1028 state_grps[1].group_id = SMSM_MODEM_STATE;
1029 state_grps[1].bits_all_set = SMSM_RESET;
1030
1031 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1032
1033 if (ret < 0) {
1034 printk(KERN_EMERG "%s(): power collapse exit "
1035 "timed out waiting for Modem's response\n", __func__);
1036 msm_pm_timeout();
1037 }
1038
1039 if (ret == 1) {
1040 MSM_PM_DPRINTK(
1041 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1042 KERN_INFO,
1043 "%s(): msm_pm_poll_state detected Modem reset\n",
1044 __func__);
1045 goto power_collapse_early_exit;
1046 }
1047
1048 /* Sanity check */
Murali Nalajala07b04022012-04-10 16:00:49 +05301049 if (collapsed && !modem_early_exit) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001050 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1051 } else {
1052 BUG_ON(!(state_grps[0].value_read &
1053 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1054 goto power_collapse_early_exit;
1055 }
1056
1057 /* Enter WFPI */
1058
1059 smsm_change_state(SMSM_APPS_DEM,
1060 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1061 DEM_SLAVE_SMSM_WFPI);
1062
1063 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1064
1065 memset(state_grps, 0, sizeof(state_grps));
1066 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1067 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1068 state_grps[1].group_id = SMSM_MODEM_STATE;
1069 state_grps[1].bits_all_set = SMSM_RESET;
1070
1071 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1072
1073 if (ret < 0) {
1074 printk(KERN_EMERG "%s(): power collapse WFPI "
1075 "timed out waiting for Modem's response\n", __func__);
1076 msm_pm_timeout();
1077 }
1078
1079 if (ret == 1) {
1080 MSM_PM_DPRINTK(
1081 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1082 KERN_INFO,
1083 "%s(): msm_pm_poll_state detected Modem reset\n",
1084 __func__);
1085 ret = -EAGAIN;
1086 goto power_collapse_restore_gpio_bail;
1087 }
1088
1089 /* DEM Master == RUN */
1090
1091 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1092 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1093
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301094 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001095 msm_pm_smem_data->wakeup_reason,
1096 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301097 msm_pm_irq_extns->exit_sleep3(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);
1100 msm_gpio_exit_sleep();
1101 msm_sirc_exit_sleep();
1102
1103 smsm_change_state(SMSM_APPS_DEM,
1104 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1105
1106 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1107
1108 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301109
1110 if (cpu_is_msm8625()) {
1111 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1112 false);
1113 WARN_ON(ret);
1114 }
1115
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001116 return 0;
1117
1118power_collapse_early_exit:
1119 /* Enter PWRC_EARLY_EXIT */
1120
1121 smsm_change_state(SMSM_APPS_DEM,
1122 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1123 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1124
1125 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1126
1127 memset(state_grps, 0, sizeof(state_grps));
1128 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1129 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1130 state_grps[1].group_id = SMSM_MODEM_STATE;
1131 state_grps[1].bits_all_set = SMSM_RESET;
1132
1133 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1134 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1135
1136 if (ret < 0) {
1137 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1138 "timed out waiting for Modem's response\n", __func__);
1139 msm_pm_timeout();
1140 }
1141
1142 if (ret == 1) {
1143 MSM_PM_DPRINTK(
1144 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1145 KERN_INFO,
1146 "%s(): msm_pm_poll_state detected Modem reset\n",
1147 __func__);
1148 }
1149
1150 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1151
1152 ret = -EAGAIN;
1153
1154power_collapse_restore_gpio_bail:
1155 msm_gpio_exit_sleep();
1156 msm_sirc_exit_sleep();
1157
1158 /* Enter RUN */
1159 smsm_change_state(SMSM_APPS_DEM,
1160 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1161 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1162
1163 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1164
1165 if (collapsed)
1166 smd_sleep_exit();
1167
1168power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301169 if (cpu_is_msm8625()) {
1170 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1171 false);
1172 WARN_ON(ret);
1173 }
1174
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001175 return ret;
1176}
1177
1178/*
1179 * Power collapse the Apps processor without involving Modem.
1180 *
1181 * Return value:
1182 * 0: success
1183 */
Stephen Boydb29750d2012-02-21 01:21:32 -08001184static int __ref msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001186 int collapsed = 0;
1187 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301188 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001189
1190 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1191 KERN_INFO, "%s()\n", __func__);
1192
1193 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1194 WARN_ON(ret);
1195
Murali Nalajala41786ab2012-03-06 10:47:32 +05301196 entry = (!smp_processor_id() || from_idle) ?
1197 msm_pm_collapse_exit : msm_secondary_startup;
1198
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001199 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301200 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001201
1202#ifdef CONFIG_VFP
Steve Mucklef132c6c2012-06-06 18:30:57 -07001203 vfp_pm_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001204#endif
1205
1206#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301207 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301208 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001209#endif
1210
1211 collapsed = msm_pm_collapse();
1212
1213#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301214 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301215 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001216#endif
1217
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001218 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001219
1220 if (collapsed) {
1221#ifdef CONFIG_VFP
Steve Mucklef132c6c2012-06-06 18:30:57 -07001222 vfp_pm_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001223#endif
1224 cpu_init();
1225 local_fiq_enable();
1226 }
1227
1228 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1229 KERN_INFO,
1230 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1231
1232 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1233 WARN_ON(ret);
1234
Anji jonnalac6816222012-03-31 10:55:14 +05301235 return !collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001236}
1237
1238/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001239 * Bring the Apps processor to SWFI.
1240 *
1241 * Return value:
1242 * -EIO: could not ramp Apps processor clock
1243 * 0: success
1244 */
1245static int msm_pm_swfi(bool ramp_acpu)
1246{
1247 unsigned long saved_acpuclk_rate = 0;
1248
1249 if (ramp_acpu) {
1250 saved_acpuclk_rate = acpuclk_wait_for_irq();
1251 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1252 "%s(): change clock rate (old rate = %lu)\n", __func__,
1253 saved_acpuclk_rate);
1254
1255 if (!saved_acpuclk_rate)
1256 return -EIO;
1257 }
1258
Murali Nalajala41786ab2012-03-06 10:47:32 +05301259 if (!cpu_is_msm8625())
1260 msm_pm_config_hw_before_swfi();
1261
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001262 msm_arch_idle();
1263
1264 if (ramp_acpu) {
1265 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1266 "%s(): restore clock rate to %lu\n", __func__,
1267 saved_acpuclk_rate);
1268 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1269 SETRATE_SWFI) < 0)
1270 printk(KERN_ERR
1271 "%s(): failed to restore clock rate(%lu)\n",
1272 __func__, saved_acpuclk_rate);
1273 }
1274
1275 return 0;
1276}
1277
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301278static int64_t msm_pm_timer_enter_suspend(int64_t *period)
1279{
1280 int time = 0;
1281
1282 time = msm_timer_get_sclk_time(period);
1283 if (!time)
1284 pr_err("%s: Unable to read sclk.\n", __func__);
1285 return time;
1286}
1287
1288static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period)
1289{
1290
1291 if (time != 0) {
1292 int64_t end_time = msm_timer_get_sclk_time(NULL);
1293 if (end_time != 0) {
1294 time = end_time - time;
1295 if (time < 0)
1296 time += period;
1297 } else
1298 time = 0;
1299 }
1300 return time;
1301}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001302
1303/******************************************************************************
1304 * External Idle/Suspend Functions
1305 *****************************************************************************/
1306
1307/*
1308 * Put CPU in low power mode.
1309 */
1310void arch_idle(void)
1311{
1312 bool allow[MSM_PM_SLEEP_MODE_NR];
1313 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1314
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001315 int64_t timer_expiration;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301316 int latency_qos;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001317 int ret;
1318 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301319 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001320 int64_t t1;
Murali Nalajalab86f3702012-03-30 17:54:57 +05301321 static DEFINE_PER_CPU(int64_t, t2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001322 int exit_stat;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001323
1324 if (!atomic_read(&msm_pm_init_done))
1325 return;
1326
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301327 cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001328 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301329 /* get the next timer expiration */
1330 timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001331
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001332 t1 = ktime_to_ns(ktime_get());
Murali Nalajalab86f3702012-03-30 17:54:57 +05301333 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001334 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301335 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001336
1337 for (i = 0; i < ARRAY_SIZE(allow); i++)
1338 allow[i] = true;
1339
Murali Nalajala41786ab2012-03-06 10:47:32 +05301340 if (num_online_cpus() > 1 ||
1341 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001342#ifdef CONFIG_HAS_WAKELOCK
1343 has_wake_lock(WAKE_LOCK_IDLE) ||
1344#endif
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);