blob: ebcbd2661ab8a6dd7719f4dd9969229f60fe84d1 [file] [log] [blame]
Girish Mahadevan40abbe12012-04-25 14:58:13 -06001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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"
30
31/*Debug Definitions*/
32enum {
33 MSM_LPMRS_DEBUG_RPM = BIT(0),
34 MSM_LPMRS_DEBUG_PXO = BIT(1),
35 MSM_LPMRS_DEBUG_VDD_DIG = BIT(2),
36 MSM_LPMRS_DEBUG_VDD_MEM = BIT(3),
37 MSM_LPMRS_DEBUG_L2 = BIT(4),
38 MSM_LPMRS_DEBUG_LVLS = BIT(5),
39};
40
41static int msm_lpm_debug_mask;
42module_param_named(
43 debug_mask, msm_lpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
44);
45
46static bool msm_lpm_get_rpm_notif = true;
47
48/*Macros*/
Mahesh Sivasubramanianb9498582012-07-25 11:22:56 -060049#define VDD_DIG_ACTIVE (5)
Girish Mahadevan40abbe12012-04-25 14:58:13 -060050#define VDD_MEM_ACTIVE (1050000)
51#define MAX_RS_NAME (16)
52#define MAX_RS_SIZE (4)
53#define IS_RPM_CTL(rs) \
54 (!strncmp(rs->name, "rpm_ctl", MAX_RS_NAME))
55
56static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits);
57static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits);
58static void msm_lpm_flush_vdd_dig(int notify_rpm);
59static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
60 *rpm_notifier_cb);
61
62static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits);
63static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits);
64static void msm_lpm_flush_vdd_mem(int notify_rpm);
65static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
66 *rpm_notifier_cb);
67
68static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits);
69static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits);
70static void msm_lpm_flush_pxo(int notify_rpm);
71static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
72 *rpm_notifier_cb);
73
74
75static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits);
76static void msm_lpm_flush_l2(int notify_rpm);
77static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits);
78
79static void msm_lpm_flush_rpm_ctl(int notify_rpm);
80
81static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
82 unsigned long action, void *rpm_notif);
83
84static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
85 unsigned long action, void *hcpu);
86
87static ssize_t msm_lpm_resource_attr_show(
88 struct kobject *kobj, struct kobj_attribute *attr, char *buf);
89static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
90 struct kobj_attribute *attr, const char *buf, size_t count);
91
92
93#define RPMRS_ATTR(_name) \
94 __ATTR(_name, S_IRUGO|S_IWUSR, \
95 msm_lpm_resource_attr_show, msm_lpm_resource_attr_store)
96
97/*Data structures*/
98struct msm_lpm_rs_data {
99 uint32_t type;
100 uint32_t id;
101 uint32_t key;
102 uint32_t value;
103 uint32_t default_value;
104 struct msm_rpm_request *handle;
105};
106
107struct msm_lpm_resource {
108 struct msm_lpm_rs_data rs_data;
109 uint32_t sleep_value;
110 char name[MAX_RS_NAME];
111
112 uint32_t enable_low_power;
113 bool valid;
114
115 bool (*beyond_limits)(struct msm_rpmrs_limits *limits);
116 void (*aggregate)(struct msm_rpmrs_limits *limits);
117 void (*flush)(int notify_rpm);
118 void (*notify)(struct msm_rpm_notifier_data *rpm_notifier_cb);
119 struct kobj_attribute ko_attr;
120};
121
122
123static struct msm_lpm_resource msm_lpm_l2 = {
124 .name = "l2",
125 .beyond_limits = msm_lpm_beyond_limits_l2,
126 .aggregate = msm_lpm_aggregate_l2,
127 .flush = msm_lpm_flush_l2,
128 .notify = NULL,
129 .valid = true,
130 .rs_data = {
131 .value = MSM_LPM_L2_CACHE_ACTIVE,
132 .default_value = MSM_LPM_L2_CACHE_ACTIVE,
133 },
134 .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,
144 .rs_data = {
145 .value = VDD_DIG_ACTIVE,
146 .default_value = VDD_DIG_ACTIVE,
147 },
148 .ko_attr = RPMRS_ATTR(vdd_dig),
149};
150
151static struct msm_lpm_resource msm_lpm_vdd_mem = {
152 .name = "vdd-mem",
153 .beyond_limits = msm_lpm_beyond_limits_vdd_mem,
154 .aggregate = msm_lpm_aggregate_vdd_mem,
155 .flush = msm_lpm_flush_vdd_mem,
156 .notify = msm_lpm_notify_vdd_mem,
157 .valid = false,
158 .rs_data = {
159 .value = VDD_MEM_ACTIVE,
160 .default_value = VDD_MEM_ACTIVE,
161 },
162 .ko_attr = RPMRS_ATTR(vdd_mem),
163};
164
165static struct msm_lpm_resource msm_lpm_pxo = {
166 .name = "pxo",
167 .beyond_limits = msm_lpm_beyond_limits_pxo,
168 .aggregate = msm_lpm_aggregate_pxo,
169 .flush = msm_lpm_flush_pxo,
170 .notify = msm_lpm_notify_pxo,
171 .valid = false,
172 .rs_data = {
173 .value = MSM_LPM_PXO_ON,
174 .default_value = MSM_LPM_PXO_ON,
175 },
176 .ko_attr = RPMRS_ATTR(pxo),
177};
178
179static struct msm_lpm_resource *msm_lpm_resources[] = {
180 &msm_lpm_vdd_dig,
181 &msm_lpm_vdd_mem,
182 &msm_lpm_pxo,
183 &msm_lpm_l2,
184};
185
186static struct msm_lpm_resource msm_lpm_rpm_ctl = {
187 .name = "rpm_ctl",
188 .beyond_limits = NULL,
189 .aggregate = NULL,
190 .flush = msm_lpm_flush_rpm_ctl,
191 .valid = true,
192 .ko_attr = RPMRS_ATTR(rpm_ctl),
193};
194
195static struct notifier_block msm_lpm_rpm_nblk = {
196 .notifier_call = msm_lpm_rpm_callback,
197};
198
199static struct notifier_block __refdata msm_lpm_cpu_nblk = {
200 .notifier_call = msm_lpm_cpu_callback,
201};
202
203static DEFINE_SPINLOCK(msm_lpm_sysfs_lock);
204
205/* Attribute Definitions */
206static struct attribute *msm_lpm_attributes[] = {
207 &msm_lpm_vdd_dig.ko_attr.attr,
208 &msm_lpm_vdd_mem.ko_attr.attr,
209 &msm_lpm_pxo.ko_attr.attr,
210 &msm_lpm_l2.ko_attr.attr,
211 NULL,
212};
213
214static struct attribute_group msm_lpm_attribute_group = {
215 .attrs = msm_lpm_attributes,
216};
217
218static struct attribute *msm_lpm_rpm_ctl_attribute[] = {
219 &msm_lpm_rpm_ctl.ko_attr.attr,
220 NULL,
221};
222
223static struct attribute_group msm_lpm_rpm_ctl_attr_group = {
224 .attrs = msm_lpm_rpm_ctl_attribute,
225};
226
227#define GET_RS_FROM_ATTR(attr) \
228 (container_of(attr, struct msm_lpm_resource, ko_attr))
229
230/* RPM */
231static struct msm_rpm_request *msm_lpm_create_rpm_request
232 (uint32_t rsc_type, uint32_t rsc_id)
233{
234 struct msm_rpm_request *handle = NULL;
235
236 handle = msm_rpm_create_request(MSM_RPM_CTX_SLEEP_SET,
237 rsc_type,
238 rsc_id, 1);
239 return handle;
240}
241
242static int msm_lpm_send_sleep_data(struct msm_rpm_request *handle,
243 uint32_t key, uint8_t *value)
244{
245 int ret = 0;
Girish Mahadevana9964a52012-06-29 10:14:09 -0600246 int msg_id;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600247
248 if (!handle)
249 return ret;
250
251 ret = msm_rpm_add_kvp_data_noirq(handle, key, value, MAX_RS_SIZE);
252
253 if (ret < 0) {
254 pr_err("%s: Error adding kvp data key %u, size %d\n",
255 __func__, key, MAX_RS_SIZE);
256 return ret;
257 }
258
Girish Mahadevana9964a52012-06-29 10:14:09 -0600259 msg_id = msm_rpm_send_request_noirq(handle);
260 if (!msg_id) {
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600261 pr_err("%s: Error sending RPM request key %u, handle 0x%x\n",
262 __func__, key, (unsigned int)handle);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600263 ret = -EIO;
264 return ret;
265 }
266
Mahesh Sivasubramanianb9498582012-07-25 11:22:56 -0600267 ret = msm_rpm_wait_for_ack_noirq(msg_id);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600268 if (ret < 0) {
269 pr_err("%s: Couldn't get ACK from RPM for Msg %d Error %d",
270 __func__, msg_id, ret);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600271 return ret;
272 }
273 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
274 pr_info("Rs key %u, value %u, size %d\n", key,
275 *(unsigned int *)value, MAX_RS_SIZE);
276 return ret;
277}
278
279/* RPM Notifier */
280static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
281 unsigned long action,
282 void *rpm_notif)
283{
284 int i;
285 struct msm_lpm_resource *rs = NULL;
286 struct msm_rpm_notifier_data *rpm_notifier_cb =
287 (struct msm_rpm_notifier_data *)rpm_notif;
288
289 if (!msm_lpm_get_rpm_notif)
290 return NOTIFY_DONE;
291
292 if (!(rpm_nb && rpm_notif))
293 return NOTIFY_BAD;
294
295 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
296 rs = msm_lpm_resources[i];
297 if (rs && rs->valid && rs->notify)
298 rs->notify(rpm_notifier_cb);
299 }
300
301 return NOTIFY_OK;
302}
303
304/* SYSFS */
305static ssize_t msm_lpm_resource_attr_show(
306 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
307{
308 struct kernel_param kp;
309 unsigned long flags;
310 unsigned int temp;
311 int rc;
312
313 spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
314 temp = GET_RS_FROM_ATTR(attr)->enable_low_power;
315 spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
316
317 kp.arg = &temp;
318 rc = param_get_uint(buf, &kp);
319
320 if (rc > 0) {
321 strlcat(buf, "\n", PAGE_SIZE);
322 rc++;
323 }
324
325 return rc;
326}
327
328static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
329 struct kobj_attribute *attr, const char *buf, size_t count)
330{
331 struct kernel_param kp;
332 unsigned long flags;
333 unsigned int temp;
334 int rc;
335
336 kp.arg = &temp;
337 rc = param_set_uint(buf, &kp);
338 if (rc)
339 return rc;
340
341 spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
342 GET_RS_FROM_ATTR(attr)->enable_low_power = temp;
343
344 if (IS_RPM_CTL(GET_RS_FROM_ATTR(attr))) {
345 struct msm_lpm_resource *rs = GET_RS_FROM_ATTR(attr);
346 rs->flush(false);
347 }
348
349 spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
350
351 return count;
352}
353
354/* lpm resource handling functions */
355/* Common */
356static void msm_lpm_notify_common(struct msm_rpm_notifier_data *rpm_notifier_cb,
357 struct msm_lpm_resource *rs)
358{
359 if ((rpm_notifier_cb->rsc_type == rs->rs_data.type) &&
360 (rpm_notifier_cb->rsc_id == rs->rs_data.id) &&
361 (rpm_notifier_cb->key == rs->rs_data.key)) {
362 BUG_ON(rpm_notifier_cb->size > MAX_RS_SIZE);
363
364 if (rs->valid) {
365 if (rpm_notifier_cb->value)
366 memcpy(&rs->rs_data.value,
367 rpm_notifier_cb->value, rpm_notifier_cb->size);
368 else
369 rs->rs_data.value = rs->rs_data.default_value;
370
371 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
372 pr_info("Notification received Rs %s value %u\n",
373 rs->name, rs->rs_data.value);
374 }
375 }
376}
377
378/* L2 */
379static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
380{
381 uint32_t l2;
382 bool ret = true;
383 struct msm_lpm_resource *rs = &msm_lpm_l2;
384
385 if (rs->valid) {
386 uint32_t l2_buf = rs->rs_data.value;
387
388 if (rs->enable_low_power == 1)
389 l2 = MSM_LPM_L2_CACHE_GDHS;
390 else if (rs->enable_low_power == 2)
391 l2 = MSM_LPM_L2_CACHE_HSFS_OPEN;
392 else
393 l2 = MSM_LPM_L2_CACHE_ACTIVE ;
394
395 if (l2_buf > l2)
396 l2 = l2_buf;
397 ret = (l2 > limits->l2_cache);
398
399 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
400 pr_info("%s: l2 buf %u, l2 %u, limits %u\n",
401 __func__, l2_buf, l2, limits->l2_cache);
402 }
403 return ret;
404}
405
406static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits)
407{
408 struct msm_lpm_resource *rs = &msm_lpm_l2;
409
410 if (rs->valid)
411 rs->sleep_value = limits->l2_cache;
412}
413
414static void msm_lpm_flush_l2(int notify_rpm)
415{
416 struct msm_lpm_resource *rs = &msm_lpm_l2;
417 int lpm;
418 int rc;
419
420 switch (rs->sleep_value) {
421 case MSM_LPM_L2_CACHE_HSFS_OPEN:
422 lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
423 msm_pm_set_l2_flush_flag(1);
424 break;
425 case MSM_LPM_L2_CACHE_GDHS:
426 lpm = MSM_SPM_L2_MODE_GDHS;
427 break;
428 case MSM_LPM_L2_CACHE_RETENTION:
429 lpm = MSM_SPM_L2_MODE_RETENTION;
430 break;
431 default:
432 case MSM_LPM_L2_CACHE_ACTIVE:
433 lpm = MSM_SPM_L2_MODE_DISABLED;
434 break;
435 }
436
437 rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
438
439 if (rc < 0)
440 pr_err("%s: Failed to set L2 low power mode %d",
441 __func__, lpm);
442
443 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
444 pr_info("%s: Requesting low power mode %d\n",
445 __func__, lpm);
446}
447
448/* RPM CTL */
449static void msm_lpm_flush_rpm_ctl(int notify_rpm)
450{
451 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
452 msm_lpm_send_sleep_data(rs->rs_data.handle,
453 rs->rs_data.key,
454 (uint8_t *)&rs->sleep_value);
455}
456
457/*VDD Dig*/
458static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits)
459{
460 bool ret = true;
461 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
462
463 if (rs->valid) {
464 uint32_t vdd_buf = rs->rs_data.value;
465 uint32_t vdd_dig = rs->enable_low_power ? rs->enable_low_power :
466 rs->rs_data.default_value;
467
468 if (vdd_buf > vdd_dig)
469 vdd_dig = vdd_buf;
470
471 ret = (vdd_dig > limits->vdd_dig_upper_bound);
472
473 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_DIG)
474 pr_info("%s:buf %d vdd dig %d limits%d\n",
475 __func__, vdd_buf, vdd_dig,
476 limits->vdd_dig_upper_bound);
477 }
478 return ret;
479}
480
481static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
482{
483 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
484
485 if (rs->valid) {
486 uint32_t vdd_buf = rs->rs_data.value;
487 if (limits->vdd_dig_lower_bound > vdd_buf)
488 rs->sleep_value = limits->vdd_dig_lower_bound;
489 else
490 rs->sleep_value = vdd_buf;
491 }
492}
493
494static void msm_lpm_flush_vdd_dig(int notify_rpm)
495{
496 if (notify_rpm) {
497 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
498 msm_lpm_send_sleep_data(rs->rs_data.handle,
499 rs->rs_data.key,
500 (uint8_t *)&rs->sleep_value);
501 }
502}
503
504static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
505 *rpm_notifier_cb)
506{
507 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
508 msm_lpm_notify_common(rpm_notifier_cb, rs);
509}
510
511/*VDD Mem*/
512static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits)
513{
514 bool ret = true;
515 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
516
517 if (rs->valid) {
518 uint32_t vdd_buf = rs->rs_data.value;
519 uint32_t vdd_mem = rs->enable_low_power ? rs->enable_low_power :
520 rs->rs_data.default_value;
521
522 if (vdd_buf > vdd_mem)
523 vdd_mem = vdd_buf;
524
525 ret = (vdd_mem > limits->vdd_mem_upper_bound);
526
527 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_MEM)
528 pr_info("%s:buf %d vdd mem %d limits%d\n",
529 __func__, vdd_buf, vdd_mem,
530 limits->vdd_mem_upper_bound);
531 }
532 return ret;
533}
534
535static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
536{
537 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
538
539 if (rs->valid) {
540 uint32_t vdd_buf = rs->rs_data.value;
541 if (limits->vdd_mem_lower_bound > vdd_buf)
542 rs->sleep_value = limits->vdd_mem_lower_bound;
543 else
544 rs->sleep_value = vdd_buf;
545 }
546}
547
548static void msm_lpm_flush_vdd_mem(int notify_rpm)
549{
550 if (notify_rpm) {
551 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
552 msm_lpm_send_sleep_data(rs->rs_data.handle,
553 rs->rs_data.key,
554 (uint8_t *)&rs->sleep_value);
555 }
556}
557
558static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
559 *rpm_notifier_cb)
560{
561 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
562 msm_lpm_notify_common(rpm_notifier_cb, rs);
563}
564
565/*PXO*/
566static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits)
567{
568 bool ret = true;
569 struct msm_lpm_resource *rs = &msm_lpm_pxo;
570
571 if (rs->valid) {
572 uint32_t pxo_buf = rs->rs_data.value;
573 uint32_t pxo = rs->enable_low_power ? MSM_LPM_PXO_OFF :
574 rs->rs_data.default_value;
575
576 if (pxo_buf > pxo)
577 pxo = pxo_buf;
578
579 ret = (pxo > limits->pxo);
580
581 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
582 pr_info("%s:pxo buf %d pxo %d limits pxo %d\n",
583 __func__, pxo_buf, pxo, limits->pxo);
584 }
585 return ret;
586}
587
588static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits)
589{
590 struct msm_lpm_resource *rs = &msm_lpm_pxo;
591
592 if (rs->valid) {
593 uint32_t pxo_buf = rs->rs_data.value;
594 if (limits->pxo > pxo_buf)
595 rs->sleep_value = limits->pxo;
596 else
597 rs->sleep_value = pxo_buf;
598
599 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
600 pr_info("%s: pxo buf %d sleep value %d\n",
601 __func__, pxo_buf, rs->sleep_value);
602 }
603}
604
605static void msm_lpm_flush_pxo(int notify_rpm)
606{
607 if (notify_rpm) {
608 struct msm_lpm_resource *rs = &msm_lpm_pxo;
609 msm_lpm_send_sleep_data(rs->rs_data.handle,
610 rs->rs_data.key,
611 (uint8_t *)&rs->sleep_value);
612 }
613}
614
615static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
616 *rpm_notifier_cb)
617{
618 struct msm_lpm_resource *rs = &msm_lpm_pxo;
619 msm_lpm_notify_common(rpm_notifier_cb, rs);
620}
621
622/* MPM
623static bool msm_lpm_use_mpm(struct msm_rpmrs_limits *limits)
624{
625 return ((limits->pxo == MSM_LPM_PXO_OFF) ||
626 (limits->vdd_dig_lower_bound <= VDD_DIG_RET_HIGH));
627}*/
628
629/* LPM levels interface */
630bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits)
631{
632 int i;
633 struct msm_lpm_resource *rs;
634 bool beyond_limit = false;
635
636 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
637 rs = msm_lpm_resources[i];
638 if (rs->beyond_limits && rs->beyond_limits(limits)) {
639 beyond_limit = true;
640 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_LVLS)
641 pr_info("%s: %s beyond limit", __func__,
642 rs->name);
643 break;
644 }
645 }
646
647 return beyond_limit;
648}
649
650int msm_lpmrs_enter_sleep(struct msm_rpmrs_limits *limits,
651 bool from_idle, bool notify_rpm)
652{
653 int ret = 0;
654 int i;
655 struct msm_lpm_resource *rs = NULL;
656
657 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
658 rs = msm_lpm_resources[i];
659 if (rs->aggregate)
660 rs->aggregate(limits);
661 }
662
663 msm_lpm_get_rpm_notif = false;
664 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
665 rs = msm_lpm_resources[i];
666 if (rs->flush)
667 rs->flush(notify_rpm);
668 }
669 msm_lpm_get_rpm_notif = true;
670
671 /* MPM Enter sleep
672 if (msm_lpm_use_mpm(limits))
673 msm_mpm_enter_sleep(from_idle);*/
674
675 return ret;
676}
677
Girish Mahadevana9964a52012-06-29 10:14:09 -0600678void msm_lpmrs_exit_sleep(struct msm_rpmrs_limits *limits,
679 bool from_idle, bool notify_rpm, bool collapsed)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600680{
681 /* MPM exit sleep
682 if (msm_lpm_use_mpm(limits))
683 msm_mpm_exit_sleep(from_idle);*/
Girish Mahadevana9964a52012-06-29 10:14:09 -0600684
685 msm_spm_l2_set_low_power_mode(MSM_SPM_MODE_DISABLED, notify_rpm);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600686}
687
688static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
689 unsigned long action, void *hcpu)
690{
691 struct msm_lpm_resource *rs = &msm_lpm_l2;
692 switch (action) {
693 case CPU_ONLINE_FROZEN:
694 case CPU_ONLINE:
695 if (num_online_cpus() > 1)
696 rs->rs_data.value = MSM_LPM_L2_CACHE_ACTIVE;
697 break;
698 case CPU_DEAD_FROZEN:
699 case CPU_DEAD:
700 if (num_online_cpus() == 1)
701 rs->rs_data.value = MSM_LPM_L2_CACHE_GDHS;
702 break;
703 }
704 return NOTIFY_OK;
705}
706
707/* RPM CTL */
708static int __devinit msm_lpm_init_rpm_ctl(void)
709{
710 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
711
712 rs->rs_data.handle = msm_rpm_create_request(
713 MSM_RPM_CTX_ACTIVE_SET,
714 rs->rs_data.type,
715 rs->rs_data.id, 1);
716 if (!rs->rs_data.handle)
717 return -EIO;
718
719 rs->valid = true;
720 return 0;
721}
722
723static int __devinit msm_lpm_resource_sysfs_add(void)
724{
725 struct kobject *module_kobj = NULL;
726 struct kobject *low_power_kobj = NULL;
727 struct kobject *mode_kobj = NULL;
728 int rc = 0;
729
730 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
731 if (!module_kobj) {
732 pr_err("%s: cannot find kobject for module %s\n",
733 __func__, KBUILD_MODNAME);
734 rc = -ENOENT;
735 goto resource_sysfs_add_exit;
736 }
737
738 low_power_kobj = kobject_create_and_add(
739 "enable_low_power", module_kobj);
740 if (!low_power_kobj) {
741 pr_err("%s: cannot create kobject\n", __func__);
742 rc = -ENOMEM;
743 goto resource_sysfs_add_exit;
744 }
745
746 mode_kobj = kobject_create_and_add(
747 "mode", module_kobj);
748 if (!mode_kobj) {
749 pr_err("%s: cannot create kobject\n", __func__);
750 rc = -ENOMEM;
751 goto resource_sysfs_add_exit;
752 }
753
754 rc = sysfs_create_group(low_power_kobj, &msm_lpm_attribute_group);
755 if (rc) {
756 pr_err("%s: cannot create kobject attribute group\n", __func__);
757 goto resource_sysfs_add_exit;
758 }
759
760 rc = sysfs_create_group(mode_kobj, &msm_lpm_rpm_ctl_attr_group);
761 if (rc) {
762 pr_err("%s: cannot create kobject attribute group\n", __func__);
763 goto resource_sysfs_add_exit;
764 }
765
766resource_sysfs_add_exit:
767 if (rc) {
768 if (low_power_kobj)
769 sysfs_remove_group(low_power_kobj,
770 &msm_lpm_attribute_group);
771 kobject_del(low_power_kobj);
772 kobject_del(mode_kobj);
773 }
774
775 return rc;
776}
777
778late_initcall(msm_lpm_resource_sysfs_add);
779
780static int __devinit msm_lpmrs_probe(struct platform_device *pdev)
781{
782 struct device_node *node = NULL;
783 char *key = NULL;
784 int ret = 0;
785
786 for_each_child_of_node(pdev->dev.of_node, node) {
787 struct msm_lpm_resource *rs = NULL;
788 const char *val;
789 int i;
790
791 key = "qcom,name";
792 ret = of_property_read_string(node, key, &val);
793 if (ret) {
794 pr_err("Cannot read string\n");
795 goto fail;
796 }
797
798 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
799 char *lpmrs_name = msm_lpm_resources[i]->name;
800 if (!msm_lpm_resources[i]->valid &&
801 !strncmp(val, lpmrs_name, strnlen(lpmrs_name,
802 MAX_RS_NAME))) {
803 rs = msm_lpm_resources[i];
804 break;
805 }
806 }
807
808 if (!rs) {
809 pr_err("LPM resource not found\n");
810 continue;
811 }
812
813 key = "qcom,type";
814 ret = of_property_read_u32(node, key, &rs->rs_data.type);
815 if (ret) {
816 pr_err("Failed to read type\n");
817 goto fail;
818 }
819
820 key = "qcom,id";
821 ret = of_property_read_u32(node, key, &rs->rs_data.id);
822 if (ret) {
823 pr_err("Failed to read id\n");
824 goto fail;
825 }
826
827 key = "qcom,key";
828 ret = of_property_read_u32(node, key, &rs->rs_data.key);
829 if (ret) {
830 pr_err("Failed to read key\n");
831 goto fail;
832 }
833
834 rs->rs_data.handle = msm_lpm_create_rpm_request(
835 rs->rs_data.type, rs->rs_data.id);
836
837 if (!rs->rs_data.handle) {
838 pr_err("%s: Failed to allocate handle for %s\n",
839 __func__, rs->name);
840 ret = -1;
841 goto fail;
842 }
843
844 rs->valid = true;
845 }
846 msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
847 msm_lpm_init_rpm_ctl();
848 register_hotcpu_notifier(&msm_lpm_cpu_nblk);
849 /* For UP mode, set the default to HSFS OPEN*/
850 if (num_possible_cpus() == 1) {
851 msm_lpm_l2.rs_data.default_value = MSM_LPM_L2_CACHE_HSFS_OPEN;
852 msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
853 }
854 return 0;
855fail:
856 return ret;
857}
858
859static struct of_device_id msm_lpmrs_match_table[] = {
860 {.compatible = "qcom,lpm-resources"},
861 {},
862};
863
864static struct platform_driver msm_lpmrs_driver = {
865 .probe = msm_lpmrs_probe,
866 .driver = {
867 .name = "lpm-resources",
868 .owner = THIS_MODULE,
869 .of_match_table = msm_lpmrs_match_table,
870 },
871};
872
873int __init msm_lpmrs_module_init(void)
874{
875 return platform_driver_register(&msm_lpmrs_driver);
876}