blob: c22d3cda6f7c02e42d1c3675561b9e05490b1f64 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -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"
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 */
Mahesh Sivasubramanian06b8d9d2012-08-06 13:26:49 -0600356static void msm_lpm_notify_common(struct msm_rpm_notifier_data *cb,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600357 struct msm_lpm_resource *rs)
358{
Mahesh Sivasubramanian06b8d9d2012-08-06 13:26:49 -0600359 if ((cb->rsc_type == rs->rs_data.type) &&
360 (cb->rsc_id == rs->rs_data.id) &&
361 (cb->key == rs->rs_data.key)) {
362
363 BUG_ON(cb->size > MAX_RS_SIZE);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600364
365 if (rs->valid) {
Mahesh Sivasubramanian06b8d9d2012-08-06 13:26:49 -0600366 if (cb->value) {
367 memcpy(&rs->rs_data.value, cb->value, cb->size);
368 msm_rpm_add_kvp_data_noirq(rs->rs_data.handle,
369 cb->key, cb->value, cb->size);
370 }
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600371 else
372 rs->rs_data.value = rs->rs_data.default_value;
373
374 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
375 pr_info("Notification received Rs %s value %u\n",
376 rs->name, rs->rs_data.value);
377 }
378 }
379}
380
381/* L2 */
382static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
383{
384 uint32_t l2;
385 bool ret = true;
386 struct msm_lpm_resource *rs = &msm_lpm_l2;
387
388 if (rs->valid) {
389 uint32_t l2_buf = rs->rs_data.value;
390
391 if (rs->enable_low_power == 1)
392 l2 = MSM_LPM_L2_CACHE_GDHS;
393 else if (rs->enable_low_power == 2)
394 l2 = MSM_LPM_L2_CACHE_HSFS_OPEN;
395 else
396 l2 = MSM_LPM_L2_CACHE_ACTIVE ;
397
398 if (l2_buf > l2)
399 l2 = l2_buf;
400 ret = (l2 > limits->l2_cache);
401
402 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
403 pr_info("%s: l2 buf %u, l2 %u, limits %u\n",
404 __func__, l2_buf, l2, limits->l2_cache);
405 }
406 return ret;
407}
408
409static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits)
410{
411 struct msm_lpm_resource *rs = &msm_lpm_l2;
412
413 if (rs->valid)
414 rs->sleep_value = limits->l2_cache;
415}
416
417static void msm_lpm_flush_l2(int notify_rpm)
418{
419 struct msm_lpm_resource *rs = &msm_lpm_l2;
420 int lpm;
421 int rc;
422
423 switch (rs->sleep_value) {
424 case MSM_LPM_L2_CACHE_HSFS_OPEN:
425 lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
426 msm_pm_set_l2_flush_flag(1);
427 break;
428 case MSM_LPM_L2_CACHE_GDHS:
429 lpm = MSM_SPM_L2_MODE_GDHS;
430 break;
431 case MSM_LPM_L2_CACHE_RETENTION:
432 lpm = MSM_SPM_L2_MODE_RETENTION;
433 break;
434 default:
435 case MSM_LPM_L2_CACHE_ACTIVE:
436 lpm = MSM_SPM_L2_MODE_DISABLED;
437 break;
438 }
439
440 rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
441
442 if (rc < 0)
443 pr_err("%s: Failed to set L2 low power mode %d",
444 __func__, lpm);
445
446 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
447 pr_info("%s: Requesting low power mode %d\n",
448 __func__, lpm);
449}
450
451/* RPM CTL */
452static void msm_lpm_flush_rpm_ctl(int notify_rpm)
453{
454 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
455 msm_lpm_send_sleep_data(rs->rs_data.handle,
456 rs->rs_data.key,
457 (uint8_t *)&rs->sleep_value);
458}
459
460/*VDD Dig*/
461static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits)
462{
463 bool ret = true;
464 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
465
466 if (rs->valid) {
467 uint32_t vdd_buf = rs->rs_data.value;
468 uint32_t vdd_dig = rs->enable_low_power ? rs->enable_low_power :
469 rs->rs_data.default_value;
470
471 if (vdd_buf > vdd_dig)
472 vdd_dig = vdd_buf;
473
474 ret = (vdd_dig > limits->vdd_dig_upper_bound);
475
476 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_DIG)
477 pr_info("%s:buf %d vdd dig %d limits%d\n",
478 __func__, vdd_buf, vdd_dig,
479 limits->vdd_dig_upper_bound);
480 }
481 return ret;
482}
483
484static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
485{
486 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
487
488 if (rs->valid) {
489 uint32_t vdd_buf = rs->rs_data.value;
490 if (limits->vdd_dig_lower_bound > vdd_buf)
491 rs->sleep_value = limits->vdd_dig_lower_bound;
492 else
493 rs->sleep_value = vdd_buf;
494 }
495}
496
497static void msm_lpm_flush_vdd_dig(int notify_rpm)
498{
499 if (notify_rpm) {
500 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
501 msm_lpm_send_sleep_data(rs->rs_data.handle,
502 rs->rs_data.key,
503 (uint8_t *)&rs->sleep_value);
504 }
505}
506
507static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
508 *rpm_notifier_cb)
509{
510 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
511 msm_lpm_notify_common(rpm_notifier_cb, rs);
512}
513
514/*VDD Mem*/
515static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits)
516{
517 bool ret = true;
518 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
519
520 if (rs->valid) {
521 uint32_t vdd_buf = rs->rs_data.value;
522 uint32_t vdd_mem = rs->enable_low_power ? rs->enable_low_power :
523 rs->rs_data.default_value;
524
525 if (vdd_buf > vdd_mem)
526 vdd_mem = vdd_buf;
527
528 ret = (vdd_mem > limits->vdd_mem_upper_bound);
529
530 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_MEM)
531 pr_info("%s:buf %d vdd mem %d limits%d\n",
532 __func__, vdd_buf, vdd_mem,
533 limits->vdd_mem_upper_bound);
534 }
535 return ret;
536}
537
538static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
539{
540 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
541
542 if (rs->valid) {
543 uint32_t vdd_buf = rs->rs_data.value;
544 if (limits->vdd_mem_lower_bound > vdd_buf)
545 rs->sleep_value = limits->vdd_mem_lower_bound;
546 else
547 rs->sleep_value = vdd_buf;
548 }
549}
550
551static void msm_lpm_flush_vdd_mem(int notify_rpm)
552{
553 if (notify_rpm) {
554 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
555 msm_lpm_send_sleep_data(rs->rs_data.handle,
556 rs->rs_data.key,
557 (uint8_t *)&rs->sleep_value);
558 }
559}
560
561static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
562 *rpm_notifier_cb)
563{
564 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
565 msm_lpm_notify_common(rpm_notifier_cb, rs);
566}
567
568/*PXO*/
569static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits)
570{
571 bool ret = true;
572 struct msm_lpm_resource *rs = &msm_lpm_pxo;
573
574 if (rs->valid) {
575 uint32_t pxo_buf = rs->rs_data.value;
576 uint32_t pxo = rs->enable_low_power ? MSM_LPM_PXO_OFF :
577 rs->rs_data.default_value;
578
579 if (pxo_buf > pxo)
580 pxo = pxo_buf;
581
582 ret = (pxo > limits->pxo);
583
584 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
585 pr_info("%s:pxo buf %d pxo %d limits pxo %d\n",
586 __func__, pxo_buf, pxo, limits->pxo);
587 }
588 return ret;
589}
590
591static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits)
592{
593 struct msm_lpm_resource *rs = &msm_lpm_pxo;
594
595 if (rs->valid) {
596 uint32_t pxo_buf = rs->rs_data.value;
597 if (limits->pxo > pxo_buf)
598 rs->sleep_value = limits->pxo;
599 else
600 rs->sleep_value = pxo_buf;
601
602 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
603 pr_info("%s: pxo buf %d sleep value %d\n",
604 __func__, pxo_buf, rs->sleep_value);
605 }
606}
607
608static void msm_lpm_flush_pxo(int notify_rpm)
609{
610 if (notify_rpm) {
611 struct msm_lpm_resource *rs = &msm_lpm_pxo;
612 msm_lpm_send_sleep_data(rs->rs_data.handle,
613 rs->rs_data.key,
614 (uint8_t *)&rs->sleep_value);
615 }
616}
617
618static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
619 *rpm_notifier_cb)
620{
621 struct msm_lpm_resource *rs = &msm_lpm_pxo;
622 msm_lpm_notify_common(rpm_notifier_cb, rs);
623}
624
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600625static inline bool msm_lpm_use_mpm(struct msm_rpmrs_limits *limits)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600626{
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600627 return (limits->pxo == MSM_LPM_PXO_OFF);
628}
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600629
630/* LPM levels interface */
631bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits)
632{
633 int i;
634 struct msm_lpm_resource *rs;
635 bool beyond_limit = false;
636
637 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
638 rs = msm_lpm_resources[i];
639 if (rs->beyond_limits && rs->beyond_limits(limits)) {
640 beyond_limit = true;
641 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_LVLS)
642 pr_info("%s: %s beyond limit", __func__,
643 rs->name);
644 break;
645 }
646 }
647
648 return beyond_limit;
649}
650
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600651int msm_lpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600652 bool from_idle, bool notify_rpm)
653{
654 int ret = 0;
655 int i;
656 struct msm_lpm_resource *rs = NULL;
657
658 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
659 rs = msm_lpm_resources[i];
660 if (rs->aggregate)
661 rs->aggregate(limits);
662 }
663
664 msm_lpm_get_rpm_notif = false;
665 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
666 rs = msm_lpm_resources[i];
667 if (rs->flush)
668 rs->flush(notify_rpm);
669 }
670 msm_lpm_get_rpm_notif = true;
671
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600672 if (msm_lpm_use_mpm(limits))
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600673 msm_mpm_enter_sleep(sclk_count, from_idle);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600674
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)
Mahesh Sivasubramanian3467c822012-08-22 11:28:57 -0600701 rs->rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600702 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 }
Karthik Parsha17a947e2012-08-21 22:17:34 -0700854 msm_pm_set_l2_flush_flag(0);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600855 return 0;
856fail:
857 return ret;
858}
859
860static struct of_device_id msm_lpmrs_match_table[] = {
861 {.compatible = "qcom,lpm-resources"},
862 {},
863};
864
865static struct platform_driver msm_lpmrs_driver = {
866 .probe = msm_lpmrs_probe,
867 .driver = {
868 .name = "lpm-resources",
869 .owner = THIS_MODULE,
870 .of_match_table = msm_lpmrs_match_table,
871 },
872};
873
874int __init msm_lpmrs_module_init(void)
875{
876 return platform_driver_register(&msm_lpmrs_driver);
877}