blob: 60184b4995d8f7a7cf5a96e37a910c54cc4c585f [file] [log] [blame]
Priyanka Mathur86cfc762013-01-10 17:03:04 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Girish Mahadevan40abbe12012-04-25 14:58:13 -06002 *
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/slab.h>
18#include <linux/platform_device.h>
19#include <linux/of.h>
20#include <linux/cpu.h>
Girish Mahadevan40abbe12012-04-25 14:58:13 -060021#include <linux/notifier.h>
22#include <linux/hrtimer.h>
23#include <linux/tick.h>
Girish Mahadevana9964a52012-06-29 10:14:09 -060024#include <mach/mpm.h>
25#include <mach/rpm-smd.h>
Girish Mahadevan40abbe12012-04-25 14:58:13 -060026#include "spm.h"
27#include "lpm_resources.h"
28#include "rpm-notifier.h"
Girish Mahadevan40abbe12012-04-25 14:58:13 -060029#include "idle.h"
Priyanka Mathurb44bf572012-11-07 12:07:45 -080030#include "trace_msm_low_power.h"
Girish Mahadevan40abbe12012-04-25 14:58:13 -060031
32/*Debug Definitions*/
33enum {
34 MSM_LPMRS_DEBUG_RPM = BIT(0),
35 MSM_LPMRS_DEBUG_PXO = BIT(1),
36 MSM_LPMRS_DEBUG_VDD_DIG = BIT(2),
37 MSM_LPMRS_DEBUG_VDD_MEM = BIT(3),
38 MSM_LPMRS_DEBUG_L2 = BIT(4),
39 MSM_LPMRS_DEBUG_LVLS = BIT(5),
40};
41
42static int msm_lpm_debug_mask;
43module_param_named(
44 debug_mask, msm_lpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
45);
46
47static bool msm_lpm_get_rpm_notif = true;
48
49/*Macros*/
Girish Mahadevan40abbe12012-04-25 14:58:13 -060050#define MAX_RS_NAME (16)
51#define MAX_RS_SIZE (4)
52#define IS_RPM_CTL(rs) \
53 (!strncmp(rs->name, "rpm_ctl", MAX_RS_NAME))
54
55static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits);
56static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits);
57static void msm_lpm_flush_vdd_dig(int notify_rpm);
58static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
59 *rpm_notifier_cb);
60
61static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits);
62static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits);
63static void msm_lpm_flush_vdd_mem(int notify_rpm);
64static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
65 *rpm_notifier_cb);
66
67static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits);
68static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits);
69static void msm_lpm_flush_pxo(int notify_rpm);
70static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
71 *rpm_notifier_cb);
72
73
74static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits);
75static void msm_lpm_flush_l2(int notify_rpm);
76static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits);
77
78static void msm_lpm_flush_rpm_ctl(int notify_rpm);
79
80static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
81 unsigned long action, void *rpm_notif);
82
83static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
84 unsigned long action, void *hcpu);
85
86static ssize_t msm_lpm_resource_attr_show(
87 struct kobject *kobj, struct kobj_attribute *attr, char *buf);
88static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
89 struct kobj_attribute *attr, const char *buf, size_t count);
90
91
92#define RPMRS_ATTR(_name) \
93 __ATTR(_name, S_IRUGO|S_IWUSR, \
94 msm_lpm_resource_attr_show, msm_lpm_resource_attr_store)
95
96/*Data structures*/
97struct msm_lpm_rs_data {
98 uint32_t type;
99 uint32_t id;
100 uint32_t key;
101 uint32_t value;
102 uint32_t default_value;
103 struct msm_rpm_request *handle;
104};
105
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600106enum {
107 MSM_LPM_RPM_RS_TYPE = 0,
108 MSM_LPM_LOCAL_RS_TYPE = 1,
109};
110
Mahesh Sivasubramanian1523acc2013-02-21 18:22:30 -0700111enum {
112 MSM_SCM_L2_ON = 0,
113 MSM_SCM_L2_OFF = 1,
114 MSM_SCM_L2_GDHS = 3,
115};
116
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600117struct msm_lpm_resource {
118 struct msm_lpm_rs_data rs_data;
119 uint32_t sleep_value;
120 char name[MAX_RS_NAME];
121
122 uint32_t enable_low_power;
123 bool valid;
124
125 bool (*beyond_limits)(struct msm_rpmrs_limits *limits);
126 void (*aggregate)(struct msm_rpmrs_limits *limits);
127 void (*flush)(int notify_rpm);
128 void (*notify)(struct msm_rpm_notifier_data *rpm_notifier_cb);
129 struct kobj_attribute ko_attr;
130};
131
132
133static struct msm_lpm_resource msm_lpm_l2 = {
134 .name = "l2",
135 .beyond_limits = msm_lpm_beyond_limits_l2,
136 .aggregate = msm_lpm_aggregate_l2,
137 .flush = msm_lpm_flush_l2,
138 .notify = NULL,
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600139 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600140 .ko_attr = RPMRS_ATTR(l2),
141};
142
143static struct msm_lpm_resource msm_lpm_vdd_dig = {
144 .name = "vdd-dig",
145 .beyond_limits = msm_lpm_beyond_limits_vdd_dig,
146 .aggregate = msm_lpm_aggregate_vdd_dig,
147 .flush = msm_lpm_flush_vdd_dig,
148 .notify = msm_lpm_notify_vdd_dig,
149 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600150 .ko_attr = RPMRS_ATTR(vdd_dig),
151};
152
153static struct msm_lpm_resource msm_lpm_vdd_mem = {
154 .name = "vdd-mem",
155 .beyond_limits = msm_lpm_beyond_limits_vdd_mem,
156 .aggregate = msm_lpm_aggregate_vdd_mem,
157 .flush = msm_lpm_flush_vdd_mem,
158 .notify = msm_lpm_notify_vdd_mem,
159 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600160 .ko_attr = RPMRS_ATTR(vdd_mem),
161};
162
163static struct msm_lpm_resource msm_lpm_pxo = {
164 .name = "pxo",
165 .beyond_limits = msm_lpm_beyond_limits_pxo,
166 .aggregate = msm_lpm_aggregate_pxo,
167 .flush = msm_lpm_flush_pxo,
168 .notify = msm_lpm_notify_pxo,
169 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600170 .ko_attr = RPMRS_ATTR(pxo),
171};
172
173static struct msm_lpm_resource *msm_lpm_resources[] = {
174 &msm_lpm_vdd_dig,
175 &msm_lpm_vdd_mem,
176 &msm_lpm_pxo,
177 &msm_lpm_l2,
178};
179
180static struct msm_lpm_resource msm_lpm_rpm_ctl = {
181 .name = "rpm_ctl",
182 .beyond_limits = NULL,
183 .aggregate = NULL,
184 .flush = msm_lpm_flush_rpm_ctl,
185 .valid = true,
186 .ko_attr = RPMRS_ATTR(rpm_ctl),
187};
188
189static struct notifier_block msm_lpm_rpm_nblk = {
190 .notifier_call = msm_lpm_rpm_callback,
191};
192
193static struct notifier_block __refdata msm_lpm_cpu_nblk = {
194 .notifier_call = msm_lpm_cpu_callback,
195};
196
197static DEFINE_SPINLOCK(msm_lpm_sysfs_lock);
198
199/* Attribute Definitions */
200static struct attribute *msm_lpm_attributes[] = {
201 &msm_lpm_vdd_dig.ko_attr.attr,
202 &msm_lpm_vdd_mem.ko_attr.attr,
203 &msm_lpm_pxo.ko_attr.attr,
204 &msm_lpm_l2.ko_attr.attr,
205 NULL,
206};
207
208static struct attribute_group msm_lpm_attribute_group = {
209 .attrs = msm_lpm_attributes,
210};
211
212static struct attribute *msm_lpm_rpm_ctl_attribute[] = {
213 &msm_lpm_rpm_ctl.ko_attr.attr,
214 NULL,
215};
216
217static struct attribute_group msm_lpm_rpm_ctl_attr_group = {
218 .attrs = msm_lpm_rpm_ctl_attribute,
219};
220
221#define GET_RS_FROM_ATTR(attr) \
222 (container_of(attr, struct msm_lpm_resource, ko_attr))
223
224/* RPM */
225static struct msm_rpm_request *msm_lpm_create_rpm_request
226 (uint32_t rsc_type, uint32_t rsc_id)
227{
228 struct msm_rpm_request *handle = NULL;
229
230 handle = msm_rpm_create_request(MSM_RPM_CTX_SLEEP_SET,
231 rsc_type,
232 rsc_id, 1);
233 return handle;
234}
235
236static int msm_lpm_send_sleep_data(struct msm_rpm_request *handle,
237 uint32_t key, uint8_t *value)
238{
239 int ret = 0;
Girish Mahadevana9964a52012-06-29 10:14:09 -0600240 int msg_id;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600241
242 if (!handle)
243 return ret;
244
245 ret = msm_rpm_add_kvp_data_noirq(handle, key, value, MAX_RS_SIZE);
246
247 if (ret < 0) {
248 pr_err("%s: Error adding kvp data key %u, size %d\n",
249 __func__, key, MAX_RS_SIZE);
250 return ret;
251 }
252
Girish Mahadevana9964a52012-06-29 10:14:09 -0600253 msg_id = msm_rpm_send_request_noirq(handle);
254 if (!msg_id) {
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600255 pr_err("%s: Error sending RPM request key %u, handle 0x%x\n",
256 __func__, key, (unsigned int)handle);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600257 ret = -EIO;
258 return ret;
259 }
260
Mahesh Sivasubramanianb9498582012-07-25 11:22:56 -0600261 ret = msm_rpm_wait_for_ack_noirq(msg_id);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600262 if (ret < 0) {
263 pr_err("%s: Couldn't get ACK from RPM for Msg %d Error %d",
264 __func__, msg_id, ret);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600265 return ret;
266 }
267 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
268 pr_info("Rs key %u, value %u, size %d\n", key,
269 *(unsigned int *)value, MAX_RS_SIZE);
270 return ret;
271}
272
273/* RPM Notifier */
274static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
275 unsigned long action,
276 void *rpm_notif)
277{
278 int i;
279 struct msm_lpm_resource *rs = NULL;
280 struct msm_rpm_notifier_data *rpm_notifier_cb =
281 (struct msm_rpm_notifier_data *)rpm_notif;
282
283 if (!msm_lpm_get_rpm_notif)
284 return NOTIFY_DONE;
285
286 if (!(rpm_nb && rpm_notif))
287 return NOTIFY_BAD;
288
289 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
290 rs = msm_lpm_resources[i];
291 if (rs && rs->valid && rs->notify)
292 rs->notify(rpm_notifier_cb);
293 }
294
295 return NOTIFY_OK;
296}
297
298/* SYSFS */
299static ssize_t msm_lpm_resource_attr_show(
300 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
301{
302 struct kernel_param kp;
303 unsigned long flags;
304 unsigned int temp;
305 int rc;
306
307 spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
308 temp = GET_RS_FROM_ATTR(attr)->enable_low_power;
309 spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
310
311 kp.arg = &temp;
312 rc = param_get_uint(buf, &kp);
313
314 if (rc > 0) {
315 strlcat(buf, "\n", PAGE_SIZE);
316 rc++;
317 }
318
319 return rc;
320}
321
322static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
323 struct kobj_attribute *attr, const char *buf, size_t count)
324{
325 struct kernel_param kp;
326 unsigned long flags;
327 unsigned int temp;
328 int rc;
329
330 kp.arg = &temp;
331 rc = param_set_uint(buf, &kp);
332 if (rc)
333 return rc;
334
335 spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
336 GET_RS_FROM_ATTR(attr)->enable_low_power = temp;
337
338 if (IS_RPM_CTL(GET_RS_FROM_ATTR(attr))) {
339 struct msm_lpm_resource *rs = GET_RS_FROM_ATTR(attr);
340 rs->flush(false);
341 }
342
343 spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
344
345 return count;
346}
347
348/* lpm resource handling functions */
349/* Common */
Mahesh Sivasubramanianf999d2e2012-08-06 13:26:49 -0600350static void msm_lpm_notify_common(struct msm_rpm_notifier_data *cb,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600351 struct msm_lpm_resource *rs)
352{
Mahesh Sivasubramanianf999d2e2012-08-06 13:26:49 -0600353 if ((cb->rsc_type == rs->rs_data.type) &&
354 (cb->rsc_id == rs->rs_data.id) &&
355 (cb->key == rs->rs_data.key)) {
356
357 BUG_ON(cb->size > MAX_RS_SIZE);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600358
359 if (rs->valid) {
Mahesh Sivasubramanianf999d2e2012-08-06 13:26:49 -0600360 if (cb->value) {
361 memcpy(&rs->rs_data.value, cb->value, cb->size);
362 msm_rpm_add_kvp_data_noirq(rs->rs_data.handle,
363 cb->key, cb->value, cb->size);
364 }
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600365 else
366 rs->rs_data.value = rs->rs_data.default_value;
367
368 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
369 pr_info("Notification received Rs %s value %u\n",
370 rs->name, rs->rs_data.value);
371 }
372 }
373}
374
375/* L2 */
376static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
377{
378 uint32_t l2;
Girish Mahadevance5a1732012-10-10 18:53:47 -0600379 bool ret = false;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600380 struct msm_lpm_resource *rs = &msm_lpm_l2;
381
382 if (rs->valid) {
383 uint32_t l2_buf = rs->rs_data.value;
384
385 if (rs->enable_low_power == 1)
386 l2 = MSM_LPM_L2_CACHE_GDHS;
387 else if (rs->enable_low_power == 2)
388 l2 = MSM_LPM_L2_CACHE_HSFS_OPEN;
389 else
390 l2 = MSM_LPM_L2_CACHE_ACTIVE ;
391
392 if (l2_buf > l2)
393 l2 = l2_buf;
394 ret = (l2 > limits->l2_cache);
395
396 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
397 pr_info("%s: l2 buf %u, l2 %u, limits %u\n",
398 __func__, l2_buf, l2, limits->l2_cache);
399 }
400 return ret;
401}
402
403static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits)
404{
405 struct msm_lpm_resource *rs = &msm_lpm_l2;
406
407 if (rs->valid)
408 rs->sleep_value = limits->l2_cache;
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800409 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600410}
411
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700412static void msm_lpm_set_l2_mode(int sleep_mode, int notify_rpm)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600413{
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700414 int lpm, rc;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600415
Mahesh Sivasubramanian1523acc2013-02-21 18:22:30 -0700416 msm_pm_set_l2_flush_flag(MSM_SCM_L2_ON);
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700417
418 switch (sleep_mode) {
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600419 case MSM_LPM_L2_CACHE_HSFS_OPEN:
420 lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
Mahesh Sivasubramanian1523acc2013-02-21 18:22:30 -0700421 msm_pm_set_l2_flush_flag(MSM_SCM_L2_OFF);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600422 break;
423 case MSM_LPM_L2_CACHE_GDHS:
424 lpm = MSM_SPM_L2_MODE_GDHS;
Mahesh Sivasubramanian1523acc2013-02-21 18:22:30 -0700425 msm_pm_set_l2_flush_flag(MSM_SCM_L2_GDHS);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600426 break;
427 case MSM_LPM_L2_CACHE_RETENTION:
428 lpm = MSM_SPM_L2_MODE_RETENTION;
429 break;
430 default:
431 case MSM_LPM_L2_CACHE_ACTIVE:
432 lpm = MSM_SPM_L2_MODE_DISABLED;
433 break;
434 }
435
436 rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
437
438 if (rc < 0)
439 pr_err("%s: Failed to set L2 low power mode %d",
440 __func__, lpm);
441
442 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
443 pr_info("%s: Requesting low power mode %d\n",
444 __func__, lpm);
445}
446
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700447static void msm_lpm_flush_l2(int notify_rpm)
448{
449 struct msm_lpm_resource *rs = &msm_lpm_l2;
450
451 msm_lpm_set_l2_mode(rs->sleep_value, notify_rpm);
452}
453
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600454/* RPM CTL */
455static void msm_lpm_flush_rpm_ctl(int notify_rpm)
456{
457 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
458 msm_lpm_send_sleep_data(rs->rs_data.handle,
459 rs->rs_data.key,
460 (uint8_t *)&rs->sleep_value);
461}
462
463/*VDD Dig*/
464static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits)
465{
466 bool ret = true;
467 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
468
469 if (rs->valid) {
470 uint32_t vdd_buf = rs->rs_data.value;
471 uint32_t vdd_dig = rs->enable_low_power ? rs->enable_low_power :
472 rs->rs_data.default_value;
473
474 if (vdd_buf > vdd_dig)
475 vdd_dig = vdd_buf;
476
477 ret = (vdd_dig > limits->vdd_dig_upper_bound);
478
479 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_DIG)
480 pr_info("%s:buf %d vdd dig %d limits%d\n",
481 __func__, vdd_buf, vdd_dig,
482 limits->vdd_dig_upper_bound);
483 }
484 return ret;
485}
486
487static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
488{
489 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
490
491 if (rs->valid) {
492 uint32_t vdd_buf = rs->rs_data.value;
493 if (limits->vdd_dig_lower_bound > vdd_buf)
494 rs->sleep_value = limits->vdd_dig_lower_bound;
495 else
496 rs->sleep_value = vdd_buf;
497 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800498 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600499}
500
501static void msm_lpm_flush_vdd_dig(int notify_rpm)
502{
503 if (notify_rpm) {
504 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
505 msm_lpm_send_sleep_data(rs->rs_data.handle,
506 rs->rs_data.key,
507 (uint8_t *)&rs->sleep_value);
508 }
509}
510
511static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
512 *rpm_notifier_cb)
513{
514 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
515 msm_lpm_notify_common(rpm_notifier_cb, rs);
516}
517
518/*VDD Mem*/
519static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits)
520{
521 bool ret = true;
522 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
523
524 if (rs->valid) {
525 uint32_t vdd_buf = rs->rs_data.value;
526 uint32_t vdd_mem = rs->enable_low_power ? rs->enable_low_power :
527 rs->rs_data.default_value;
528
529 if (vdd_buf > vdd_mem)
530 vdd_mem = vdd_buf;
531
532 ret = (vdd_mem > limits->vdd_mem_upper_bound);
533
534 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_MEM)
535 pr_info("%s:buf %d vdd mem %d limits%d\n",
536 __func__, vdd_buf, vdd_mem,
537 limits->vdd_mem_upper_bound);
538 }
539 return ret;
540}
541
542static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
543{
544 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
545
546 if (rs->valid) {
547 uint32_t vdd_buf = rs->rs_data.value;
548 if (limits->vdd_mem_lower_bound > vdd_buf)
549 rs->sleep_value = limits->vdd_mem_lower_bound;
550 else
551 rs->sleep_value = vdd_buf;
552 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800553 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600554}
555
556static void msm_lpm_flush_vdd_mem(int notify_rpm)
557{
558 if (notify_rpm) {
559 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
560 msm_lpm_send_sleep_data(rs->rs_data.handle,
561 rs->rs_data.key,
562 (uint8_t *)&rs->sleep_value);
563 }
564}
565
566static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
567 *rpm_notifier_cb)
568{
569 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
570 msm_lpm_notify_common(rpm_notifier_cb, rs);
571}
572
573/*PXO*/
574static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits)
575{
576 bool ret = true;
577 struct msm_lpm_resource *rs = &msm_lpm_pxo;
578
579 if (rs->valid) {
580 uint32_t pxo_buf = rs->rs_data.value;
581 uint32_t pxo = rs->enable_low_power ? MSM_LPM_PXO_OFF :
582 rs->rs_data.default_value;
583
584 if (pxo_buf > pxo)
585 pxo = pxo_buf;
586
587 ret = (pxo > limits->pxo);
588
589 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
590 pr_info("%s:pxo buf %d pxo %d limits pxo %d\n",
591 __func__, pxo_buf, pxo, limits->pxo);
592 }
593 return ret;
594}
595
596static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits)
597{
598 struct msm_lpm_resource *rs = &msm_lpm_pxo;
599
600 if (rs->valid) {
601 uint32_t pxo_buf = rs->rs_data.value;
602 if (limits->pxo > pxo_buf)
603 rs->sleep_value = limits->pxo;
604 else
605 rs->sleep_value = pxo_buf;
606
607 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
608 pr_info("%s: pxo buf %d sleep value %d\n",
609 __func__, pxo_buf, rs->sleep_value);
610 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800611 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600612}
613
614static void msm_lpm_flush_pxo(int notify_rpm)
615{
616 if (notify_rpm) {
617 struct msm_lpm_resource *rs = &msm_lpm_pxo;
618 msm_lpm_send_sleep_data(rs->rs_data.handle,
619 rs->rs_data.key,
620 (uint8_t *)&rs->sleep_value);
621 }
622}
623
624static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
625 *rpm_notifier_cb)
626{
627 struct msm_lpm_resource *rs = &msm_lpm_pxo;
628 msm_lpm_notify_common(rpm_notifier_cb, rs);
629}
630
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600631static inline bool msm_lpm_use_mpm(struct msm_rpmrs_limits *limits)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600632{
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600633 return (limits->pxo == MSM_LPM_PXO_OFF);
634}
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600635
636/* LPM levels interface */
637bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits)
638{
639 int i;
640 struct msm_lpm_resource *rs;
641 bool beyond_limit = false;
642
643 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
644 rs = msm_lpm_resources[i];
645 if (rs->beyond_limits && rs->beyond_limits(limits)) {
646 beyond_limit = true;
647 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_LVLS)
648 pr_info("%s: %s beyond limit", __func__,
649 rs->name);
650 break;
651 }
652 }
653
654 return beyond_limit;
655}
656
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600657int msm_lpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600658 bool from_idle, bool notify_rpm)
659{
660 int ret = 0;
661 int i;
662 struct msm_lpm_resource *rs = NULL;
663
664 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
665 rs = msm_lpm_resources[i];
666 if (rs->aggregate)
667 rs->aggregate(limits);
668 }
669
670 msm_lpm_get_rpm_notif = false;
671 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
672 rs = msm_lpm_resources[i];
Girish Mahadevance5a1732012-10-10 18:53:47 -0600673 if (rs->valid && rs->flush)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600674 rs->flush(notify_rpm);
675 }
676 msm_lpm_get_rpm_notif = true;
677
Priyanka Mathur86cfc762013-01-10 17:03:04 -0800678 if (notify_rpm)
679 msm_mpm_enter_sleep(sclk_count, from_idle);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600680
681 return ret;
682}
683
Girish Mahadevana9964a52012-06-29 10:14:09 -0600684void msm_lpmrs_exit_sleep(struct msm_rpmrs_limits *limits,
685 bool from_idle, bool notify_rpm, bool collapsed)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600686{
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600687 if (msm_lpm_use_mpm(limits))
Mahesh Sivasubramanianc7fe6222012-11-21 16:31:21 -0700688 msm_mpm_exit_sleep(from_idle);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600689
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700690 if (msm_lpm_l2.valid)
691 msm_lpm_set_l2_mode(msm_lpm_l2.rs_data.default_value, false);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600692}
693
694static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
695 unsigned long action, void *hcpu)
696{
697 struct msm_lpm_resource *rs = &msm_lpm_l2;
698 switch (action) {
Mahesh Sivasubramanianbfc73942012-10-17 18:04:33 -0600699 case CPU_UP_PREPARE:
700 case CPU_UP_PREPARE_FROZEN:
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700701 rs->rs_data.value = rs->rs_data.default_value;
Mahesh Sivasubramanianbfc73942012-10-17 18:04:33 -0600702 break;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600703 case CPU_ONLINE_FROZEN:
704 case CPU_ONLINE:
705 if (num_online_cpus() > 1)
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700706 rs->rs_data.value = rs->rs_data.default_value;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600707 break;
708 case CPU_DEAD_FROZEN:
709 case CPU_DEAD:
710 if (num_online_cpus() == 1)
Mahesh Sivasubramanianc373eb12012-08-22 11:28:57 -0600711 rs->rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600712 break;
713 }
714 return NOTIFY_OK;
715}
716
717/* RPM CTL */
718static int __devinit msm_lpm_init_rpm_ctl(void)
719{
720 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
721
722 rs->rs_data.handle = msm_rpm_create_request(
723 MSM_RPM_CTX_ACTIVE_SET,
724 rs->rs_data.type,
725 rs->rs_data.id, 1);
726 if (!rs->rs_data.handle)
727 return -EIO;
728
729 rs->valid = true;
730 return 0;
731}
732
733static int __devinit msm_lpm_resource_sysfs_add(void)
734{
735 struct kobject *module_kobj = NULL;
736 struct kobject *low_power_kobj = NULL;
737 struct kobject *mode_kobj = NULL;
738 int rc = 0;
739
740 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
741 if (!module_kobj) {
742 pr_err("%s: cannot find kobject for module %s\n",
743 __func__, KBUILD_MODNAME);
744 rc = -ENOENT;
745 goto resource_sysfs_add_exit;
746 }
747
748 low_power_kobj = kobject_create_and_add(
749 "enable_low_power", module_kobj);
750 if (!low_power_kobj) {
751 pr_err("%s: cannot create kobject\n", __func__);
752 rc = -ENOMEM;
753 goto resource_sysfs_add_exit;
754 }
755
756 mode_kobj = kobject_create_and_add(
757 "mode", module_kobj);
758 if (!mode_kobj) {
759 pr_err("%s: cannot create kobject\n", __func__);
760 rc = -ENOMEM;
761 goto resource_sysfs_add_exit;
762 }
763
764 rc = sysfs_create_group(low_power_kobj, &msm_lpm_attribute_group);
765 if (rc) {
766 pr_err("%s: cannot create kobject attribute group\n", __func__);
767 goto resource_sysfs_add_exit;
768 }
769
770 rc = sysfs_create_group(mode_kobj, &msm_lpm_rpm_ctl_attr_group);
771 if (rc) {
772 pr_err("%s: cannot create kobject attribute group\n", __func__);
773 goto resource_sysfs_add_exit;
774 }
775
776resource_sysfs_add_exit:
777 if (rc) {
778 if (low_power_kobj)
779 sysfs_remove_group(low_power_kobj,
780 &msm_lpm_attribute_group);
781 kobject_del(low_power_kobj);
782 kobject_del(mode_kobj);
783 }
784
785 return rc;
786}
787
788late_initcall(msm_lpm_resource_sysfs_add);
789
790static int __devinit msm_lpmrs_probe(struct platform_device *pdev)
791{
792 struct device_node *node = NULL;
793 char *key = NULL;
794 int ret = 0;
795
796 for_each_child_of_node(pdev->dev.of_node, node) {
797 struct msm_lpm_resource *rs = NULL;
798 const char *val;
799 int i;
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600800 uint32_t resource_type;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600801
802 key = "qcom,name";
803 ret = of_property_read_string(node, key, &val);
804 if (ret) {
805 pr_err("Cannot read string\n");
806 goto fail;
807 }
808
809 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
810 char *lpmrs_name = msm_lpm_resources[i]->name;
811 if (!msm_lpm_resources[i]->valid &&
812 !strncmp(val, lpmrs_name, strnlen(lpmrs_name,
813 MAX_RS_NAME))) {
814 rs = msm_lpm_resources[i];
815 break;
816 }
817 }
818
819 if (!rs) {
820 pr_err("LPM resource not found\n");
821 continue;
822 }
823
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700824 key = "qcom,init-value";
825 ret = of_property_read_u32(node, key,
826 &rs->rs_data.default_value);
827 if (ret) {
828 pr_err("%s():Failed to read %s\n", __func__, key);
829 goto fail;
830 }
831
832 rs->rs_data.value = rs->rs_data.default_value;
833
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600834 key = "qcom,resource-type";
835 ret = of_property_read_u32(node, key, &resource_type);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600836 if (ret) {
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600837 pr_err("Failed to read resource-type\n");
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600838 goto fail;
839 }
840
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600841 switch (resource_type) {
842 case MSM_LPM_RPM_RS_TYPE:
843 key = "qcom,type";
844 ret = of_property_read_u32(node, key,
845 &rs->rs_data.type);
846 if (ret) {
847 pr_err("Failed to read type\n");
848 goto fail;
849 }
850
851 key = "qcom,id";
852 ret = of_property_read_u32(node, key, &rs->rs_data.id);
853 if (ret) {
854 pr_err("Failed to read id\n");
855 goto fail;
856 }
857
858 key = "qcom,key";
859 ret = of_property_read_u32(node, key, &rs->rs_data.key);
860 if (ret) {
861 pr_err("Failed to read key\n");
862 goto fail;
863 }
864
865 rs->rs_data.handle = msm_lpm_create_rpm_request(
866 rs->rs_data.type,
867 rs->rs_data.id);
868
869 if (!rs->rs_data.handle) {
870 pr_err("%s: Failed to allocate handle for %s\n",
871 __func__, rs->name);
872 ret = -1;
873 goto fail;
874 }
875 /* fall through */
876
877 case MSM_LPM_LOCAL_RS_TYPE:
878 rs->valid = true;
879 break;
880 default:
881 pr_err("%s: Invalid resource type %d", __func__,
882 resource_type);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600883 goto fail;
884 }
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600885 }
886 msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
887 msm_lpm_init_rpm_ctl();
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600888
889 if (msm_lpm_l2.valid) {
890 register_hotcpu_notifier(&msm_lpm_cpu_nblk);
891 /* For UP mode, set the default to HSFS OPEN*/
892 if (num_possible_cpus() == 1) {
893 msm_lpm_l2.rs_data.default_value =
894 MSM_LPM_L2_CACHE_HSFS_OPEN;
895 msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
896 }
897 msm_pm_set_l2_flush_flag(0);
898 } else
899 msm_pm_set_l2_flush_flag(1);
900
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600901fail:
902 return ret;
903}
904
905static struct of_device_id msm_lpmrs_match_table[] = {
906 {.compatible = "qcom,lpm-resources"},
907 {},
908};
909
910static struct platform_driver msm_lpmrs_driver = {
911 .probe = msm_lpmrs_probe,
912 .driver = {
913 .name = "lpm-resources",
914 .owner = THIS_MODULE,
915 .of_match_table = msm_lpmrs_match_table,
916 },
917};
918
919int __init msm_lpmrs_module_init(void)
920{
921 return platform_driver_register(&msm_lpmrs_driver);
922}