blob: 0c3c4da3013d1f7d096bb26293b1f0b3c1571c2e [file] [log] [blame]
Priyanka Mathurb44bf572012-11-07 12:07:45 -08001/* Copyright (c) 2012, 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
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600111struct msm_lpm_resource {
112 struct msm_lpm_rs_data rs_data;
113 uint32_t sleep_value;
114 char name[MAX_RS_NAME];
115
116 uint32_t enable_low_power;
117 bool valid;
118
119 bool (*beyond_limits)(struct msm_rpmrs_limits *limits);
120 void (*aggregate)(struct msm_rpmrs_limits *limits);
121 void (*flush)(int notify_rpm);
122 void (*notify)(struct msm_rpm_notifier_data *rpm_notifier_cb);
123 struct kobj_attribute ko_attr;
124};
125
126
127static struct msm_lpm_resource msm_lpm_l2 = {
128 .name = "l2",
129 .beyond_limits = msm_lpm_beyond_limits_l2,
130 .aggregate = msm_lpm_aggregate_l2,
131 .flush = msm_lpm_flush_l2,
132 .notify = NULL,
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600133 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600134 .ko_attr = RPMRS_ATTR(l2),
135};
136
137static struct msm_lpm_resource msm_lpm_vdd_dig = {
138 .name = "vdd-dig",
139 .beyond_limits = msm_lpm_beyond_limits_vdd_dig,
140 .aggregate = msm_lpm_aggregate_vdd_dig,
141 .flush = msm_lpm_flush_vdd_dig,
142 .notify = msm_lpm_notify_vdd_dig,
143 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600144 .ko_attr = RPMRS_ATTR(vdd_dig),
145};
146
147static struct msm_lpm_resource msm_lpm_vdd_mem = {
148 .name = "vdd-mem",
149 .beyond_limits = msm_lpm_beyond_limits_vdd_mem,
150 .aggregate = msm_lpm_aggregate_vdd_mem,
151 .flush = msm_lpm_flush_vdd_mem,
152 .notify = msm_lpm_notify_vdd_mem,
153 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600154 .ko_attr = RPMRS_ATTR(vdd_mem),
155};
156
157static struct msm_lpm_resource msm_lpm_pxo = {
158 .name = "pxo",
159 .beyond_limits = msm_lpm_beyond_limits_pxo,
160 .aggregate = msm_lpm_aggregate_pxo,
161 .flush = msm_lpm_flush_pxo,
162 .notify = msm_lpm_notify_pxo,
163 .valid = false,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600164 .ko_attr = RPMRS_ATTR(pxo),
165};
166
167static struct msm_lpm_resource *msm_lpm_resources[] = {
168 &msm_lpm_vdd_dig,
169 &msm_lpm_vdd_mem,
170 &msm_lpm_pxo,
171 &msm_lpm_l2,
172};
173
174static struct msm_lpm_resource msm_lpm_rpm_ctl = {
175 .name = "rpm_ctl",
176 .beyond_limits = NULL,
177 .aggregate = NULL,
178 .flush = msm_lpm_flush_rpm_ctl,
179 .valid = true,
180 .ko_attr = RPMRS_ATTR(rpm_ctl),
181};
182
183static struct notifier_block msm_lpm_rpm_nblk = {
184 .notifier_call = msm_lpm_rpm_callback,
185};
186
187static struct notifier_block __refdata msm_lpm_cpu_nblk = {
188 .notifier_call = msm_lpm_cpu_callback,
189};
190
191static DEFINE_SPINLOCK(msm_lpm_sysfs_lock);
192
193/* Attribute Definitions */
194static struct attribute *msm_lpm_attributes[] = {
195 &msm_lpm_vdd_dig.ko_attr.attr,
196 &msm_lpm_vdd_mem.ko_attr.attr,
197 &msm_lpm_pxo.ko_attr.attr,
198 &msm_lpm_l2.ko_attr.attr,
199 NULL,
200};
201
202static struct attribute_group msm_lpm_attribute_group = {
203 .attrs = msm_lpm_attributes,
204};
205
206static struct attribute *msm_lpm_rpm_ctl_attribute[] = {
207 &msm_lpm_rpm_ctl.ko_attr.attr,
208 NULL,
209};
210
211static struct attribute_group msm_lpm_rpm_ctl_attr_group = {
212 .attrs = msm_lpm_rpm_ctl_attribute,
213};
214
215#define GET_RS_FROM_ATTR(attr) \
216 (container_of(attr, struct msm_lpm_resource, ko_attr))
217
218/* RPM */
219static struct msm_rpm_request *msm_lpm_create_rpm_request
220 (uint32_t rsc_type, uint32_t rsc_id)
221{
222 struct msm_rpm_request *handle = NULL;
223
224 handle = msm_rpm_create_request(MSM_RPM_CTX_SLEEP_SET,
225 rsc_type,
226 rsc_id, 1);
227 return handle;
228}
229
230static int msm_lpm_send_sleep_data(struct msm_rpm_request *handle,
231 uint32_t key, uint8_t *value)
232{
233 int ret = 0;
Girish Mahadevana9964a52012-06-29 10:14:09 -0600234 int msg_id;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600235
236 if (!handle)
237 return ret;
238
239 ret = msm_rpm_add_kvp_data_noirq(handle, key, value, MAX_RS_SIZE);
240
241 if (ret < 0) {
242 pr_err("%s: Error adding kvp data key %u, size %d\n",
243 __func__, key, MAX_RS_SIZE);
244 return ret;
245 }
246
Girish Mahadevana9964a52012-06-29 10:14:09 -0600247 msg_id = msm_rpm_send_request_noirq(handle);
248 if (!msg_id) {
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600249 pr_err("%s: Error sending RPM request key %u, handle 0x%x\n",
250 __func__, key, (unsigned int)handle);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600251 ret = -EIO;
252 return ret;
253 }
254
Mahesh Sivasubramanianb9498582012-07-25 11:22:56 -0600255 ret = msm_rpm_wait_for_ack_noirq(msg_id);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600256 if (ret < 0) {
257 pr_err("%s: Couldn't get ACK from RPM for Msg %d Error %d",
258 __func__, msg_id, ret);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600259 return ret;
260 }
261 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
262 pr_info("Rs key %u, value %u, size %d\n", key,
263 *(unsigned int *)value, MAX_RS_SIZE);
264 return ret;
265}
266
267/* RPM Notifier */
268static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
269 unsigned long action,
270 void *rpm_notif)
271{
272 int i;
273 struct msm_lpm_resource *rs = NULL;
274 struct msm_rpm_notifier_data *rpm_notifier_cb =
275 (struct msm_rpm_notifier_data *)rpm_notif;
276
277 if (!msm_lpm_get_rpm_notif)
278 return NOTIFY_DONE;
279
280 if (!(rpm_nb && rpm_notif))
281 return NOTIFY_BAD;
282
283 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
284 rs = msm_lpm_resources[i];
285 if (rs && rs->valid && rs->notify)
286 rs->notify(rpm_notifier_cb);
287 }
288
289 return NOTIFY_OK;
290}
291
292/* SYSFS */
293static ssize_t msm_lpm_resource_attr_show(
294 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
295{
296 struct kernel_param kp;
297 unsigned long flags;
298 unsigned int temp;
299 int rc;
300
301 spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
302 temp = GET_RS_FROM_ATTR(attr)->enable_low_power;
303 spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
304
305 kp.arg = &temp;
306 rc = param_get_uint(buf, &kp);
307
308 if (rc > 0) {
309 strlcat(buf, "\n", PAGE_SIZE);
310 rc++;
311 }
312
313 return rc;
314}
315
316static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
317 struct kobj_attribute *attr, const char *buf, size_t count)
318{
319 struct kernel_param kp;
320 unsigned long flags;
321 unsigned int temp;
322 int rc;
323
324 kp.arg = &temp;
325 rc = param_set_uint(buf, &kp);
326 if (rc)
327 return rc;
328
329 spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
330 GET_RS_FROM_ATTR(attr)->enable_low_power = temp;
331
332 if (IS_RPM_CTL(GET_RS_FROM_ATTR(attr))) {
333 struct msm_lpm_resource *rs = GET_RS_FROM_ATTR(attr);
334 rs->flush(false);
335 }
336
337 spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
338
339 return count;
340}
341
342/* lpm resource handling functions */
343/* Common */
Mahesh Sivasubramanianf999d2e2012-08-06 13:26:49 -0600344static void msm_lpm_notify_common(struct msm_rpm_notifier_data *cb,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600345 struct msm_lpm_resource *rs)
346{
Mahesh Sivasubramanianf999d2e2012-08-06 13:26:49 -0600347 if ((cb->rsc_type == rs->rs_data.type) &&
348 (cb->rsc_id == rs->rs_data.id) &&
349 (cb->key == rs->rs_data.key)) {
350
351 BUG_ON(cb->size > MAX_RS_SIZE);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600352
353 if (rs->valid) {
Mahesh Sivasubramanianf999d2e2012-08-06 13:26:49 -0600354 if (cb->value) {
355 memcpy(&rs->rs_data.value, cb->value, cb->size);
356 msm_rpm_add_kvp_data_noirq(rs->rs_data.handle,
357 cb->key, cb->value, cb->size);
358 }
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600359 else
360 rs->rs_data.value = rs->rs_data.default_value;
361
362 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
363 pr_info("Notification received Rs %s value %u\n",
364 rs->name, rs->rs_data.value);
365 }
366 }
367}
368
369/* L2 */
370static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
371{
372 uint32_t l2;
Girish Mahadevance5a1732012-10-10 18:53:47 -0600373 bool ret = false;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600374 struct msm_lpm_resource *rs = &msm_lpm_l2;
375
376 if (rs->valid) {
377 uint32_t l2_buf = rs->rs_data.value;
378
379 if (rs->enable_low_power == 1)
380 l2 = MSM_LPM_L2_CACHE_GDHS;
381 else if (rs->enable_low_power == 2)
382 l2 = MSM_LPM_L2_CACHE_HSFS_OPEN;
383 else
384 l2 = MSM_LPM_L2_CACHE_ACTIVE ;
385
386 if (l2_buf > l2)
387 l2 = l2_buf;
388 ret = (l2 > limits->l2_cache);
389
390 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
391 pr_info("%s: l2 buf %u, l2 %u, limits %u\n",
392 __func__, l2_buf, l2, limits->l2_cache);
393 }
394 return ret;
395}
396
397static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits)
398{
399 struct msm_lpm_resource *rs = &msm_lpm_l2;
400
401 if (rs->valid)
402 rs->sleep_value = limits->l2_cache;
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800403 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600404}
405
406static void msm_lpm_flush_l2(int notify_rpm)
407{
408 struct msm_lpm_resource *rs = &msm_lpm_l2;
409 int lpm;
410 int rc;
411
412 switch (rs->sleep_value) {
413 case MSM_LPM_L2_CACHE_HSFS_OPEN:
414 lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
415 msm_pm_set_l2_flush_flag(1);
416 break;
417 case MSM_LPM_L2_CACHE_GDHS:
418 lpm = MSM_SPM_L2_MODE_GDHS;
419 break;
420 case MSM_LPM_L2_CACHE_RETENTION:
421 lpm = MSM_SPM_L2_MODE_RETENTION;
422 break;
423 default:
424 case MSM_LPM_L2_CACHE_ACTIVE:
425 lpm = MSM_SPM_L2_MODE_DISABLED;
426 break;
427 }
428
429 rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
430
431 if (rc < 0)
432 pr_err("%s: Failed to set L2 low power mode %d",
433 __func__, lpm);
434
435 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
436 pr_info("%s: Requesting low power mode %d\n",
437 __func__, lpm);
438}
439
440/* RPM CTL */
441static void msm_lpm_flush_rpm_ctl(int notify_rpm)
442{
443 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
444 msm_lpm_send_sleep_data(rs->rs_data.handle,
445 rs->rs_data.key,
446 (uint8_t *)&rs->sleep_value);
447}
448
449/*VDD Dig*/
450static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits)
451{
452 bool ret = true;
453 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
454
455 if (rs->valid) {
456 uint32_t vdd_buf = rs->rs_data.value;
457 uint32_t vdd_dig = rs->enable_low_power ? rs->enable_low_power :
458 rs->rs_data.default_value;
459
460 if (vdd_buf > vdd_dig)
461 vdd_dig = vdd_buf;
462
463 ret = (vdd_dig > limits->vdd_dig_upper_bound);
464
465 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_DIG)
466 pr_info("%s:buf %d vdd dig %d limits%d\n",
467 __func__, vdd_buf, vdd_dig,
468 limits->vdd_dig_upper_bound);
469 }
470 return ret;
471}
472
473static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
474{
475 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
476
477 if (rs->valid) {
478 uint32_t vdd_buf = rs->rs_data.value;
479 if (limits->vdd_dig_lower_bound > vdd_buf)
480 rs->sleep_value = limits->vdd_dig_lower_bound;
481 else
482 rs->sleep_value = vdd_buf;
483 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800484 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600485}
486
487static void msm_lpm_flush_vdd_dig(int notify_rpm)
488{
489 if (notify_rpm) {
490 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
491 msm_lpm_send_sleep_data(rs->rs_data.handle,
492 rs->rs_data.key,
493 (uint8_t *)&rs->sleep_value);
494 }
495}
496
497static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
498 *rpm_notifier_cb)
499{
500 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
501 msm_lpm_notify_common(rpm_notifier_cb, rs);
502}
503
504/*VDD Mem*/
505static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits)
506{
507 bool ret = true;
508 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
509
510 if (rs->valid) {
511 uint32_t vdd_buf = rs->rs_data.value;
512 uint32_t vdd_mem = rs->enable_low_power ? rs->enable_low_power :
513 rs->rs_data.default_value;
514
515 if (vdd_buf > vdd_mem)
516 vdd_mem = vdd_buf;
517
518 ret = (vdd_mem > limits->vdd_mem_upper_bound);
519
520 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_MEM)
521 pr_info("%s:buf %d vdd mem %d limits%d\n",
522 __func__, vdd_buf, vdd_mem,
523 limits->vdd_mem_upper_bound);
524 }
525 return ret;
526}
527
528static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
529{
530 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
531
532 if (rs->valid) {
533 uint32_t vdd_buf = rs->rs_data.value;
534 if (limits->vdd_mem_lower_bound > vdd_buf)
535 rs->sleep_value = limits->vdd_mem_lower_bound;
536 else
537 rs->sleep_value = vdd_buf;
538 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800539 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600540}
541
542static void msm_lpm_flush_vdd_mem(int notify_rpm)
543{
544 if (notify_rpm) {
545 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
546 msm_lpm_send_sleep_data(rs->rs_data.handle,
547 rs->rs_data.key,
548 (uint8_t *)&rs->sleep_value);
549 }
550}
551
552static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
553 *rpm_notifier_cb)
554{
555 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
556 msm_lpm_notify_common(rpm_notifier_cb, rs);
557}
558
559/*PXO*/
560static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits)
561{
562 bool ret = true;
563 struct msm_lpm_resource *rs = &msm_lpm_pxo;
564
565 if (rs->valid) {
566 uint32_t pxo_buf = rs->rs_data.value;
567 uint32_t pxo = rs->enable_low_power ? MSM_LPM_PXO_OFF :
568 rs->rs_data.default_value;
569
570 if (pxo_buf > pxo)
571 pxo = pxo_buf;
572
573 ret = (pxo > limits->pxo);
574
575 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
576 pr_info("%s:pxo buf %d pxo %d limits pxo %d\n",
577 __func__, pxo_buf, pxo, limits->pxo);
578 }
579 return ret;
580}
581
582static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits)
583{
584 struct msm_lpm_resource *rs = &msm_lpm_pxo;
585
586 if (rs->valid) {
587 uint32_t pxo_buf = rs->rs_data.value;
588 if (limits->pxo > pxo_buf)
589 rs->sleep_value = limits->pxo;
590 else
591 rs->sleep_value = pxo_buf;
592
593 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
594 pr_info("%s: pxo buf %d sleep value %d\n",
595 __func__, pxo_buf, rs->sleep_value);
596 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800597 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600598}
599
600static void msm_lpm_flush_pxo(int notify_rpm)
601{
602 if (notify_rpm) {
603 struct msm_lpm_resource *rs = &msm_lpm_pxo;
604 msm_lpm_send_sleep_data(rs->rs_data.handle,
605 rs->rs_data.key,
606 (uint8_t *)&rs->sleep_value);
607 }
608}
609
610static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
611 *rpm_notifier_cb)
612{
613 struct msm_lpm_resource *rs = &msm_lpm_pxo;
614 msm_lpm_notify_common(rpm_notifier_cb, rs);
615}
616
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600617static inline bool msm_lpm_use_mpm(struct msm_rpmrs_limits *limits)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600618{
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600619 return (limits->pxo == MSM_LPM_PXO_OFF);
620}
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600621
622/* LPM levels interface */
623bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits)
624{
625 int i;
626 struct msm_lpm_resource *rs;
627 bool beyond_limit = false;
628
629 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
630 rs = msm_lpm_resources[i];
631 if (rs->beyond_limits && rs->beyond_limits(limits)) {
632 beyond_limit = true;
633 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_LVLS)
634 pr_info("%s: %s beyond limit", __func__,
635 rs->name);
636 break;
637 }
638 }
639
640 return beyond_limit;
641}
642
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600643int msm_lpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600644 bool from_idle, bool notify_rpm)
645{
646 int ret = 0;
647 int i;
648 struct msm_lpm_resource *rs = NULL;
649
650 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
651 rs = msm_lpm_resources[i];
652 if (rs->aggregate)
653 rs->aggregate(limits);
654 }
655
656 msm_lpm_get_rpm_notif = false;
657 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
658 rs = msm_lpm_resources[i];
Girish Mahadevance5a1732012-10-10 18:53:47 -0600659 if (rs->valid && rs->flush)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600660 rs->flush(notify_rpm);
661 }
662 msm_lpm_get_rpm_notif = true;
663
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600664 if (msm_lpm_use_mpm(limits))
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600665 msm_mpm_enter_sleep(sclk_count, from_idle);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600666
667 return ret;
668}
669
Girish Mahadevana9964a52012-06-29 10:14:09 -0600670void msm_lpmrs_exit_sleep(struct msm_rpmrs_limits *limits,
671 bool from_idle, bool notify_rpm, bool collapsed)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600672{
673 /* MPM exit sleep
674 if (msm_lpm_use_mpm(limits))
675 msm_mpm_exit_sleep(from_idle);*/
Girish Mahadevana9964a52012-06-29 10:14:09 -0600676
677 msm_spm_l2_set_low_power_mode(MSM_SPM_MODE_DISABLED, notify_rpm);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600678}
679
680static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
681 unsigned long action, void *hcpu)
682{
683 struct msm_lpm_resource *rs = &msm_lpm_l2;
684 switch (action) {
Mahesh Sivasubramanianbfc73942012-10-17 18:04:33 -0600685 case CPU_UP_PREPARE:
686 case CPU_UP_PREPARE_FROZEN:
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700687 rs->rs_data.value = rs->rs_data.default_value;
Mahesh Sivasubramanianbfc73942012-10-17 18:04:33 -0600688 break;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600689 case CPU_ONLINE_FROZEN:
690 case CPU_ONLINE:
691 if (num_online_cpus() > 1)
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700692 rs->rs_data.value = rs->rs_data.default_value;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600693 break;
694 case CPU_DEAD_FROZEN:
695 case CPU_DEAD:
696 if (num_online_cpus() == 1)
Mahesh Sivasubramanianc373eb12012-08-22 11:28:57 -0600697 rs->rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600698 break;
699 }
700 return NOTIFY_OK;
701}
702
703/* RPM CTL */
704static int __devinit msm_lpm_init_rpm_ctl(void)
705{
706 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
707
708 rs->rs_data.handle = msm_rpm_create_request(
709 MSM_RPM_CTX_ACTIVE_SET,
710 rs->rs_data.type,
711 rs->rs_data.id, 1);
712 if (!rs->rs_data.handle)
713 return -EIO;
714
715 rs->valid = true;
716 return 0;
717}
718
719static int __devinit msm_lpm_resource_sysfs_add(void)
720{
721 struct kobject *module_kobj = NULL;
722 struct kobject *low_power_kobj = NULL;
723 struct kobject *mode_kobj = NULL;
724 int rc = 0;
725
726 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
727 if (!module_kobj) {
728 pr_err("%s: cannot find kobject for module %s\n",
729 __func__, KBUILD_MODNAME);
730 rc = -ENOENT;
731 goto resource_sysfs_add_exit;
732 }
733
734 low_power_kobj = kobject_create_and_add(
735 "enable_low_power", module_kobj);
736 if (!low_power_kobj) {
737 pr_err("%s: cannot create kobject\n", __func__);
738 rc = -ENOMEM;
739 goto resource_sysfs_add_exit;
740 }
741
742 mode_kobj = kobject_create_and_add(
743 "mode", module_kobj);
744 if (!mode_kobj) {
745 pr_err("%s: cannot create kobject\n", __func__);
746 rc = -ENOMEM;
747 goto resource_sysfs_add_exit;
748 }
749
750 rc = sysfs_create_group(low_power_kobj, &msm_lpm_attribute_group);
751 if (rc) {
752 pr_err("%s: cannot create kobject attribute group\n", __func__);
753 goto resource_sysfs_add_exit;
754 }
755
756 rc = sysfs_create_group(mode_kobj, &msm_lpm_rpm_ctl_attr_group);
757 if (rc) {
758 pr_err("%s: cannot create kobject attribute group\n", __func__);
759 goto resource_sysfs_add_exit;
760 }
761
762resource_sysfs_add_exit:
763 if (rc) {
764 if (low_power_kobj)
765 sysfs_remove_group(low_power_kobj,
766 &msm_lpm_attribute_group);
767 kobject_del(low_power_kobj);
768 kobject_del(mode_kobj);
769 }
770
771 return rc;
772}
773
774late_initcall(msm_lpm_resource_sysfs_add);
775
776static int __devinit msm_lpmrs_probe(struct platform_device *pdev)
777{
778 struct device_node *node = NULL;
779 char *key = NULL;
780 int ret = 0;
781
782 for_each_child_of_node(pdev->dev.of_node, node) {
783 struct msm_lpm_resource *rs = NULL;
784 const char *val;
785 int i;
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600786 uint32_t resource_type;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600787
788 key = "qcom,name";
789 ret = of_property_read_string(node, key, &val);
790 if (ret) {
791 pr_err("Cannot read string\n");
792 goto fail;
793 }
794
795 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
796 char *lpmrs_name = msm_lpm_resources[i]->name;
797 if (!msm_lpm_resources[i]->valid &&
798 !strncmp(val, lpmrs_name, strnlen(lpmrs_name,
799 MAX_RS_NAME))) {
800 rs = msm_lpm_resources[i];
801 break;
802 }
803 }
804
805 if (!rs) {
806 pr_err("LPM resource not found\n");
807 continue;
808 }
809
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700810 key = "qcom,init-value";
811 ret = of_property_read_u32(node, key,
812 &rs->rs_data.default_value);
813 if (ret) {
814 pr_err("%s():Failed to read %s\n", __func__, key);
815 goto fail;
816 }
817
818 rs->rs_data.value = rs->rs_data.default_value;
819
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600820 key = "qcom,resource-type";
821 ret = of_property_read_u32(node, key, &resource_type);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600822 if (ret) {
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600823 pr_err("Failed to read resource-type\n");
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600824 goto fail;
825 }
826
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600827 switch (resource_type) {
828 case MSM_LPM_RPM_RS_TYPE:
829 key = "qcom,type";
830 ret = of_property_read_u32(node, key,
831 &rs->rs_data.type);
832 if (ret) {
833 pr_err("Failed to read type\n");
834 goto fail;
835 }
836
837 key = "qcom,id";
838 ret = of_property_read_u32(node, key, &rs->rs_data.id);
839 if (ret) {
840 pr_err("Failed to read id\n");
841 goto fail;
842 }
843
844 key = "qcom,key";
845 ret = of_property_read_u32(node, key, &rs->rs_data.key);
846 if (ret) {
847 pr_err("Failed to read key\n");
848 goto fail;
849 }
850
851 rs->rs_data.handle = msm_lpm_create_rpm_request(
852 rs->rs_data.type,
853 rs->rs_data.id);
854
855 if (!rs->rs_data.handle) {
856 pr_err("%s: Failed to allocate handle for %s\n",
857 __func__, rs->name);
858 ret = -1;
859 goto fail;
860 }
861 /* fall through */
862
863 case MSM_LPM_LOCAL_RS_TYPE:
864 rs->valid = true;
865 break;
866 default:
867 pr_err("%s: Invalid resource type %d", __func__,
868 resource_type);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600869 goto fail;
870 }
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600871 }
872 msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
873 msm_lpm_init_rpm_ctl();
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600874
875 if (msm_lpm_l2.valid) {
876 register_hotcpu_notifier(&msm_lpm_cpu_nblk);
877 /* For UP mode, set the default to HSFS OPEN*/
878 if (num_possible_cpus() == 1) {
879 msm_lpm_l2.rs_data.default_value =
880 MSM_LPM_L2_CACHE_HSFS_OPEN;
881 msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
882 }
883 msm_pm_set_l2_flush_flag(0);
884 } else
885 msm_pm_set_l2_flush_flag(1);
886
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600887fail:
888 return ret;
889}
890
891static struct of_device_id msm_lpmrs_match_table[] = {
892 {.compatible = "qcom,lpm-resources"},
893 {},
894};
895
896static struct platform_driver msm_lpmrs_driver = {
897 .probe = msm_lpmrs_probe,
898 .driver = {
899 .name = "lpm-resources",
900 .owner = THIS_MODULE,
901 .of_match_table = msm_lpmrs_match_table,
902 },
903};
904
905int __init msm_lpmrs_module_init(void)
906{
907 return platform_driver_register(&msm_lpmrs_driver);
908}