blob: cb65a7031b142f19adcff197b9f55663a78f55d9 [file] [log] [blame]
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -07001/* Copyright (c) 2010-2014, The Linux Foundation. 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
Priyanka Mathur13bdad12013-01-28 15:52:56 -080014#include <linux/debugfs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -060018#include <linux/clk.h>
19#include <linux/clkdev.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/io.h>
21#include <linux/ktime.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <linux/smp.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023#include <linux/tick.h>
Anji Jonnala02dac8d2013-03-06 21:31:04 +053024#include <linux/delay.h>
Mahesh Sivasubramaniancb396622012-03-14 14:50:37 -060025#include <linux/platform_device.h>
Anji Jonnala02dac8d2013-03-06 21:31:04 +053026#include <linux/of_platform.h>
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060027#include <linux/cpu_pm.h>
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -070028#include <linux/remote_spinlock.h>
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060029#include <asm/uaccess.h>
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -060030#include <asm/suspend.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031#include <asm/cacheflush.h>
Girish Mahadevan55944992012-10-26 11:03:07 -060032#include <asm/outercache.h>
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -070033#include <mach/remote_spinlock.h>
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060034#include <mach/scm.h>
35#include <mach/msm_bus.h>
36#include <mach/jtag.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#include "acpuclock.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#include "avs.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039#include "idle.h"
Matt Wagantall7cca4642012-02-01 16:43:24 -080040#include "pm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041#include "scm-boot.h"
42#include "spm.h"
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060043#include "pm-boot.h"
Anil kumar mamidala448cb602014-01-21 12:07:00 +053044#include "clock.h"
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060045
46#define CREATE_TRACE_POINTS
47#include <mach/trace_msm_low_power.h>
Hanumant Singhbd82a3e2013-02-26 13:51:17 -080048
Praveen Chidambaramf27a5152013-02-01 11:44:53 -070049#define SCM_CMD_TERMINATE_PC (0x2)
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -060050#define SCM_CMD_CORE_HOTPLUGGED (0x10)
Praveen Chidambaramf27a5152013-02-01 11:44:53 -070051
52#define GET_CPU_OF_ATTR(attr) \
53 (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu)
54
55#define SCLK_HZ (32768)
Praveen Chidambaramf27a5152013-02-01 11:44:53 -070056
Priyanka Mathur13bdad12013-01-28 15:52:56 -080057#define MAX_BUF_SIZE 512
58
Praveen Chidambaramf27a5152013-02-01 11:44:53 -070059static int msm_pm_debug_mask = 1;
60module_param_named(
61 debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
62);
63
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -060064static bool use_acpuclk_apis;
65
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066enum {
67 MSM_PM_DEBUG_SUSPEND = BIT(0),
68 MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
69 MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2),
70 MSM_PM_DEBUG_CLOCK = BIT(3),
71 MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
Karthik Parsha6fb932d2012-01-24 18:04:12 -080072 MSM_PM_DEBUG_IDLE_CLK = BIT(5),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073 MSM_PM_DEBUG_IDLE = BIT(6),
74 MSM_PM_DEBUG_IDLE_LIMITS = BIT(7),
75 MSM_PM_DEBUG_HOTPLUG = BIT(8),
76};
77
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -060078enum msm_pc_count_offsets {
79 MSM_PC_ENTRY_COUNTER,
80 MSM_PC_EXIT_COUNTER,
81 MSM_PC_FALLTHRU_COUNTER,
82 MSM_PC_NUM_COUNTERS,
83};
84
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085enum {
86 MSM_PM_MODE_ATTR_SUSPEND,
87 MSM_PM_MODE_ATTR_IDLE,
88 MSM_PM_MODE_ATTR_NR,
89};
90
91static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
92 [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
93 [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
94};
95
96struct msm_pm_kobj_attribute {
97 unsigned int cpu;
98 struct kobj_attribute ka;
99};
100
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101struct msm_pm_sysfs_sleep_mode {
102 struct kobject *kobj;
103 struct attribute_group attr_group;
104 struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1];
105 struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR];
106};
107
108static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
109 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
110 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600111 [MSM_PM_SLEEP_MODE_RETENTION] = "retention",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
113 "standalone_power_collapse",
114};
115
Mahesh Sivasubramanian1b8601b2012-12-20 14:11:23 -0700116static bool msm_pm_ldo_retention_enabled = true;
Praveen Chidambaram4b8df032013-01-18 16:21:16 -0700117static bool msm_no_ramp_down_pc;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530118static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -0600119DEFINE_PER_CPU(struct clk *, cpu_clks);
120static struct clk *l2_clk;
121
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -0700122static int cpu_count;
123static DEFINE_SPINLOCK(cpu_cnt_lock);
124#define SCM_HANDOFF_LOCK_ID "S:7"
125static bool need_scm_handoff_lock;
126static remote_spinlock_t scm_handoff_lock;
127
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600128static void (*msm_pm_disable_l2_fn)(void);
129static void (*msm_pm_enable_l2_fn)(void);
130static void (*msm_pm_flush_l2_fn)(void);
131static void __iomem *msm_pc_debug_counters;
132
133/*
134 * Default the l2 flush flag to OFF so the caches are flushed during power
135 * collapse unless the explicitly voted by lpm driver.
136 */
137static enum msm_pm_l2_scm_flag msm_pm_flush_l2_flag = MSM_SCM_L2_OFF;
138
139void msm_pm_set_l2_flush_flag(enum msm_pm_l2_scm_flag flag)
140{
141 msm_pm_flush_l2_flag = flag;
142}
143EXPORT_SYMBOL(msm_pm_set_l2_flush_flag);
144
145static enum msm_pm_l2_scm_flag msm_pm_get_l2_flush_flag(void)
146{
147 return msm_pm_flush_l2_flag;
148}
149
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600150static cpumask_t retention_cpus;
151static DEFINE_SPINLOCK(retention_lock);
152
Archana Sathyakumar2b91dc82013-02-01 17:38:23 -0700153static int msm_pm_get_pc_mode(struct device_node *node,
154 const char *key, uint32_t *pc_mode_val)
155{
156 struct pc_mode_of {
157 uint32_t mode;
158 char *mode_name;
159 };
160 int i;
161 struct pc_mode_of pc_modes[] = {
162 {MSM_PM_PC_TZ_L2_INT, "tz_l2_int"},
163 {MSM_PM_PC_NOTZ_L2_EXT, "no_tz_l2_ext"},
164 {MSM_PM_PC_TZ_L2_EXT , "tz_l2_ext"} };
165 int ret;
166 const char *pc_mode_str;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600167 *pc_mode_val = MSM_PM_PC_TZ_L2_INT;
Archana Sathyakumar2b91dc82013-02-01 17:38:23 -0700168
169 ret = of_property_read_string(node, key, &pc_mode_str);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600170 if (!ret) {
Archana Sathyakumar2b91dc82013-02-01 17:38:23 -0700171 ret = -EINVAL;
172 for (i = 0; i < ARRAY_SIZE(pc_modes); i++) {
173 if (!strncmp(pc_mode_str, pc_modes[i].mode_name,
174 strlen(pc_modes[i].mode_name))) {
175 *pc_mode_val = pc_modes[i].mode;
176 ret = 0;
177 break;
178 }
179 }
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600180 } else {
181 pr_debug("%s: Cannot read %s,defaulting to 0", __func__, key);
182 ret = 0;
Archana Sathyakumar2b91dc82013-02-01 17:38:23 -0700183 }
184 return ret;
185}
186
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187/*
188 * Write out the attribute.
189 */
190static ssize_t msm_pm_mode_attr_show(
191 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
192{
193 int ret = -EINVAL;
194 int i;
195
196 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
197 struct kernel_param kp;
198 unsigned int cpu;
199 struct msm_pm_platform_data *mode;
200
201 if (msm_pm_sleep_mode_labels[i] == NULL)
202 continue;
203
204 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
205 continue;
206
207 cpu = GET_CPU_OF_ATTR(attr);
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600208 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209
210 if (!strcmp(attr->attr.name,
211 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
212 u32 arg = mode->suspend_enabled;
213 kp.arg = &arg;
214 ret = param_get_ulong(buf, &kp);
215 } else if (!strcmp(attr->attr.name,
216 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
217 u32 arg = mode->idle_enabled;
218 kp.arg = &arg;
219 ret = param_get_ulong(buf, &kp);
220 }
221
222 break;
223 }
224
225 if (ret > 0) {
Praveen Chidambaram2b0fdd02011-10-28 16:40:58 -0600226 strlcat(buf, "\n", PAGE_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 ret++;
228 }
229
230 return ret;
231}
232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
234 struct kobj_attribute *attr, const char *buf, size_t count)
235{
236 int ret = -EINVAL;
237 int i;
238
239 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
240 struct kernel_param kp;
241 unsigned int cpu;
242 struct msm_pm_platform_data *mode;
243
244 if (msm_pm_sleep_mode_labels[i] == NULL)
245 continue;
246
247 if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
248 continue;
249
250 cpu = GET_CPU_OF_ATTR(attr);
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600251 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252
253 if (!strcmp(attr->attr.name,
254 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
255 kp.arg = &mode->suspend_enabled;
256 ret = param_set_byte(buf, &kp);
257 } else if (!strcmp(attr->attr.name,
258 msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
259 kp.arg = &mode->idle_enabled;
260 ret = param_set_byte(buf, &kp);
261 }
262
263 break;
264 }
265
266 return ret ? ret : count;
267}
268
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600269static int msm_pm_mode_sysfs_add_cpu(
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270 unsigned int cpu, struct kobject *modes_kobj)
271{
272 char cpu_name[8];
273 struct kobject *cpu_kobj;
Praveen Chidambaram2b0fdd02011-10-28 16:40:58 -0600274 struct msm_pm_sysfs_sleep_mode *mode = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 int i, j, k;
276 int ret;
277
278 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
279 cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj);
280 if (!cpu_kobj) {
281 pr_err("%s: cannot create %s kobject\n", __func__, cpu_name);
282 ret = -ENOMEM;
283 goto mode_sysfs_add_cpu_exit;
284 }
285
286 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
287 int idx = MSM_PM_MODE(cpu, i);
288
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600289 if ((!msm_pm_sleep_modes[idx].suspend_supported)
290 && (!msm_pm_sleep_modes[idx].idle_supported))
291 continue;
292
293 if (!msm_pm_sleep_mode_labels[i] ||
294 !msm_pm_sleep_mode_labels[i][0])
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 continue;
296
297 mode = kzalloc(sizeof(*mode), GFP_KERNEL);
298 if (!mode) {
299 pr_err("%s: cannot allocate memory for attributes\n",
300 __func__);
301 ret = -ENOMEM;
302 goto mode_sysfs_add_cpu_exit;
303 }
304
305 mode->kobj = kobject_create_and_add(
306 msm_pm_sleep_mode_labels[i], cpu_kobj);
307 if (!mode->kobj) {
308 pr_err("%s: cannot create kobject\n", __func__);
309 ret = -ENOMEM;
310 goto mode_sysfs_add_cpu_exit;
311 }
312
313 for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
314 if ((k == MSM_PM_MODE_ATTR_IDLE) &&
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600315 !msm_pm_sleep_modes[idx].idle_supported)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 continue;
317 if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600318 !msm_pm_sleep_modes[idx].suspend_supported)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 continue;
Stephen Boydd2059c32012-07-03 15:11:15 -0700320 sysfs_attr_init(&mode->kas[j].ka.attr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 mode->kas[j].cpu = cpu;
322 mode->kas[j].ka.attr.mode = 0644;
323 mode->kas[j].ka.show = msm_pm_mode_attr_show;
324 mode->kas[j].ka.store = msm_pm_mode_attr_store;
325 mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k];
326 mode->attrs[j] = &mode->kas[j].ka.attr;
327 j++;
328 }
329 mode->attrs[j] = NULL;
330
331 mode->attr_group.attrs = mode->attrs;
332 ret = sysfs_create_group(mode->kobj, &mode->attr_group);
333 if (ret) {
334 pr_err("%s: cannot create kobject attribute group\n",
335 __func__);
336 goto mode_sysfs_add_cpu_exit;
337 }
338 }
339
340 ret = 0;
341
342mode_sysfs_add_cpu_exit:
Praveen Chidambaramd5ac2d32011-10-24 14:30:27 -0600343 if (ret) {
Praveen Chidambaram2cfda632011-10-11 16:58:09 -0600344 if (mode && mode->kobj)
345 kobject_del(mode->kobj);
346 kfree(mode);
347 }
348
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 return ret;
350}
351
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600352int msm_pm_mode_sysfs_add(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700353{
354 struct kobject *module_kobj;
355 struct kobject *modes_kobj;
356 unsigned int cpu;
357 int ret;
358
359 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
360 if (!module_kobj) {
361 pr_err("%s: cannot find kobject for module %s\n",
362 __func__, KBUILD_MODNAME);
363 ret = -ENOENT;
364 goto mode_sysfs_add_exit;
365 }
366
367 modes_kobj = kobject_create_and_add("modes", module_kobj);
368 if (!modes_kobj) {
369 pr_err("%s: cannot create modes kobject\n", __func__);
370 ret = -ENOMEM;
371 goto mode_sysfs_add_exit;
372 }
373
374 for_each_possible_cpu(cpu) {
375 ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj);
376 if (ret)
377 goto mode_sysfs_add_exit;
378 }
379
380 ret = 0;
381
382mode_sysfs_add_exit:
383 return ret;
384}
385
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600386static inline void msm_arch_idle(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600388 mb();
389 wfi();
Girish Mahadevand27ca4a2012-08-15 09:21:23 -0600390}
391
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600392static bool msm_pm_is_L1_writeback(void)
393{
394 u32 sel = 0, cache_id;
395
396 asm volatile ("mcr p15, 2, %[ccselr], c0, c0, 0\n\t"
397 "isb\n\t"
398 "mrc p15, 1, %[ccsidr], c0, c0, 0\n\t"
399 :[ccsidr]"=r" (cache_id)
400 :[ccselr]"r" (sel)
401 );
402 return cache_id & BIT(31);
403}
404
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600405static enum msm_pm_time_stats_id msm_pm_swfi(bool from_idle)
Praveen Chidambaramc594a092012-09-18 19:48:29 -0600406{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 msm_arch_idle();
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600408 return MSM_PM_STAT_IDLE_WFI;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409}
410
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600411static enum msm_pm_time_stats_id msm_pm_retention(bool from_idle)
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600412{
413 int ret = 0;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600414 int cpu = smp_processor_id();
Mahesh Sivasubramanian2f886912013-09-17 11:00:38 -0600415 int saved_rate = 0;
416 struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600417
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600418 spin_lock(&retention_lock);
419
420 if (!msm_pm_ldo_retention_enabled)
421 goto bailout;
422
423 cpumask_set_cpu(cpu, &retention_cpus);
424 spin_unlock(&retention_lock);
425
Mahesh Sivasubramanian2f886912013-09-17 11:00:38 -0600426 if (use_acpuclk_apis)
427 saved_rate = acpuclk_power_collapse();
428 else
429 clk_disable(cpu_clk);
430
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600431 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_RETENTION, false);
432 WARN_ON(ret);
Girish Mahadevand27ca4a2012-08-15 09:21:23 -0600433
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600434 msm_arch_idle();
Mahesh Sivasubramanian2f886912013-09-17 11:00:38 -0600435
436 if (use_acpuclk_apis) {
437 if (acpuclk_set_rate(cpu, saved_rate, SETRATE_PC))
438 pr_err("%s(): Error setting acpuclk_set_rate\n",
439 __func__);
440 } else {
441 if (clk_enable(cpu_clk))
442 pr_err("%s(): Error restoring cpu clk\n", __func__);
443 }
444
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600445 spin_lock(&retention_lock);
446 cpumask_clear_cpu(cpu, &retention_cpus);
447bailout:
448 spin_unlock(&retention_lock);
Girish Mahadevand27ca4a2012-08-15 09:21:23 -0600449
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600450 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
451 WARN_ON(ret);
452
453 return MSM_PM_STAT_RETENTION;
Praveen Chidambaramd3d844d2012-04-24 09:47:38 -0600454}
455
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600456static inline void msm_pc_inc_debug_count(uint32_t cpu,
457 enum msm_pc_count_offsets offset)
458{
459 uint32_t cnt;
460
461 if (!msm_pc_debug_counters)
462 return;
463
Anil kumar mamidalaadfeebe2014-02-06 16:00:19 +0530464 cnt = readl_relaxed(msm_pc_debug_counters + cpu * 4 * MSM_PC_NUM_COUNTERS + offset * 4);
465 writel_relaxed(++cnt, msm_pc_debug_counters + cpu * 4 * MSM_PC_NUM_COUNTERS + offset * 4);
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600466 mb();
467}
468
469static bool msm_pm_pc_hotplug(void)
470{
471 uint32_t cpu = smp_processor_id();
472
473 if (msm_pm_is_L1_writeback())
474 flush_cache_louis();
475
476 msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
477
478 scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
479 SCM_CMD_CORE_HOTPLUGGED);
480
481 /* Should not return here */
482 msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
483 return 0;
484}
485
486static int msm_pm_collapse(unsigned long unused)
487{
488 uint32_t cpu = smp_processor_id();
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -0700489 enum msm_pm_l2_scm_flag flag = MSM_SCM_L2_ON;
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600490
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -0700491 spin_lock(&cpu_cnt_lock);
492 cpu_count++;
493 if (cpu_count == num_online_cpus())
494 flag = msm_pm_get_l2_flush_flag();
495
496 pr_debug("cpu:%d cores_in_pc:%d L2 flag: %d\n",
497 cpu, cpu_count, flag);
498
499 /*
500 * The scm_handoff_lock will be release by the secure monitor.
501 * It is used to serialize power-collapses from this point on,
502 * so that both Linux and the secure context have a consistent
503 * view regarding the number of running cpus (cpu_count).
504 *
505 * It must be acquired before releasing cpu_cnt_lock.
506 */
507 if (need_scm_handoff_lock)
508 remote_spin_lock_rlock_id(&scm_handoff_lock,
509 REMOTE_SPINLOCK_TID_START + cpu);
510 spin_unlock(&cpu_cnt_lock);
511
512 if (flag == MSM_SCM_L2_OFF) {
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600513 flush_cache_all();
514 if (msm_pm_flush_l2_fn)
515 msm_pm_flush_l2_fn();
516 } else if (msm_pm_is_L1_writeback())
517 flush_cache_louis();
518
519 if (msm_pm_disable_l2_fn)
520 msm_pm_disable_l2_fn();
521
522 msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
523
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -0700524 scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600525
526 msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
527
528 if (msm_pm_enable_l2_fn)
529 msm_pm_enable_l2_fn();
530
531 return 0;
532}
533
Stephen Boydb29750d2012-02-21 01:21:32 -0800534static bool __ref msm_pm_spm_power_collapse(
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700535 unsigned int cpu, bool from_idle, bool notify_rpm)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536{
537 void *entry;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600538 bool collapsed = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 int ret;
Mahesh Sivasubramanianda746252013-05-02 09:58:08 -0600540 bool save_cpu_regs = !cpu || from_idle;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541
542 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
543 pr_info("CPU%u: %s: notify_rpm %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700544 cpu, __func__, (int) notify_rpm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600546 if (from_idle)
Venkat Devarasetty722f91f2013-06-03 19:41:27 +0530547 cpu_pm_enter();
548
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549 ret = msm_spm_set_low_power_mode(
550 MSM_SPM_MODE_POWER_COLLAPSE, notify_rpm);
551 WARN_ON(ret);
552
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600553 entry = save_cpu_regs ? cpu_resume : msm_secondary_startup;
Mahesh Sivasubramanianda746252013-05-02 09:58:08 -0600554
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700555 msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556
557 if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
558 pr_info("CPU%u: %s: program vector to %p\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700559 cpu, __func__, entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600561 msm_jtag_save_state();
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600562
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600563 collapsed = save_cpu_regs ?
564 !cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600565
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -0700566 if (save_cpu_regs) {
567 spin_lock(&cpu_cnt_lock);
568 cpu_count--;
569 BUG_ON(cpu_count > num_online_cpus());
570 spin_unlock(&cpu_cnt_lock);
571 }
Mahesh Sivasubramanian56a35432013-10-15 14:36:58 -0600572 msm_jtag_restore_state();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600574 if (collapsed) {
575 cpu_init();
576 local_fiq_enable();
577 }
Mahesh Sivasubramanianb7bda882013-04-08 09:55:48 -0600578
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700579 msm_pm_boot_config_after_pc(cpu);
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600580
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600581 if (from_idle)
582 cpu_pm_exit();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583
584 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
585 pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700586 cpu, __func__, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587
588 ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
589 WARN_ON(ret);
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600590 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591}
592
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600593static enum msm_pm_time_stats_id msm_pm_power_collapse_standalone(
594 bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700596 unsigned int cpu = smp_processor_id();
Praveen Chidambaram5e614112012-11-08 17:53:34 -0700597 unsigned int avsdscr;
598 unsigned int avscsr;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600599 bool collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600
Praveen Chidambaram5e614112012-11-08 17:53:34 -0700601 avsdscr = avs_get_avsdscr();
602 avscsr = avs_get_avscsr();
603 avs_set_avscsr(0); /* Disable AVS */
604
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700605 collapsed = msm_pm_spm_power_collapse(cpu, from_idle, false);
Praveen Chidambaram5e614112012-11-08 17:53:34 -0700606
607 avs_set_avsdscr(avsdscr);
608 avs_set_avscsr(avscsr);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600609 return collapsed ? MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE :
610 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700611}
612
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -0600613static int ramp_down_last_cpu(int cpu)
614{
615 struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
616 int ret = 0;
617
618 if (use_acpuclk_apis) {
619 ret = acpuclk_power_collapse();
620 if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
621 pr_info("CPU%u: %s: change clk rate(old rate = %d)\n",
622 cpu, __func__, ret);
623 } else {
624 clk_disable(cpu_clk);
625 clk_disable(l2_clk);
626 }
627 return ret;
628}
629
630static int ramp_up_first_cpu(int cpu, int saved_rate)
631{
632 struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
633 int rc = 0;
634
635 if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
636 pr_info("CPU%u: %s: restore clock rate\n",
637 cpu, __func__);
638
639 if (use_acpuclk_apis) {
640 rc = acpuclk_set_rate(cpu, saved_rate, SETRATE_PC);
641 if (rc)
642 pr_err("CPU:%u: Error restoring cpu clk\n", cpu);
643 } else {
644 if (l2_clk) {
645 rc = clk_enable(l2_clk);
646 if (rc)
647 pr_err("%s(): Error restoring l2 clk\n",
648 __func__);
649 }
650
651 if (cpu_clk) {
652 int ret = clk_enable(cpu_clk);
653
654 if (ret) {
655 pr_err("%s(): Error restoring cpu clk\n",
656 __func__);
657 return ret;
658 }
659 }
660 }
661
662 return rc;
663}
664
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600665static enum msm_pm_time_stats_id msm_pm_power_collapse(bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700666{
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700667 unsigned int cpu = smp_processor_id();
Praveen Chidambaram4b8df032013-01-18 16:21:16 -0700668 unsigned long saved_acpuclk_rate = 0;
Praveen Chidambaram5e614112012-11-08 17:53:34 -0700669 unsigned int avsdscr;
670 unsigned int avscsr;
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600671 bool collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700672
673 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
674 pr_info("CPU%u: %s: idle %d\n",
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700675 cpu, __func__, (int)from_idle);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700676
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700678 pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679
Anil kumar mamidala448cb602014-01-21 12:07:00 +0530680 /* This spews a lot of messages when a core is hotplugged. This
681 * information is most useful from last core going down during
682 * power collapse
683 */
684 if ((!from_idle && cpu_online(cpu))
685 || (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask))
686 clock_debug_print_enabled();
687
Praveen Chidambaram5e614112012-11-08 17:53:34 -0700688 avsdscr = avs_get_avsdscr();
689 avscsr = avs_get_avscsr();
690 avs_set_avscsr(0); /* Disable AVS */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691
Praveen Chidambaram4b8df032013-01-18 16:21:16 -0700692 if (cpu_online(cpu) && !msm_no_ramp_down_pc)
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -0600693 saved_acpuclk_rate = ramp_down_last_cpu(cpu);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700694
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700695 collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700696
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600697 if (cpu_online(cpu) && !msm_no_ramp_down_pc)
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -0600698 ramp_up_first_cpu(cpu, saved_acpuclk_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699
Praveen Chidambaram5e614112012-11-08 17:53:34 -0700700 avs_set_avsdscr(avsdscr);
701 avs_set_avscsr(avscsr);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600702
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700703 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700704 pr_info("CPU%u: %s: post power up\n", cpu, __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700705
706 if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700707 pr_info("CPU%u: %s: return\n", cpu, __func__);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600708 return collapsed ? MSM_PM_STAT_IDLE_POWER_COLLAPSE :
709 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700710}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711/******************************************************************************
712 * External Idle/Suspend Functions
713 *****************************************************************************/
714
715void arch_idle(void)
716{
717 return;
718}
719
Priyanka Mathur26b4a4b2012-11-05 13:45:45 -0800720static inline void msm_pm_ftrace_lpm_enter(unsigned int cpu,
721 uint32_t latency, uint32_t sleep_us,
722 uint32_t wake_up,
723 enum msm_pm_sleep_mode mode)
724{
725 switch (mode) {
726 case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
727 trace_msm_pm_enter_wfi(cpu, latency, sleep_us, wake_up);
728 break;
729 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
730 trace_msm_pm_enter_spc(cpu, latency, sleep_us, wake_up);
731 break;
732 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
733 trace_msm_pm_enter_pc(cpu, latency, sleep_us, wake_up);
734 break;
735 case MSM_PM_SLEEP_MODE_RETENTION:
736 trace_msm_pm_enter_ret(cpu, latency, sleep_us, wake_up);
737 break;
738 default:
739 break;
740 }
741}
742
743static inline void msm_pm_ftrace_lpm_exit(unsigned int cpu,
744 enum msm_pm_sleep_mode mode,
745 bool success)
746{
747 switch (mode) {
748 case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
749 trace_msm_pm_exit_wfi(cpu, success);
750 break;
751 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
752 trace_msm_pm_exit_spc(cpu, success);
753 break;
754 case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
755 trace_msm_pm_exit_pc(cpu, success);
756 break;
757 case MSM_PM_SLEEP_MODE_RETENTION:
758 trace_msm_pm_exit_ret(cpu, success);
759 break;
760 default:
761 break;
762 }
763}
764
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600765static enum msm_pm_time_stats_id (*execute[MSM_PM_SLEEP_MODE_NR])(bool idle) = {
766 [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = msm_pm_swfi,
767 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
768 msm_pm_power_collapse_standalone,
769 [MSM_PM_SLEEP_MODE_RETENTION] = msm_pm_retention,
770 [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = msm_pm_power_collapse,
771};
772
773bool msm_cpu_pm_check_mode(unsigned int cpu, enum msm_pm_sleep_mode mode,
774 bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700775{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600776 int idx = MSM_PM_MODE(cpu, mode);
777 struct msm_pm_platform_data *d = &msm_pm_sleep_modes[idx];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600779 if ((mode == MSM_PM_SLEEP_MODE_RETENTION)
780 && !msm_pm_ldo_retention_enabled)
781 return false;
Girish Mahadevandc318fd2012-08-17 16:48:05 -0600782
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600783 if (from_idle)
784 return d->idle_enabled && d->idle_supported;
Girish Mahadevandc318fd2012-08-17 16:48:05 -0600785 else
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600786 return d->suspend_enabled && d->suspend_supported;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700787}
788
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600789int msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700790{
791 int64_t time;
Priyanka Mathur26b4a4b2012-11-05 13:45:45 -0800792 bool collapsed = 1;
Priyanka Mathur92fe5752013-01-17 10:58:04 -0800793 int exit_stat = -1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794
795 if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
796 pr_info("CPU%u: %s: mode %d\n",
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600797 smp_processor_id(), __func__, mode);
798 if (!from_idle)
799 pr_info("CPU%u: %s mode:%d\n",
800 smp_processor_id(), __func__, mode);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801
Anil kumar mamidalab2bfc492014-03-05 15:50:30 +0530802 if (from_idle)
803 time = sched_clock();
804
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600805 if (execute[mode])
806 exit_stat = execute[mode](from_idle);
Anil kumar mamidalab2bfc492014-03-05 15:50:30 +0530807
808 if (from_idle) {
809 time = sched_clock() - time;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600810 msm_pm_ftrace_lpm_exit(smp_processor_id(), mode, collapsed);
Anil kumar mamidalab2bfc492014-03-05 15:50:30 +0530811 if (exit_stat >= 0)
812 msm_pm_add_stat(exit_stat, time);
813 }
814
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600815 return collapsed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816}
817
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530818int msm_pm_wait_cpu_shutdown(unsigned int cpu)
819{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600820 int timeout = 10;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530821
822 if (!msm_pm_slp_sts)
823 return 0;
824 if (!msm_pm_slp_sts[cpu].base_addr)
825 return 0;
Mahesh Sivasubramanian98d1a582013-05-20 13:50:16 -0600826 while (1) {
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530827 /*
828 * Check for the SPM of the core being hotplugged to set
829 * its sleep state.The SPM sleep state indicates that the
830 * core has been power collapsed.
831 */
832 int acc_sts = __raw_readl(msm_pm_slp_sts[cpu].base_addr);
833
834 if (acc_sts & msm_pm_slp_sts[cpu].mask)
835 return 0;
Mahesh Sivasubramanian65187f92013-03-19 15:14:06 -0600836 udelay(100);
Mahesh Sivasubramanian98d1a582013-05-20 13:50:16 -0600837 WARN(++timeout == 20, "CPU%u didn't collapse in 2 ms\n", cpu);
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530838 }
839
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530840 return -EBUSY;
841}
842
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700843void msm_pm_cpu_enter_lowpower(unsigned int cpu)
844{
845 int i;
846 bool allow[MSM_PM_SLEEP_MODE_NR];
847
848 for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
849 struct msm_pm_platform_data *mode;
850
Praveen Chidambaram42da9d22012-03-30 12:16:34 -0600851 mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)];
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700852 allow[i] = mode->suspend_supported && mode->suspend_enabled;
853 }
854
855 if (MSM_PM_DEBUG_HOTPLUG & msm_pm_debug_mask)
856 pr_notice("CPU%u: %s: shutting down cpu\n", cpu, __func__);
857
Matt Wagantall5375aa72012-07-02 19:59:51 -0700858 if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700859 msm_pm_power_collapse(false);
Matt Wagantall5375aa72012-07-02 19:59:51 -0700860 else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE])
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700861 msm_pm_power_collapse_standalone(false);
Matt Wagantall5375aa72012-07-02 19:59:51 -0700862 else if (allow[MSM_PM_SLEEP_MODE_RETENTION])
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600863 msm_pm_retention(false);
Stephen Boydbda74272012-08-09 14:01:27 -0700864 else
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600865 msm_pm_swfi(false);
Mahesh Sivasubramaniand23add12011-11-18 14:30:11 -0700866}
867
Mahesh Sivasubramanian1b8601b2012-12-20 14:11:23 -0700868static void msm_pm_ack_retention_disable(void *data)
869{
870 /*
871 * This is a NULL function to ensure that the core has woken up
872 * and is safe to disable retention.
873 */
874}
875/**
876 * msm_pm_enable_retention() - Disable/Enable retention on all cores
877 * @enable: Enable/Disable retention
878 *
879 */
880void msm_pm_enable_retention(bool enable)
881{
Matt Wagantalld06f5c22013-03-14 19:31:14 -0700882 if (enable == msm_pm_ldo_retention_enabled)
883 return;
884
Mahesh Sivasubramanian1b8601b2012-12-20 14:11:23 -0700885 msm_pm_ldo_retention_enabled = enable;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600886
Mahesh Sivasubramanian1b8601b2012-12-20 14:11:23 -0700887 /*
888 * If retention is being disabled, wakeup all online core to ensure
889 * that it isn't executing retention. Offlined cores need not be woken
890 * up as they enter the deepest sleep mode, namely RPM assited power
891 * collapse
892 */
Mahesh Sivasubramanian96f67d12013-03-14 22:44:42 -0600893 if (!enable) {
894 preempt_disable();
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600895 smp_call_function_many(&retention_cpus,
Mahesh Sivasubramanian1b8601b2012-12-20 14:11:23 -0700896 msm_pm_ack_retention_disable,
897 NULL, true);
Mahesh Sivasubramanian96f67d12013-03-14 22:44:42 -0600898 preempt_enable();
Mahesh Sivasubramanian96f67d12013-03-14 22:44:42 -0600899 }
Mahesh Sivasubramanian1b8601b2012-12-20 14:11:23 -0700900}
901EXPORT_SYMBOL(msm_pm_enable_retention);
902
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600903static int msm_pm_snoc_client_probe(struct platform_device *pdev)
Girish Mahadevanec50d2d2013-05-15 12:07:38 -0600904{
905 int rc = 0;
906 static struct msm_bus_scale_pdata *msm_pm_bus_pdata;
907 static uint32_t msm_pm_bus_client;
908
909 msm_pm_bus_pdata = msm_bus_cl_get_pdata(pdev);
910
911 if (msm_pm_bus_pdata) {
912 msm_pm_bus_client =
913 msm_bus_scale_register_client(msm_pm_bus_pdata);
914
915 if (!msm_pm_bus_client) {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600916 pr_err("%s: Failed to register SNOC client", __func__);
Girish Mahadevanec50d2d2013-05-15 12:07:38 -0600917 rc = -ENXIO;
918 goto snoc_cl_probe_done;
919 }
920
921 rc = msm_bus_scale_client_update_request(msm_pm_bus_client, 1);
922
923 if (rc)
924 pr_err("%s: Error setting bus rate", __func__);
925 }
926
927snoc_cl_probe_done:
928 return rc;
929}
930
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600931static int msm_cpu_status_probe(struct platform_device *pdev)
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530932{
933 struct msm_pm_sleep_status_data *pdata;
934 char *key;
935 u32 cpu;
936
937 if (!pdev)
938 return -EFAULT;
939
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600940 msm_pm_slp_sts = devm_kzalloc(&pdev->dev,
941 sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
942 GFP_KERNEL);
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530943
944 if (!msm_pm_slp_sts)
945 return -ENOMEM;
946
947 if (pdev->dev.of_node) {
948 struct resource *res;
949 u32 offset;
950 int rc;
951 u32 mask;
952
953 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
954 if (!res)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600955 return -ENODEV;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530956
957 key = "qcom,cpu-alias-addr";
958 rc = of_property_read_u32(pdev->dev.of_node, key, &offset);
959
960 if (rc)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600961 return -ENODEV;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530962
963 key = "qcom,sleep-status-mask";
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600964 rc = of_property_read_u32(pdev->dev.of_node, key, &mask);
965
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530966 if (rc)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600967 return -ENODEV;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530968
969 for_each_possible_cpu(cpu) {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600970 phys_addr_t base_c = res->start + cpu * offset;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530971 msm_pm_slp_sts[cpu].base_addr =
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600972 devm_ioremap(&pdev->dev, base_c,
973 resource_size(res));
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530974 msm_pm_slp_sts[cpu].mask = mask;
975
976 if (!msm_pm_slp_sts[cpu].base_addr)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600977 return -ENOMEM;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530978 }
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530979 } else {
980 pdata = pdev->dev.platform_data;
981 if (!pdev->dev.platform_data)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600982 return -EINVAL;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530983
984 for_each_possible_cpu(cpu) {
985 msm_pm_slp_sts[cpu].base_addr =
986 pdata->base_addr + cpu * pdata->cpu_offset;
987 msm_pm_slp_sts[cpu].mask = pdata->mask;
988 }
989 }
990
991 return 0;
Anji Jonnala02dac8d2013-03-06 21:31:04 +0530992};
993
994static struct of_device_id msm_slp_sts_match_tbl[] = {
995 {.compatible = "qcom,cpu-sleep-status"},
996 {},
997};
998
999static struct platform_driver msm_cpu_status_driver = {
1000 .probe = msm_cpu_status_probe,
1001 .driver = {
1002 .name = "cpu_slp_status",
1003 .owner = THIS_MODULE,
1004 .of_match_table = msm_slp_sts_match_tbl,
1005 },
1006};
Mahesh Sivasubramaniancb396622012-03-14 14:50:37 -06001007
Girish Mahadevanec50d2d2013-05-15 12:07:38 -06001008static struct of_device_id msm_snoc_clnt_match_tbl[] = {
1009 {.compatible = "qcom,pm-snoc-client"},
1010 {},
1011};
1012
1013static struct platform_driver msm_cpu_pm_snoc_client_driver = {
1014 .probe = msm_pm_snoc_client_probe,
1015 .driver = {
1016 .name = "pm_snoc_client",
1017 .owner = THIS_MODULE,
1018 .of_match_table = msm_snoc_clnt_match_tbl,
1019 },
1020};
1021
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001022static int msm_pm_init(void)
Praveen Chidambaramede45d02013-02-20 17:45:21 -07001023{
1024 enum msm_pm_time_stats_id enable_stats[] = {
1025 MSM_PM_STAT_IDLE_WFI,
1026 MSM_PM_STAT_RETENTION,
1027 MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001028 MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
Praveen Chidambaramede45d02013-02-20 17:45:21 -07001029 MSM_PM_STAT_IDLE_POWER_COLLAPSE,
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001030 MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
Praveen Chidambaramede45d02013-02-20 17:45:21 -07001031 MSM_PM_STAT_SUSPEND,
1032 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001033 msm_pm_mode_sysfs_add();
Praveen Chidambaram3895bde2012-05-14 19:42:40 +05301034 msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
Mahesh Sivasubramanianb7bda882013-04-08 09:55:48 -06001035
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036 return 0;
1037}
1038
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001039static void msm_pm_set_flush_fn(uint32_t pc_mode)
Girish Mahadevan55944992012-10-26 11:03:07 -06001040{
1041 msm_pm_disable_l2_fn = NULL;
1042 msm_pm_enable_l2_fn = NULL;
1043 msm_pm_flush_l2_fn = outer_flush_all;
1044
1045 if (pc_mode == MSM_PM_PC_NOTZ_L2_EXT) {
1046 msm_pm_disable_l2_fn = outer_disable;
1047 msm_pm_enable_l2_fn = outer_resume;
1048 }
1049}
1050
Priyanka Mathur13bdad12013-01-28 15:52:56 -08001051struct msm_pc_debug_counters_buffer {
1052 void __iomem *reg;
1053 u32 len;
1054 char buf[MAX_BUF_SIZE];
1055};
1056
1057static inline u32 msm_pc_debug_counters_read_register(
1058 void __iomem *reg, int index , int offset)
1059{
1060 return readl_relaxed(reg + (index * 4 + offset) * 4);
1061}
1062
1063static char *counter_name[] = {
1064 "PC Entry Counter",
1065 "Warmboot Entry Counter",
1066 "PC Bailout Counter"
1067};
1068
1069static int msm_pc_debug_counters_copy(
1070 struct msm_pc_debug_counters_buffer *data)
1071{
1072 int j;
1073 u32 stat;
1074 unsigned int cpu;
1075
1076 for_each_possible_cpu(cpu) {
1077 data->len += scnprintf(data->buf + data->len,
1078 sizeof(data->buf)-data->len,
1079 "CPU%d\n", cpu);
1080
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001081 for (j = 0; j < MSM_PC_NUM_COUNTERS; j++) {
1082 stat = msm_pc_debug_counters_read_register(
1083 data->reg, cpu, j);
1084 data->len += scnprintf(data->buf + data->len,
Priyanka Mathur13bdad12013-01-28 15:52:56 -08001085 sizeof(data->buf)-data->len,
1086 "\t%s : %d\n", counter_name[j],
1087 stat);
1088 }
1089
1090 }
1091
1092 return data->len;
1093}
1094
1095static int msm_pc_debug_counters_file_read(struct file *file,
1096 char __user *bufu, size_t count, loff_t *ppos)
1097{
1098 struct msm_pc_debug_counters_buffer *data;
1099
1100 data = file->private_data;
1101
1102 if (!data)
1103 return -EINVAL;
1104
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001105 if (!bufu || count < 0)
Priyanka Mathur13bdad12013-01-28 15:52:56 -08001106 return -EINVAL;
1107
1108 if (!access_ok(VERIFY_WRITE, bufu, count))
1109 return -EFAULT;
1110
1111 if (*ppos >= data->len && data->len == 0)
1112 data->len = msm_pc_debug_counters_copy(data);
1113
1114 return simple_read_from_buffer(bufu, count, ppos,
1115 data->buf, data->len);
1116}
1117
1118static int msm_pc_debug_counters_file_open(struct inode *inode,
1119 struct file *file)
1120{
1121 struct msm_pc_debug_counters_buffer *buf;
1122 void __iomem *msm_pc_debug_counters_reg;
1123
1124 msm_pc_debug_counters_reg = inode->i_private;
1125
1126 if (!msm_pc_debug_counters_reg)
1127 return -EINVAL;
1128
1129 file->private_data = kzalloc(
1130 sizeof(struct msm_pc_debug_counters_buffer), GFP_KERNEL);
1131
1132 if (!file->private_data) {
1133 pr_err("%s: ERROR kmalloc failed to allocate %d bytes\n",
1134 __func__, sizeof(struct msm_pc_debug_counters_buffer));
1135
1136 return -ENOMEM;
1137 }
1138
1139 buf = file->private_data;
1140 buf->reg = msm_pc_debug_counters_reg;
1141
1142 return 0;
1143}
1144
1145static int msm_pc_debug_counters_file_close(struct inode *inode,
1146 struct file *file)
1147{
1148 kfree(file->private_data);
1149 return 0;
1150}
1151
1152static const struct file_operations msm_pc_debug_counters_fops = {
1153 .open = msm_pc_debug_counters_file_open,
1154 .read = msm_pc_debug_counters_file_read,
1155 .release = msm_pc_debug_counters_file_close,
1156 .llseek = no_llseek,
1157};
1158
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -06001159static int msm_pm_clk_init(struct platform_device *pdev)
1160{
1161 bool synced_clocks;
1162 u32 cpu;
1163 char clk_name[] = "cpu??_clk";
1164 bool cpu_as_clocks;
1165 char *key;
1166
1167 key = "qcom,cpus-as-clocks";
1168 cpu_as_clocks = of_property_read_bool(pdev->dev.of_node, key);
1169
1170 if (!cpu_as_clocks) {
1171 use_acpuclk_apis = true;
1172 return 0;
1173 }
1174
1175 key = "qcom,synced-clocks";
1176 synced_clocks = of_property_read_bool(pdev->dev.of_node, key);
1177
1178 for_each_possible_cpu(cpu) {
1179 struct clk *clk;
1180 snprintf(clk_name, sizeof(clk_name), "cpu%d_clk", cpu);
1181 clk = devm_clk_get(&pdev->dev, clk_name);
1182 if (IS_ERR(clk)) {
1183 if (cpu && synced_clocks)
1184 return 0;
1185 else
1186 return PTR_ERR(clk);
1187 }
1188 per_cpu(cpu_clks, cpu) = clk;
1189 }
1190
1191 if (synced_clocks)
1192 return 0;
1193
1194 l2_clk = devm_clk_get(&pdev->dev, "l2_clk");
1195
1196 return PTR_RET(l2_clk);
1197}
1198
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001199static int msm_cpu_pm_probe(struct platform_device *pdev)
Girish Mahadevan55944992012-10-26 11:03:07 -06001200{
1201 char *key = NULL;
Priyanka Mathur13bdad12013-01-28 15:52:56 -08001202 struct dentry *dent = NULL;
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001203 struct resource *res = NULL;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001204 int i;
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001205 struct msm_pm_init_data_type pdata_local;
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -07001206 struct device_node *lpm_node;
Girish Mahadevan55944992012-10-26 11:03:07 -06001207 int ret = 0;
1208
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001209 memset(&pdata_local, 0, sizeof(struct msm_pm_init_data_type));
1210
1211 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001212 if (!res)
1213 return 0;
1214 msm_pc_debug_counters_phys = res->start;
1215 WARN_ON(resource_size(res) < SZ_64);
1216 msm_pc_debug_counters = devm_ioremap(&pdev->dev, res->start,
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001217 resource_size(res));
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001218 if (msm_pc_debug_counters) {
1219 for (i = 0; i < resource_size(res)/4; i++)
1220 __raw_writel(0, msm_pc_debug_counters + i * 4);
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001221
Priyanka Mathur13bdad12013-01-28 15:52:56 -08001222 dent = debugfs_create_file("pc_debug_counter", S_IRUGO, NULL,
1223 msm_pc_debug_counters,
1224 &msm_pc_debug_counters_fops);
1225 if (!dent)
1226 pr_err("%s: ERROR debugfs_create_file failed\n",
1227 __func__);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001228 } else {
1229 msm_pc_debug_counters = 0;
1230 msm_pc_debug_counters_phys = 0;
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001231 }
1232
Mahesh Sivasubramaniand9827c02014-01-23 12:46:22 -07001233 lpm_node = of_parse_phandle(pdev->dev.of_node, "qcom,lpm-levels", 0);
1234 if (!lpm_node) {
1235 pr_warn("Could not get qcom,lpm-levels handle\n");
1236 return -EINVAL;
1237 }
1238 need_scm_handoff_lock = of_property_read_bool(lpm_node,
1239 "qcom,allow-synced-levels");
1240 if (need_scm_handoff_lock) {
1241 ret = remote_spin_lock_init(&scm_handoff_lock,
1242 SCM_HANDOFF_LOCK_ID);
1243 if (ret) {
1244 pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
1245 __func__, ret);
1246 return ret;
1247 }
1248 }
1249
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001250 if (pdev->dev.of_node) {
1251 enum msm_pm_pc_mode_type pc_mode;
Girish Mahadevan55944992012-10-26 11:03:07 -06001252
Mahesh Sivasubramanian3cf21562013-07-19 10:44:26 -06001253 ret = msm_pm_clk_init(pdev);
1254 if (ret) {
1255 pr_info("msm_pm_clk_init returned error\n");
1256 return ret;
1257 }
1258
Girish Mahadevan55944992012-10-26 11:03:07 -06001259 key = "qcom,pc-mode";
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001260 ret = msm_pm_get_pc_mode(pdev->dev.of_node, key, &pc_mode);
Girish Mahadevan55944992012-10-26 11:03:07 -06001261 if (ret) {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001262 pr_debug("%s: Error reading key %s", __func__, key);
Archana Sathyakumar2b91dc82013-02-01 17:38:23 -07001263 return -EINVAL;
Girish Mahadevan55944992012-10-26 11:03:07 -06001264 }
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001265 msm_pm_set_flush_fn(pc_mode);
Girish Mahadevan55944992012-10-26 11:03:07 -06001266 }
1267
Praveen Chidambaramf27a5152013-02-01 11:44:53 -07001268 msm_pm_init();
Anji Jonnala02dac8d2013-03-06 21:31:04 +05301269 if (pdev->dev.of_node)
1270 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
1271
Girish Mahadevan55944992012-10-26 11:03:07 -06001272 return ret;
1273}
1274
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001275static struct of_device_id msm_cpu_pm_table[] = {
1276 {.compatible = "qcom,pm-8x60"},
1277 {},
Girish Mahadevan55944992012-10-26 11:03:07 -06001278};
1279
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001280static struct platform_driver msm_cpu_pm_driver = {
1281 .probe = msm_cpu_pm_probe,
1282 .driver = {
1283 .name = "pm-8x60",
1284 .owner = THIS_MODULE,
1285 .of_match_table = msm_cpu_pm_table,
1286 },
Girish Mahadevan55944992012-10-26 11:03:07 -06001287};
1288
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001289static int __init msm_cpu_pm_init(void)
Girish Mahadevan55944992012-10-26 11:03:07 -06001290{
Anji Jonnala02dac8d2013-03-06 21:31:04 +05301291 int rc;
1292
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001293 cpumask_clear(&retention_cpus);
1294
Girish Mahadevanec50d2d2013-05-15 12:07:38 -06001295 rc = platform_driver_register(&msm_cpu_pm_snoc_client_driver);
1296
1297 if (rc) {
1298 pr_err("%s(): failed to register driver %s\n", __func__,
1299 msm_cpu_pm_snoc_client_driver.driver.name);
1300 return rc;
1301 }
1302
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001303 return platform_driver_register(&msm_cpu_pm_driver);
Girish Mahadevan55944992012-10-26 11:03:07 -06001304}
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001305device_initcall(msm_cpu_pm_init);
Mahesh Sivasubramanian59ffcb02013-05-31 15:08:15 -06001306
1307void __init msm_pm_sleep_status_init(void)
1308{
1309 platform_driver_register(&msm_cpu_status_driver);
1310}