blob: 2b0cc1877956c936add44e38f15024b0cd95f194 [file] [log] [blame]
Karthik Parsha6fb932d2012-01-24 18:04:12 -08001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/completion.h>
18#include <linux/cpuidle.h>
19#include <linux/interrupt.h>
20#include <linux/io.h>
21#include <linux/ktime.h>
22#include <linux/pm.h>
23#include <linux/pm_qos_params.h>
24#include <linux/proc_fs.h>
25#include <linux/smp.h>
26#include <linux/suspend.h>
27#include <linux/tick.h>
28#include <linux/uaccess.h>
29#include <linux/wakelock.h>
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -060030#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031#include <mach/msm_iomap.h>
32#include <mach/system.h>
33#include <asm/cacheflush.h>
34#include <asm/hardware/gic.h>
35#include <asm/pgtable.h>
36#include <asm/pgalloc.h>
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -060037#include <asm/hardware/cache-l2x0.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#ifdef CONFIG_VFP
39#include <asm/vfp.h>
40#endif
41
42#include "acpuclock.h"
43#include "clock.h"
44#include "avs.h"
Abhijeet Dharmapurikarefaca4f2011-12-27 16:24:07 -080045#include <mach/cpuidle.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046#include "idle.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080047#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048#include "rpm_resources.h"
49#include "scm-boot.h"
50#include "spm.h"
51#include "timer.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060052#include "pm-boot.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053
54/******************************************************************************
55 * Debug Definitions
56 *****************************************************************************/
57
58enum {
59 MSM_PM_DEBUG_SUSPEND = BIT(0),
60 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
61 MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2),
62 MSM_PM_DEBUG_CLOCK = BIT(3),
63 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
Karthik Parsha6fb932d2012-01-24 18:04:12 -080064 MSM_PM_DEBUG_IDLE_CLK = BIT(5),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065 MSM_PM_DEBUG_IDLE = BIT(6),
66 MSM_PM_DEBUG_IDLE_LIMITS = BIT(7),
67 MSM_PM_DEBUG_HOTPLUG = BIT(8),
68};
69
70static int msm_pm_debug_mask = 1;
71module_param_named(
72 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
73);
74
75
76/******************************************************************************
77 * Sleep Modes and Parameters
78 *****************************************************************************/
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079enum {
80 MSM_PM_MODE_ATTR_SUSPEND,
81 MSM_PM_MODE_ATTR_IDLE,
82 MSM_PM_MODE_ATTR_NR,
83};
84
85static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
86 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
87 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
88};
89
90struct msm_pm_kobj_attribute {
91 unsigned int cpu;
92 struct kobj_attribute ka;
93};
94
95#define GET_CPU_OF_ATTR(attr) \
96 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
97
98struct msm_pm_sysfs_sleep_mode {
99 struct kobject *kobj;
100 struct attribute_group attr_group;
101 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
102 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
103};
104
105static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
106 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
107 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
108 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
109 "standalone_power_collapse",
110};
111
112/*
113 * Write out the attribute.
114 */
115static ssize_t msm_pm_mode_attr_show(
116 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
117{
118 int ret = -EINVAL;
119 int i;
120
121 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
122 struct kernel_param kp;
123 unsigned int cpu;
124 struct msm_pm_platform_data *mode;
125
126 if (msm_pm_sleep_mode_labels[i] == NULL)
127 continue;
128
129 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
130 continue;
131
132 cpu = GET_CPU_OF_ATTR(attr);
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600133 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134
135 if (!strcmp(attr->attr.name,
136 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
137 u32 arg = mode->suspend_enabled;
138 kp.arg = &arg;
139 ret = param_get_ulong(buf, &kp);
140 } else if (!strcmp(attr->attr.name,
141 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
142 u32 arg = mode->idle_enabled;
143 kp.arg = &arg;
144 ret = param_get_ulong(buf, &kp);
145 }
146
147 break;
148 }
149
150 if (ret > 0) {
Praveen Chidambaram2b0fdd02011-10-28 16:40:58 -0600151 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 ret++;
153 }
154
155 return ret;
156}
157
158/*
159 * Read in the new attribute value.
160 */
161static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
162 struct kobj_attribute *attr, const char *buf, size_t count)
163{
164 int ret = -EINVAL;
165 int i;
166
167 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
168 struct kernel_param kp;
169 unsigned int cpu;
170 struct msm_pm_platform_data *mode;
171
172 if (msm_pm_sleep_mode_labels[i] == NULL)
173 continue;
174
175 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
176 continue;
177
178 cpu = GET_CPU_OF_ATTR(attr);
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600179 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180
181 if (!strcmp(attr->attr.name,
182 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
183 kp.arg = &mode->suspend_enabled;
184 ret = param_set_byte(buf, &kp);
185 } else if (!strcmp(attr->attr.name,
186 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
187 kp.arg = &mode->idle_enabled;
188 ret = param_set_byte(buf, &kp);
189 }
190
191 break;
192 }
193
194 return ret ? ret : count;
195}
196
197/*
198 * Add sysfs entries for one cpu.
199 */
200static int __init msm_pm_mode_sysfs_add_cpu(
201 unsigned int cpu, struct kobject *modes_kobj)
202{
203 char cpu_name[8];
204 struct kobject *cpu_kobj;
Praveen Chidambaram2b0fdd02011-10-28 16:40:58 -0600205 struct msm_pm_sysfs_sleep_mode *mode = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 int i, j, k;
207 int ret;
208
209 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
210 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
211 if (!cpu_kobj) {
212 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
213 ret = -ENOMEM;
214 goto mode_sysfs_add_cpu_exit;
215 }
216
217 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
218 int idx = MSM_PM_MODE(cpu, i);
219
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600220 if ((!msm_pm_sleep_modes[idx].suspend_supported)
221 && (!msm_pm_sleep_modes[idx].idle_supported))
222 continue;
223
224 if (!msm_pm_sleep_mode_labels[i] ||
225 !msm_pm_sleep_mode_labels[i][0])
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 continue;
227
228 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
229 if (!mode) {
230 pr_err("%s: cannot allocate memory for attributes\n",
231 __func__);
232 ret = -ENOMEM;
233 goto mode_sysfs_add_cpu_exit;
234 }
235
236 mode->kobj = kobject_create_and_add(
237 msm_pm_sleep_mode_labels[i], cpu_kobj);
238 if (!mode->kobj) {
239 pr_err("%s: cannot create kobject\n", __func__);
240 ret = -ENOMEM;
241 goto mode_sysfs_add_cpu_exit;
242 }
243
244 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
245 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600246 !msm_pm_sleep_modes[idx].idle_supported)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 continue;
248 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600249 !msm_pm_sleep_modes[idx].suspend_supported)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250 continue;
251 mode->kas[j].cpu = cpu;
252 mode->kas[j].ka.attr.mode = 0644;
253 mode->kas[j].ka.show = msm_pm_mode_attr_show;
254 mode->kas[j].ka.store = msm_pm_mode_attr_store;
255 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
256 mode->attrs[j] = &mode->kas[j].ka.attr;
257 j++;
258 }
259 mode->attrs[j] = NULL;
260
261 mode->attr_group.attrs = mode->attrs;
262 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
263 if (ret) {
264 pr_err("%s: cannot create kobject attribute group\n",
265 __func__);
266 goto mode_sysfs_add_cpu_exit;
267 }
268 }
269
270 ret = 0;
271
272mode_sysfs_add_cpu_exit:
Praveen Chidambaramd5ac2d32011-10-24 14:30:27 -0600273 if (ret) {
Praveen Chidambaram2cfda632011-10-11 16:58:09 -0600274 if (mode && mode->kobj)
275 kobject_del(mode->kobj);
276 kfree(mode);
277 }
278
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 return ret;
280}
281
282/*
283 * Add sysfs entries for the sleep modes.
284 */
285static int __init msm_pm_mode_sysfs_add(void)
286{
287 struct kobject *module_kobj;
288 struct kobject *modes_kobj;
289 unsigned int cpu;
290 int ret;
291
292 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
293 if (!module_kobj) {
294 pr_err("%s: cannot find kobject for module %s\n",
295 __func__, KBUILD_MODNAME);
296 ret = -ENOENT;
297 goto mode_sysfs_add_exit;
298 }
299
300 modes_kobj = kobject_create_and_add("modes", module_kobj);
301 if (!modes_kobj) {
302 pr_err("%s: cannot create modes kobject\n", __func__);
303 ret = -ENOMEM;
304 goto mode_sysfs_add_exit;
305 }
306
307 for_each_possible_cpu(cpu) {
308 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
309 if (ret)
310 goto mode_sysfs_add_exit;
311 }
312
313 ret = 0;
314
315mode_sysfs_add_exit:
316 return ret;
317}
318
319/******************************************************************************
320 * CONFIG_MSM_IDLE_STATS
321 *****************************************************************************/
322
323#ifdef CONFIG_MSM_IDLE_STATS
324enum msm_pm_time_stats_id {
325 MSM_PM_STAT_REQUESTED_IDLE,
326 MSM_PM_STAT_IDLE_WFI,
327 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
328 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
329 MSM_PM_STAT_SUSPEND,
330 MSM_PM_STAT_COUNT
331};
332
333struct msm_pm_time_stats {
334 const char *name;
335 int64_t first_bucket_time;
336 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
337 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
338 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
339 int count;
340 int64_t total_time;
341};
342
343struct msm_pm_cpu_time_stats {
344 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
345};
346
347static DEFINE_SPINLOCK(msm_pm_stats_lock);
348static DEFINE_PER_CPU_SHARED_ALIGNED(
349 struct msm_pm_cpu_time_stats, msm_pm_stats);
350
351/*
352 * Add the given time data to the statistics collection.
353 */
354static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
355{
356 unsigned long flags;
357 struct msm_pm_time_stats *stats;
358 int64_t bt;
359 int i;
360
361 spin_lock_irqsave(&msm_pm_stats_lock, flags);
362 stats = __get_cpu_var(msm_pm_stats).stats;
363
364 stats[id].total_time += t;
365 stats[id].count++;
366
367 bt = t;
368 do_div(bt, stats[id].first_bucket_time);
369
370 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
371 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
372 i = DIV_ROUND_UP(fls((uint32_t)bt),
373 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
374 else
375 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
376
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600377 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
378 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
379
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 stats[id].bucket[i]++;
381
382 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
383 stats[id].min_time[i] = t;
384 if (t > stats[id].max_time[i])
385 stats[id].max_time[i] = t;
386
387 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
388}
389
390/*
391 * Helper function of snprintf where buf is auto-incremented, size is auto-
392 * decremented, and there is no return value.
393 *
394 * NOTE: buf and size must be l-values (e.g. variables)
395 */
396#define SNPRINTF(buf, size, format, ...) \
397 do { \
398 if (size > 0) { \
399 int ret; \
400 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
401 if (ret > size) { \
402 buf += size; \
403 size = 0; \
404 } else { \
405 buf += ret; \
406 size -= ret; \
407 } \
408 } \
409 } while (0)
410
411/*
412 * Write out the power management statistics.
413 */
414static int msm_pm_read_proc
415 (char *page, char **start, off_t off, int count, int *eof, void *data)
416{
417 unsigned int cpu = off / MSM_PM_STAT_COUNT;
418 int id = off % MSM_PM_STAT_COUNT;
419 char *p = page;
420
421 if (count < 1024) {
422 *start = (char *) 0;
423 *eof = 0;
424 return 0;
425 }
426
427 if (cpu < num_possible_cpus()) {
428 unsigned long flags;
429 struct msm_pm_time_stats *stats;
430 int i;
431 int64_t bucket_time;
432 int64_t s;
433 uint32_t ns;
434
435 spin_lock_irqsave(&msm_pm_stats_lock, flags);
436 stats = per_cpu(msm_pm_stats, cpu).stats;
437
438 s = stats[id].total_time;
439 ns = do_div(s, NSEC_PER_SEC);
440 SNPRINTF(p, count,
441 "[cpu %u] %s:\n"
442 " count: %7d\n"
443 " total_time: %lld.%09u\n",
444 cpu, stats[id].name,
445 stats[id].count,
446 s, ns);
447
448 bucket_time = stats[id].first_bucket_time;
449 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
450 s = bucket_time;
451 ns = do_div(s, NSEC_PER_SEC);
452 SNPRINTF(p, count,
453 " <%6lld.%09u: %7d (%lld-%lld)\n",
454 s, ns, stats[id].bucket[i],
455 stats[id].min_time[i],
456 stats[id].max_time[i]);
457
458 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
459 }
460
461 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
462 s, ns, stats[id].bucket[i],
463 stats[id].min_time[i],
464 stats[id].max_time[i]);
465
466 *start = (char *) 1;
467 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
468
469 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
470 }
471
472 return p - page;
473}
474#undef SNPRINTF
475
476#define MSM_PM_STATS_RESET "reset"
477
478/*
479 * Reset the power management statistics values.
480 */
481static int msm_pm_write_proc(struct file *file, const char __user *buffer,
482 unsigned long count, void *data)
483{
484 char buf[sizeof(MSM_PM_STATS_RESET)];
485 int ret;
486 unsigned long flags;
487 unsigned int cpu;
488
489 if (count < strlen(MSM_PM_STATS_RESET)) {
490 ret = -EINVAL;
491 goto write_proc_failed;
492 }
493
494 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
495 ret = -EFAULT;
496 goto write_proc_failed;
497 }
498
499 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
500 ret = -EINVAL;
501 goto write_proc_failed;
502 }
503
504 spin_lock_irqsave(&msm_pm_stats_lock, flags);
505 for_each_possible_cpu(cpu) {
506 struct msm_pm_time_stats *stats;
507 int i;
508
509 stats = per_cpu(msm_pm_stats, cpu).stats;
510 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
511 memset(stats[i].bucket,
512 0, sizeof(stats[i].bucket));
513 memset(stats[i].min_time,
514 0, sizeof(stats[i].min_time));
515 memset(stats[i].max_time,
516 0, sizeof(stats[i].max_time));
517 stats[i].count = 0;
518 stats[i].total_time = 0;
519 }
520 }
521
522 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
523 return count;
524
525write_proc_failed:
526 return ret;
527}
528#undef MSM_PM_STATS_RESET
529#endif /* CONFIG_MSM_IDLE_STATS */
530
531
532/******************************************************************************
533 * Configure Hardware before/after Low Power Mode
534 *****************************************************************************/
535
536/*
537 * Configure hardware registers in preparation for Apps power down.
538 */
539static void msm_pm_config_hw_before_power_down(void)
540{
541 return;
542}
543
544/*
545 * Clear hardware registers after Apps powers up.
546 */
547static void msm_pm_config_hw_after_power_up(void)
548{
549 return;
550}
551
552/*
553 * Configure hardware registers in preparation for SWFI.
554 */
555static void msm_pm_config_hw_before_swfi(void)
556{
557 return;
558}
559
560
561/******************************************************************************
562 * Suspend Max Sleep Time
563 *****************************************************************************/
564
565#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
566static int msm_pm_sleep_time_override;
567module_param_named(sleep_time_override,
568 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
569#endif
570
571#define SCLK_HZ (32768)
572#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
573
574static uint32_t msm_pm_max_sleep_time;
575
576/*
577 * Convert time from nanoseconds to slow clock ticks, then cap it to the
578 * specified limit
579 */
580static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
581{
582 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
583 return (time_ns > limit) ? limit : time_ns;
584}
585
586/*
587 * Set the sleep time for suspend. 0 means infinite sleep time.
588 */
589void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
590{
591 if (max_sleep_time_ns == 0) {
592 msm_pm_max_sleep_time = 0;
593 } else {
594 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
595 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
596
597 if (msm_pm_max_sleep_time == 0)
598 msm_pm_max_sleep_time = 1;
599 }
600
601 if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND)
602 pr_info("%s: Requested %lld ns Giving %u sclk ticks\n",
603 __func__, max_sleep_time_ns, msm_pm_max_sleep_time);
604}
605EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
606
607
608/******************************************************************************
609 *
610 *****************************************************************************/
611
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700612static struct msm_rpmrs_limits *msm_pm_idle_rs_limits;
613
614static void msm_pm_swfi(void)
615{
616 msm_pm_config_hw_before_swfi();
617 msm_arch_idle();
618}
619
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600620#ifdef CONFIG_CACHE_L2X0
621static inline bool msm_pm_l2x0_power_collapse(void)
622{
623 bool collapsed = 0;
624
Taniya Dasf51e94e2012-04-12 12:02:38 +0530625 l2cc_suspend();
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600626 collapsed = msm_pm_collapse();
Taniya Dasf51e94e2012-04-12 12:02:38 +0530627 l2cc_resume(collapsed);
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600628
629 return collapsed;
630}
631#else
632static inline bool msm_pm_l2x0_power_collapse(void)
633{
634 return msm_pm_collapse();
635}
636#endif
637
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600638static bool msm_pm_spm_power_collapse(
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700639 unsigned int cpu, bool from_idle, bool notify_rpm)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640{
641 void *entry;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600642 bool collapsed = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643 int ret;
Rohit Vaswanie78dfb62012-02-21 10:29:29 -0800644 unsigned int saved_gic_cpu_ctrl;
645
646 saved_gic_cpu_ctrl = readl_relaxed(MSM_QGIC_CPU_BASE + GIC_CPU_CTRL);
647 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648
649 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
650 pr_info("CPU%u: %s: notify_rpm %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700651 cpu, __func__, (int) notify_rpm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652
653 ret = msm_spm_set_low_power_mode(
654 MSM_SPM_MODE_POWER_COLLAPSE, notify_rpm);
655 WARN_ON(ret);
656
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700657 entry = (!cpu || from_idle) ?
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 msm_pm_collapse_exit : msm_secondary_startup;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700659 msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660
661 if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
662 pr_info("CPU%u: %s: program vector to %p\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700663 cpu, __func__, entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664
665#ifdef CONFIG_VFP
666 vfp_flush_context();
667#endif
668
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600669 collapsed = msm_pm_l2x0_power_collapse();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700670
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700671 msm_pm_boot_config_after_pc(cpu);
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600672
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 if (collapsed) {
674#ifdef CONFIG_VFP
675 vfp_reinit();
676#endif
677 cpu_init();
678 writel(0xF0, MSM_QGIC_CPU_BASE + GIC_CPU_PRIMASK);
Rohit Vaswanie78dfb62012-02-21 10:29:29 -0800679 writel_relaxed(saved_gic_cpu_ctrl,
680 MSM_QGIC_CPU_BASE + GIC_CPU_CTRL);
681 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 local_fiq_enable();
683 }
684
685 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
686 pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700687 cpu, __func__, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700688
689 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
690 WARN_ON(ret);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600691 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692}
693
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600694static bool msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700696 unsigned int cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700697 unsigned int avsdscr_setting;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600698 bool collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699
700 avsdscr_setting = avs_get_avsdscr();
701 avs_disable();
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700702 collapsed = msm_pm_spm_power_collapse(cpu, from_idle, false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700703 avs_reset_delays(avsdscr_setting);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600704 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700705}
706
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600707static bool msm_pm_power_collapse(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700708{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700709 unsigned int cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700710 unsigned long saved_acpuclk_rate;
711 unsigned int avsdscr_setting;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600712 bool collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700713
714 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
715 pr_info("CPU%u: %s: idle %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700716 cpu, __func__, (int)from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700717
718 msm_pm_config_hw_before_power_down();
719 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700720 pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721
722 avsdscr_setting = avs_get_avsdscr();
723 avs_disable();
724
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700725 if (cpu_online(cpu))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726 saved_acpuclk_rate = acpuclk_power_collapse();
727 else
728 saved_acpuclk_rate = 0;
729
730 if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
731 pr_info("CPU%u: %s: change clock rate (old rate = %lu)\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700732 cpu, __func__, saved_acpuclk_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700734 collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700735
736 if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
737 pr_info("CPU%u: %s: restore clock rate to %lu\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700738 cpu, __func__, saved_acpuclk_rate);
739 if (acpuclk_set_rate(cpu, saved_acpuclk_rate, SETRATE_PC) < 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700740 pr_err("CPU%u: %s: failed to restore clock rate(%lu)\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700741 cpu, __func__, saved_acpuclk_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700742
743 avs_reset_delays(avsdscr_setting);
744 msm_pm_config_hw_after_power_up();
745 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700746 pr_info("CPU%u: %s: post power up\n", cpu, __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747
748 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700749 pr_info("CPU%u: %s: return\n", cpu, __func__);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600750 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700751}
752
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700753/******************************************************************************
754 * External Idle/Suspend Functions
755 *****************************************************************************/
756
757void arch_idle(void)
758{
759 return;
760}
761
762int msm_pm_idle_prepare(struct cpuidle_device *dev)
763{
764 uint32_t latency_us;
765 uint32_t sleep_us;
766 int i;
767
768 latency_us = (uint32_t) pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
769 sleep_us = (uint32_t) ktime_to_ns(tick_nohz_get_sleep_length());
770 sleep_us = DIV_ROUND_UP(sleep_us, 1000);
771
772 for (i = 0; i < dev->state_count; i++) {
773 struct cpuidle_state *state = &dev->states[i];
774 enum msm_pm_sleep_mode mode;
775 bool allow;
776 struct msm_rpmrs_limits *rs_limits = NULL;
777 int idx;
778
779 mode = (enum msm_pm_sleep_mode) state->driver_data;
780 idx = MSM_PM_MODE(dev->cpu, mode);
781
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600782 allow = msm_pm_sleep_modes[idx].idle_enabled &&
783 msm_pm_sleep_modes[idx].idle_supported;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784
785 switch (mode) {
786 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
787 if (!allow)
788 break;
789
790 if (num_online_cpus() > 1) {
791 allow = false;
792 break;
793 }
794#ifdef CONFIG_HAS_WAKELOCK
795 if (has_wake_lock(WAKE_LOCK_IDLE)) {
796 allow = false;
797 break;
798 }
799#endif
800 /* fall through */
801
802 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
803 if (!allow)
804 break;
805
806 if (!dev->cpu &&
807 msm_rpm_local_request_is_outstanding()) {
808 allow = false;
809 break;
810 }
811 /* fall through */
812
813 case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
814 if (!allow)
815 break;
816
817 rs_limits = msm_rpmrs_lowest_limits(true,
818 mode, latency_us, sleep_us);
819
820 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
821 pr_info("CPU%u: %s: %s, latency %uus, "
822 "sleep %uus, limit %p\n",
823 dev->cpu, __func__, state->desc,
824 latency_us, sleep_us, rs_limits);
825
826 if ((MSM_PM_DEBUG_IDLE_LIMITS & msm_pm_debug_mask) &&
827 rs_limits)
828 pr_info("CPU%u: %s: limit %p: "
829 "pxo %d, l2_cache %d, "
830 "vdd_mem %d, vdd_dig %d\n",
831 dev->cpu, __func__, rs_limits,
832 rs_limits->pxo,
833 rs_limits->l2_cache,
834 rs_limits->vdd_mem,
835 rs_limits->vdd_dig);
836
837 if (!rs_limits)
838 allow = false;
839 break;
840
841 default:
842 allow = false;
843 break;
844 }
845
846 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
847 pr_info("CPU%u: %s: allow %s: %d\n",
848 dev->cpu, __func__, state->desc, (int)allow);
849
850 if (allow) {
851 state->flags &= ~CPUIDLE_FLAG_IGNORE;
852 state->target_residency = 0;
853 state->exit_latency = 0;
854 state->power_usage = rs_limits->power[dev->cpu];
855
856 if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == mode)
857 msm_pm_idle_rs_limits = rs_limits;
858 } else {
859 state->flags |= CPUIDLE_FLAG_IGNORE;
860 }
861 }
862
863 return 0;
864}
865
866int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode)
867{
868 int64_t time;
869#ifdef CONFIG_MSM_IDLE_STATS
870 int exit_stat;
871#endif
872
873 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
874 pr_info("CPU%u: %s: mode %d\n",
875 smp_processor_id(), __func__, sleep_mode);
876
877 time = ktime_to_ns(ktime_get());
878
879 switch (sleep_mode) {
880 case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
881 msm_pm_swfi();
882#ifdef CONFIG_MSM_IDLE_STATS
883 exit_stat = MSM_PM_STAT_IDLE_WFI;
884#endif
885 break;
886
887 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
888 msm_pm_power_collapse_standalone(true);
889#ifdef CONFIG_MSM_IDLE_STATS
890 exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
891#endif
892 break;
893
894 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: {
895 int64_t timer_expiration = msm_timer_enter_idle();
896 bool timer_halted = false;
897 uint32_t sleep_delay;
898 int ret;
899 int notify_rpm =
900 (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600901 int collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902
903 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
904 timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
905 if (sleep_delay == 0) /* 0 would mean infinite time */
906 sleep_delay = 1;
907
Karthik Parsha6fb932d2012-01-24 18:04:12 -0800908 if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
909 clock_debug_print_enabled();
910
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700911 ret = msm_rpmrs_enter_sleep(
912 sleep_delay, msm_pm_idle_rs_limits, true, notify_rpm);
913 if (!ret) {
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600914 collapsed = msm_pm_power_collapse(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700915 timer_halted = true;
916
917 msm_rpmrs_exit_sleep(msm_pm_idle_rs_limits, true,
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600918 notify_rpm, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700919 }
920
921 msm_timer_exit_idle((int) timer_halted);
922#ifdef CONFIG_MSM_IDLE_STATS
923 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
924#endif
925 break;
926 }
927
928 default:
929 __WARN();
930 goto cpuidle_enter_bail;
931 }
932
933 time = ktime_to_ns(ktime_get()) - time;
934#ifdef CONFIG_MSM_IDLE_STATS
935 msm_pm_add_stat(exit_stat, time);
936#endif
937
938 do_div(time, 1000);
939 return (int) time;
940
941cpuidle_enter_bail:
942 return 0;
943}
944
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600945static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
946
947static DEFINE_PER_CPU_SHARED_ALIGNED(enum msm_pm_sleep_mode,
948 msm_pm_last_slp_mode);
949
950bool msm_pm_verify_cpu_pc(unsigned int cpu)
951{
952 enum msm_pm_sleep_mode mode = per_cpu(msm_pm_last_slp_mode, cpu);
953
954 if (msm_pm_slp_sts)
955 if ((mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) ||
956 (mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE))
957 return true;
958
959 return false;
960}
961
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700962void msm_pm_cpu_enter_lowpower(unsigned int cpu)
963{
964 int i;
965 bool allow[MSM_PM_SLEEP_MODE_NR];
966
967 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
968 struct msm_pm_platform_data *mode;
969
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600970 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700971 allow[i] = mode->suspend_supported && mode->suspend_enabled;
972 }
973
974 if (MSM_PM_DEBUG_HOTPLUG & msm_pm_debug_mask)
975 pr_notice("CPU%u: %s: shutting down cpu\n", cpu, __func__);
976
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600977 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
978 per_cpu(msm_pm_last_slp_mode, cpu)
979 = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700980 msm_pm_power_collapse(false);
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600981 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
982 per_cpu(msm_pm_last_slp_mode, cpu)
983 = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700984 msm_pm_power_collapse_standalone(false);
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600985 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
986 per_cpu(msm_pm_last_slp_mode, cpu)
987 = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700988 msm_pm_swfi();
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600989 } else
990 per_cpu(msm_pm_last_slp_mode, cpu) = MSM_PM_SLEEP_MODE_NR;
991}
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700992
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600993int msm_pm_wait_cpu_shutdown(unsigned int cpu)
994{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700995
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600996 int timeout = 10;
997
998 if (!msm_pm_slp_sts)
999 return 0;
1000
1001 while (timeout--) {
1002
1003 /*
1004 * Check for the SPM of the core being hotplugged to set
1005 * its sleep state.The SPM sleep state indicates that the
1006 * core has been power collapsed.
1007 */
1008
1009 int acc_sts = __raw_readl(msm_pm_slp_sts->base_addr
1010 + cpu * msm_pm_slp_sts->cpu_offset);
1011 mb();
1012
1013 if (acc_sts & msm_pm_slp_sts->mask)
1014 return 0;
1015
1016 usleep(100);
1017 }
1018 pr_warn("%s(): Timed out waiting for CPU %u SPM to enter sleep state",
1019 __func__, cpu);
1020 return -EBUSY;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001021}
1022
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001023static int msm_pm_enter(suspend_state_t state)
1024{
1025 bool allow[MSM_PM_SLEEP_MODE_NR];
1026 int i;
1027
1028#ifdef CONFIG_MSM_IDLE_STATS
1029 int64_t period = 0;
1030 int64_t time = msm_timer_get_sclk_time(&period);
1031#endif
1032
1033 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1034 pr_info("%s\n", __func__);
1035
1036 if (smp_processor_id()) {
1037 __WARN();
1038 goto enter_exit;
1039 }
1040
1041
1042 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1043 struct msm_pm_platform_data *mode;
1044
Praveen Chidambaram42da9d22012-03-30 12:16:34 -06001045 mode = &msm_pm_sleep_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001046 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1047 }
1048
1049 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
1050 struct msm_rpmrs_limits *rs_limits;
1051 int ret;
1052
1053 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1054 pr_info("%s: power collapse\n", __func__);
1055
1056 clock_debug_print_enabled();
1057
1058#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1059 if (msm_pm_sleep_time_override > 0) {
1060 int64_t ns = NSEC_PER_SEC *
1061 (int64_t) msm_pm_sleep_time_override;
1062 msm_pm_set_max_sleep_time(ns);
1063 msm_pm_sleep_time_override = 0;
1064 }
1065#endif /* CONFIG_MSM_SLEEP_TIME_OVERRIDE */
1066
1067 if (MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask)
1068 msm_rpmrs_show_resources();
1069
1070 rs_limits = msm_rpmrs_lowest_limits(false,
1071 MSM_PM_SLEEP_MODE_POWER_COLLAPSE, -1, -1);
1072
1073 if ((MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask) &&
1074 rs_limits)
1075 pr_info("%s: limit %p: pxo %d, l2_cache %d, "
1076 "vdd_mem %d, vdd_dig %d\n",
1077 __func__, rs_limits,
1078 rs_limits->pxo, rs_limits->l2_cache,
1079 rs_limits->vdd_mem, rs_limits->vdd_dig);
1080
1081 if (rs_limits) {
1082 ret = msm_rpmrs_enter_sleep(
1083 msm_pm_max_sleep_time, rs_limits, false, true);
1084 if (!ret) {
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -06001085 int collapsed = msm_pm_power_collapse(false);
1086 msm_rpmrs_exit_sleep(rs_limits, false, true,
1087 collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001088 }
1089 } else {
1090 pr_err("%s: cannot find the lowest power limit\n",
1091 __func__);
1092 }
1093
1094#ifdef CONFIG_MSM_IDLE_STATS
1095 if (time != 0) {
1096 int64_t end_time = msm_timer_get_sclk_time(NULL);
1097 if (end_time != 0) {
1098 time = end_time - time;
1099 if (time < 0)
1100 time += period;
1101 } else
1102 time = 0;
1103 }
1104
1105 msm_pm_add_stat(MSM_PM_STAT_SUSPEND, time);
1106#endif /* CONFIG_MSM_IDLE_STATS */
1107 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1108 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1109 pr_info("%s: standalone power collapse\n", __func__);
1110 msm_pm_power_collapse_standalone(false);
1111 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1112 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1113 pr_info("%s: swfi\n", __func__);
1114 msm_pm_swfi();
1115 }
1116
1117
1118enter_exit:
1119 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1120 pr_info("%s: return\n", __func__);
1121
1122 return 0;
1123}
1124
1125static struct platform_suspend_ops msm_pm_ops = {
1126 .enter = msm_pm_enter,
1127 .valid = suspend_valid_only_mem,
1128};
1129
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001130/******************************************************************************
1131 * Initialization routine
1132 *****************************************************************************/
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001133void __init msm_pm_init_sleep_status_data(
1134 struct msm_pm_sleep_status_data *data)
1135{
1136 msm_pm_slp_sts = data;
1137}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001138
1139static int __init msm_pm_init(void)
1140{
1141 pgd_t *pc_pgd;
1142 pmd_t *pmd;
1143 unsigned long pmdval;
1144 unsigned int cpu;
1145#ifdef CONFIG_MSM_IDLE_STATS
1146 struct proc_dir_entry *d_entry;
1147#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001148
1149 /* Page table for cores to come back up safely. */
1150 pc_pgd = pgd_alloc(&init_mm);
1151 if (!pc_pgd)
1152 return -ENOMEM;
1153
1154 pmd = pmd_offset(pc_pgd +
1155 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1156 virt_to_phys(msm_pm_collapse_exit));
1157 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1158 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1159 pmd[0] = __pmd(pmdval);
1160 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1161
Steve Mucklefcece052012-02-18 20:09:58 -08001162 msm_saved_state_phys =
1163 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1164 num_possible_cpus(), 4);
1165 if (!msm_saved_state_phys)
1166 return -ENOMEM;
1167 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1168 CPU_SAVED_STATE_SIZE *
1169 num_possible_cpus());
1170 if (!msm_saved_state)
1171 return -ENOMEM;
1172
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001173 /* It is remotely possible that the code in msm_pm_collapse_exit()
1174 * which turns on the MMU with this mapping is in the
1175 * next even-numbered megabyte beyond the
1176 * start of msm_pm_collapse_exit().
1177 * Map this megabyte in as well.
1178 */
1179 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1180 flush_pmd_entry(pmd);
1181 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001182 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1183 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001184
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185#ifdef CONFIG_MSM_IDLE_STATS
1186 for_each_possible_cpu(cpu) {
1187 struct msm_pm_time_stats *stats =
1188 per_cpu(msm_pm_stats, cpu).stats;
1189
1190 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1191 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1192 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1193
1194 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1195 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1196 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1197
1198 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1199 "idle-standalone-power-collapse";
1200 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1201 first_bucket_time = CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1202
1203 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
1204 "idle-power-collapse";
1205 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
1206 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1207
1208 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
1209 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
1210 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
1211 }
1212
1213 d_entry = create_proc_entry("msm_pm_stats",
1214 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
1215 if (d_entry) {
1216 d_entry->read_proc = msm_pm_read_proc;
1217 d_entry->write_proc = msm_pm_write_proc;
1218 d_entry->data = NULL;
1219 }
1220#endif /* CONFIG_MSM_IDLE_STATS */
1221
1222 msm_pm_mode_sysfs_add();
1223 msm_spm_allow_x_cpu_set_vdd(false);
1224
1225 suspend_set_ops(&msm_pm_ops);
1226 msm_cpuidle_init();
1227
1228 return 0;
1229}
1230
1231late_initcall(msm_pm_init);