blob: 6d8f2a2252f9a2e2717a44d876cc09b8026f27dc [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"
Murali Nalajala19d33a22012-05-18 14:11:19 +053068#include "devices-msm7x2xa.h"
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()) {
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/******************************************************************************
768 * CONFIG_MSM_IDLE_STATS
769 *****************************************************************************/
770
771#ifdef CONFIG_MSM_IDLE_STATS
772enum msm_pm_time_stats_id {
773 MSM_PM_STAT_REQUESTED_IDLE,
774 MSM_PM_STAT_IDLE_SPIN,
775 MSM_PM_STAT_IDLE_WFI,
776 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
777 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
779 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
780 MSM_PM_STAT_SUSPEND,
781 MSM_PM_STAT_FAILED_SUSPEND,
782 MSM_PM_STAT_NOT_IDLE,
783 MSM_PM_STAT_COUNT
784};
785
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530786struct msm_pm_time_stats {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700787 const char *name;
788 int64_t first_bucket_time;
789 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
790 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
791 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
792 int count;
793 int64_t total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794};
795
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530796struct msm_pm_cpu_time_stats {
797 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
798};
799
800static DEFINE_PER_CPU_SHARED_ALIGNED(
801 struct msm_pm_cpu_time_stats, msm_pm_stats);
802
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
804
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530805static DEFINE_SPINLOCK(msm_pm_stats_lock);
806
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700807/*
808 * Add the given time data to the statistics collection.
809 */
810static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
811{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530812 unsigned long flags;
813 struct msm_pm_time_stats *stats;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700814 int i;
815 int64_t bt;
816
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530817 spin_lock_irqsave(&msm_pm_stats_lock, flags);
818 stats = __get_cpu_var(msm_pm_stats).stats;
819
820 stats[id].total_time += t;
821 stats[id].count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822
823 bt = t;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530824 do_div(bt, stats[id].first_bucket_time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700825
826 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
827 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
828 i = DIV_ROUND_UP(fls((uint32_t)bt),
829 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
830 else
831 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
832
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530833 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
834 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700835
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530836 stats[id].bucket[i]++;
837
838 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
839 stats[id].min_time[i] = t;
840 if (t > stats[id].max_time[i])
841 stats[id].max_time[i] = t;
842
843 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844}
845
846/*
847 * Helper function of snprintf where buf is auto-incremented, size is auto-
848 * decremented, and there is no return value.
849 *
850 * NOTE: buf and size must be l-values (e.g. variables)
851 */
852#define SNPRINTF(buf, size, format, ...) \
853 do { \
854 if (size > 0) { \
855 int ret; \
856 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
857 if (ret > size) { \
858 buf += size; \
859 size = 0; \
860 } else { \
861 buf += ret; \
862 size -= ret; \
863 } \
864 } \
865 } while (0)
866
867/*
868 * Write out the power management statistics.
869 */
870static int msm_pm_read_proc
871 (char *page, char **start, off_t off, int count, int *eof, void *data)
872{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530873 unsigned int cpu = off / MSM_PM_STAT_COUNT;
874 int id = off % MSM_PM_STAT_COUNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875 char *p = page;
876
877 if (count < 1024) {
878 *start = (char *) 0;
879 *eof = 0;
880 return 0;
881 }
882
883 if (!off) {
884 SNPRINTF(p, count, "Last power collapse voted ");
885 if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
886 SLEEP_LIMIT_NONE)
887 SNPRINTF(p, count, "for TCXO shutdown\n\n");
888 else
889 SNPRINTF(p, count, "against TCXO shutdown\n\n");
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530890 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700891
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530892 if (cpu < num_possible_cpus()) {
893 unsigned long flags;
894 struct msm_pm_time_stats *stats;
895 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896 int64_t bucket_time;
897 int64_t s;
898 uint32_t ns;
899
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530900 spin_lock_irqsave(&msm_pm_stats_lock, flags);
901 stats = per_cpu(msm_pm_stats, cpu).stats;
902
903 s = stats[id].total_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700904 ns = do_div(s, NSEC_PER_SEC);
905 SNPRINTF(p, count,
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530906 "[cpu %u] %s:\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700907 " count: %7d\n"
908 " total_time: %lld.%09u\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530909 cpu, stats[id].name,
910 stats[id].count,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700911 s, ns);
912
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530913 bucket_time = stats[id].first_bucket_time;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700914 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
915 s = bucket_time;
916 ns = do_div(s, NSEC_PER_SEC);
917 SNPRINTF(p, count,
918 " <%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530919 s, ns, stats[id].bucket[i],
920 stats[id].min_time[i],
921 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700922
923 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
924 }
925
926 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530927 s, ns, stats[id].bucket[i],
928 stats[id].min_time[i],
929 stats[id].max_time[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930
931 *start = (char *) 1;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530932 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
933
934 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700935 }
936
937 return p - page;
938}
939#undef SNPRINTF
940
941#define MSM_PM_STATS_RESET "reset"
942
943/*
944 * Reset the power management statistics values.
945 */
946static int msm_pm_write_proc(struct file *file, const char __user *buffer,
947 unsigned long count, void *data)
948{
949 char buf[sizeof(MSM_PM_STATS_RESET)];
950 int ret;
951 unsigned long flags;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530952 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953
954 if (count < strlen(MSM_PM_STATS_RESET)) {
955 ret = -EINVAL;
956 goto write_proc_failed;
957 }
958
959 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
960 ret = -EFAULT;
961 goto write_proc_failed;
962 }
963
964 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
965 ret = -EINVAL;
966 goto write_proc_failed;
967 }
968
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530969 spin_lock_irqsave(&msm_pm_stats_lock, flags);
970 for_each_possible_cpu(cpu) {
971 struct msm_pm_time_stats *stats;
972 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530974 stats = per_cpu(msm_pm_stats, cpu).stats;
975 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
976 memset(stats[i].bucket,
977 0, sizeof(stats[i].bucket));
978 memset(stats[i].min_time,
979 0, sizeof(stats[i].min_time));
980 memset(stats[i].max_time,
981 0, sizeof(stats[i].max_time));
982 stats[i].count = 0;
983 stats[i].total_time = 0;
984 }
985 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700986 msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530987 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700988
989 return count;
990
991write_proc_failed:
992 return ret;
993}
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530994
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995#undef MSM_PM_STATS_RESET
996#endif /* CONFIG_MSM_IDLE_STATS */
997
998
999/******************************************************************************
1000 * Shared Memory Bits
1001 *****************************************************************************/
1002
1003#define DEM_MASTER_BITS_PER_CPU 6
1004
1005/* Power Master State Bits - Per CPU */
1006#define DEM_MASTER_SMSM_RUN \
1007 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1008#define DEM_MASTER_SMSM_RSA \
1009 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1010#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
1011 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1012#define DEM_MASTER_SMSM_SLEEP_EXIT \
1013 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1014#define DEM_MASTER_SMSM_READY \
1015 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1016#define DEM_MASTER_SMSM_SLEEP \
1017 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
1018
1019/* Power Slave State Bits */
1020#define DEM_SLAVE_SMSM_RUN (0x0001)
1021#define DEM_SLAVE_SMSM_PWRC (0x0002)
1022#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
1023#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
1024#define DEM_SLAVE_SMSM_WFPI (0x0010)
1025#define DEM_SLAVE_SMSM_SLEEP (0x0020)
1026#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
1027#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
1028#define DEM_SLAVE_SMSM_RESET (0x0100)
1029#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
1030
1031
1032/******************************************************************************
1033 * Shared Memory Data
1034 *****************************************************************************/
1035
1036#define DEM_MAX_PORT_NAME_LEN (20)
1037
1038struct msm_pm_smem_t {
1039 uint32_t sleep_time;
1040 uint32_t irq_mask;
1041 uint32_t resources_used;
1042 uint32_t reserved1;
1043
1044 uint32_t wakeup_reason;
1045 uint32_t pending_irqs;
1046 uint32_t rpc_prog;
1047 uint32_t rpc_proc;
1048 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
1049 uint32_t reserved2;
1050};
1051
1052
1053/******************************************************************************
1054 *
1055 *****************************************************************************/
1056static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001057static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
1058
1059static int msm_pm_modem_busy(void)
1060{
1061 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1062 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
1063 KERN_INFO, "%s(): master not ready\n", __func__);
1064 return -EBUSY;
1065 }
1066
1067 return 0;
1068}
1069
1070/*
1071 * Power collapse the Apps processor. This function executes the handshake
1072 * protocol with Modem.
1073 *
1074 * Return value:
1075 * -EAGAIN: modem reset occurred or early exit from power collapse
1076 * -EBUSY: modem not ready for our power collapse -- no power loss
1077 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
1078 * 0: success
1079 */
1080static int msm_pm_power_collapse
1081 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
1082{
1083 struct msm_pm_polled_group state_grps[2];
1084 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001085 int collapsed = 0;
1086 int ret;
Murali Nalajala07b04022012-04-10 16:00:49 +05301087 int val;
1088 int modem_early_exit = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001089
1090 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1091 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
1092 (int)from_idle, sleep_delay, sleep_limit);
1093
1094 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
1095 MSM_PM_DPRINTK(
1096 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1097 KERN_INFO, "%s(): master not ready\n", __func__);
1098 ret = -EBUSY;
1099 goto power_collapse_bail;
1100 }
1101
1102 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
1103
Murali Nalajala41786ab2012-03-06 10:47:32 +05301104 if (cpu_is_msm8625()) {
1105 /* Program the SPM */
1106 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
1107 false);
1108 WARN_ON(ret);
1109 }
1110
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301111 msm_pm_irq_extns->enter_sleep1(true, from_idle,
1112 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001113 msm_sirc_enter_sleep();
1114 msm_gpio_enter_sleep(from_idle);
1115
1116 msm_pm_smem_data->sleep_time = sleep_delay;
1117 msm_pm_smem_data->resources_used = sleep_limit;
1118
1119 /* Enter PWRC/PWRC_SUSPEND */
1120
1121 if (from_idle)
1122 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1123 DEM_SLAVE_SMSM_PWRC);
1124 else
1125 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
1126 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
1127
1128 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
1129 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1130
1131 memset(state_grps, 0, sizeof(state_grps));
1132 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1133 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
1134 state_grps[1].group_id = SMSM_MODEM_STATE;
1135 state_grps[1].bits_all_set = SMSM_RESET;
1136
1137 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1138
1139 if (ret < 0) {
1140 printk(KERN_EMERG "%s(): power collapse entry "
1141 "timed out waiting for Modem's response\n", __func__);
1142 msm_pm_timeout();
1143 }
1144
1145 if (ret == 1) {
1146 MSM_PM_DPRINTK(
1147 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1148 KERN_INFO,
1149 "%s(): msm_pm_poll_state detected Modem reset\n",
1150 __func__);
1151 goto power_collapse_early_exit;
1152 }
1153
1154 /* DEM Master in RSA */
1155
1156 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
1157
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301158 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001159 if (ret < 0) {
1160 MSM_PM_DPRINTK(
1161 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1162 KERN_INFO,
1163 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
1164 ret);
1165 goto power_collapse_early_exit;
1166 }
1167
1168 msm_pm_config_hw_before_power_down();
1169 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
1170
1171 saved_acpuclk_rate = acpuclk_power_collapse();
1172 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1173 "%s(): change clock rate (old rate = %lu)\n", __func__,
1174 saved_acpuclk_rate);
1175
1176 if (saved_acpuclk_rate == 0) {
1177 msm_pm_config_hw_after_power_up();
1178 goto power_collapse_early_exit;
1179 }
1180
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001181 msm_pm_boot_config_before_pc(smp_processor_id(),
1182 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001183
1184#ifdef CONFIG_VFP
1185 if (from_idle)
1186 vfp_flush_context();
1187#endif
1188
1189#ifdef CONFIG_CACHE_L2X0
Taniya Das38a8c6e2012-05-09 20:34:39 +05301190 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001191#endif
1192
1193 collapsed = msm_pm_collapse();
Murali Nalajala07b04022012-04-10 16:00:49 +05301194
1195 /*
1196 * TBD: Currently recognise the MODEM early exit
1197 * path by reading the MPA5_GDFS_CNT_VAL register.
1198 */
1199 if (cpu_is_msm8625()) {
1200 /*
Murali Nalajala93e6ed02012-05-13 12:57:22 +05301201 * on system reset, default value of MPA5_GDFS_CNT_VAL
1202 * is = 0x0, later modem reprogram this value to
1203 * 0x00030004. Once APPS did a power collapse and
1204 * coming out of it expected value of this register
1205 * always be 0x00030004. Incase if APPS sees the value
1206 * as 0x00030002 consider this case as a modem early
1207 * exit.
Murali Nalajala07b04022012-04-10 16:00:49 +05301208 */
1209 val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
Murali Nalajala93e6ed02012-05-13 12:57:22 +05301210 if (val != 0x00030002)
Murali Nalajala07b04022012-04-10 16:00:49 +05301211 power_collapsed = 1;
Murali Nalajala93e6ed02012-05-13 12:57:22 +05301212 else
1213 modem_early_exit = 1;
Murali Nalajala07b04022012-04-10 16:00:49 +05301214 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001215
1216#ifdef CONFIG_CACHE_L2X0
Taniya Das38a8c6e2012-05-09 20:34:39 +05301217 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001218#endif
1219
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001220 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001221
1222 if (collapsed) {
1223#ifdef CONFIG_VFP
1224 if (from_idle)
1225 vfp_reinit();
1226#endif
1227 cpu_init();
1228 local_fiq_enable();
1229 }
1230
1231 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1232 KERN_INFO,
1233 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1234
1235 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1236 "%s(): restore clock rate to %lu\n", __func__,
1237 saved_acpuclk_rate);
1238 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1239 SETRATE_PC) < 0)
1240 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1241 __func__, saved_acpuclk_rate);
1242
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301243 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001244 msm_pm_smem_data->wakeup_reason,
1245 msm_pm_smem_data->pending_irqs);
1246
1247 msm_pm_config_hw_after_power_up();
1248 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1249
1250 memset(state_grps, 0, sizeof(state_grps));
1251 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1252 state_grps[0].bits_any_set =
1253 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1254 state_grps[1].group_id = SMSM_MODEM_STATE;
1255 state_grps[1].bits_all_set = SMSM_RESET;
1256
1257 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1258
1259 if (ret < 0) {
1260 printk(KERN_EMERG "%s(): power collapse exit "
1261 "timed out waiting for Modem's response\n", __func__);
1262 msm_pm_timeout();
1263 }
1264
1265 if (ret == 1) {
1266 MSM_PM_DPRINTK(
1267 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1268 KERN_INFO,
1269 "%s(): msm_pm_poll_state detected Modem reset\n",
1270 __func__);
1271 goto power_collapse_early_exit;
1272 }
1273
1274 /* Sanity check */
Murali Nalajala07b04022012-04-10 16:00:49 +05301275 if (collapsed && !modem_early_exit) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001276 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1277 } else {
1278 BUG_ON(!(state_grps[0].value_read &
1279 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1280 goto power_collapse_early_exit;
1281 }
1282
1283 /* Enter WFPI */
1284
1285 smsm_change_state(SMSM_APPS_DEM,
1286 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1287 DEM_SLAVE_SMSM_WFPI);
1288
1289 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1290
1291 memset(state_grps, 0, sizeof(state_grps));
1292 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1293 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1294 state_grps[1].group_id = SMSM_MODEM_STATE;
1295 state_grps[1].bits_all_set = SMSM_RESET;
1296
1297 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1298
1299 if (ret < 0) {
1300 printk(KERN_EMERG "%s(): power collapse WFPI "
1301 "timed out waiting for Modem's response\n", __func__);
1302 msm_pm_timeout();
1303 }
1304
1305 if (ret == 1) {
1306 MSM_PM_DPRINTK(
1307 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1308 KERN_INFO,
1309 "%s(): msm_pm_poll_state detected Modem reset\n",
1310 __func__);
1311 ret = -EAGAIN;
1312 goto power_collapse_restore_gpio_bail;
1313 }
1314
1315 /* DEM Master == RUN */
1316
1317 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1318 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1319
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301320 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001321 msm_pm_smem_data->wakeup_reason,
1322 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301323 msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001324 msm_pm_smem_data->wakeup_reason,
1325 msm_pm_smem_data->pending_irqs);
1326 msm_gpio_exit_sleep();
1327 msm_sirc_exit_sleep();
1328
1329 smsm_change_state(SMSM_APPS_DEM,
1330 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1331
1332 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1333
1334 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301335
1336 if (cpu_is_msm8625()) {
1337 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1338 false);
1339 WARN_ON(ret);
1340 }
1341
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001342 return 0;
1343
1344power_collapse_early_exit:
1345 /* Enter PWRC_EARLY_EXIT */
1346
1347 smsm_change_state(SMSM_APPS_DEM,
1348 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1349 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1350
1351 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1352
1353 memset(state_grps, 0, sizeof(state_grps));
1354 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1355 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1356 state_grps[1].group_id = SMSM_MODEM_STATE;
1357 state_grps[1].bits_all_set = SMSM_RESET;
1358
1359 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1360 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1361
1362 if (ret < 0) {
1363 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1364 "timed out waiting for Modem's response\n", __func__);
1365 msm_pm_timeout();
1366 }
1367
1368 if (ret == 1) {
1369 MSM_PM_DPRINTK(
1370 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1371 KERN_INFO,
1372 "%s(): msm_pm_poll_state detected Modem reset\n",
1373 __func__);
1374 }
1375
1376 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1377
1378 ret = -EAGAIN;
1379
1380power_collapse_restore_gpio_bail:
1381 msm_gpio_exit_sleep();
1382 msm_sirc_exit_sleep();
1383
1384 /* Enter RUN */
1385 smsm_change_state(SMSM_APPS_DEM,
1386 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1387 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1388
1389 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1390
1391 if (collapsed)
1392 smd_sleep_exit();
1393
1394power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301395 if (cpu_is_msm8625()) {
1396 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1397 false);
1398 WARN_ON(ret);
1399 }
1400
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001401 return ret;
1402}
1403
1404/*
1405 * Power collapse the Apps processor without involving Modem.
1406 *
1407 * Return value:
1408 * 0: success
1409 */
Stephen Boydb29750d2012-02-21 01:21:32 -08001410static int __ref msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001411{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001412 int collapsed = 0;
1413 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301414 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001415
1416 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1417 KERN_INFO, "%s()\n", __func__);
1418
1419 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1420 WARN_ON(ret);
1421
Murali Nalajala41786ab2012-03-06 10:47:32 +05301422 entry = (!smp_processor_id() || from_idle) ?
1423 msm_pm_collapse_exit : msm_secondary_startup;
1424
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001425 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301426 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001427
1428#ifdef CONFIG_VFP
1429 vfp_flush_context();
1430#endif
1431
1432#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301433 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301434 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001435#endif
1436
1437 collapsed = msm_pm_collapse();
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_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001442#endif
1443
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001444 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001445
1446 if (collapsed) {
1447#ifdef CONFIG_VFP
1448 vfp_reinit();
1449#endif
1450 cpu_init();
1451 local_fiq_enable();
1452 }
1453
1454 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1455 KERN_INFO,
1456 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1457
1458 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1459 WARN_ON(ret);
1460
Anji jonnalac6816222012-03-31 10:55:14 +05301461 return !collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001462}
1463
1464/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001465 * Bring the Apps processor to SWFI.
1466 *
1467 * Return value:
1468 * -EIO: could not ramp Apps processor clock
1469 * 0: success
1470 */
1471static int msm_pm_swfi(bool ramp_acpu)
1472{
1473 unsigned long saved_acpuclk_rate = 0;
1474
1475 if (ramp_acpu) {
1476 saved_acpuclk_rate = acpuclk_wait_for_irq();
1477 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1478 "%s(): change clock rate (old rate = %lu)\n", __func__,
1479 saved_acpuclk_rate);
1480
1481 if (!saved_acpuclk_rate)
1482 return -EIO;
1483 }
1484
Murali Nalajala41786ab2012-03-06 10:47:32 +05301485 if (!cpu_is_msm8625())
1486 msm_pm_config_hw_before_swfi();
1487
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001488 msm_arch_idle();
1489
1490 if (ramp_acpu) {
1491 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1492 "%s(): restore clock rate to %lu\n", __func__,
1493 saved_acpuclk_rate);
1494 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1495 SETRATE_SWFI) < 0)
1496 printk(KERN_ERR
1497 "%s(): failed to restore clock rate(%lu)\n",
1498 __func__, saved_acpuclk_rate);
1499 }
1500
1501 return 0;
1502}
1503
1504
1505/******************************************************************************
1506 * External Idle/Suspend Functions
1507 *****************************************************************************/
1508
1509/*
1510 * Put CPU in low power mode.
1511 */
1512void arch_idle(void)
1513{
1514 bool allow[MSM_PM_SLEEP_MODE_NR];
1515 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1516
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001517 int64_t timer_expiration;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301518 int latency_qos;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001519 int ret;
1520 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301521 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001522
1523#ifdef CONFIG_MSM_IDLE_STATS
1524 int64_t t1;
Murali Nalajalab86f3702012-03-30 17:54:57 +05301525 static DEFINE_PER_CPU(int64_t, t2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001526 int exit_stat;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301527 #endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001528
1529 if (!atomic_read(&msm_pm_init_done))
1530 return;
1531
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301532 cpu = smp_processor_id();
1533
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001534 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301535 /* get the next timer expiration */
1536 timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001537
1538#ifdef CONFIG_MSM_IDLE_STATS
1539 t1 = ktime_to_ns(ktime_get());
Murali Nalajalab86f3702012-03-30 17:54:57 +05301540 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001541 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301542 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301543#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001544
1545 for (i = 0; i < ARRAY_SIZE(allow); i++)
1546 allow[i] = true;
1547
Murali Nalajala41786ab2012-03-06 10:47:32 +05301548 if (num_online_cpus() > 1 ||
1549 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001550#ifdef CONFIG_HAS_WAKELOCK
1551 has_wake_lock(WAKE_LOCK_IDLE) ||
1552#endif
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301553 !msm_pm_irq_extns->idle_sleep_allowed()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001554 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1555 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001556 }
1557
1558 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301559 struct msm_pm_platform_data *mode =
1560 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001561 if (!mode->idle_supported || !mode->idle_enabled ||
1562 mode->latency >= latency_qos ||
1563 mode->residency * 1000ULL >= timer_expiration)
1564 allow[i] = false;
1565 }
1566
1567 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1568 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1569 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1570 while (msm_pm_modem_busy() && wait_us) {
1571 if (wait_us > 100) {
1572 udelay(100);
1573 wait_us -= 100;
1574 } else {
1575 udelay(wait_us);
1576 wait_us = 0;
1577 }
1578 }
1579
1580 if (msm_pm_modem_busy()) {
1581 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1582 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1583 = false;
1584 }
1585 }
1586
1587 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1588 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1589 __func__, latency_qos, timer_expiration, sleep_limit);
1590
1591 for (i = 0; i < ARRAY_SIZE(allow); i++)
1592 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1593 "%s(): allow %s: %d\n", __func__,
1594 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1595
1596 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1597 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Murali Nalajala8fda4492012-03-19 18:22:59 +05301598 /* Sync the timer with SCLK, it is needed only for modem
1599 * assissted pollapse case.
1600 */
1601 int64_t next_timer_exp = msm_timer_enter_idle();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001602 uint32_t sleep_delay;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301603 bool low_power = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001604
1605 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
Murali Nalajala8fda4492012-03-19 18:22:59 +05301606 next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT);
1607
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001608 if (sleep_delay == 0) /* 0 would mean infinite time */
1609 sleep_delay = 1;
1610
1611 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1612 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1613
1614#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1615 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1616#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1617 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1618#endif
1619
1620 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1621 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301622 msm_timer_exit_idle(low_power);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001623
1624#ifdef CONFIG_MSM_IDLE_STATS
1625 if (ret)
1626 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1627 else {
1628 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1629 msm_pm_sleep_limit = sleep_limit;
1630 }
Murali Nalajala41786ab2012-03-06 10:47:32 +05301631#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001632 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301633 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001634#ifdef CONFIG_MSM_IDLE_STATS
1635 exit_stat = ret ?
1636 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1637 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301638#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001639 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1640 ret = msm_pm_swfi(true);
1641 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301642 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001643 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001644#ifdef CONFIG_MSM_IDLE_STATS
1645 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301646#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001647 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1648 msm_pm_swfi(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001649#ifdef CONFIG_MSM_IDLE_STATS
1650 exit_stat = MSM_PM_STAT_IDLE_WFI;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301651#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001652 } else {
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301653 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001654 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001655#ifdef CONFIG_MSM_IDLE_STATS
1656 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301657#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001658 }
1659
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001660#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajalab86f3702012-03-30 17:54:57 +05301661 __get_cpu_var(t2) = ktime_to_ns(ktime_get());
1662 msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1);
Murali Nalajala41786ab2012-03-06 10:47:32 +05301663#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001664}
1665
1666/*
1667 * Suspend the Apps processor.
1668 *
1669 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301670 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001671 * -EAGAIN: modem reset occurred or early exit from suspend
1672 * -EBUSY: modem not ready for our suspend
1673 * -EINVAL: invalid sleep mode
1674 * -EIO: could not ramp Apps processor clock
1675 * -ETIMEDOUT: timed out waiting for modem's handshake
1676 * 0: success
1677 */
1678static int msm_pm_enter(suspend_state_t state)
1679{
1680 bool allow[MSM_PM_SLEEP_MODE_NR];
1681 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301682 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001683 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001684#ifdef CONFIG_MSM_IDLE_STATS
1685 int64_t period = 0;
1686 int64_t time = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301687#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001688
Murali Nalajala41786ab2012-03-06 10:47:32 +05301689 /* Must executed by CORE0 */
1690 if (smp_processor_id()) {
1691 __WARN();
1692 goto suspend_exit;
1693 }
1694
1695#ifdef CONFIG_MSM_IDLE_STATS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001696 time = msm_timer_get_sclk_time(&period);
1697#endif
1698
1699 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1700 "%s(): sleep limit %u\n", __func__, sleep_limit);
1701
1702 for (i = 0; i < ARRAY_SIZE(allow); i++)
1703 allow[i] = true;
1704
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001705 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301706 struct msm_pm_platform_data *mode;
1707 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001708 if (!mode->suspend_supported || !mode->suspend_enabled)
1709 allow[i] = false;
1710 }
1711
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001712 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1713 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1714#ifdef CONFIG_MSM_IDLE_STATS
1715 enum msm_pm_time_stats_id id;
1716 int64_t end_time;
1717#endif
1718
1719 clock_debug_print_enabled();
1720
1721#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1722 if (msm_pm_sleep_time_override > 0) {
1723 int64_t ns;
1724 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1725 msm_pm_set_max_sleep_time(ns);
1726 msm_pm_sleep_time_override = 0;
1727 }
1728#endif
1729 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1730 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1731
1732#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1733 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1734#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1735 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1736#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1737 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1738 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1739#endif
1740
1741 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1742 udelay(500);
1743
1744 ret = msm_pm_power_collapse(
1745 false, msm_pm_max_sleep_time, sleep_limit);
1746
1747#ifdef CONFIG_MSM_IDLE_STATS
1748 if (ret)
1749 id = MSM_PM_STAT_FAILED_SUSPEND;
1750 else {
1751 id = MSM_PM_STAT_SUSPEND;
1752 msm_pm_sleep_limit = sleep_limit;
1753 }
1754
1755 if (time != 0) {
1756 end_time = msm_timer_get_sclk_time(NULL);
1757 if (end_time != 0) {
1758 time = end_time - time;
1759 if (time < 0)
1760 time += period;
1761 } else
1762 time = 0;
1763 }
1764
1765 msm_pm_add_stat(id, time);
1766#endif
1767 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301768 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001769 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1770 ret = msm_pm_swfi(true);
1771 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301772 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001773 udelay(1);
1774 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1775 msm_pm_swfi(false);
1776 }
1777
Murali Nalajala41786ab2012-03-06 10:47:32 +05301778suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001779 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1780 "%s(): return %d\n", __func__, ret);
1781
1782 return ret;
1783}
1784
1785static struct platform_suspend_ops msm_pm_ops = {
1786 .enter = msm_pm_enter,
1787 .valid = suspend_valid_only_mem,
1788};
1789
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301790/* Hotplug the "non boot" CPU's and put
1791 * the cores into low power mode
1792 */
1793void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1794{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301795 bool allow[MSM_PM_SLEEP_MODE_NR];
1796 int i;
1797
1798 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1799 struct msm_pm_platform_data *mode;
1800
1801 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1802 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1803 }
1804
1805 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1806 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1807
1808 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1809 msm_pm_power_collapse_standalone(false);
1810 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1811 msm_pm_swfi(false);
1812 } else {
1813 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1814 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1815 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301816}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001817
1818/******************************************************************************
1819 * Restart Definitions
1820 *****************************************************************************/
1821
1822static uint32_t restart_reason = 0x776655AA;
1823
1824static void msm_pm_power_off(void)
1825{
1826 msm_rpcrouter_close();
1827 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1828 for (;;)
1829 ;
1830}
1831
1832static void msm_pm_restart(char str, const char *cmd)
1833{
1834 msm_rpcrouter_close();
1835 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1836
1837 for (;;)
1838 ;
1839}
1840
1841static int msm_reboot_call
1842 (struct notifier_block *this, unsigned long code, void *_cmd)
1843{
1844 if ((code == SYS_RESTART) && _cmd) {
1845 char *cmd = _cmd;
1846 if (!strcmp(cmd, "bootloader")) {
1847 restart_reason = 0x77665500;
1848 } else if (!strcmp(cmd, "recovery")) {
1849 restart_reason = 0x77665502;
1850 } else if (!strcmp(cmd, "eraseflash")) {
1851 restart_reason = 0x776655EF;
1852 } else if (!strncmp(cmd, "oem-", 4)) {
1853 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1854 restart_reason = 0x6f656d00 | code;
1855 } else {
1856 restart_reason = 0x77665501;
1857 }
1858 }
1859 return NOTIFY_DONE;
1860}
1861
1862static struct notifier_block msm_reboot_notifier = {
1863 .notifier_call = msm_reboot_call,
1864};
1865
1866
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001867/*
1868 * Initialize the power management subsystem.
1869 *
1870 * Return value:
1871 * -ENODEV: initialization failed
1872 * 0: success
1873 */
1874static int __init msm_pm_init(void)
1875{
1876#ifdef CONFIG_MSM_IDLE_STATS
1877 struct proc_dir_entry *d_entry;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301878 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001879#endif
1880 int ret;
Murali Nalajala93f29992012-03-21 15:59:27 +05301881 int val;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001882#ifdef CONFIG_CPU_V7
1883 pgd_t *pc_pgd;
1884 pmd_t *pmd;
1885 unsigned long pmdval;
1886
1887 /* Page table for cores to come back up safely. */
1888 pc_pgd = pgd_alloc(&init_mm);
1889 if (!pc_pgd)
1890 return -ENOMEM;
1891 pmd = pmd_offset(pc_pgd +
1892 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1893 virt_to_phys(msm_pm_collapse_exit));
1894 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1895 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1896 pmd[0] = __pmd(pmdval);
1897 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1898
Steve Mucklefcece052012-02-18 20:09:58 -08001899 msm_saved_state_phys =
1900 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1901 num_possible_cpus(), 4);
1902 if (!msm_saved_state_phys)
1903 return -ENOMEM;
1904 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1905 CPU_SAVED_STATE_SIZE *
1906 num_possible_cpus());
1907 if (!msm_saved_state)
1908 return -ENOMEM;
1909
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001910 /* It is remotely possible that the code in msm_pm_collapse_exit()
1911 * which turns on the MMU with this mapping is in the
1912 * next even-numbered megabyte beyond the
1913 * start of msm_pm_collapse_exit().
1914 * Map this megabyte in as well.
1915 */
1916 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1917 flush_pmd_entry(pmd);
1918 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001919 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1920 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001921#endif
1922
1923 pm_power_off = msm_pm_power_off;
1924 arm_pm_restart = msm_pm_restart;
1925 register_reboot_notifier(&msm_reboot_notifier);
1926
1927 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1928 sizeof(*msm_pm_smem_data));
1929 if (msm_pm_smem_data == NULL) {
1930 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1931 return -ENODEV;
1932 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001933
1934 ret = msm_timer_init_time_sync(msm_pm_timeout);
1935 if (ret)
1936 return ret;
1937
1938 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1939 if (ret) {
1940 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1941 __func__, ret);
1942 return ret;
1943 }
1944
Murali Nalajala93f29992012-03-21 15:59:27 +05301945 if (cpu_is_msm8625()) {
1946 target_type = TARGET_IS_8625;
1947 clean_caches((unsigned long)&target_type, sizeof(target_type),
1948 virt_to_phys(&target_type));
1949
Anji jonnalae644f8e2012-05-09 19:52:18 +05301950 /*
1951 * Configure the MPA5_GDFS_CNT_VAL register for
1952 * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the
1953 * DBGNOPOWERDN for each cpu.
1954 * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for
1955 * GDFS control.
Murali Nalajala93f29992012-03-21 15:59:27 +05301956 */
Anji jonnalae644f8e2012-05-09 19:52:18 +05301957 val = 0x00030002;
Murali Nalajala93f29992012-03-21 15:59:27 +05301958 __raw_writel(val, (MSM_CFG_CTL_BASE + 0x38));
1959 }
1960
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001961#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1962 /* The wakeup_reason field is overloaded during initialization time
1963 to signal Modem that Apps will control the low power modes of
1964 the memory.
1965 */
1966 msm_pm_smem_data->wakeup_reason = 1;
1967 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1968#endif
1969
1970 BUG_ON(msm_pm_modes == NULL);
1971
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001972 suspend_set_ops(&msm_pm_ops);
1973
1974 msm_pm_mode_sysfs_add();
1975#ifdef CONFIG_MSM_IDLE_STATS
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301976 for_each_possible_cpu(cpu) {
1977 struct msm_pm_time_stats *stats =
1978 per_cpu(msm_pm_stats, cpu).stats;
1979
1980 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1981 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1982 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1983
1984 stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin";
1985 stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
1986 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1987
1988 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1989 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1990 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1991
1992 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1993 "idle-standalone-power-collapse";
1994 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1995 first_bucket_time =
1996 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1997
1998 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
1999 "idle-failed-standalone-power-collapse";
2000 stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].
2001 first_bucket_time =
2002 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2003
Murali Nalajala0df9fee2012-01-12 15:26:09 +05302004 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
2005 "idle-power-collapse";
2006 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
2007 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2008
2009 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
2010 "idle-failed-power-collapse";
2011 stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].
2012 first_bucket_time =
2013 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2014
2015 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
2016 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
2017 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
2018
2019 stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend";
2020 stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
2021 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2022
2023 stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle";
2024 stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
2025 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
2026 }
Murali Nalajala558c0ce2012-03-29 19:42:08 +05302027
2028 atomic_set(&msm_pm_init_done, 1);
2029
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002030 d_entry = create_proc_entry("msm_pm_stats",
2031 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
2032 if (d_entry) {
2033 d_entry->read_proc = msm_pm_read_proc;
2034 d_entry->write_proc = msm_pm_write_proc;
2035 d_entry->data = NULL;
2036 }
2037#endif
2038
2039 return 0;
2040}
2041
2042late_initcall_sync(msm_pm_init);