blob: c23701374d0ad5b40c212b0bc7e24cb46af54cfc [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",
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600108 [MSM_PM_SLEEP_MODE_RETENTION] = "retention",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
110 "standalone_power_collapse",
111};
112
113/*
114 * Write out the attribute.
115 */
116static ssize_t msm_pm_mode_attr_show(
117 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
118{
119 int ret = -EINVAL;
120 int i;
121
122 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
123 struct kernel_param kp;
124 unsigned int cpu;
125 struct msm_pm_platform_data *mode;
126
127 if (msm_pm_sleep_mode_labels[i] == NULL)
128 continue;
129
130 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
131 continue;
132
133 cpu = GET_CPU_OF_ATTR(attr);
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600134 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135
136 if (!strcmp(attr->attr.name,
137 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
138 u32 arg = mode->suspend_enabled;
139 kp.arg = &arg;
140 ret = param_get_ulong(buf, &kp);
141 } else if (!strcmp(attr->attr.name,
142 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
143 u32 arg = mode->idle_enabled;
144 kp.arg = &arg;
145 ret = param_get_ulong(buf, &kp);
146 }
147
148 break;
149 }
150
151 if (ret > 0) {
Praveen Chidambaram2b0fdd02011-10-28 16:40:58 -0600152 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153 ret++;
154 }
155
156 return ret;
157}
158
159/*
160 * Read in the new attribute value.
161 */
162static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
163 struct kobj_attribute *attr, const char *buf, size_t count)
164{
165 int ret = -EINVAL;
166 int i;
167
168 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
169 struct kernel_param kp;
170 unsigned int cpu;
171 struct msm_pm_platform_data *mode;
172
173 if (msm_pm_sleep_mode_labels[i] == NULL)
174 continue;
175
176 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
177 continue;
178
179 cpu = GET_CPU_OF_ATTR(attr);
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600180 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181
182 if (!strcmp(attr->attr.name,
183 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
184 kp.arg = &mode->suspend_enabled;
185 ret = param_set_byte(buf, &kp);
186 } else if (!strcmp(attr->attr.name,
187 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
188 kp.arg = &mode->idle_enabled;
189 ret = param_set_byte(buf, &kp);
190 }
191
192 break;
193 }
194
195 return ret ? ret : count;
196}
197
198/*
199 * Add sysfs entries for one cpu.
200 */
201static int __init msm_pm_mode_sysfs_add_cpu(
202 unsigned int cpu, struct kobject *modes_kobj)
203{
204 char cpu_name[8];
205 struct kobject *cpu_kobj;
Praveen Chidambaram2b0fdd02011-10-28 16:40:58 -0600206 struct msm_pm_sysfs_sleep_mode *mode = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 int i, j, k;
208 int ret;
209
210 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
211 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
212 if (!cpu_kobj) {
213 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
214 ret = -ENOMEM;
215 goto mode_sysfs_add_cpu_exit;
216 }
217
218 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
219 int idx = MSM_PM_MODE(cpu, i);
220
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600221 if ((!msm_pm_sleep_modes[idx].suspend_supported)
222 && (!msm_pm_sleep_modes[idx].idle_supported))
223 continue;
224
225 if (!msm_pm_sleep_mode_labels[i] ||
226 !msm_pm_sleep_mode_labels[i][0])
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 continue;
228
229 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
230 if (!mode) {
231 pr_err("%s: cannot allocate memory for attributes\n",
232 __func__);
233 ret = -ENOMEM;
234 goto mode_sysfs_add_cpu_exit;
235 }
236
237 mode->kobj = kobject_create_and_add(
238 msm_pm_sleep_mode_labels[i], cpu_kobj);
239 if (!mode->kobj) {
240 pr_err("%s: cannot create kobject\n", __func__);
241 ret = -ENOMEM;
242 goto mode_sysfs_add_cpu_exit;
243 }
244
245 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
246 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600247 !msm_pm_sleep_modes[idx].idle_supported)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 continue;
249 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600250 !msm_pm_sleep_modes[idx].suspend_supported)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251 continue;
252 mode->kas[j].cpu = cpu;
253 mode->kas[j].ka.attr.mode = 0644;
254 mode->kas[j].ka.show = msm_pm_mode_attr_show;
255 mode->kas[j].ka.store = msm_pm_mode_attr_store;
256 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
257 mode->attrs[j] = &mode->kas[j].ka.attr;
258 j++;
259 }
260 mode->attrs[j] = NULL;
261
262 mode->attr_group.attrs = mode->attrs;
263 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
264 if (ret) {
265 pr_err("%s: cannot create kobject attribute group\n",
266 __func__);
267 goto mode_sysfs_add_cpu_exit;
268 }
269 }
270
271 ret = 0;
272
273mode_sysfs_add_cpu_exit:
Praveen Chidambaramd5ac2d32011-10-24 14:30:27 -0600274 if (ret) {
Praveen Chidambaram2cfda632011-10-11 16:58:09 -0600275 if (mode && mode->kobj)
276 kobject_del(mode->kobj);
277 kfree(mode);
278 }
279
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 return ret;
281}
282
283/*
284 * Add sysfs entries for the sleep modes.
285 */
286static int __init msm_pm_mode_sysfs_add(void)
287{
288 struct kobject *module_kobj;
289 struct kobject *modes_kobj;
290 unsigned int cpu;
291 int ret;
292
293 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
294 if (!module_kobj) {
295 pr_err("%s: cannot find kobject for module %s\n",
296 __func__, KBUILD_MODNAME);
297 ret = -ENOENT;
298 goto mode_sysfs_add_exit;
299 }
300
301 modes_kobj = kobject_create_and_add("modes", module_kobj);
302 if (!modes_kobj) {
303 pr_err("%s: cannot create modes kobject\n", __func__);
304 ret = -ENOMEM;
305 goto mode_sysfs_add_exit;
306 }
307
308 for_each_possible_cpu(cpu) {
309 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
310 if (ret)
311 goto mode_sysfs_add_exit;
312 }
313
314 ret = 0;
315
316mode_sysfs_add_exit:
317 return ret;
318}
319
320/******************************************************************************
321 * CONFIG_MSM_IDLE_STATS
322 *****************************************************************************/
323
324#ifdef CONFIG_MSM_IDLE_STATS
325enum msm_pm_time_stats_id {
326 MSM_PM_STAT_REQUESTED_IDLE,
327 MSM_PM_STAT_IDLE_WFI,
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600328 MSM_PM_STAT_RETENTION,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
330 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
331 MSM_PM_STAT_SUSPEND,
332 MSM_PM_STAT_COUNT
333};
334
335struct msm_pm_time_stats {
336 const char *name;
337 int64_t first_bucket_time;
338 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
339 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
340 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
341 int count;
342 int64_t total_time;
343};
344
345struct msm_pm_cpu_time_stats {
346 struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT];
347};
348
349static DEFINE_SPINLOCK(msm_pm_stats_lock);
350static DEFINE_PER_CPU_SHARED_ALIGNED(
351 struct msm_pm_cpu_time_stats, msm_pm_stats);
352
353/*
354 * Add the given time data to the statistics collection.
355 */
356static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
357{
358 unsigned long flags;
359 struct msm_pm_time_stats *stats;
360 int64_t bt;
361 int i;
362
363 spin_lock_irqsave(&msm_pm_stats_lock, flags);
364 stats = __get_cpu_var(msm_pm_stats).stats;
365
366 stats[id].total_time += t;
367 stats[id].count++;
368
369 bt = t;
370 do_div(bt, stats[id].first_bucket_time);
371
372 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
373 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
374 i = DIV_ROUND_UP(fls((uint32_t)bt),
375 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
376 else
377 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
378
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600379 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
380 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
381
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 stats[id].bucket[i]++;
383
384 if (t < stats[id].min_time[i] || !stats[id].max_time[i])
385 stats[id].min_time[i] = t;
386 if (t > stats[id].max_time[i])
387 stats[id].max_time[i] = t;
388
389 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
390}
391
392/*
393 * Helper function of snprintf where buf is auto-incremented, size is auto-
394 * decremented, and there is no return value.
395 *
396 * NOTE: buf and size must be l-values (e.g. variables)
397 */
398#define SNPRINTF(buf, size, format, ...) \
399 do { \
400 if (size > 0) { \
401 int ret; \
402 ret = snprintf(buf, size, format, ## __VA_ARGS__); \
403 if (ret > size) { \
404 buf += size; \
405 size = 0; \
406 } else { \
407 buf += ret; \
408 size -= ret; \
409 } \
410 } \
411 } while (0)
412
413/*
414 * Write out the power management statistics.
415 */
416static int msm_pm_read_proc
417 (char *page, char **start, off_t off, int count, int *eof, void *data)
418{
419 unsigned int cpu = off / MSM_PM_STAT_COUNT;
420 int id = off % MSM_PM_STAT_COUNT;
421 char *p = page;
422
423 if (count < 1024) {
424 *start = (char *) 0;
425 *eof = 0;
426 return 0;
427 }
428
429 if (cpu < num_possible_cpus()) {
430 unsigned long flags;
431 struct msm_pm_time_stats *stats;
432 int i;
433 int64_t bucket_time;
434 int64_t s;
435 uint32_t ns;
436
437 spin_lock_irqsave(&msm_pm_stats_lock, flags);
438 stats = per_cpu(msm_pm_stats, cpu).stats;
439
440 s = stats[id].total_time;
441 ns = do_div(s, NSEC_PER_SEC);
442 SNPRINTF(p, count,
443 "[cpu %u] %s:\n"
444 " count: %7d\n"
445 " total_time: %lld.%09u\n",
446 cpu, stats[id].name,
447 stats[id].count,
448 s, ns);
449
450 bucket_time = stats[id].first_bucket_time;
451 for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
452 s = bucket_time;
453 ns = do_div(s, NSEC_PER_SEC);
454 SNPRINTF(p, count,
455 " <%6lld.%09u: %7d (%lld-%lld)\n",
456 s, ns, stats[id].bucket[i],
457 stats[id].min_time[i],
458 stats[id].max_time[i]);
459
460 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
461 }
462
463 SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n",
464 s, ns, stats[id].bucket[i],
465 stats[id].min_time[i],
466 stats[id].max_time[i]);
467
468 *start = (char *) 1;
469 *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus());
470
471 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
472 }
473
474 return p - page;
475}
476#undef SNPRINTF
477
478#define MSM_PM_STATS_RESET "reset"
479
480/*
481 * Reset the power management statistics values.
482 */
483static int msm_pm_write_proc(struct file *file, const char __user *buffer,
484 unsigned long count, void *data)
485{
486 char buf[sizeof(MSM_PM_STATS_RESET)];
487 int ret;
488 unsigned long flags;
489 unsigned int cpu;
490
491 if (count < strlen(MSM_PM_STATS_RESET)) {
492 ret = -EINVAL;
493 goto write_proc_failed;
494 }
495
496 if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
497 ret = -EFAULT;
498 goto write_proc_failed;
499 }
500
501 if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
502 ret = -EINVAL;
503 goto write_proc_failed;
504 }
505
506 spin_lock_irqsave(&msm_pm_stats_lock, flags);
507 for_each_possible_cpu(cpu) {
508 struct msm_pm_time_stats *stats;
509 int i;
510
511 stats = per_cpu(msm_pm_stats, cpu).stats;
512 for (i = 0; i < MSM_PM_STAT_COUNT; i++) {
513 memset(stats[i].bucket,
514 0, sizeof(stats[i].bucket));
515 memset(stats[i].min_time,
516 0, sizeof(stats[i].min_time));
517 memset(stats[i].max_time,
518 0, sizeof(stats[i].max_time));
519 stats[i].count = 0;
520 stats[i].total_time = 0;
521 }
522 }
523
524 spin_unlock_irqrestore(&msm_pm_stats_lock, flags);
525 return count;
526
527write_proc_failed:
528 return ret;
529}
530#undef MSM_PM_STATS_RESET
531#endif /* CONFIG_MSM_IDLE_STATS */
532
533
534/******************************************************************************
535 * Configure Hardware before/after Low Power Mode
536 *****************************************************************************/
537
538/*
539 * Configure hardware registers in preparation for Apps power down.
540 */
541static void msm_pm_config_hw_before_power_down(void)
542{
543 return;
544}
545
546/*
547 * Clear hardware registers after Apps powers up.
548 */
549static void msm_pm_config_hw_after_power_up(void)
550{
551 return;
552}
553
554/*
555 * Configure hardware registers in preparation for SWFI.
556 */
557static void msm_pm_config_hw_before_swfi(void)
558{
559 return;
560}
561
562
563/******************************************************************************
564 * Suspend Max Sleep Time
565 *****************************************************************************/
566
567#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
568static int msm_pm_sleep_time_override;
569module_param_named(sleep_time_override,
570 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
571#endif
572
573#define SCLK_HZ (32768)
574#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
575
576static uint32_t msm_pm_max_sleep_time;
577
578/*
579 * Convert time from nanoseconds to slow clock ticks, then cap it to the
580 * specified limit
581 */
582static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
583{
584 do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
585 return (time_ns > limit) ? limit : time_ns;
586}
587
588/*
589 * Set the sleep time for suspend. 0 means infinite sleep time.
590 */
591void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
592{
593 if (max_sleep_time_ns == 0) {
594 msm_pm_max_sleep_time = 0;
595 } else {
596 msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
597 max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
598
599 if (msm_pm_max_sleep_time == 0)
600 msm_pm_max_sleep_time = 1;
601 }
602
603 if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND)
604 pr_info("%s: Requested %lld ns Giving %u sclk ticks\n",
605 __func__, max_sleep_time_ns, msm_pm_max_sleep_time);
606}
607EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
608
609
610/******************************************************************************
611 *
612 *****************************************************************************/
613
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614static struct msm_rpmrs_limits *msm_pm_idle_rs_limits;
615
616static void msm_pm_swfi(void)
617{
618 msm_pm_config_hw_before_swfi();
619 msm_arch_idle();
620}
621
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600622
623static void msm_pm_retention(void)
624{
625 int ret = 0;
626
627 msm_pm_config_hw_before_swfi();
628 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_RETENTION, false);
629 WARN_ON(ret);
630 msm_arch_idle();
631 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
632 WARN_ON(ret);
633}
634
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600635#ifdef CONFIG_CACHE_L2X0
636static inline bool msm_pm_l2x0_power_collapse(void)
637{
638 bool collapsed = 0;
639
Sridhar Parasurama0222902012-04-27 11:18:02 -0700640 l2x0_suspend();
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600641 collapsed = msm_pm_collapse();
Sridhar Parasurama0222902012-04-27 11:18:02 -0700642 l2x0_resume(collapsed);
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600643
644 return collapsed;
645}
646#else
647static inline bool msm_pm_l2x0_power_collapse(void)
648{
649 return msm_pm_collapse();
650}
651#endif
652
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600653static bool msm_pm_spm_power_collapse(
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700654 unsigned int cpu, bool from_idle, bool notify_rpm)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655{
656 void *entry;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600657 bool collapsed = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 int ret;
Rohit Vaswanie78dfb62012-02-21 10:29:29 -0800659 unsigned int saved_gic_cpu_ctrl;
660
661 saved_gic_cpu_ctrl = readl_relaxed(MSM_QGIC_CPU_BASE + GIC_CPU_CTRL);
662 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663
664 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
665 pr_info("CPU%u: %s: notify_rpm %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700666 cpu, __func__, (int) notify_rpm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667
668 ret = msm_spm_set_low_power_mode(
669 MSM_SPM_MODE_POWER_COLLAPSE, notify_rpm);
670 WARN_ON(ret);
671
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700672 entry = (!cpu || from_idle) ?
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 msm_pm_collapse_exit : msm_secondary_startup;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700674 msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700675
676 if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
677 pr_info("CPU%u: %s: program vector to %p\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700678 cpu, __func__, entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679
680#ifdef CONFIG_VFP
681 vfp_flush_context();
682#endif
683
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600684 collapsed = msm_pm_l2x0_power_collapse();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700685
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700686 msm_pm_boot_config_after_pc(cpu);
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600687
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700688 if (collapsed) {
689#ifdef CONFIG_VFP
690 vfp_reinit();
691#endif
692 cpu_init();
693 writel(0xF0, MSM_QGIC_CPU_BASE + GIC_CPU_PRIMASK);
Rohit Vaswanie78dfb62012-02-21 10:29:29 -0800694 writel_relaxed(saved_gic_cpu_ctrl,
695 MSM_QGIC_CPU_BASE + GIC_CPU_CTRL);
696 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700697 local_fiq_enable();
698 }
699
700 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
701 pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700702 cpu, __func__, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700703
704 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
705 WARN_ON(ret);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600706 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700707}
708
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600709static bool msm_pm_power_collapse_standalone(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700710{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700711 unsigned int cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700712 unsigned int avsdscr_setting;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600713 bool collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714
715 avsdscr_setting = avs_get_avsdscr();
716 avs_disable();
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700717 collapsed = msm_pm_spm_power_collapse(cpu, from_idle, false);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718 avs_reset_delays(avsdscr_setting);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600719 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700720}
721
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600722static bool msm_pm_power_collapse(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700724 unsigned int cpu = smp_processor_id();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725 unsigned long saved_acpuclk_rate;
726 unsigned int avsdscr_setting;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600727 bool collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700728
729 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
730 pr_info("CPU%u: %s: idle %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700731 cpu, __func__, (int)from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732
733 msm_pm_config_hw_before_power_down();
734 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700735 pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736
737 avsdscr_setting = avs_get_avsdscr();
738 avs_disable();
739
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700740 if (cpu_online(cpu))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741 saved_acpuclk_rate = acpuclk_power_collapse();
742 else
743 saved_acpuclk_rate = 0;
744
745 if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
746 pr_info("CPU%u: %s: change clock rate (old rate = %lu)\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700747 cpu, __func__, saved_acpuclk_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700749 collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750
751 if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
752 pr_info("CPU%u: %s: restore clock rate to %lu\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700753 cpu, __func__, saved_acpuclk_rate);
754 if (acpuclk_set_rate(cpu, saved_acpuclk_rate, SETRATE_PC) < 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700755 pr_err("CPU%u: %s: failed to restore clock rate(%lu)\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700756 cpu, __func__, saved_acpuclk_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700757
758 avs_reset_delays(avsdscr_setting);
759 msm_pm_config_hw_after_power_up();
760 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700761 pr_info("CPU%u: %s: post power up\n", cpu, __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762
763 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700764 pr_info("CPU%u: %s: return\n", cpu, __func__);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600765 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766}
767
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768/******************************************************************************
769 * External Idle/Suspend Functions
770 *****************************************************************************/
771
772void arch_idle(void)
773{
774 return;
775}
776
777int msm_pm_idle_prepare(struct cpuidle_device *dev)
778{
779 uint32_t latency_us;
780 uint32_t sleep_us;
781 int i;
782
783 latency_us = (uint32_t) pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
784 sleep_us = (uint32_t) ktime_to_ns(tick_nohz_get_sleep_length());
785 sleep_us = DIV_ROUND_UP(sleep_us, 1000);
786
787 for (i = 0; i < dev->state_count; i++) {
788 struct cpuidle_state *state = &dev->states[i];
789 enum msm_pm_sleep_mode mode;
790 bool allow;
791 struct msm_rpmrs_limits *rs_limits = NULL;
792 int idx;
793
794 mode = (enum msm_pm_sleep_mode) state->driver_data;
795 idx = MSM_PM_MODE(dev->cpu, mode);
796
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600797 allow = msm_pm_sleep_modes[idx].idle_enabled &&
798 msm_pm_sleep_modes[idx].idle_supported;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700799
800 switch (mode) {
801 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
802 if (!allow)
803 break;
804
805 if (num_online_cpus() > 1) {
806 allow = false;
807 break;
808 }
809#ifdef CONFIG_HAS_WAKELOCK
810 if (has_wake_lock(WAKE_LOCK_IDLE)) {
811 allow = false;
812 break;
813 }
814#endif
815 /* fall through */
816
817 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
818 if (!allow)
819 break;
820
821 if (!dev->cpu &&
822 msm_rpm_local_request_is_outstanding()) {
823 allow = false;
824 break;
825 }
826 /* fall through */
827
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600828 case MSM_PM_SLEEP_MODE_RETENTION:
829 if (!allow)
830 break;
831 /* fall through */
832
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700833 case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
834 if (!allow)
835 break;
836
837 rs_limits = msm_rpmrs_lowest_limits(true,
838 mode, latency_us, sleep_us);
839
840 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
841 pr_info("CPU%u: %s: %s, latency %uus, "
842 "sleep %uus, limit %p\n",
843 dev->cpu, __func__, state->desc,
844 latency_us, sleep_us, rs_limits);
845
846 if ((MSM_PM_DEBUG_IDLE_LIMITS & msm_pm_debug_mask) &&
847 rs_limits)
848 pr_info("CPU%u: %s: limit %p: "
849 "pxo %d, l2_cache %d, "
850 "vdd_mem %d, vdd_dig %d\n",
851 dev->cpu, __func__, rs_limits,
852 rs_limits->pxo,
853 rs_limits->l2_cache,
854 rs_limits->vdd_mem,
855 rs_limits->vdd_dig);
856
857 if (!rs_limits)
858 allow = false;
859 break;
860
861 default:
862 allow = false;
863 break;
864 }
865
866 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
867 pr_info("CPU%u: %s: allow %s: %d\n",
868 dev->cpu, __func__, state->desc, (int)allow);
869
870 if (allow) {
871 state->flags &= ~CPUIDLE_FLAG_IGNORE;
872 state->target_residency = 0;
873 state->exit_latency = 0;
874 state->power_usage = rs_limits->power[dev->cpu];
875
876 if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == mode)
877 msm_pm_idle_rs_limits = rs_limits;
878 } else {
879 state->flags |= CPUIDLE_FLAG_IGNORE;
880 }
881 }
882
883 return 0;
884}
885
886int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode)
887{
888 int64_t time;
889#ifdef CONFIG_MSM_IDLE_STATS
890 int exit_stat;
891#endif
892
893 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
894 pr_info("CPU%u: %s: mode %d\n",
895 smp_processor_id(), __func__, sleep_mode);
896
897 time = ktime_to_ns(ktime_get());
898
899 switch (sleep_mode) {
900 case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
901 msm_pm_swfi();
902#ifdef CONFIG_MSM_IDLE_STATS
903 exit_stat = MSM_PM_STAT_IDLE_WFI;
904#endif
905 break;
906
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600907 case MSM_PM_SLEEP_MODE_RETENTION:
908 msm_pm_retention();
909#ifdef CONFIG_MSM_IDLE_STATS
910 exit_stat = MSM_PM_STAT_RETENTION;
911#endif
912 break;
913
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700914 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
915 msm_pm_power_collapse_standalone(true);
916#ifdef CONFIG_MSM_IDLE_STATS
917 exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
918#endif
919 break;
920
921 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: {
922 int64_t timer_expiration = msm_timer_enter_idle();
923 bool timer_halted = false;
924 uint32_t sleep_delay;
925 int ret;
926 int notify_rpm =
927 (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600928 int collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929
930 sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
931 timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
932 if (sleep_delay == 0) /* 0 would mean infinite time */
933 sleep_delay = 1;
934
Karthik Parsha6fb932d2012-01-24 18:04:12 -0800935 if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
936 clock_debug_print_enabled();
937
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938 ret = msm_rpmrs_enter_sleep(
939 sleep_delay, msm_pm_idle_rs_limits, true, notify_rpm);
940 if (!ret) {
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600941 collapsed = msm_pm_power_collapse(true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942 timer_halted = true;
943
944 msm_rpmrs_exit_sleep(msm_pm_idle_rs_limits, true,
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600945 notify_rpm, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700946 }
947
948 msm_timer_exit_idle((int) timer_halted);
949#ifdef CONFIG_MSM_IDLE_STATS
950 exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
951#endif
952 break;
953 }
954
955 default:
956 __WARN();
957 goto cpuidle_enter_bail;
958 }
959
960 time = ktime_to_ns(ktime_get()) - time;
961#ifdef CONFIG_MSM_IDLE_STATS
962 msm_pm_add_stat(exit_stat, time);
963#endif
964
965 do_div(time, 1000);
966 return (int) time;
967
968cpuidle_enter_bail:
969 return 0;
970}
971
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -0600972static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
973
974static DEFINE_PER_CPU_SHARED_ALIGNED(enum msm_pm_sleep_mode,
975 msm_pm_last_slp_mode);
976
977bool msm_pm_verify_cpu_pc(unsigned int cpu)
978{
979 enum msm_pm_sleep_mode mode = per_cpu(msm_pm_last_slp_mode, cpu);
980
981 if (msm_pm_slp_sts)
982 if ((mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) ||
983 (mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE))
984 return true;
985
986 return false;
987}
988
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700989void msm_pm_cpu_enter_lowpower(unsigned int cpu)
990{
991 int i;
992 bool allow[MSM_PM_SLEEP_MODE_NR];
993
994 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
995 struct msm_pm_platform_data *mode;
996
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600997 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700998 allow[i] = mode->suspend_supported && mode->suspend_enabled;
999 }
1000
1001 if (MSM_PM_DEBUG_HOTPLUG & msm_pm_debug_mask)
1002 pr_notice("CPU%u: %s: shutting down cpu\n", cpu, __func__);
1003
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001004 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
1005 per_cpu(msm_pm_last_slp_mode, cpu)
1006 = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001007 msm_pm_power_collapse(false);
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001008 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1009 per_cpu(msm_pm_last_slp_mode, cpu)
1010 = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001011 msm_pm_power_collapse_standalone(false);
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -06001012 } else if (allow[MSM_PM_SLEEP_MODE_RETENTION]) {
1013 per_cpu(msm_pm_last_slp_mode, cpu)
1014 = MSM_PM_SLEEP_MODE_RETENTION;
1015 msm_pm_retention();
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001016 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1017 per_cpu(msm_pm_last_slp_mode, cpu)
1018 = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001019 msm_pm_swfi();
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001020 } else
1021 per_cpu(msm_pm_last_slp_mode, cpu) = MSM_PM_SLEEP_MODE_NR;
1022}
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001023
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001024int msm_pm_wait_cpu_shutdown(unsigned int cpu)
1025{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001026
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001027 int timeout = 10;
1028
1029 if (!msm_pm_slp_sts)
1030 return 0;
1031
1032 while (timeout--) {
1033
1034 /*
1035 * Check for the SPM of the core being hotplugged to set
1036 * its sleep state.The SPM sleep state indicates that the
1037 * core has been power collapsed.
1038 */
1039
1040 int acc_sts = __raw_readl(msm_pm_slp_sts->base_addr
1041 + cpu * msm_pm_slp_sts->cpu_offset);
1042 mb();
1043
1044 if (acc_sts & msm_pm_slp_sts->mask)
1045 return 0;
1046
1047 usleep(100);
1048 }
1049 pr_warn("%s(): Timed out waiting for CPU %u SPM to enter sleep state",
1050 __func__, cpu);
1051 return -EBUSY;
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -07001052}
1053
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001054static int msm_pm_enter(suspend_state_t state)
1055{
1056 bool allow[MSM_PM_SLEEP_MODE_NR];
1057 int i;
1058
1059#ifdef CONFIG_MSM_IDLE_STATS
1060 int64_t period = 0;
1061 int64_t time = msm_timer_get_sclk_time(&period);
1062#endif
1063
1064 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1065 pr_info("%s\n", __func__);
1066
1067 if (smp_processor_id()) {
1068 __WARN();
1069 goto enter_exit;
1070 }
1071
1072
1073 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
1074 struct msm_pm_platform_data *mode;
1075
Praveen Chidambaram42da9d22012-03-30 12:16:34 -06001076 mode = &msm_pm_sleep_modes[MSM_PM_MODE(0, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001077 allow[i] = mode->suspend_supported && mode->suspend_enabled;
1078 }
1079
1080 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
1081 struct msm_rpmrs_limits *rs_limits;
1082 int ret;
1083
1084 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1085 pr_info("%s: power collapse\n", __func__);
1086
1087 clock_debug_print_enabled();
1088
1089#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
1090 if (msm_pm_sleep_time_override > 0) {
1091 int64_t ns = NSEC_PER_SEC *
1092 (int64_t) msm_pm_sleep_time_override;
1093 msm_pm_set_max_sleep_time(ns);
1094 msm_pm_sleep_time_override = 0;
1095 }
1096#endif /* CONFIG_MSM_SLEEP_TIME_OVERRIDE */
1097
1098 if (MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask)
1099 msm_rpmrs_show_resources();
1100
1101 rs_limits = msm_rpmrs_lowest_limits(false,
1102 MSM_PM_SLEEP_MODE_POWER_COLLAPSE, -1, -1);
1103
1104 if ((MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask) &&
1105 rs_limits)
1106 pr_info("%s: limit %p: pxo %d, l2_cache %d, "
1107 "vdd_mem %d, vdd_dig %d\n",
1108 __func__, rs_limits,
1109 rs_limits->pxo, rs_limits->l2_cache,
1110 rs_limits->vdd_mem, rs_limits->vdd_dig);
1111
1112 if (rs_limits) {
1113 ret = msm_rpmrs_enter_sleep(
1114 msm_pm_max_sleep_time, rs_limits, false, true);
1115 if (!ret) {
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -06001116 int collapsed = msm_pm_power_collapse(false);
1117 msm_rpmrs_exit_sleep(rs_limits, false, true,
1118 collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001119 }
1120 } else {
1121 pr_err("%s: cannot find the lowest power limit\n",
1122 __func__);
1123 }
1124
1125#ifdef CONFIG_MSM_IDLE_STATS
1126 if (time != 0) {
1127 int64_t end_time = msm_timer_get_sclk_time(NULL);
1128 if (end_time != 0) {
1129 time = end_time - time;
1130 if (time < 0)
1131 time += period;
1132 } else
1133 time = 0;
1134 }
1135
1136 msm_pm_add_stat(MSM_PM_STAT_SUSPEND, time);
1137#endif /* CONFIG_MSM_IDLE_STATS */
1138 } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
1139 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1140 pr_info("%s: standalone power collapse\n", __func__);
1141 msm_pm_power_collapse_standalone(false);
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -06001142 } else if (allow[MSM_PM_SLEEP_MODE_RETENTION]) {
1143 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1144 pr_info("%s: retention\n", __func__);
1145 msm_pm_retention();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001146 } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
1147 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1148 pr_info("%s: swfi\n", __func__);
1149 msm_pm_swfi();
1150 }
1151
1152
1153enter_exit:
1154 if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
1155 pr_info("%s: return\n", __func__);
1156
1157 return 0;
1158}
1159
1160static struct platform_suspend_ops msm_pm_ops = {
1161 .enter = msm_pm_enter,
1162 .valid = suspend_valid_only_mem,
1163};
1164
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001165/******************************************************************************
1166 * Initialization routine
1167 *****************************************************************************/
Maheshkumar Sivasubramanian6866b1c2011-06-07 14:20:33 -06001168void __init msm_pm_init_sleep_status_data(
1169 struct msm_pm_sleep_status_data *data)
1170{
1171 msm_pm_slp_sts = data;
1172}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001173
1174static int __init msm_pm_init(void)
1175{
1176 pgd_t *pc_pgd;
1177 pmd_t *pmd;
1178 unsigned long pmdval;
1179 unsigned int cpu;
1180#ifdef CONFIG_MSM_IDLE_STATS
1181 struct proc_dir_entry *d_entry;
1182#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001183
1184 /* Page table for cores to come back up safely. */
1185 pc_pgd = pgd_alloc(&init_mm);
1186 if (!pc_pgd)
1187 return -ENOMEM;
1188
1189 pmd = pmd_offset(pc_pgd +
1190 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
1191 virt_to_phys(msm_pm_collapse_exit));
1192 pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
1193 PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
1194 pmd[0] = __pmd(pmdval);
1195 pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
1196
Steve Mucklefcece052012-02-18 20:09:58 -08001197 msm_saved_state_phys =
1198 allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE *
1199 num_possible_cpus(), 4);
1200 if (!msm_saved_state_phys)
1201 return -ENOMEM;
1202 msm_saved_state = ioremap_nocache(msm_saved_state_phys,
1203 CPU_SAVED_STATE_SIZE *
1204 num_possible_cpus());
1205 if (!msm_saved_state)
1206 return -ENOMEM;
1207
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001208 /* It is remotely possible that the code in msm_pm_collapse_exit()
1209 * which turns on the MMU with this mapping is in the
1210 * next even-numbered megabyte beyond the
1211 * start of msm_pm_collapse_exit().
1212 * Map this megabyte in as well.
1213 */
1214 pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
1215 flush_pmd_entry(pmd);
1216 msm_pm_pc_pgd = virt_to_phys(pc_pgd);
Steve Muckle730ad7a2012-02-21 15:26:37 -08001217 clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd),
1218 virt_to_phys(&msm_pm_pc_pgd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001219
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001220#ifdef CONFIG_MSM_IDLE_STATS
1221 for_each_possible_cpu(cpu) {
1222 struct msm_pm_time_stats *stats =
1223 per_cpu(msm_pm_stats, cpu).stats;
1224
1225 stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request";
1226 stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
1227 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1228
1229 stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi";
1230 stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
1231 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1232
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -06001233 stats[MSM_PM_STAT_RETENTION].name = "retention";
1234 stats[MSM_PM_STAT_RETENTION].first_bucket_time =
1235 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1236
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001237 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
1238 "idle-standalone-power-collapse";
1239 stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].
1240 first_bucket_time = CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1241
1242 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name =
1243 "idle-power-collapse";
1244 stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
1245 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
1246
1247 stats[MSM_PM_STAT_SUSPEND].name = "suspend";
1248 stats[MSM_PM_STAT_SUSPEND].first_bucket_time =
1249 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
1250 }
1251
1252 d_entry = create_proc_entry("msm_pm_stats",
1253 S_IRUGO | S_IWUSR | S_IWGRP, NULL);
1254 if (d_entry) {
1255 d_entry->read_proc = msm_pm_read_proc;
1256 d_entry->write_proc = msm_pm_write_proc;
1257 d_entry->data = NULL;
1258 }
1259#endif /* CONFIG_MSM_IDLE_STATS */
1260
1261 msm_pm_mode_sysfs_add();
1262 msm_spm_allow_x_cpu_set_vdd(false);
1263
1264 suspend_set_ops(&msm_pm_ops);
1265 msm_cpuidle_init();
1266
1267 return 0;
1268}
1269
1270late_initcall(msm_pm_init);