blob: f4bfe237dbb09d28317ddce981787ddd2de89efd [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/pm2.c
2 *
3 * MSM Power Management Routines
4 *
5 * Copyright (C) 2007 Google, Inc.
Murali Nalajala0df9fee2012-01-12 15:26:09 +05306 * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07007 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/clk.h>
22#include <linux/delay.h>
23#include <linux/init.h>
24#include <linux/pm.h>
25#include <linux/pm_qos_params.h>
26#include <linux/proc_fs.h>
27#include <linux/suspend.h>
28#include <linux/reboot.h>
29#include <linux/uaccess.h>
30#include <linux/io.h>
Murali Nalajala8fda4492012-03-19 18:22:59 +053031#include <linux/tick.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#include <linux/memory.h>
33#ifdef CONFIG_HAS_WAKELOCK
34#include <linux/wakelock.h>
35#endif
36#include <mach/msm_iomap.h>
37#include <mach/system.h>
38#ifdef CONFIG_CPU_V7
39#include <asm/pgtable.h>
40#include <asm/pgalloc.h>
41#endif
42#ifdef CONFIG_CACHE_L2X0
43#include <asm/hardware/cache-l2x0.h>
44#endif
45#ifdef CONFIG_VFP
46#include <asm/vfp.h>
47#endif
48
49#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
50#include <mach/msm_migrate_pages.h>
51#endif
Murali Nalajala41786ab2012-03-06 10:47:32 +053052#include <mach/socinfo.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"
59#include "proc_comm.h"
60#include "idle.h"
61#include "irq.h"
62#include "gpio.h"
63#include "timer.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080064#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065#include "spm.h"
66#include "sirc.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060067#include "pm-boot.h"
Anji jonnala1f2377c2012-03-27 14:35:55 +053068#define MSM_CORE1_RESET 0xA8600590
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069
70/******************************************************************************
71 * Debug Definitions
72 *****************************************************************************/
73
74enum {
Murali Nalajalaa7efba12012-02-23 18:13:52 +053075 MSM_PM_DEBUG_SUSPEND = BIT(0),
76 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
77 MSM_PM_DEBUG_STATE = BIT(2),
78 MSM_PM_DEBUG_CLOCK = BIT(3),
79 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
80 MSM_PM_DEBUG_SMSM_STATE = BIT(5),
81 MSM_PM_DEBUG_IDLE = BIT(6),
82 MSM_PM_DEBUG_HOTPLUG = BIT(7),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083};
84
85static int msm_pm_debug_mask;
Taniya Dase30a6b22012-03-20 11:37:45 +053086int power_collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087module_param_named(
88 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
89);
90
91#define MSM_PM_DPRINTK(mask, level, message, ...) \
92 do { \
93 if ((mask) & msm_pm_debug_mask) \
94 printk(level message, ## __VA_ARGS__); \
95 } while (0)
96
97#define MSM_PM_DEBUG_PRINT_STATE(tag) \
98 do { \
99 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
100 KERN_INFO, "%s: " \
101 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
102 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
103 "SMSM_APPS_DEM %x\n", \
104 tag, \
105 __raw_readl(APPS_CLK_SLEEP_EN), \
106 __raw_readl(APPS_PWRDOWN), \
107 smsm_get_state(SMSM_POWER_MASTER_DEM), \
108 smsm_get_state(SMSM_MODEM_STATE), \
109 smsm_get_state(SMSM_APPS_DEM)); \
110 } while (0)
111
112#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
113 do { \
114 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
115 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
116 msm_pm_smem_data->resources_used, \
117 msm_pm_smem_data->irq_mask, \
118 msm_pm_smem_data->wakeup_reason, \
119 msm_pm_smem_data->pending_irqs); \
120 } while (0)
121
122
123/******************************************************************************
124 * Sleep Modes and Parameters
125 *****************************************************************************/
126
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
128module_param_named(
129 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
130 int, S_IRUGO | S_IWUSR | S_IWGRP
131);
132
133enum {
134 MSM_PM_MODE_ATTR_SUSPEND,
135 MSM_PM_MODE_ATTR_IDLE,
136 MSM_PM_MODE_ATTR_LATENCY,
137 MSM_PM_MODE_ATTR_RESIDENCY,
138 MSM_PM_MODE_ATTR_NR,
139};
140
141static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
142 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
143 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
144 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
145 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
146};
147
148static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
149 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
150 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
152 "ramp_down_and_wfi",
153 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
154 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
155 "power_collapse_no_xo_shutdown",
156 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
157 "standalone_power_collapse",
158};
159
160static struct msm_pm_platform_data *msm_pm_modes;
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530161static struct msm_pm_irq_calls *msm_pm_irq_extns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530163struct msm_pm_kobj_attribute {
164 unsigned int cpu;
165 struct kobj_attribute ka;
166};
167
168#define GET_CPU_OF_ATTR(attr) \
169 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
170
171struct msm_pm_sysfs_sleep_mode {
172 struct kobject *kobj;
173 struct attribute_group attr_group;
174 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
175 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
176};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177
178/*
179 * Write out the attribute.
180 */
181static ssize_t msm_pm_mode_attr_show(
182 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
183{
184 int ret = -EINVAL;
185 int i;
186
187 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
188 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530189 unsigned int cpu;
190 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191
192 if (msm_pm_sleep_mode_labels[i] == NULL)
193 continue;
194
195 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
196 continue;
197
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530198 cpu = GET_CPU_OF_ATTR(attr);
199 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
200
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 if (!strcmp(attr->attr.name,
202 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530203 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 kp.arg = &arg;
205 ret = param_get_ulong(buf, &kp);
206 } else if (!strcmp(attr->attr.name,
207 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530208 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 kp.arg = &arg;
210 ret = param_get_ulong(buf, &kp);
211 } else if (!strcmp(attr->attr.name,
212 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530213 u32 arg = mode->latency;
214 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 ret = param_get_ulong(buf, &kp);
216 } else if (!strcmp(attr->attr.name,
217 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530218 u32 arg = mode->residency;
219 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220 ret = param_get_ulong(buf, &kp);
221 }
222
223 break;
224 }
225
226 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530227 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228 ret++;
229 }
230
231 return ret;
232}
233
234/*
235 * Read in the new attribute value.
236 */
237static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
238 struct kobj_attribute *attr, const char *buf, size_t count)
239{
240 int ret = -EINVAL;
241 int i;
242
243 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
244 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530245 unsigned int cpu;
246 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247
248 if (msm_pm_sleep_mode_labels[i] == NULL)
249 continue;
250
251 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
252 continue;
253
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530254 cpu = GET_CPU_OF_ATTR(attr);
255 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 if (!strcmp(attr->attr.name,
258 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530259 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 ret = param_set_byte(buf, &kp);
261 } else if (!strcmp(attr->attr.name,
262 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530263 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 ret = param_set_byte(buf, &kp);
265 } else if (!strcmp(attr->attr.name,
266 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530267 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 ret = param_set_ulong(buf, &kp);
269 } else if (!strcmp(attr->attr.name,
270 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530271 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 ret = param_set_ulong(buf, &kp);
273 }
274
275 break;
276 }
277
278 return ret ? ret : count;
279}
280
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530281 /* Add sysfs entries for one cpu. */
282static int __init msm_pm_mode_sysfs_add_cpu(
283 unsigned int cpu, struct kobject *modes_kobj)
284{
285 char cpu_name[8];
286 struct kobject *cpu_kobj;
287 struct msm_pm_sysfs_sleep_mode *mode = NULL;
288 int i, j, k;
289 int ret;
290
291 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
292 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
293 if (!cpu_kobj) {
294 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
295 ret = -ENOMEM;
296 goto mode_sysfs_add_cpu_exit;
297 }
298
299 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
300 int idx = MSM_PM_MODE(cpu, i);
301
302 if ((!msm_pm_modes[idx].suspend_supported) &&
303 (!msm_pm_modes[idx].idle_supported))
304 continue;
305
306 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
307 if (!mode) {
308 pr_err("%s: cannot allocate memory for attributes\n",
309 __func__);
310 ret = -ENOMEM;
311 goto mode_sysfs_add_cpu_exit;
312 }
313
314 mode->kobj = kobject_create_and_add(
315 msm_pm_sleep_mode_labels[i], cpu_kobj);
316 if (!mode->kobj) {
317 pr_err("%s: cannot create kobject\n", __func__);
318 ret = -ENOMEM;
319 goto mode_sysfs_add_cpu_exit;
320 }
321
322 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
323 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
324 !msm_pm_modes[idx].idle_supported)
325 continue;
326 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
327 !msm_pm_modes[idx].suspend_supported)
328 continue;
329 mode->kas[j].cpu = cpu;
330 mode->kas[j].ka.attr.mode = 0644;
331 mode->kas[j].ka.show = msm_pm_mode_attr_show;
332 mode->kas[j].ka.store = msm_pm_mode_attr_store;
333 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
334 mode->attrs[j] = &mode->kas[j].ka.attr;
335 j++;
336 }
337 mode->attrs[j] = NULL;
338
339 mode->attr_group.attrs = mode->attrs;
340 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
341 if (ret) {
342 printk(KERN_ERR
343 "%s: cannot create kobject attribute group\n",
344 __func__);
345 goto mode_sysfs_add_cpu_exit;
346 }
347 }
348
349 ret = 0;
350
351mode_sysfs_add_cpu_exit:
352 if (ret) {
353 if (mode && mode->kobj)
354 kobject_del(mode->kobj);
355 kfree(mode);
356 }
357
358 return ret;
359}
360
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361/*
362 * Add sysfs entries for the sleep modes.
363 */
364static int __init msm_pm_mode_sysfs_add(void)
365{
366 struct kobject *module_kobj = NULL;
367 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530368 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 int ret;
370
371 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
372 if (!module_kobj) {
373 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
374 __func__, KBUILD_MODNAME);
375 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530376 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 }
378
379 modes_kobj = kobject_create_and_add("modes", module_kobj);
380 if (!modes_kobj) {
381 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
382 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530383 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 }
385
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530386 for_each_possible_cpu(cpu) {
387 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
388 if (ret)
389 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 }
391
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530392 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530394mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 return ret;
396}
397
398void __init msm_pm_set_platform_data(
399 struct msm_pm_platform_data *data, int count)
400{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530401 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402 msm_pm_modes = data;
403}
404
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530405void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls)
406{
407 /* sanity check */
408 BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL ||
409 irq_calls->idle_sleep_allowed == NULL ||
410 irq_calls->enter_sleep1 == NULL ||
411 irq_calls->enter_sleep2 == NULL ||
412 irq_calls->exit_sleep1 == NULL ||
413 irq_calls->exit_sleep2 == NULL ||
414 irq_calls->exit_sleep3 == NULL);
415
416 msm_pm_irq_extns = irq_calls;
417}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418
419/******************************************************************************
420 * Sleep Limitations
421 *****************************************************************************/
422enum {
423 SLEEP_LIMIT_NONE = 0,
424 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
425 SLEEP_LIMIT_MASK = 0x03,
426};
427
428#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()) {
458 __raw_writel(1, APPS_PWRDOWN);
459 mb();
460 __raw_writel(4, APPS_SECOP);
461 mb();
462 } else if (cpu_is_msm7x27()) {
463 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
464 mb();
465 __raw_writel(1, APPS_PWRDOWN);
466 mb();
467 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530468 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
469 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530470 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
471 mb();
472 __raw_writel(1, APPS_PWRDOWN);
473 mb();
474 } else {
475 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
476 mb();
477 __raw_writel(1, APPS_PWRDOWN);
478 mb();
479 __raw_writel(0, APPS_STANDBY_CTL);
480 mb();
481 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482}
483
484/*
Anji jonnala1f2377c2012-03-27 14:35:55 +0530485 * Program the top csr from core0 context to put the
486 * core1 into GDFS, as core1 is not running yet.
487 */
488static void configure_top_csr(void)
489{
490 void __iomem *base_ptr;
491 unsigned int value = 0;
492
493 base_ptr = ioremap_nocache(MSM_CORE1_RESET, SZ_4);
494 if (!base_ptr)
495 return;
496
497 /* bring the core1 out of reset */
498 __raw_writel(0x3, base_ptr);
499 mb();
500 /*
501 * override DBGNOPOWERDN and program the GDFS
502 * count val
503 */
504
505 __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38));
506 mb();
507
508 /* Initialize the SPM0 and SPM1 registers */
509 msm_spm_reinit();
510
511 /* enable TCSR for core1 */
512 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
513 value |= BIT(22);
514 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
515 mb();
516
517 /* set reset bit for SPM1 */
518 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
519 value |= BIT(20);
520 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
521 mb();
522
523 /* set CLK_OFF bit */
524 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
525 value |= BIT(18);
526 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
527 mb();
528
529 /* set clamps bit */
530 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
531 value |= BIT(21);
532 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
533 mb();
534
535 /* set power_up bit */
536 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
537 value |= BIT(19);
538 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
539 mb();
540
541 /* Disable TSCR for core0 */
542 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
543 value &= ~BIT(22);
544 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
545 mb();
546 __raw_writel(0x0, base_ptr);
547 mb();
548 iounmap(base_ptr);
549}
550
551/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552 * Clear hardware registers after Apps powers up.
553 */
554static void msm_pm_config_hw_after_power_up(void)
555{
Anji jonnala1f2377c2012-03-27 14:35:55 +0530556
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530557 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
558 __raw_writel(0, APPS_SECOP);
559 mb();
560 __raw_writel(0, APPS_PWRDOWN);
561 mb();
562 msm_spm_reinit();
563 } else {
564 __raw_writel(0, APPS_PWRDOWN);
565 mb();
566 __raw_writel(0, APPS_CLK_SLEEP_EN);
567 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530568
Murali Nalajala07b04022012-04-10 16:00:49 +0530569 if (cpu_is_msm8625() && power_collapsed) {
Anji jonnala1f2377c2012-03-27 14:35:55 +0530570 /*
571 * enable the SCU while coming out of power
572 * collapse.
573 */
574 scu_enable(MSM_SCU_BASE);
575 /*
576 * Program the top csr to put the core1 into GDFS.
577 */
578 configure_top_csr();
579 }
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530580 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700581}
582
583/*
584 * Configure hardware registers in preparation for SWFI.
585 */
586static void msm_pm_config_hw_before_swfi(void)
587{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530588 if (cpu_is_qsd8x50()) {
589 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
590 mb();
591 } else if (cpu_is_msm7x27()) {
592 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
593 mb();
594 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530595 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
596 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530597 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
598 mb();
599 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600}
601
602/*
603 * Respond to timing out waiting for Modem
604 *
605 * NOTE: The function never returns.
606 */
607static void msm_pm_timeout(void)
608{
609#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
610 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
611 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
612#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
613 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
614 msm_proc_comm_reset_modem_now();
615#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
616 printk(KERN_EMERG "%s(): halting\n", __func__);
617#endif
618 for (;;)
619 ;
620}
621
622
623/******************************************************************************
624 * State Polling Definitions
625 *****************************************************************************/
626
627struct msm_pm_polled_group {
628 uint32_t group_id;
629
630 uint32_t bits_all_set;
631 uint32_t bits_all_clear;
632 uint32_t bits_any_set;
633 uint32_t bits_any_clear;
634
635 uint32_t value_read;
636};
637
638/*
639 * Return true if all bits indicated by flag are set in source.
640 */
641static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
642{
643 return (source & flag) == flag;
644}
645
646/*
647 * Return true if any bit indicated by flag are set in source.
648 */
649static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
650{
651 return !flag || (source & flag);
652}
653
654/*
655 * Return true if all bits indicated by flag are cleared in source.
656 */
657static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
658{
659 return (~source & flag) == flag;
660}
661
662/*
663 * Return true if any bit indicated by flag are cleared in source.
664 */
665static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
666{
667 return !flag || (~source & flag);
668}
669
670/*
671 * Poll the shared memory states as indicated by the poll groups.
672 *
673 * nr_grps: number of groups in the array
674 * grps: array of groups
675 *
676 * The function returns when conditions specified by any of the poll
677 * groups become true. The conditions specified by a poll group are
678 * deemed true when 1) at least one bit from bits_any_set is set OR one
679 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
680 * are set; and 3) all bits in bits_all_clear are cleared.
681 *
682 * Return value:
683 * >=0: index of the poll group whose conditions have become true
684 * -ETIMEDOUT: timed out
685 */
686static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
687{
688 int i, k;
689
690 for (i = 0; i < 50000; i++) {
691 for (k = 0; k < nr_grps; k++) {
692 bool all_set, all_clear;
693 bool any_set, any_clear;
694
695 grps[k].value_read = smsm_get_state(grps[k].group_id);
696
697 all_set = msm_pm_all_set(grps[k].value_read,
698 grps[k].bits_all_set);
699 all_clear = msm_pm_all_clear(grps[k].value_read,
700 grps[k].bits_all_clear);
701 any_set = msm_pm_any_set(grps[k].value_read,
702 grps[k].bits_any_set);
703 any_clear = msm_pm_any_clear(grps[k].value_read,
704 grps[k].bits_any_clear);
705
706 if (all_set && all_clear && (any_set || any_clear))
707 return k;
708 }
709 udelay(50);
710 }
711
712 printk(KERN_ERR "%s failed:\n", __func__);
713 for (k = 0; k < nr_grps; k++)
714 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
715 grps[k].bits_all_set, grps[k].bits_all_clear,
716 grps[k].bits_any_set, grps[k].bits_any_clear,
717 grps[k].value_read);
718
719 return -ETIMEDOUT;
720}
721
722
723/******************************************************************************
724 * Suspend Max Sleep Time
725 *****************************************************************************/
726
727#define SCLK_HZ (32768)
728#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
729
730#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
731static int msm_pm_sleep_time_override;
732module_param_named(sleep_time_override,
733 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
734#endif
735
736static uint32_t msm_pm_max_sleep_time;
737
738/*
739 * Convert time from nanoseconds to slow clock ticks, then cap it to the
740 * specified limit
741 */
742static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
743{
744 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
745 return (time_ns > limit) ? limit : time_ns;
746}
747
748/*
749 * Set the sleep time for suspend. 0 means infinite sleep time.
750 */
751void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
752{
753 unsigned long flags;
754
755 local_irq_save(flags);
756 if (max_sleep_time_ns == 0) {
757 msm_pm_max_sleep_time = 0;
758 } else {
759 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
760 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
761
762 if (msm_pm_max_sleep_time == 0)
763 msm_pm_max_sleep_time = 1;
764 }
765
766 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
767 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
768 max_sleep_time_ns, msm_pm_max_sleep_time);
769 local_irq_restore(flags);
770}
771EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
772
773
774/******************************************************************************
775 * CONFIG_MSM_IDLE_STATS
776 *****************************************************************************/
777
778#ifdef CONFIG_MSM_IDLE_STATS
779enum msm_pm_time_stats_id {
780 MSM_PM_STAT_REQUESTED_IDLE,
781 MSM_PM_STAT_IDLE_SPIN,
782 MSM_PM_STAT_IDLE_WFI,
783 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
784 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
786 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
787 MSM_PM_STAT_SUSPEND,
788 MSM_PM_STAT_FAILED_SUSPEND,
789 MSM_PM_STAT_NOT_IDLE,
790 MSM_PM_STAT_COUNT
791};
792
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530793struct msm_pm_time_stats {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794 const char *name;
795 int64_t first_bucket_time;
796 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
797 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
798 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
799 int count;
800 int64_t total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801};
802
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530803struct msm_pm_cpu_time_stats {
804 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
805};
806
807static DEFINE_PER_CPU_SHARED_ALIGNED(
808 struct msm_pm_cpu_time_stats, msm_pm_stats);
809
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700810static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
811
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530812static DEFINE_SPINLOCK(msm_pm_stats_lock);
813
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700814/*
815 * Add the given time data to the statistics collection.
816 */
817static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
818{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530819 unsigned long flags;
820 struct msm_pm_time_stats *stats;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700821 int i;
822 int64_t bt;
823
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530824 spin_lock_irqsave(&msm_pm_stats_lock, flags);
825 stats = __get_cpu_var(msm_pm_stats).stats;
826
827 stats[id].total_time += t;
828 stats[id].count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829
830 bt = t;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530831 do_div(bt, stats[id].first_bucket_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700832
833 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
834 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
835 i = DIV_ROUND_UP(fls((uint32_t)bt),
836 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
837 else
838 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
839
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530840 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
841 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700842
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530843 stats[id].bucket[i]++;
844
845 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
846 stats[id].min_time[i] = t;
847 if (t > stats[id].max_time[i])
848 stats[id].max_time[i] = t;
849
850 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851}
852
853/*
854 * Helper function of snprintf where buf is auto-incremented, size is auto-
855 * decremented, and there is no return value.
856 *
857 * NOTE: buf and size must be l-values (e.g. variables)
858 */
859#define SNPRINTF(buf, size, format, ...) \
860 do { \
861 if (size > 0) { \
862 int ret; \
863 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
864 if (ret > size) { \
865 buf += size; \
866 size = 0; \
867 } else { \
868 buf += ret; \
869 size -= ret; \
870 } \
871 } \
872 } while (0)
873
874/*
875 * Write out the power management statistics.
876 */
877static int msm_pm_read_proc
878 (char *page, char **start, off_t off, int count, int *eof, void *data)
879{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530880 unsigned int cpu = off / MSM_PM_STAT_COUNT;
881 int id = off % MSM_PM_STAT_COUNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700882 char *p = page;
883
884 if (count < 1024) {
885 *start = (char *) 0;
886 *eof = 0;
887 return 0;
888 }
889
890 if (!off) {
891 SNPRINTF(p, count, "Last power collapse voted ");
892 if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
893 SLEEP_LIMIT_NONE)
894 SNPRINTF(p, count, "for TCXO shutdown\n\n");
895 else
896 SNPRINTF(p, count, "against TCXO shutdown\n\n");
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530897 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700898
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530899 if (cpu < num_possible_cpus()) {
900 unsigned long flags;
901 struct msm_pm_time_stats *stats;
902 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700903 int64_t bucket_time;
904 int64_t s;
905 uint32_t ns;
906
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530907 spin_lock_irqsave(&msm_pm_stats_lock, flags);
908 stats = per_cpu(msm_pm_stats, cpu).stats;
909
910 s = stats[id].total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700911 ns = do_div(s, NSEC_PER_SEC);
912 SNPRINTF(p, count,
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530913 "[cpu %u] %s:\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700914 " count: %7d\n"
915 " total_time: %lld.%09u\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530916 cpu, stats[id].name,
917 stats[id].count,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918 s, ns);
919
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530920 bucket_time = stats[id].first_bucket_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700921 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
922 s = bucket_time;
923 ns = do_div(s, NSEC_PER_SEC);
924 SNPRINTF(p, count,
925 " <%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530926 s, ns, stats[id].bucket[i],
927 stats[id].min_time[i],
928 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929
930 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
931 }
932
933 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530934 s, ns, stats[id].bucket[i],
935 stats[id].min_time[i],
936 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700937
938 *start = (char *) 1;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530939 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
940
941 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942 }
943
944 return p - page;
945}
946#undef SNPRINTF
947
948#define MSM_PM_STATS_RESET "reset"
949
950/*
951 * Reset the power management statistics values.
952 */
953static int msm_pm_write_proc(struct file *file, const char __user *buffer,
954 unsigned long count, void *data)
955{
956 char buf[sizeof(MSM_PM_STATS_RESET)];
957 int ret;
958 unsigned long flags;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530959 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700960
961 if (count < strlen(MSM_PM_STATS_RESET)) {
962 ret = -EINVAL;
963 goto write_proc_failed;
964 }
965
966 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
967 ret = -EFAULT;
968 goto write_proc_failed;
969 }
970
971 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
972 ret = -EINVAL;
973 goto write_proc_failed;
974 }
975
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530976 spin_lock_irqsave(&msm_pm_stats_lock, flags);
977 for_each_possible_cpu(cpu) {
978 struct msm_pm_time_stats *stats;
979 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530981 stats = per_cpu(msm_pm_stats, cpu).stats;
982 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
983 memset(stats[i].bucket,
984 0, sizeof(stats[i].bucket));
985 memset(stats[i].min_time,
986 0, sizeof(stats[i].min_time));
987 memset(stats[i].max_time,
988 0, sizeof(stats[i].max_time));
989 stats[i].count = 0;
990 stats[i].total_time = 0;
991 }
992 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993 msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530994 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995
996 return count;
997
998write_proc_failed:
999 return ret;
1000}
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301001
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001002#undef MSM_PM_STATS_RESET
1003#endif /* CONFIG_MSM_IDLE_STATS */
1004
1005
1006/******************************************************************************
1007 * Shared Memory Bits
1008 *****************************************************************************/
1009
1010#define DEM_MASTER_BITS_PER_CPU 6
1011
1012/* Power Master State Bits - Per CPU */
1013#define DEM_MASTER_SMSM_RUN \
1014 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1015#define DEM_MASTER_SMSM_RSA \
1016 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1017#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
1018 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1019#define DEM_MASTER_SMSM_SLEEP_EXIT \
1020 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1021#define DEM_MASTER_SMSM_READY \
1022 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1023#define DEM_MASTER_SMSM_SLEEP \
1024 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1025
1026/* Power Slave State Bits */
1027#define DEM_SLAVE_SMSM_RUN (0x0001)
1028#define DEM_SLAVE_SMSM_PWRC (0x0002)
1029#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
1030#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
1031#define DEM_SLAVE_SMSM_WFPI (0x0010)
1032#define DEM_SLAVE_SMSM_SLEEP (0x0020)
1033#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
1034#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
1035#define DEM_SLAVE_SMSM_RESET (0x0100)
1036#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
1037
1038
1039/******************************************************************************
1040 * Shared Memory Data
1041 *****************************************************************************/
1042
1043#define DEM_MAX_PORT_NAME_LEN (20)
1044
1045struct msm_pm_smem_t {
1046 uint32_t sleep_time;
1047 uint32_t irq_mask;
1048 uint32_t resources_used;
1049 uint32_t reserved1;
1050
1051 uint32_t wakeup_reason;
1052 uint32_t pending_irqs;
1053 uint32_t rpc_prog;
1054 uint32_t rpc_proc;
1055 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
1056 uint32_t reserved2;
1057};
1058
1059
1060/******************************************************************************
1061 *
1062 *****************************************************************************/
1063static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001064static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
1065
1066static int msm_pm_modem_busy(void)
1067{
1068 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1069 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
1070 KERN_INFO, "%s(): master not ready\n", __func__);
1071 return -EBUSY;
1072 }
1073
1074 return 0;
1075}
1076
1077/*
1078 * Power collapse the Apps processor. This function executes the handshake
1079 * protocol with Modem.
1080 *
1081 * Return value:
1082 * -EAGAIN: modem reset occurred or early exit from power collapse
1083 * -EBUSY: modem not ready for our power collapse -- no power loss
1084 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
1085 * 0: success
1086 */
1087static int msm_pm_power_collapse
1088 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
1089{
1090 struct msm_pm_polled_group state_grps[2];
1091 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001092 int collapsed = 0;
1093 int ret;
Murali Nalajala07b04022012-04-10 16:00:49 +05301094 int val;
1095 int modem_early_exit = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001096
1097 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1098 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
1099 (int)from_idle, sleep_delay, sleep_limit);
1100
1101 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1102 MSM_PM_DPRINTK(
1103 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1104 KERN_INFO, "%s(): master not ready\n", __func__);
1105 ret = -EBUSY;
1106 goto power_collapse_bail;
1107 }
1108
1109 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
1110
Murali Nalajala41786ab2012-03-06 10:47:32 +05301111 if (cpu_is_msm8625()) {
1112 /* Program the SPM */
1113 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
1114 false);
1115 WARN_ON(ret);
1116 }
1117
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301118 msm_pm_irq_extns->enter_sleep1(true, from_idle,
1119 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001120 msm_sirc_enter_sleep();
1121 msm_gpio_enter_sleep(from_idle);
1122
1123 msm_pm_smem_data->sleep_time = sleep_delay;
1124 msm_pm_smem_data->resources_used = sleep_limit;
1125
1126 /* Enter PWRC/PWRC_SUSPEND */
1127
1128 if (from_idle)
1129 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1130 DEM_SLAVE_SMSM_PWRC);
1131 else
1132 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1133 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
1134
1135 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
1136 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1137
1138 memset(state_grps, 0, sizeof(state_grps));
1139 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1140 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
1141 state_grps[1].group_id = SMSM_MODEM_STATE;
1142 state_grps[1].bits_all_set = SMSM_RESET;
1143
1144 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1145
1146 if (ret < 0) {
1147 printk(KERN_EMERG "%s(): power collapse entry "
1148 "timed out waiting for Modem's response\n", __func__);
1149 msm_pm_timeout();
1150 }
1151
1152 if (ret == 1) {
1153 MSM_PM_DPRINTK(
1154 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1155 KERN_INFO,
1156 "%s(): msm_pm_poll_state detected Modem reset\n",
1157 __func__);
1158 goto power_collapse_early_exit;
1159 }
1160
1161 /* DEM Master in RSA */
1162
1163 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
1164
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301165 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166 if (ret < 0) {
1167 MSM_PM_DPRINTK(
1168 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1169 KERN_INFO,
1170 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
1171 ret);
1172 goto power_collapse_early_exit;
1173 }
1174
1175 msm_pm_config_hw_before_power_down();
1176 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
1177
1178 saved_acpuclk_rate = acpuclk_power_collapse();
1179 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1180 "%s(): change clock rate (old rate = %lu)\n", __func__,
1181 saved_acpuclk_rate);
1182
1183 if (saved_acpuclk_rate == 0) {
1184 msm_pm_config_hw_after_power_up();
1185 goto power_collapse_early_exit;
1186 }
1187
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001188 msm_pm_boot_config_before_pc(smp_processor_id(),
1189 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001190
1191#ifdef CONFIG_VFP
1192 if (from_idle)
1193 vfp_flush_context();
1194#endif
1195
1196#ifdef CONFIG_CACHE_L2X0
Taniya Das38a8c6e2012-05-09 20:34:39 +05301197 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001198#endif
1199
1200 collapsed = msm_pm_collapse();
Murali Nalajala07b04022012-04-10 16:00:49 +05301201
1202 /*
1203 * TBD: Currently recognise the MODEM early exit
1204 * path by reading the MPA5_GDFS_CNT_VAL register.
1205 */
1206 if (cpu_is_msm8625()) {
1207 /*
Murali Nalajala93e6ed02012-05-13 12:57:22 +05301208 * on system reset, default value of MPA5_GDFS_CNT_VAL
1209 * is = 0x0, later modem reprogram this value to
1210 * 0x00030004. Once APPS did a power collapse and
1211 * coming out of it expected value of this register
1212 * always be 0x00030004. Incase if APPS sees the value
1213 * as 0x00030002 consider this case as a modem early
1214 * exit.
Murali Nalajala07b04022012-04-10 16:00:49 +05301215 */
1216 val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
Murali Nalajala93e6ed02012-05-13 12:57:22 +05301217 if (val != 0x00030002)
Murali Nalajala07b04022012-04-10 16:00:49 +05301218 power_collapsed = 1;
Murali Nalajala93e6ed02012-05-13 12:57:22 +05301219 else
1220 modem_early_exit = 1;
Murali Nalajala07b04022012-04-10 16:00:49 +05301221 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001222
1223#ifdef CONFIG_CACHE_L2X0
Taniya Das38a8c6e2012-05-09 20:34:39 +05301224 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001225#endif
1226
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001227 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001228
1229 if (collapsed) {
1230#ifdef CONFIG_VFP
1231 if (from_idle)
1232 vfp_reinit();
1233#endif
1234 cpu_init();
1235 local_fiq_enable();
1236 }
1237
1238 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1239 KERN_INFO,
1240 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1241
1242 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1243 "%s(): restore clock rate to %lu\n", __func__,
1244 saved_acpuclk_rate);
1245 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1246 SETRATE_PC) < 0)
1247 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1248 __func__, saved_acpuclk_rate);
1249
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301250 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001251 msm_pm_smem_data->wakeup_reason,
1252 msm_pm_smem_data->pending_irqs);
1253
1254 msm_pm_config_hw_after_power_up();
1255 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1256
1257 memset(state_grps, 0, sizeof(state_grps));
1258 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1259 state_grps[0].bits_any_set =
1260 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1261 state_grps[1].group_id = SMSM_MODEM_STATE;
1262 state_grps[1].bits_all_set = SMSM_RESET;
1263
1264 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1265
1266 if (ret < 0) {
1267 printk(KERN_EMERG "%s(): power collapse exit "
1268 "timed out waiting for Modem's response\n", __func__);
1269 msm_pm_timeout();
1270 }
1271
1272 if (ret == 1) {
1273 MSM_PM_DPRINTK(
1274 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1275 KERN_INFO,
1276 "%s(): msm_pm_poll_state detected Modem reset\n",
1277 __func__);
1278 goto power_collapse_early_exit;
1279 }
1280
1281 /* Sanity check */
Murali Nalajala07b04022012-04-10 16:00:49 +05301282 if (collapsed && !modem_early_exit) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001283 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1284 } else {
1285 BUG_ON(!(state_grps[0].value_read &
1286 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1287 goto power_collapse_early_exit;
1288 }
1289
1290 /* Enter WFPI */
1291
1292 smsm_change_state(SMSM_APPS_DEM,
1293 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1294 DEM_SLAVE_SMSM_WFPI);
1295
1296 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1297
1298 memset(state_grps, 0, sizeof(state_grps));
1299 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1300 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1301 state_grps[1].group_id = SMSM_MODEM_STATE;
1302 state_grps[1].bits_all_set = SMSM_RESET;
1303
1304 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1305
1306 if (ret < 0) {
1307 printk(KERN_EMERG "%s(): power collapse WFPI "
1308 "timed out waiting for Modem's response\n", __func__);
1309 msm_pm_timeout();
1310 }
1311
1312 if (ret == 1) {
1313 MSM_PM_DPRINTK(
1314 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1315 KERN_INFO,
1316 "%s(): msm_pm_poll_state detected Modem reset\n",
1317 __func__);
1318 ret = -EAGAIN;
1319 goto power_collapse_restore_gpio_bail;
1320 }
1321
1322 /* DEM Master == RUN */
1323
1324 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1325 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1326
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301327 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001328 msm_pm_smem_data->wakeup_reason,
1329 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301330 msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001331 msm_pm_smem_data->wakeup_reason,
1332 msm_pm_smem_data->pending_irqs);
1333 msm_gpio_exit_sleep();
1334 msm_sirc_exit_sleep();
1335
1336 smsm_change_state(SMSM_APPS_DEM,
1337 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1338
1339 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1340
1341 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301342
1343 if (cpu_is_msm8625()) {
1344 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1345 false);
1346 WARN_ON(ret);
1347 }
1348
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001349 return 0;
1350
1351power_collapse_early_exit:
1352 /* Enter PWRC_EARLY_EXIT */
1353
1354 smsm_change_state(SMSM_APPS_DEM,
1355 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1356 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1357
1358 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1359
1360 memset(state_grps, 0, sizeof(state_grps));
1361 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1362 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1363 state_grps[1].group_id = SMSM_MODEM_STATE;
1364 state_grps[1].bits_all_set = SMSM_RESET;
1365
1366 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1367 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1368
1369 if (ret < 0) {
1370 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1371 "timed out waiting for Modem's response\n", __func__);
1372 msm_pm_timeout();
1373 }
1374
1375 if (ret == 1) {
1376 MSM_PM_DPRINTK(
1377 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1378 KERN_INFO,
1379 "%s(): msm_pm_poll_state detected Modem reset\n",
1380 __func__);
1381 }
1382
1383 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1384
1385 ret = -EAGAIN;
1386
1387power_collapse_restore_gpio_bail:
1388 msm_gpio_exit_sleep();
1389 msm_sirc_exit_sleep();
1390
1391 /* Enter RUN */
1392 smsm_change_state(SMSM_APPS_DEM,
1393 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1394 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1395
1396 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1397
1398 if (collapsed)
1399 smd_sleep_exit();
1400
1401power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301402 if (cpu_is_msm8625()) {
1403 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1404 false);
1405 WARN_ON(ret);
1406 }
1407
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001408 return ret;
1409}
1410
1411/*
1412 * Power collapse the Apps processor without involving Modem.
1413 *
1414 * Return value:
1415 * 0: success
1416 */
Stephen Boydb29750d2012-02-21 01:21:32 -08001417static int __ref msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001418{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001419 int collapsed = 0;
1420 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301421 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001422
1423 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1424 KERN_INFO, "%s()\n", __func__);
1425
1426 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1427 WARN_ON(ret);
1428
Murali Nalajala41786ab2012-03-06 10:47:32 +05301429 entry = (!smp_processor_id() || from_idle) ?
1430 msm_pm_collapse_exit : msm_secondary_startup;
1431
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001432 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301433 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001434
1435#ifdef CONFIG_VFP
1436 vfp_flush_context();
1437#endif
1438
1439#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301440 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301441 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001442#endif
1443
1444 collapsed = msm_pm_collapse();
1445
1446#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301447 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301448 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001449#endif
1450
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001451 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001452
1453 if (collapsed) {
1454#ifdef CONFIG_VFP
1455 vfp_reinit();
1456#endif
1457 cpu_init();
1458 local_fiq_enable();
1459 }
1460
1461 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1462 KERN_INFO,
1463 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1464
1465 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1466 WARN_ON(ret);
1467
Anji jonnalac6816222012-03-31 10:55:14 +05301468 return !collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001469}
1470
1471/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001472 * Bring the Apps processor to SWFI.
1473 *
1474 * Return value:
1475 * -EIO: could not ramp Apps processor clock
1476 * 0: success
1477 */
1478static int msm_pm_swfi(bool ramp_acpu)
1479{
1480 unsigned long saved_acpuclk_rate = 0;
1481
1482 if (ramp_acpu) {
1483 saved_acpuclk_rate = acpuclk_wait_for_irq();
1484 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1485 "%s(): change clock rate (old rate = %lu)\n", __func__,
1486 saved_acpuclk_rate);
1487
1488 if (!saved_acpuclk_rate)
1489 return -EIO;
1490 }
1491
Murali Nalajala41786ab2012-03-06 10:47:32 +05301492 if (!cpu_is_msm8625())
1493 msm_pm_config_hw_before_swfi();
1494
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001495 msm_arch_idle();
1496
1497 if (ramp_acpu) {
1498 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1499 "%s(): restore clock rate to %lu\n", __func__,
1500 saved_acpuclk_rate);
1501 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1502 SETRATE_SWFI) < 0)
1503 printk(KERN_ERR
1504 "%s(): failed to restore clock rate(%lu)\n",
1505 __func__, saved_acpuclk_rate);
1506 }
1507
1508 return 0;
1509}
1510
1511
1512/******************************************************************************
1513 * External Idle/Suspend Functions
1514 *****************************************************************************/
1515
1516/*
1517 * Put CPU in low power mode.
1518 */
1519void arch_idle(void)
1520{
1521 bool allow[MSM_PM_SLEEP_MODE_NR];
1522 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1523
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001524 int64_t timer_expiration;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301525 int latency_qos;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001526 int ret;
1527 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301528 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529
1530#ifdef CONFIG_MSM_IDLE_STATS
1531 int64_t t1;
Murali Nalajalab86f3702012-03-30 17:54:57 +05301532 static DEFINE_PER_CPU(int64_t, t2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001533 int exit_stat;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301534 #endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535
1536 if (!atomic_read(&msm_pm_init_done))
1537 return;
1538
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301539 cpu = smp_processor_id();
1540
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001541 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301542 /* get the next timer expiration */
1543 timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001544
1545#ifdef CONFIG_MSM_IDLE_STATS
1546 t1 = ktime_to_ns(ktime_get());
Murali Nalajalab86f3702012-03-30 17:54:57 +05301547 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001548 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301549 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301550#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001551
1552 for (i = 0; i < ARRAY_SIZE(allow); i++)
1553 allow[i] = true;
1554
Murali Nalajala41786ab2012-03-06 10:47:32 +05301555 if (num_online_cpus() > 1 ||
1556 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001557#ifdef CONFIG_HAS_WAKELOCK
1558 has_wake_lock(WAKE_LOCK_IDLE) ||
1559#endif
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301560 !msm_pm_irq_extns->idle_sleep_allowed()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001561 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1562 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001563 }
1564
1565 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301566 struct msm_pm_platform_data *mode =
1567 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001568 if (!mode->idle_supported || !mode->idle_enabled ||
1569 mode->latency >= latency_qos ||
1570 mode->residency * 1000ULL >= timer_expiration)
1571 allow[i] = false;
1572 }
1573
1574 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1575 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1576 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1577 while (msm_pm_modem_busy() && wait_us) {
1578 if (wait_us > 100) {
1579 udelay(100);
1580 wait_us -= 100;
1581 } else {
1582 udelay(wait_us);
1583 wait_us = 0;
1584 }
1585 }
1586
1587 if (msm_pm_modem_busy()) {
1588 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1589 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1590 = false;
1591 }
1592 }
1593
1594 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1595 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1596 __func__, latency_qos, timer_expiration, sleep_limit);
1597
1598 for (i = 0; i < ARRAY_SIZE(allow); i++)
1599 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1600 "%s(): allow %s: %d\n", __func__,
1601 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1602
1603 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1604 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Murali Nalajala8fda4492012-03-19 18:22:59 +05301605 /* Sync the timer with SCLK, it is needed only for modem
1606 * assissted pollapse case.
1607 */
1608 int64_t next_timer_exp = msm_timer_enter_idle();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001609 uint32_t sleep_delay;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301610 bool low_power = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001611
1612 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
Murali Nalajala8fda4492012-03-19 18:22:59 +05301613 next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT);
1614
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001615 if (sleep_delay == 0) /* 0 would mean infinite time */
1616 sleep_delay = 1;
1617
1618 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1619 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1620
1621#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1622 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1623#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1624 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1625#endif
1626
1627 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1628 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301629 msm_timer_exit_idle(low_power);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001630
1631#ifdef CONFIG_MSM_IDLE_STATS
1632 if (ret)
1633 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1634 else {
1635 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1636 msm_pm_sleep_limit = sleep_limit;
1637 }
Murali Nalajala41786ab2012-03-06 10:47:32 +05301638#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001639 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301640 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001641#ifdef CONFIG_MSM_IDLE_STATS
1642 exit_stat = ret ?
1643 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1644 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301645#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001646 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1647 ret = msm_pm_swfi(true);
1648 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301649 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001650 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001651#ifdef CONFIG_MSM_IDLE_STATS
1652 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301653#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001654 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1655 msm_pm_swfi(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001656#ifdef CONFIG_MSM_IDLE_STATS
1657 exit_stat = MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301658#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001659 } else {
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301660 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001661 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001662#ifdef CONFIG_MSM_IDLE_STATS
1663 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301664#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001665 }
1666
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001667#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajalab86f3702012-03-30 17:54:57 +05301668 __get_cpu_var(t2) = ktime_to_ns(ktime_get());
1669 msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1);
Murali Nalajala41786ab2012-03-06 10:47:32 +05301670#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001671}
1672
1673/*
1674 * Suspend the Apps processor.
1675 *
1676 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301677 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001678 * -EAGAIN: modem reset occurred or early exit from suspend
1679 * -EBUSY: modem not ready for our suspend
1680 * -EINVAL: invalid sleep mode
1681 * -EIO: could not ramp Apps processor clock
1682 * -ETIMEDOUT: timed out waiting for modem's handshake
1683 * 0: success
1684 */
1685static int msm_pm_enter(suspend_state_t state)
1686{
1687 bool allow[MSM_PM_SLEEP_MODE_NR];
1688 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301689 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001690 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001691#ifdef CONFIG_MSM_IDLE_STATS
1692 int64_t period = 0;
1693 int64_t time = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301694#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001695
Murali Nalajala41786ab2012-03-06 10:47:32 +05301696 /* Must executed by CORE0 */
1697 if (smp_processor_id()) {
1698 __WARN();
1699 goto suspend_exit;
1700 }
1701
1702#ifdef CONFIG_MSM_IDLE_STATS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001703 time = msm_timer_get_sclk_time(&period);
1704#endif
1705
1706 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1707 "%s(): sleep limit %u\n", __func__, sleep_limit);
1708
1709 for (i = 0; i < ARRAY_SIZE(allow); i++)
1710 allow[i] = true;
1711
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001712 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301713 struct msm_pm_platform_data *mode;
1714 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001715 if (!mode->suspend_supported || !mode->suspend_enabled)
1716 allow[i] = false;
1717 }
1718
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001719 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1720 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1721#ifdef CONFIG_MSM_IDLE_STATS
1722 enum msm_pm_time_stats_id id;
1723 int64_t end_time;
1724#endif
1725
1726 clock_debug_print_enabled();
1727
1728#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1729 if (msm_pm_sleep_time_override > 0) {
1730 int64_t ns;
1731 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1732 msm_pm_set_max_sleep_time(ns);
1733 msm_pm_sleep_time_override = 0;
1734 }
1735#endif
1736 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1737 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1738
1739#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1740 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1741#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1742 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1743#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1744 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1745 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1746#endif
1747
1748 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1749 udelay(500);
1750
1751 ret = msm_pm_power_collapse(
1752 false, msm_pm_max_sleep_time, sleep_limit);
1753
1754#ifdef CONFIG_MSM_IDLE_STATS
1755 if (ret)
1756 id = MSM_PM_STAT_FAILED_SUSPEND;
1757 else {
1758 id = MSM_PM_STAT_SUSPEND;
1759 msm_pm_sleep_limit = sleep_limit;
1760 }
1761
1762 if (time != 0) {
1763 end_time = msm_timer_get_sclk_time(NULL);
1764 if (end_time != 0) {
1765 time = end_time - time;
1766 if (time < 0)
1767 time += period;
1768 } else
1769 time = 0;
1770 }
1771
1772 msm_pm_add_stat(id, time);
1773#endif
1774 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301775 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001776 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1777 ret = msm_pm_swfi(true);
1778 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301779 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001780 udelay(1);
1781 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1782 msm_pm_swfi(false);
1783 }
1784
Murali Nalajala41786ab2012-03-06 10:47:32 +05301785suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001786 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1787 "%s(): return %d\n", __func__, ret);
1788
1789 return ret;
1790}
1791
1792static struct platform_suspend_ops msm_pm_ops = {
1793 .enter = msm_pm_enter,
1794 .valid = suspend_valid_only_mem,
1795};
1796
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301797/* Hotplug the "non boot" CPU's and put
1798 * the cores into low power mode
1799 */
1800void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1801{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301802 bool allow[MSM_PM_SLEEP_MODE_NR];
1803 int i;
1804
1805 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1806 struct msm_pm_platform_data *mode;
1807
1808 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1809 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1810 }
1811
1812 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1813 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1814
1815 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1816 msm_pm_power_collapse_standalone(false);
1817 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1818 msm_pm_swfi(false);
1819 } else {
1820 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1821 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1822 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301823}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001824
1825/******************************************************************************
1826 * Restart Definitions
1827 *****************************************************************************/
1828
1829static uint32_t restart_reason = 0x776655AA;
1830
1831static void msm_pm_power_off(void)
1832{
1833 msm_rpcrouter_close();
1834 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1835 for (;;)
1836 ;
1837}
1838
1839static void msm_pm_restart(char str, const char *cmd)
1840{
1841 msm_rpcrouter_close();
1842 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1843
1844 for (;;)
1845 ;
1846}
1847
1848static int msm_reboot_call
1849 (struct notifier_block *this, unsigned long code, void *_cmd)
1850{
1851 if ((code == SYS_RESTART) && _cmd) {
1852 char *cmd = _cmd;
1853 if (!strcmp(cmd, "bootloader")) {
1854 restart_reason = 0x77665500;
1855 } else if (!strcmp(cmd, "recovery")) {
1856 restart_reason = 0x77665502;
1857 } else if (!strcmp(cmd, "eraseflash")) {
1858 restart_reason = 0x776655EF;
1859 } else if (!strncmp(cmd, "oem-", 4)) {
1860 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1861 restart_reason = 0x6f656d00 | code;
1862 } else {
1863 restart_reason = 0x77665501;
1864 }
1865 }
1866 return NOTIFY_DONE;
1867}
1868
1869static struct notifier_block msm_reboot_notifier = {
1870 .notifier_call = msm_reboot_call,
1871};
1872
1873
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001874/*
1875 * Initialize the power management subsystem.
1876 *
1877 * Return value:
1878 * -ENODEV: initialization failed
1879 * 0: success
1880 */
1881static int __init msm_pm_init(void)
1882{
1883#ifdef CONFIG_MSM_IDLE_STATS
1884 struct proc_dir_entry *d_entry;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301885 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001886#endif
1887 int ret;
Murali Nalajala93f29992012-03-21 15:59:27 +05301888 int val;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001889#ifdef CONFIG_CPU_V7
1890 pgd_t *pc_pgd;
1891 pmd_t *pmd;
1892 unsigned long pmdval;
1893
1894 /* Page table for cores to come back up safely. */
1895 pc_pgd = pgd_alloc(&init_mm);
1896 if (!pc_pgd)
1897 return -ENOMEM;
1898 pmd = pmd_offset(pc_pgd +
1899 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1900 virt_to_phys(msm_pm_collapse_exit));
1901 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1902 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1903 pmd[0] = __pmd(pmdval);
1904 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1905
Steve Mucklefcece052012-02-18 20:09:58 -08001906 msm_saved_state_phys =
1907 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1908 num_possible_cpus(), 4);
1909 if (!msm_saved_state_phys)
1910 return -ENOMEM;
1911 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1912 CPU_SAVED_STATE_SIZE *
1913 num_possible_cpus());
1914 if (!msm_saved_state)
1915 return -ENOMEM;
1916
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001917 /* It is remotely possible that the code in msm_pm_collapse_exit()
1918 * which turns on the MMU with this mapping is in the
1919 * next even-numbered megabyte beyond the
1920 * start of msm_pm_collapse_exit().
1921 * Map this megabyte in as well.
1922 */
1923 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1924 flush_pmd_entry(pmd);
1925 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001926 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1927 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001928#endif
1929
1930 pm_power_off = msm_pm_power_off;
1931 arm_pm_restart = msm_pm_restart;
1932 register_reboot_notifier(&msm_reboot_notifier);
1933
1934 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1935 sizeof(*msm_pm_smem_data));
1936 if (msm_pm_smem_data == NULL) {
1937 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1938 return -ENODEV;
1939 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001940
1941 ret = msm_timer_init_time_sync(msm_pm_timeout);
1942 if (ret)
1943 return ret;
1944
1945 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1946 if (ret) {
1947 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1948 __func__, ret);
1949 return ret;
1950 }
1951
Murali Nalajala93f29992012-03-21 15:59:27 +05301952 if (cpu_is_msm8625()) {
1953 target_type = TARGET_IS_8625;
1954 clean_caches((unsigned long)&target_type, sizeof(target_type),
1955 virt_to_phys(&target_type));
1956
Anji jonnalae644f8e2012-05-09 19:52:18 +05301957 /*
1958 * Configure the MPA5_GDFS_CNT_VAL register for
1959 * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the
1960 * DBGNOPOWERDN for each cpu.
1961 * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for
1962 * GDFS control.
Murali Nalajala93f29992012-03-21 15:59:27 +05301963 */
Anji jonnalae644f8e2012-05-09 19:52:18 +05301964 val = 0x00030002;
Murali Nalajala93f29992012-03-21 15:59:27 +05301965 __raw_writel(val, (MSM_CFG_CTL_BASE + 0x38));
1966 }
1967
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001968#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1969 /* The wakeup_reason field is overloaded during initialization time
1970 to signal Modem that Apps will control the low power modes of
1971 the memory.
1972 */
1973 msm_pm_smem_data->wakeup_reason = 1;
1974 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1975#endif
1976
1977 BUG_ON(msm_pm_modes == NULL);
1978
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001979 suspend_set_ops(&msm_pm_ops);
1980
1981 msm_pm_mode_sysfs_add();
1982#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301983 for_each_possible_cpu(cpu) {
1984 struct msm_pm_time_stats *stats =
1985 per_cpu(msm_pm_stats, cpu).stats;
1986
1987 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1988 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1989 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1990
1991 stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin";
1992 stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
1993 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1994
1995 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1996 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1997 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1998
1999 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
2000 "idle-standalone-power-collapse";
2001 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
2002 first_bucket_time =
2003 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2004
2005 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
2006 "idle-failed-standalone-power-collapse";
2007 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].
2008 first_bucket_time =
2009 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2010
Murali Nalajala0df9fee2012-01-12 15:26:09 +05302011 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
2012 "idle-power-collapse";
2013 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
2014 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2015
2016 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
2017 "idle-failed-power-collapse";
2018 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].
2019 first_bucket_time =
2020 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2021
2022 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
2023 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
2024 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
2025
2026 stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend";
2027 stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
2028 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2029
2030 stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle";
2031 stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
2032 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2033 }
Murali Nalajala558c0ce2012-03-29 19:42:08 +05302034
2035 atomic_set(&msm_pm_init_done, 1);
2036
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002037 d_entry = create_proc_entry("msm_pm_stats",
2038 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
2039 if (d_entry) {
2040 d_entry->read_proc = msm_pm_read_proc;
2041 d_entry->write_proc = msm_pm_write_proc;
2042 d_entry->data = NULL;
2043 }
2044#endif
2045
2046 return 0;
2047}
2048
2049late_initcall_sync(msm_pm_init);