blob: 748d61241290d265c38fb949841418fc731d2fff [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/pm2.c
2 *
3 * MSM Power Management Routines
4 *
5 * Copyright (C) 2007 Google, Inc.
Murali Nalajala0df9fee2012-01-12 15:26:09 +05306 * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07007 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/clk.h>
22#include <linux/delay.h>
23#include <linux/init.h>
24#include <linux/pm.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070025#include <linux/pm_qos.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <linux/suspend.h>
27#include <linux/reboot.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include <linux/io.h>
Murali Nalajala8fda4492012-03-19 18:22:59 +053029#include <linux/tick.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include <linux/memory.h>
31#ifdef CONFIG_HAS_WAKELOCK
32#include <linux/wakelock.h>
33#endif
34#include <mach/msm_iomap.h>
35#include <mach/system.h>
36#ifdef CONFIG_CPU_V7
37#include <asm/pgtable.h>
38#include <asm/pgalloc.h>
39#endif
Steve Mucklef132c6c2012-06-06 18:30:57 -070040#include <asm/system_misc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041#ifdef CONFIG_CACHE_L2X0
42#include <asm/hardware/cache-l2x0.h>
43#endif
44#ifdef CONFIG_VFP
45#include <asm/vfp.h>
46#endif
47
48#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
49#include <mach/msm_migrate_pages.h>
50#endif
Murali Nalajala41786ab2012-03-06 10:47:32 +053051#include <mach/socinfo.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070052#include <mach/proc_comm.h>
Anji jonnala1f2377c2012-03-27 14:35:55 +053053#include <asm/smp_scu.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054
55#include "smd_private.h"
56#include "smd_rpcrouter.h"
57#include "acpuclock.h"
58#include "clock.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#include "idle.h"
60#include "irq.h"
61#include "gpio.h"
62#include "timer.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080063#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064#include "spm.h"
65#include "sirc.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060066#include "pm-boot.h"
Murali Nalajala19d33a22012-05-18 14:11:19 +053067#include "devices-msm7x2xa.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068
69/******************************************************************************
70 * Debug Definitions
71 *****************************************************************************/
72
73enum {
Murali Nalajalaa7efba12012-02-23 18:13:52 +053074 MSM_PM_DEBUG_SUSPEND = BIT(0),
75 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
76 MSM_PM_DEBUG_STATE = BIT(2),
77 MSM_PM_DEBUG_CLOCK = BIT(3),
78 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
79 MSM_PM_DEBUG_SMSM_STATE = BIT(5),
80 MSM_PM_DEBUG_IDLE = BIT(6),
81 MSM_PM_DEBUG_HOTPLUG = BIT(7),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082};
83
84static int msm_pm_debug_mask;
Taniya Dase30a6b22012-03-20 11:37:45 +053085int power_collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086module_param_named(
87 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
88);
89
90#define MSM_PM_DPRINTK(mask, level, message, ...) \
91 do { \
92 if ((mask) & msm_pm_debug_mask) \
93 printk(level message, ## __VA_ARGS__); \
94 } while (0)
95
96#define MSM_PM_DEBUG_PRINT_STATE(tag) \
97 do { \
98 MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
99 KERN_INFO, "%s: " \
100 "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
101 "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
102 "SMSM_APPS_DEM %x\n", \
103 tag, \
104 __raw_readl(APPS_CLK_SLEEP_EN), \
105 __raw_readl(APPS_PWRDOWN), \
106 smsm_get_state(SMSM_POWER_MASTER_DEM), \
107 smsm_get_state(SMSM_MODEM_STATE), \
108 smsm_get_state(SMSM_APPS_DEM)); \
109 } while (0)
110
111#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
112 do { \
113 if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
114 smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
115 msm_pm_smem_data->resources_used, \
116 msm_pm_smem_data->irq_mask, \
117 msm_pm_smem_data->wakeup_reason, \
118 msm_pm_smem_data->pending_irqs); \
119 } while (0)
120
121
122/******************************************************************************
123 * Sleep Modes and Parameters
124 *****************************************************************************/
125
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
127module_param_named(
128 idle_sleep_min_time, msm_pm_idle_sleep_min_time,
129 int, S_IRUGO | S_IWUSR | S_IWGRP
130);
131
132enum {
133 MSM_PM_MODE_ATTR_SUSPEND,
134 MSM_PM_MODE_ATTR_IDLE,
135 MSM_PM_MODE_ATTR_LATENCY,
136 MSM_PM_MODE_ATTR_RESIDENCY,
137 MSM_PM_MODE_ATTR_NR,
138};
139
140static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
141 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
142 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
143 [MSM_PM_MODE_ATTR_LATENCY] = "latency",
144 [MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
145};
146
147static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
148 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
149 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
151 "ramp_down_and_wfi",
152 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
153 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
154 "power_collapse_no_xo_shutdown",
155 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
156 "standalone_power_collapse",
157};
158
159static struct msm_pm_platform_data *msm_pm_modes;
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530160static struct msm_pm_irq_calls *msm_pm_irq_extns;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530162struct msm_pm_kobj_attribute {
163 unsigned int cpu;
164 struct kobj_attribute ka;
165};
166
167#define GET_CPU_OF_ATTR(attr) \
168 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
169
170struct msm_pm_sysfs_sleep_mode {
171 struct kobject *kobj;
172 struct attribute_group attr_group;
173 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
174 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
175};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176
177/*
178 * Write out the attribute.
179 */
180static ssize_t msm_pm_mode_attr_show(
181 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
182{
183 int ret = -EINVAL;
184 int i;
185
186 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
187 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530188 unsigned int cpu;
189 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190
191 if (msm_pm_sleep_mode_labels[i] == NULL)
192 continue;
193
194 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
195 continue;
196
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530197 cpu = GET_CPU_OF_ATTR(attr);
198 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
199
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 if (!strcmp(attr->attr.name,
201 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530202 u32 arg = mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203 kp.arg = &arg;
204 ret = param_get_ulong(buf, &kp);
205 } else if (!strcmp(attr->attr.name,
206 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530207 u32 arg = mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 kp.arg = &arg;
209 ret = param_get_ulong(buf, &kp);
210 } else if (!strcmp(attr->attr.name,
211 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530212 u32 arg = mode->latency;
213 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 ret = param_get_ulong(buf, &kp);
215 } else if (!strcmp(attr->attr.name,
216 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530217 u32 arg = mode->residency;
218 kp.arg = &arg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 ret = param_get_ulong(buf, &kp);
220 }
221
222 break;
223 }
224
225 if (ret > 0) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530226 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 ret++;
228 }
229
230 return ret;
231}
232
233/*
234 * Read in the new attribute value.
235 */
236static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
237 struct kobj_attribute *attr, const char *buf, size_t count)
238{
239 int ret = -EINVAL;
240 int i;
241
242 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
243 struct kernel_param kp;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530244 unsigned int cpu;
245 struct msm_pm_platform_data *mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246
247 if (msm_pm_sleep_mode_labels[i] == NULL)
248 continue;
249
250 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
251 continue;
252
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530253 cpu = GET_CPU_OF_ATTR(attr);
254 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
255
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 if (!strcmp(attr->attr.name,
257 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530258 kp.arg = &mode->suspend_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259 ret = param_set_byte(buf, &kp);
260 } else if (!strcmp(attr->attr.name,
261 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530262 kp.arg = &mode->idle_enabled;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 ret = param_set_byte(buf, &kp);
264 } else if (!strcmp(attr->attr.name,
265 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530266 kp.arg = &mode->latency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 ret = param_set_ulong(buf, &kp);
268 } else if (!strcmp(attr->attr.name,
269 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530270 kp.arg = &mode->residency;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271 ret = param_set_ulong(buf, &kp);
272 }
273
274 break;
275 }
276
277 return ret ? ret : count;
278}
279
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530280 /* Add sysfs entries for one cpu. */
281static int __init msm_pm_mode_sysfs_add_cpu(
282 unsigned int cpu, struct kobject *modes_kobj)
283{
284 char cpu_name[8];
285 struct kobject *cpu_kobj;
286 struct msm_pm_sysfs_sleep_mode *mode = NULL;
287 int i, j, k;
288 int ret;
289
290 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
291 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
292 if (!cpu_kobj) {
293 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
294 ret = -ENOMEM;
295 goto mode_sysfs_add_cpu_exit;
296 }
297
298 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
299 int idx = MSM_PM_MODE(cpu, i);
300
301 if ((!msm_pm_modes[idx].suspend_supported) &&
302 (!msm_pm_modes[idx].idle_supported))
303 continue;
304
305 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
306 if (!mode) {
307 pr_err("%s: cannot allocate memory for attributes\n",
308 __func__);
309 ret = -ENOMEM;
310 goto mode_sysfs_add_cpu_exit;
311 }
312
313 mode->kobj = kobject_create_and_add(
314 msm_pm_sleep_mode_labels[i], cpu_kobj);
315 if (!mode->kobj) {
316 pr_err("%s: cannot create kobject\n", __func__);
317 ret = -ENOMEM;
318 goto mode_sysfs_add_cpu_exit;
319 }
320
321 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
322 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
323 !msm_pm_modes[idx].idle_supported)
324 continue;
325 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
326 !msm_pm_modes[idx].suspend_supported)
327 continue;
328 mode->kas[j].cpu = cpu;
329 mode->kas[j].ka.attr.mode = 0644;
330 mode->kas[j].ka.show = msm_pm_mode_attr_show;
331 mode->kas[j].ka.store = msm_pm_mode_attr_store;
332 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
333 mode->attrs[j] = &mode->kas[j].ka.attr;
334 j++;
335 }
336 mode->attrs[j] = NULL;
337
338 mode->attr_group.attrs = mode->attrs;
339 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
340 if (ret) {
341 printk(KERN_ERR
342 "%s: cannot create kobject attribute group\n",
343 __func__);
344 goto mode_sysfs_add_cpu_exit;
345 }
346 }
347
348 ret = 0;
349
350mode_sysfs_add_cpu_exit:
351 if (ret) {
352 if (mode && mode->kobj)
353 kobject_del(mode->kobj);
354 kfree(mode);
355 }
356
357 return ret;
358}
359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360/*
361 * Add sysfs entries for the sleep modes.
362 */
363static int __init msm_pm_mode_sysfs_add(void)
364{
365 struct kobject *module_kobj = NULL;
366 struct kobject *modes_kobj = NULL;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530367 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 int ret;
369
370 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
371 if (!module_kobj) {
372 printk(KERN_ERR "%s: cannot find kobject for module %s\n",
373 __func__, KBUILD_MODNAME);
374 ret = -ENOENT;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530375 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376 }
377
378 modes_kobj = kobject_create_and_add("modes", module_kobj);
379 if (!modes_kobj) {
380 printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
381 ret = -ENOMEM;
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530382 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 }
384
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530385 for_each_possible_cpu(cpu) {
386 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
387 if (ret)
388 goto mode_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 }
390
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530391 ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530393mode_sysfs_add_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 return ret;
395}
396
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700397s32 msm_cpuidle_get_deep_idle_latency(void)
398{
399 int i = MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN);
400 return msm_pm_modes[i].latency - 1;
401}
402
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403void __init msm_pm_set_platform_data(
404 struct msm_pm_platform_data *data, int count)
405{
Murali Nalajala0df9fee2012-01-12 15:26:09 +0530406 BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 msm_pm_modes = data;
408}
409
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530410void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls)
411{
412 /* sanity check */
413 BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL ||
414 irq_calls->idle_sleep_allowed == NULL ||
415 irq_calls->enter_sleep1 == NULL ||
416 irq_calls->enter_sleep2 == NULL ||
417 irq_calls->exit_sleep1 == NULL ||
418 irq_calls->exit_sleep2 == NULL ||
419 irq_calls->exit_sleep3 == NULL);
420
421 msm_pm_irq_extns = irq_calls;
422}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423
424/******************************************************************************
425 * Sleep Limitations
426 *****************************************************************************/
427enum {
428 SLEEP_LIMIT_NONE = 0,
429 SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
430 SLEEP_LIMIT_MASK = 0x03,
431};
432
Praveen Chidambaram3895bde2012-05-14 19:42:40 +0530433static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
435enum {
436 SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
437 SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
438};
439#endif
440
441
442/******************************************************************************
443 * Configure Hardware for Power Down/Up
444 *****************************************************************************/
445
446#if defined(CONFIG_ARCH_MSM7X30)
Taniya Das298de8c2012-02-16 11:45:31 +0530447#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020)
448#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449#define APPS_SECOP (MSM_TCSR_BASE + 0x038)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530450#define APPS_STANDBY_CTL NULL
451#else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
453#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440)
454#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108)
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530455#define APPS_SECOP NULL
456#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457
458/*
459 * Configure hardware registers in preparation for Apps power down.
460 */
461static void msm_pm_config_hw_before_power_down(void)
462{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530463 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530464 __raw_writel(4, APPS_SECOP);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530465 } else if (cpu_is_msm7x27()) {
466 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530467 } 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);
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530471 } else if (cpu_is_qsd8x50()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530472 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
473 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530474 __raw_writel(0, APPS_STANDBY_CTL);
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530475 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530476 mb();
477 __raw_writel(1, APPS_PWRDOWN);
478 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479}
480
481/*
Anji jonnala1f2377c2012-03-27 14:35:55 +0530482 * Program the top csr from core0 context to put the
483 * core1 into GDFS, as core1 is not running yet.
484 */
485static void configure_top_csr(void)
486{
487 void __iomem *base_ptr;
488 unsigned int value = 0;
489
Murali Nalajala19d33a22012-05-18 14:11:19 +0530490 base_ptr = core1_reset_base();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530491 if (!base_ptr)
492 return;
493
494 /* bring the core1 out of reset */
495 __raw_writel(0x3, base_ptr);
496 mb();
497 /*
498 * override DBGNOPOWERDN and program the GDFS
499 * count val
500 */
501
502 __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38));
503 mb();
504
505 /* Initialize the SPM0 and SPM1 registers */
506 msm_spm_reinit();
507
508 /* enable TCSR for core1 */
509 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
510 value |= BIT(22);
511 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
512 mb();
513
514 /* set reset bit for SPM1 */
515 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
516 value |= BIT(20);
517 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
518 mb();
519
520 /* set CLK_OFF bit */
521 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
522 value |= BIT(18);
523 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
524 mb();
525
526 /* set clamps bit */
527 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
528 value |= BIT(21);
529 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
530 mb();
531
532 /* set power_up bit */
533 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
534 value |= BIT(19);
535 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
536 mb();
537
538 /* Disable TSCR for core0 */
539 value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
540 value &= ~BIT(22);
541 __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
542 mb();
543 __raw_writel(0x0, base_ptr);
544 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530545}
546
547/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 * Clear hardware registers after Apps powers up.
549 */
550static void msm_pm_config_hw_after_power_up(void)
551{
Anji jonnala1f2377c2012-03-27 14:35:55 +0530552
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530553 if (cpu_is_msm7x30() || cpu_is_msm8x55()) {
554 __raw_writel(0, APPS_SECOP);
555 mb();
556 __raw_writel(0, APPS_PWRDOWN);
557 mb();
558 msm_spm_reinit();
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530559 } else if (cpu_is_msm8625()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530560 __raw_writel(0, APPS_PWRDOWN);
561 mb();
Anji jonnala1f2377c2012-03-27 14:35:55 +0530562
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530563 if (power_collapsed) {
Anji jonnala1f2377c2012-03-27 14:35:55 +0530564 /*
565 * enable the SCU while coming out of power
566 * collapse.
567 */
568 scu_enable(MSM_SCU_BASE);
569 /*
570 * Program the top csr to put the core1 into GDFS.
571 */
572 configure_top_csr();
573 }
Murali Nalajalab87e88c2012-05-18 15:12:13 +0530574 } else {
575 __raw_writel(0, APPS_PWRDOWN);
576 mb();
577 __raw_writel(0, APPS_CLK_SLEEP_EN);
578 mb();
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530579 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580}
581
582/*
583 * Configure hardware registers in preparation for SWFI.
584 */
585static void msm_pm_config_hw_before_swfi(void)
586{
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530587 if (cpu_is_qsd8x50()) {
588 __raw_writel(0x1f, APPS_CLK_SLEEP_EN);
589 mb();
590 } else if (cpu_is_msm7x27()) {
591 __raw_writel(0x0f, APPS_CLK_SLEEP_EN);
592 mb();
593 } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
Pankaj Kumarfee56a82012-04-17 14:26:49 +0530594 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
595 cpu_is_msm7x25ab()) {
Murali Nalajala1bc1d7c2012-02-09 11:18:42 +0530596 __raw_writel(0x7, APPS_CLK_SLEEP_EN);
597 mb();
598 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599}
600
601/*
602 * Respond to timing out waiting for Modem
603 *
604 * NOTE: The function never returns.
605 */
606static void msm_pm_timeout(void)
607{
608#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
609 printk(KERN_EMERG "%s(): resetting chip\n", __func__);
610 msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
611#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
612 printk(KERN_EMERG "%s(): resetting modem\n", __func__);
613 msm_proc_comm_reset_modem_now();
614#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
615 printk(KERN_EMERG "%s(): halting\n", __func__);
616#endif
617 for (;;)
618 ;
619}
620
621
622/******************************************************************************
623 * State Polling Definitions
624 *****************************************************************************/
625
626struct msm_pm_polled_group {
627 uint32_t group_id;
628
629 uint32_t bits_all_set;
630 uint32_t bits_all_clear;
631 uint32_t bits_any_set;
632 uint32_t bits_any_clear;
633
634 uint32_t value_read;
635};
636
637/*
638 * Return true if all bits indicated by flag are set in source.
639 */
640static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
641{
642 return (source & flag) == flag;
643}
644
645/*
646 * Return true if any bit indicated by flag are set in source.
647 */
648static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
649{
650 return !flag || (source & flag);
651}
652
653/*
654 * Return true if all bits indicated by flag are cleared in source.
655 */
656static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
657{
658 return (~source & flag) == flag;
659}
660
661/*
662 * Return true if any bit indicated by flag are cleared in source.
663 */
664static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
665{
666 return !flag || (~source & flag);
667}
668
669/*
670 * Poll the shared memory states as indicated by the poll groups.
671 *
672 * nr_grps: number of groups in the array
673 * grps: array of groups
674 *
675 * The function returns when conditions specified by any of the poll
676 * groups become true. The conditions specified by a poll group are
677 * deemed true when 1) at least one bit from bits_any_set is set OR one
678 * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
679 * are set; and 3) all bits in bits_all_clear are cleared.
680 *
681 * Return value:
682 * >=0: index of the poll group whose conditions have become true
683 * -ETIMEDOUT: timed out
684 */
685static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
686{
687 int i, k;
688
689 for (i = 0; i < 50000; i++) {
690 for (k = 0; k < nr_grps; k++) {
691 bool all_set, all_clear;
692 bool any_set, any_clear;
693
694 grps[k].value_read = smsm_get_state(grps[k].group_id);
695
696 all_set = msm_pm_all_set(grps[k].value_read,
697 grps[k].bits_all_set);
698 all_clear = msm_pm_all_clear(grps[k].value_read,
699 grps[k].bits_all_clear);
700 any_set = msm_pm_any_set(grps[k].value_read,
701 grps[k].bits_any_set);
702 any_clear = msm_pm_any_clear(grps[k].value_read,
703 grps[k].bits_any_clear);
704
705 if (all_set && all_clear && (any_set || any_clear))
706 return k;
707 }
708 udelay(50);
709 }
710
711 printk(KERN_ERR "%s failed:\n", __func__);
712 for (k = 0; k < nr_grps; k++)
713 printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
714 grps[k].bits_all_set, grps[k].bits_all_clear,
715 grps[k].bits_any_set, grps[k].bits_any_clear,
716 grps[k].value_read);
717
718 return -ETIMEDOUT;
719}
720
721
722/******************************************************************************
723 * Suspend Max Sleep Time
724 *****************************************************************************/
725
726#define SCLK_HZ (32768)
727#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
728
729#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
730static int msm_pm_sleep_time_override;
731module_param_named(sleep_time_override,
732 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
733#endif
734
735static uint32_t msm_pm_max_sleep_time;
736
737/*
738 * Convert time from nanoseconds to slow clock ticks, then cap it to the
739 * specified limit
740 */
741static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
742{
743 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
744 return (time_ns > limit) ? limit : time_ns;
745}
746
747/*
748 * Set the sleep time for suspend. 0 means infinite sleep time.
749 */
750void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
751{
752 unsigned long flags;
753
754 local_irq_save(flags);
755 if (max_sleep_time_ns == 0) {
756 msm_pm_max_sleep_time = 0;
757 } else {
758 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
759 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
760
761 if (msm_pm_max_sleep_time == 0)
762 msm_pm_max_sleep_time = 1;
763 }
764
765 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
766 "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
767 max_sleep_time_ns, msm_pm_max_sleep_time);
768 local_irq_restore(flags);
769}
770EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
771
772
773/******************************************************************************
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700774 * Shared Memory Bits
775 *****************************************************************************/
776
777#define DEM_MASTER_BITS_PER_CPU 6
778
779/* Power Master State Bits - Per CPU */
780#define DEM_MASTER_SMSM_RUN \
781 (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
782#define DEM_MASTER_SMSM_RSA \
783 (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
784#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
785 (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
786#define DEM_MASTER_SMSM_SLEEP_EXIT \
787 (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
788#define DEM_MASTER_SMSM_READY \
789 (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
790#define DEM_MASTER_SMSM_SLEEP \
791 (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
792
793/* Power Slave State Bits */
794#define DEM_SLAVE_SMSM_RUN (0x0001)
795#define DEM_SLAVE_SMSM_PWRC (0x0002)
796#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004)
797#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008)
798#define DEM_SLAVE_SMSM_WFPI (0x0010)
799#define DEM_SLAVE_SMSM_SLEEP (0x0020)
800#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040)
801#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080)
802#define DEM_SLAVE_SMSM_RESET (0x0100)
803#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200)
804
805
806/******************************************************************************
807 * Shared Memory Data
808 *****************************************************************************/
809
810#define DEM_MAX_PORT_NAME_LEN (20)
811
812struct msm_pm_smem_t {
813 uint32_t sleep_time;
814 uint32_t irq_mask;
815 uint32_t resources_used;
816 uint32_t reserved1;
817
818 uint32_t wakeup_reason;
819 uint32_t pending_irqs;
820 uint32_t rpc_prog;
821 uint32_t rpc_proc;
822 char smd_port_name[DEM_MAX_PORT_NAME_LEN];
823 uint32_t reserved2;
824};
825
826
827/******************************************************************************
828 *
829 *****************************************************************************/
830static struct msm_pm_smem_t *msm_pm_smem_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700831static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
832
833static int msm_pm_modem_busy(void)
834{
835 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
836 MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
837 KERN_INFO, "%s(): master not ready\n", __func__);
838 return -EBUSY;
839 }
840
841 return 0;
842}
843
844/*
845 * Power collapse the Apps processor. This function executes the handshake
846 * protocol with Modem.
847 *
848 * Return value:
849 * -EAGAIN: modem reset occurred or early exit from power collapse
850 * -EBUSY: modem not ready for our power collapse -- no power loss
851 * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
852 * 0: success
853 */
854static int msm_pm_power_collapse
855 (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
856{
857 struct msm_pm_polled_group state_grps[2];
858 unsigned long saved_acpuclk_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859 int collapsed = 0;
860 int ret;
Murali Nalajala07b04022012-04-10 16:00:49 +0530861 int val;
862 int modem_early_exit = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700863
864 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
865 KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
866 (int)from_idle, sleep_delay, sleep_limit);
867
868 if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
869 MSM_PM_DPRINTK(
870 MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
871 KERN_INFO, "%s(): master not ready\n", __func__);
872 ret = -EBUSY;
873 goto power_collapse_bail;
874 }
875
876 memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
877
Murali Nalajala41786ab2012-03-06 10:47:32 +0530878 if (cpu_is_msm8625()) {
879 /* Program the SPM */
880 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE,
881 false);
882 WARN_ON(ret);
883 }
884
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530885 msm_pm_irq_extns->enter_sleep1(true, from_idle,
886 &msm_pm_smem_data->irq_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 msm_sirc_enter_sleep();
888 msm_gpio_enter_sleep(from_idle);
889
890 msm_pm_smem_data->sleep_time = sleep_delay;
891 msm_pm_smem_data->resources_used = sleep_limit;
892
893 /* Enter PWRC/PWRC_SUSPEND */
894
895 if (from_idle)
896 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
897 DEM_SLAVE_SMSM_PWRC);
898 else
899 smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
900 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
901
902 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
903 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
904
905 memset(state_grps, 0, sizeof(state_grps));
906 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
907 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
908 state_grps[1].group_id = SMSM_MODEM_STATE;
909 state_grps[1].bits_all_set = SMSM_RESET;
910
911 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
912
913 if (ret < 0) {
914 printk(KERN_EMERG "%s(): power collapse entry "
915 "timed out waiting for Modem's response\n", __func__);
916 msm_pm_timeout();
917 }
918
919 if (ret == 1) {
920 MSM_PM_DPRINTK(
921 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
922 KERN_INFO,
923 "%s(): msm_pm_poll_state detected Modem reset\n",
924 __func__);
925 goto power_collapse_early_exit;
926 }
927
928 /* DEM Master in RSA */
929
930 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
931
Murali Nalajala2a0bbda2012-03-28 12:12:54 +0530932 ret = msm_pm_irq_extns->enter_sleep2(true, from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700933 if (ret < 0) {
934 MSM_PM_DPRINTK(
935 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
936 KERN_INFO,
937 "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
938 ret);
939 goto power_collapse_early_exit;
940 }
941
942 msm_pm_config_hw_before_power_down();
943 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
944
945 saved_acpuclk_rate = acpuclk_power_collapse();
946 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
947 "%s(): change clock rate (old rate = %lu)\n", __func__,
948 saved_acpuclk_rate);
949
950 if (saved_acpuclk_rate == 0) {
951 msm_pm_config_hw_after_power_up();
952 goto power_collapse_early_exit;
953 }
954
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600955 msm_pm_boot_config_before_pc(smp_processor_id(),
956 virt_to_phys(msm_pm_collapse_exit));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957
958#ifdef CONFIG_VFP
959 if (from_idle)
Steve Mucklef132c6c2012-06-06 18:30:57 -0700960 vfp_pm_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961#endif
962
963#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530964 if (!cpu_is_msm8625())
965 l2cc_suspend();
966 else
967 apps_power_collapse = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968#endif
969
970 collapsed = msm_pm_collapse();
Murali Nalajala07b04022012-04-10 16:00:49 +0530971
972 /*
973 * TBD: Currently recognise the MODEM early exit
974 * path by reading the MPA5_GDFS_CNT_VAL register.
975 */
976 if (cpu_is_msm8625()) {
977 /*
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530978 * on system reset, default value of MPA5_GDFS_CNT_VAL
979 * is = 0x0, later modem reprogram this value to
980 * 0x00030004. Once APPS did a power collapse and
981 * coming out of it expected value of this register
982 * always be 0x00030004. Incase if APPS sees the value
983 * as 0x00030002 consider this case as a modem early
984 * exit.
Murali Nalajala07b04022012-04-10 16:00:49 +0530985 */
986 val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530987 if (val != 0x00030002)
Murali Nalajala07b04022012-04-10 16:00:49 +0530988 power_collapsed = 1;
Murali Nalajala93e6ed02012-05-13 12:57:22 +0530989 else
990 modem_early_exit = 1;
Murali Nalajala07b04022012-04-10 16:00:49 +0530991 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700992
993#ifdef CONFIG_CACHE_L2X0
Murali Nalajala73c13332012-05-15 11:30:59 +0530994 if (!cpu_is_msm8625())
995 l2cc_resume();
996 else
997 apps_power_collapse = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998#endif
999
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001000 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001001
1002 if (collapsed) {
1003#ifdef CONFIG_VFP
1004 if (from_idle)
Steve Mucklef132c6c2012-06-06 18:30:57 -07001005 vfp_pm_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001006#endif
1007 cpu_init();
1008 local_fiq_enable();
1009 }
1010
1011 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1012 KERN_INFO,
1013 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1014
1015 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1016 "%s(): restore clock rate to %lu\n", __func__,
1017 saved_acpuclk_rate);
1018 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1019 SETRATE_PC) < 0)
1020 printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
1021 __func__, saved_acpuclk_rate);
1022
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301023 msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001024 msm_pm_smem_data->wakeup_reason,
1025 msm_pm_smem_data->pending_irqs);
1026
1027 msm_pm_config_hw_after_power_up();
1028 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
1029
1030 memset(state_grps, 0, sizeof(state_grps));
1031 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1032 state_grps[0].bits_any_set =
1033 DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1034 state_grps[1].group_id = SMSM_MODEM_STATE;
1035 state_grps[1].bits_all_set = SMSM_RESET;
1036
1037 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1038
1039 if (ret < 0) {
1040 printk(KERN_EMERG "%s(): power collapse exit "
1041 "timed out waiting for Modem's response\n", __func__);
1042 msm_pm_timeout();
1043 }
1044
1045 if (ret == 1) {
1046 MSM_PM_DPRINTK(
1047 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1048 KERN_INFO,
1049 "%s(): msm_pm_poll_state detected Modem reset\n",
1050 __func__);
1051 goto power_collapse_early_exit;
1052 }
1053
1054 /* Sanity check */
Murali Nalajala07b04022012-04-10 16:00:49 +05301055 if (collapsed && !modem_early_exit) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001056 BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
1057 } else {
1058 BUG_ON(!(state_grps[0].value_read &
1059 DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
1060 goto power_collapse_early_exit;
1061 }
1062
1063 /* Enter WFPI */
1064
1065 smsm_change_state(SMSM_APPS_DEM,
1066 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1067 DEM_SLAVE_SMSM_WFPI);
1068
1069 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
1070
1071 memset(state_grps, 0, sizeof(state_grps));
1072 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1073 state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
1074 state_grps[1].group_id = SMSM_MODEM_STATE;
1075 state_grps[1].bits_all_set = SMSM_RESET;
1076
1077 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1078
1079 if (ret < 0) {
1080 printk(KERN_EMERG "%s(): power collapse WFPI "
1081 "timed out waiting for Modem's response\n", __func__);
1082 msm_pm_timeout();
1083 }
1084
1085 if (ret == 1) {
1086 MSM_PM_DPRINTK(
1087 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1088 KERN_INFO,
1089 "%s(): msm_pm_poll_state detected Modem reset\n",
1090 __func__);
1091 ret = -EAGAIN;
1092 goto power_collapse_restore_gpio_bail;
1093 }
1094
1095 /* DEM Master == RUN */
1096
1097 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
1098 MSM_PM_DEBUG_PRINT_SLEEP_INFO();
1099
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301100 msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001101 msm_pm_smem_data->wakeup_reason,
1102 msm_pm_smem_data->pending_irqs);
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301103 msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001104 msm_pm_smem_data->wakeup_reason,
1105 msm_pm_smem_data->pending_irqs);
1106 msm_gpio_exit_sleep();
1107 msm_sirc_exit_sleep();
1108
1109 smsm_change_state(SMSM_APPS_DEM,
1110 DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
1111
1112 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1113
1114 smd_sleep_exit();
Murali Nalajala41786ab2012-03-06 10:47:32 +05301115
1116 if (cpu_is_msm8625()) {
1117 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1118 false);
1119 WARN_ON(ret);
1120 }
1121
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001122 return 0;
1123
1124power_collapse_early_exit:
1125 /* Enter PWRC_EARLY_EXIT */
1126
1127 smsm_change_state(SMSM_APPS_DEM,
1128 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
1129 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
1130
1131 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
1132
1133 memset(state_grps, 0, sizeof(state_grps));
1134 state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
1135 state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
1136 state_grps[1].group_id = SMSM_MODEM_STATE;
1137 state_grps[1].bits_all_set = SMSM_RESET;
1138
1139 ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
1140 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
1141
1142 if (ret < 0) {
1143 printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
1144 "timed out waiting for Modem's response\n", __func__);
1145 msm_pm_timeout();
1146 }
1147
1148 if (ret == 1) {
1149 MSM_PM_DPRINTK(
1150 MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1151 KERN_INFO,
1152 "%s(): msm_pm_poll_state detected Modem reset\n",
1153 __func__);
1154 }
1155
1156 /* DEM Master == RESET or PWRC_EARLY_EXIT */
1157
1158 ret = -EAGAIN;
1159
1160power_collapse_restore_gpio_bail:
1161 msm_gpio_exit_sleep();
1162 msm_sirc_exit_sleep();
1163
1164 /* Enter RUN */
1165 smsm_change_state(SMSM_APPS_DEM,
1166 DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
1167 DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
1168
1169 MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
1170
1171 if (collapsed)
1172 smd_sleep_exit();
1173
1174power_collapse_bail:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301175 if (cpu_is_msm8625()) {
1176 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING,
1177 false);
1178 WARN_ON(ret);
1179 }
1180
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001181 return ret;
1182}
1183
1184/*
1185 * Power collapse the Apps processor without involving Modem.
1186 *
1187 * Return value:
1188 * 0: success
1189 */
Stephen Boydb29750d2012-02-21 01:21:32 -08001190static int __ref msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001191{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001192 int collapsed = 0;
1193 int ret;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301194 void *entry;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001195
1196 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
1197 KERN_INFO, "%s()\n", __func__);
1198
1199 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
1200 WARN_ON(ret);
1201
Murali Nalajala41786ab2012-03-06 10:47:32 +05301202 entry = (!smp_processor_id() || from_idle) ?
1203 msm_pm_collapse_exit : msm_secondary_startup;
1204
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001205 msm_pm_boot_config_before_pc(smp_processor_id(),
Murali Nalajala41786ab2012-03-06 10:47:32 +05301206 virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001207
1208#ifdef CONFIG_VFP
Steve Mucklef132c6c2012-06-06 18:30:57 -07001209 vfp_pm_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001210#endif
1211
1212#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301213 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301214 l2cc_suspend();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001215#endif
1216
1217 collapsed = msm_pm_collapse();
1218
1219#ifdef CONFIG_CACHE_L2X0
Murali Nalajala41786ab2012-03-06 10:47:32 +05301220 if (!cpu_is_msm8625())
Taniya Das38a8c6e2012-05-09 20:34:39 +05301221 l2cc_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001222#endif
1223
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06001224 msm_pm_boot_config_after_pc(smp_processor_id());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001225
1226 if (collapsed) {
1227#ifdef CONFIG_VFP
Steve Mucklef132c6c2012-06-06 18:30:57 -07001228 vfp_pm_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001229#endif
1230 cpu_init();
1231 local_fiq_enable();
1232 }
1233
1234 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
1235 KERN_INFO,
1236 "%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
1237
1238 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
1239 WARN_ON(ret);
1240
Anji jonnalac6816222012-03-31 10:55:14 +05301241 return !collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001242}
1243
1244/*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001245 * Bring the Apps processor to SWFI.
1246 *
1247 * Return value:
1248 * -EIO: could not ramp Apps processor clock
1249 * 0: success
1250 */
1251static int msm_pm_swfi(bool ramp_acpu)
1252{
1253 unsigned long saved_acpuclk_rate = 0;
1254
1255 if (ramp_acpu) {
1256 saved_acpuclk_rate = acpuclk_wait_for_irq();
1257 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1258 "%s(): change clock rate (old rate = %lu)\n", __func__,
1259 saved_acpuclk_rate);
1260
1261 if (!saved_acpuclk_rate)
1262 return -EIO;
1263 }
1264
Murali Nalajala41786ab2012-03-06 10:47:32 +05301265 if (!cpu_is_msm8625())
1266 msm_pm_config_hw_before_swfi();
1267
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001268 msm_arch_idle();
1269
1270 if (ramp_acpu) {
1271 MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
1272 "%s(): restore clock rate to %lu\n", __func__,
1273 saved_acpuclk_rate);
1274 if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
1275 SETRATE_SWFI) < 0)
1276 printk(KERN_ERR
1277 "%s(): failed to restore clock rate(%lu)\n",
1278 __func__, saved_acpuclk_rate);
1279 }
1280
1281 return 0;
1282}
1283
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301284static int64_t msm_pm_timer_enter_suspend(int64_t *period)
1285{
1286 int time = 0;
1287
1288 time = msm_timer_get_sclk_time(period);
1289 if (!time)
1290 pr_err("%s: Unable to read sclk.\n", __func__);
1291 return time;
1292}
1293
1294static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period)
1295{
1296
1297 if (time != 0) {
1298 int64_t end_time = msm_timer_get_sclk_time(NULL);
1299 if (end_time != 0) {
1300 time = end_time - time;
1301 if (time < 0)
1302 time += period;
1303 } else
1304 time = 0;
1305 }
1306 return time;
1307}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001308
1309/******************************************************************************
1310 * External Idle/Suspend Functions
1311 *****************************************************************************/
1312
1313/*
1314 * Put CPU in low power mode.
1315 */
1316void arch_idle(void)
1317{
1318 bool allow[MSM_PM_SLEEP_MODE_NR];
1319 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
1320
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001321 int64_t timer_expiration;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301322 int latency_qos;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001323 int ret;
1324 int i;
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301325 unsigned int cpu;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001326 int64_t t1;
Murali Nalajalab86f3702012-03-30 17:54:57 +05301327 static DEFINE_PER_CPU(int64_t, t2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001328 int exit_stat;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001329
1330 if (!atomic_read(&msm_pm_init_done))
1331 return;
1332
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301333 cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001334 latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301335 /* get the next timer expiration */
1336 timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length());
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001337
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001338 t1 = ktime_to_ns(ktime_get());
Murali Nalajalab86f3702012-03-30 17:54:57 +05301339 msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001340 msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
Murali Nalajala7744d162012-01-13 13:06:03 +05301341 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001342
1343 for (i = 0; i < ARRAY_SIZE(allow); i++)
1344 allow[i] = true;
1345
Murali Nalajala41786ab2012-03-06 10:47:32 +05301346 if (num_online_cpus() > 1 ||
1347 (timer_expiration < msm_pm_idle_sleep_min_time) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001348#ifdef CONFIG_HAS_WAKELOCK
1349 has_wake_lock(WAKE_LOCK_IDLE) ||
1350#endif
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301351 !msm_pm_irq_extns->idle_sleep_allowed()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001352 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1353 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001354 }
1355
1356 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301357 struct msm_pm_platform_data *mode =
1358 &msm_pm_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001359 if (!mode->idle_supported || !mode->idle_enabled ||
1360 mode->latency >= latency_qos ||
1361 mode->residency * 1000ULL >= timer_expiration)
1362 allow[i] = false;
1363 }
1364
1365 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1366 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
1367 uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
1368 while (msm_pm_modem_busy() && wait_us) {
1369 if (wait_us > 100) {
1370 udelay(100);
1371 wait_us -= 100;
1372 } else {
1373 udelay(wait_us);
1374 wait_us = 0;
1375 }
1376 }
1377
1378 if (msm_pm_modem_busy()) {
1379 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
1380 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
1381 = false;
1382 }
1383 }
1384
1385 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1386 "%s(): latency qos %d, next timer %lld, sleep limit %u\n",
1387 __func__, latency_qos, timer_expiration, sleep_limit);
1388
1389 for (i = 0; i < ARRAY_SIZE(allow); i++)
1390 MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
1391 "%s(): allow %s: %d\n", __func__,
1392 msm_pm_sleep_mode_labels[i], (int)allow[i]);
1393
1394 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1395 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Murali Nalajala8fda4492012-03-19 18:22:59 +05301396 /* Sync the timer with SCLK, it is needed only for modem
1397 * assissted pollapse case.
1398 */
1399 int64_t next_timer_exp = msm_timer_enter_idle();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001400 uint32_t sleep_delay;
Murali Nalajala8fda4492012-03-19 18:22:59 +05301401 bool low_power = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001402
1403 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
Murali Nalajala8fda4492012-03-19 18:22:59 +05301404 next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT);
1405
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001406 if (sleep_delay == 0) /* 0 would mean infinite time */
1407 sleep_delay = 1;
1408
1409 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1410 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1411
1412#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
1413 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1414#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
1415 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1416#endif
1417
1418 ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
1419 low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
Murali Nalajala8fda4492012-03-19 18:22:59 +05301420 msm_timer_exit_idle(low_power);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001421
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001422 if (ret)
1423 exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
1424 else {
1425 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
1426 msm_pm_sleep_limit = sleep_limit;
1427 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001428 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301429 ret = msm_pm_power_collapse_standalone(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001430 exit_stat = ret ?
1431 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
1432 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001433 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1434 ret = msm_pm_swfi(true);
1435 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301436 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001437 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001438 exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001439 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1440 msm_pm_swfi(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001441 exit_stat = MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001442 } else {
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301443 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001444 udelay(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001445 exit_stat = MSM_PM_STAT_IDLE_SPIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001446 }
1447
Murali Nalajalab86f3702012-03-30 17:54:57 +05301448 __get_cpu_var(t2) = ktime_to_ns(ktime_get());
1449 msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001450}
1451
1452/*
1453 * Suspend the Apps processor.
1454 *
1455 * Return value:
Murali Nalajala41786ab2012-03-06 10:47:32 +05301456 * -EPERM: Suspend happened by a not permitted core
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001457 * -EAGAIN: modem reset occurred or early exit from suspend
1458 * -EBUSY: modem not ready for our suspend
1459 * -EINVAL: invalid sleep mode
1460 * -EIO: could not ramp Apps processor clock
1461 * -ETIMEDOUT: timed out waiting for modem's handshake
1462 * 0: success
1463 */
1464static int msm_pm_enter(suspend_state_t state)
1465{
1466 bool allow[MSM_PM_SLEEP_MODE_NR];
1467 uint32_t sleep_limit = SLEEP_LIMIT_NONE;
Murali Nalajala41786ab2012-03-06 10:47:32 +05301468 int ret = -EPERM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001469 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001470 int64_t period = 0;
1471 int64_t time = 0;
1472
Murali Nalajala41786ab2012-03-06 10:47:32 +05301473 /* Must executed by CORE0 */
1474 if (smp_processor_id()) {
1475 __WARN();
1476 goto suspend_exit;
1477 }
1478
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301479 time = msm_pm_timer_enter_suspend(&period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001480
1481 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1482 "%s(): sleep limit %u\n", __func__, sleep_limit);
1483
1484 for (i = 0; i < ARRAY_SIZE(allow); i++)
1485 allow[i] = true;
1486
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001487 for (i = 0; i < ARRAY_SIZE(allow); i++) {
Murali Nalajala0df9fee2012-01-12 15:26:09 +05301488 struct msm_pm_platform_data *mode;
1489 mode = &msm_pm_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001490 if (!mode->suspend_supported || !mode->suspend_enabled)
1491 allow[i] = false;
1492 }
1493
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001494 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
1495 allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001496 enum msm_pm_time_stats_id id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001497
1498 clock_debug_print_enabled();
1499
1500#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1501 if (msm_pm_sleep_time_override > 0) {
1502 int64_t ns;
1503 ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
1504 msm_pm_set_max_sleep_time(ns);
1505 msm_pm_sleep_time_override = 0;
1506 }
1507#endif
1508 if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
1509 sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
1510
1511#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
1512 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
1513#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
1514 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1515#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
1516 if (get_msm_migrate_pages_status() != MEM_OFFLINE)
1517 sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
1518#endif
1519
1520 for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
1521 udelay(500);
1522
1523 ret = msm_pm_power_collapse(
1524 false, msm_pm_max_sleep_time, sleep_limit);
1525
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001526 if (ret)
1527 id = MSM_PM_STAT_FAILED_SUSPEND;
1528 else {
1529 id = MSM_PM_STAT_SUSPEND;
1530 msm_pm_sleep_limit = sleep_limit;
1531 }
1532
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301533 time = msm_pm_timer_exit_suspend(time, period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001534 msm_pm_add_stat(id, time);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
Murali Nalajala41786ab2012-03-06 10:47:32 +05301536 ret = msm_pm_power_collapse_standalone(false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001537 } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
1538 ret = msm_pm_swfi(true);
1539 if (ret)
Murali Nalajala2a0bbda2012-03-28 12:12:54 +05301540 while (!msm_pm_irq_extns->irq_pending())
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001541 udelay(1);
1542 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1543 msm_pm_swfi(false);
1544 }
1545
Murali Nalajala41786ab2012-03-06 10:47:32 +05301546suspend_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001547 MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
1548 "%s(): return %d\n", __func__, ret);
1549
1550 return ret;
1551}
1552
1553static struct platform_suspend_ops msm_pm_ops = {
1554 .enter = msm_pm_enter,
1555 .valid = suspend_valid_only_mem,
1556};
1557
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301558/* Hotplug the "non boot" CPU's and put
1559 * the cores into low power mode
1560 */
1561void msm_pm_cpu_enter_lowpower(unsigned int cpu)
1562{
Murali Nalajalaa7efba12012-02-23 18:13:52 +05301563 bool allow[MSM_PM_SLEEP_MODE_NR];
1564 int i;
1565
1566 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1567 struct msm_pm_platform_data *mode;
1568
1569 mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)];
1570 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1571 }
1572
1573 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1574 "CPU%u: %s: shutting down cpu\n", cpu, __func__);
1575
1576 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1577 msm_pm_power_collapse_standalone(false);
1578 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1579 msm_pm_swfi(false);
1580 } else {
1581 MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO,
1582 "CPU%u: %s: shutting down failed!!!\n", cpu, __func__);
1583 }
Murali Nalajalac89f2f32012-02-07 19:23:52 +05301584}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001585
1586/******************************************************************************
1587 * Restart Definitions
1588 *****************************************************************************/
1589
1590static uint32_t restart_reason = 0x776655AA;
1591
1592static void msm_pm_power_off(void)
1593{
1594 msm_rpcrouter_close();
1595 msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
1596 for (;;)
1597 ;
1598}
1599
1600static void msm_pm_restart(char str, const char *cmd)
1601{
1602 msm_rpcrouter_close();
1603 msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
1604
1605 for (;;)
1606 ;
1607}
1608
1609static int msm_reboot_call
1610 (struct notifier_block *this, unsigned long code, void *_cmd)
1611{
1612 if ((code == SYS_RESTART) && _cmd) {
1613 char *cmd = _cmd;
1614 if (!strcmp(cmd, "bootloader")) {
1615 restart_reason = 0x77665500;
1616 } else if (!strcmp(cmd, "recovery")) {
1617 restart_reason = 0x77665502;
1618 } else if (!strcmp(cmd, "eraseflash")) {
1619 restart_reason = 0x776655EF;
1620 } else if (!strncmp(cmd, "oem-", 4)) {
1621 unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
1622 restart_reason = 0x6f656d00 | code;
1623 } else {
1624 restart_reason = 0x77665501;
1625 }
1626 }
1627 return NOTIFY_DONE;
1628}
1629
1630static struct notifier_block msm_reboot_notifier = {
1631 .notifier_call = msm_reboot_call,
1632};
1633
1634
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001635/*
1636 * Initialize the power management subsystem.
1637 *
1638 * Return value:
1639 * -ENODEV: initialization failed
1640 * 0: success
1641 */
1642static int __init msm_pm_init(void)
1643{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001644 int ret;
Murali Nalajala93f29992012-03-21 15:59:27 +05301645 int val;
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301646 enum msm_pm_time_stats_id enable_stats[] = {
1647 MSM_PM_STAT_REQUESTED_IDLE,
1648 MSM_PM_STAT_IDLE_SPIN,
1649 MSM_PM_STAT_IDLE_WFI,
1650 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
1651 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
1652 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
1653 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
1654 MSM_PM_STAT_SUSPEND,
1655 MSM_PM_STAT_FAILED_SUSPEND,
1656 MSM_PM_STAT_NOT_IDLE,
1657 };
1658
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001659#ifdef CONFIG_CPU_V7
1660 pgd_t *pc_pgd;
1661 pmd_t *pmd;
1662 unsigned long pmdval;
Steve Mucklef132c6c2012-06-06 18:30:57 -07001663 unsigned long exit_phys;
1664
1665 exit_phys = virt_to_phys(msm_pm_collapse_exit);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001666
1667 /* Page table for cores to come back up safely. */
1668 pc_pgd = pgd_alloc(&init_mm);
1669 if (!pc_pgd)
1670 return -ENOMEM;
Steve Mucklef132c6c2012-06-06 18:30:57 -07001671 pmd = pmd_offset(pud_offset(pc_pgd + pgd_index(exit_phys), exit_phys),
1672 exit_phys);
1673 pmdval = (exit_phys & PGDIR_MASK) |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001674 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1675 pmd[0] = __pmd(pmdval);
1676 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1677
Steve Mucklefcece052012-02-18 20:09:58 -08001678 msm_saved_state_phys =
1679 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1680 num_possible_cpus(), 4);
1681 if (!msm_saved_state_phys)
1682 return -ENOMEM;
1683 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1684 CPU_SAVED_STATE_SIZE *
1685 num_possible_cpus());
1686 if (!msm_saved_state)
1687 return -ENOMEM;
1688
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001689 /* It is remotely possible that the code in msm_pm_collapse_exit()
1690 * which turns on the MMU with this mapping is in the
1691 * next even-numbered megabyte beyond the
1692 * start of msm_pm_collapse_exit().
1693 * Map this megabyte in as well.
1694 */
1695 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1696 flush_pmd_entry(pmd);
1697 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001698 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1699 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001700#endif
1701
1702 pm_power_off = msm_pm_power_off;
1703 arm_pm_restart = msm_pm_restart;
1704 register_reboot_notifier(&msm_reboot_notifier);
1705
1706 msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
1707 sizeof(*msm_pm_smem_data));
1708 if (msm_pm_smem_data == NULL) {
1709 printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
1710 return -ENODEV;
1711 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001712
1713 ret = msm_timer_init_time_sync(msm_pm_timeout);
1714 if (ret)
1715 return ret;
1716
1717 ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
1718 if (ret) {
1719 printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
1720 __func__, ret);
1721 return ret;
1722 }
1723
Murali Nalajala93f29992012-03-21 15:59:27 +05301724 if (cpu_is_msm8625()) {
1725 target_type = TARGET_IS_8625;
1726 clean_caches((unsigned long)&target_type, sizeof(target_type),
1727 virt_to_phys(&target_type));
1728
Anji jonnalae644f8e2012-05-09 19:52:18 +05301729 /*
1730 * Configure the MPA5_GDFS_CNT_VAL register for
1731 * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the
1732 * DBGNOPOWERDN for each cpu.
1733 * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for
1734 * GDFS control.
Murali Nalajala93f29992012-03-21 15:59:27 +05301735 */
Anji jonnalae644f8e2012-05-09 19:52:18 +05301736 val = 0x00030002;
Murali Nalajala93f29992012-03-21 15:59:27 +05301737 __raw_writel(val, (MSM_CFG_CTL_BASE + 0x38));
Murali Nalajala73c13332012-05-15 11:30:59 +05301738
1739 l2x0_base_addr = MSM_L2CC_BASE;
Murali Nalajala93f29992012-03-21 15:59:27 +05301740 }
1741
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001742#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
1743 /* The wakeup_reason field is overloaded during initialization time
1744 to signal Modem that Apps will control the low power modes of
1745 the memory.
1746 */
1747 msm_pm_smem_data->wakeup_reason = 1;
1748 smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
1749#endif
1750
1751 BUG_ON(msm_pm_modes == NULL);
1752
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001753 suspend_set_ops(&msm_pm_ops);
1754
1755 msm_pm_mode_sysfs_add();
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301756 msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
Murali Nalajala558c0ce2012-03-29 19:42:08 +05301757
1758 atomic_set(&msm_pm_init_done, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001759 return 0;
1760}
1761
1762late_initcall_sync(msm_pm_init);