blob: a62ee92581d71d21ab877d371684e30d9ce8e6bf [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
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
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700406static void msm_lpm_set_l2_mode(int sleep_mode, int notify_rpm)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600407{
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700408 int lpm, rc;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600409
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700410 msm_pm_set_l2_flush_flag(0);
411
412 switch (sleep_mode) {
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600413 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
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700440static void msm_lpm_flush_l2(int notify_rpm)
441{
442 struct msm_lpm_resource *rs = &msm_lpm_l2;
443
444 msm_lpm_set_l2_mode(rs->sleep_value, notify_rpm);
445}
446
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600447/* RPM CTL */
448static void msm_lpm_flush_rpm_ctl(int notify_rpm)
449{
450 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
451 msm_lpm_send_sleep_data(rs->rs_data.handle,
452 rs->rs_data.key,
453 (uint8_t *)&rs->sleep_value);
454}
455
456/*VDD Dig*/
457static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits)
458{
459 bool ret = true;
460 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
461
462 if (rs->valid) {
463 uint32_t vdd_buf = rs->rs_data.value;
464 uint32_t vdd_dig = rs->enable_low_power ? rs->enable_low_power :
465 rs->rs_data.default_value;
466
467 if (vdd_buf > vdd_dig)
468 vdd_dig = vdd_buf;
469
470 ret = (vdd_dig > limits->vdd_dig_upper_bound);
471
472 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_DIG)
473 pr_info("%s:buf %d vdd dig %d limits%d\n",
474 __func__, vdd_buf, vdd_dig,
475 limits->vdd_dig_upper_bound);
476 }
477 return ret;
478}
479
480static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
481{
482 struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
483
484 if (rs->valid) {
485 uint32_t vdd_buf = rs->rs_data.value;
486 if (limits->vdd_dig_lower_bound > vdd_buf)
487 rs->sleep_value = limits->vdd_dig_lower_bound;
488 else
489 rs->sleep_value = vdd_buf;
490 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800491 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600492}
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 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800546 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600547}
548
549static void msm_lpm_flush_vdd_mem(int notify_rpm)
550{
551 if (notify_rpm) {
552 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
553 msm_lpm_send_sleep_data(rs->rs_data.handle,
554 rs->rs_data.key,
555 (uint8_t *)&rs->sleep_value);
556 }
557}
558
559static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
560 *rpm_notifier_cb)
561{
562 struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
563 msm_lpm_notify_common(rpm_notifier_cb, rs);
564}
565
566/*PXO*/
567static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits)
568{
569 bool ret = true;
570 struct msm_lpm_resource *rs = &msm_lpm_pxo;
571
572 if (rs->valid) {
573 uint32_t pxo_buf = rs->rs_data.value;
574 uint32_t pxo = rs->enable_low_power ? MSM_LPM_PXO_OFF :
575 rs->rs_data.default_value;
576
577 if (pxo_buf > pxo)
578 pxo = pxo_buf;
579
580 ret = (pxo > limits->pxo);
581
582 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
583 pr_info("%s:pxo buf %d pxo %d limits pxo %d\n",
584 __func__, pxo_buf, pxo, limits->pxo);
585 }
586 return ret;
587}
588
589static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits)
590{
591 struct msm_lpm_resource *rs = &msm_lpm_pxo;
592
593 if (rs->valid) {
594 uint32_t pxo_buf = rs->rs_data.value;
595 if (limits->pxo > pxo_buf)
596 rs->sleep_value = limits->pxo;
597 else
598 rs->sleep_value = pxo_buf;
599
600 if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
601 pr_info("%s: pxo buf %d sleep value %d\n",
602 __func__, pxo_buf, rs->sleep_value);
603 }
Priyanka Mathurb44bf572012-11-07 12:07:45 -0800604 trace_lpm_resources(rs->sleep_value, rs->name);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600605}
606
607static void msm_lpm_flush_pxo(int notify_rpm)
608{
609 if (notify_rpm) {
610 struct msm_lpm_resource *rs = &msm_lpm_pxo;
611 msm_lpm_send_sleep_data(rs->rs_data.handle,
612 rs->rs_data.key,
613 (uint8_t *)&rs->sleep_value);
614 }
615}
616
617static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
618 *rpm_notifier_cb)
619{
620 struct msm_lpm_resource *rs = &msm_lpm_pxo;
621 msm_lpm_notify_common(rpm_notifier_cb, rs);
622}
623
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600624static inline bool msm_lpm_use_mpm(struct msm_rpmrs_limits *limits)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600625{
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600626 return (limits->pxo == MSM_LPM_PXO_OFF);
627}
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600628
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
Mahesh Sivasubramanian2efbc352012-07-18 14:15:44 -0600650int msm_lpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600651 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];
Girish Mahadevance5a1732012-10-10 18:53:47 -0600666 if (rs->valid && rs->flush)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600667 rs->flush(notify_rpm);
668 }
669 msm_lpm_get_rpm_notif = true;
670
Priyanka Mathur86cfc762013-01-10 17:03:04 -0800671 if (notify_rpm)
672 msm_mpm_enter_sleep(sclk_count, from_idle);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600673
674 return ret;
675}
676
Girish Mahadevana9964a52012-06-29 10:14:09 -0600677void msm_lpmrs_exit_sleep(struct msm_rpmrs_limits *limits,
678 bool from_idle, bool notify_rpm, bool collapsed)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600679{
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600680 if (msm_lpm_use_mpm(limits))
Mahesh Sivasubramanianc7fe6222012-11-21 16:31:21 -0700681 msm_mpm_exit_sleep(from_idle);
Girish Mahadevana9964a52012-06-29 10:14:09 -0600682
Mahesh Sivasubramaniane5d039a2012-11-30 14:22:00 -0700683 if (msm_lpm_l2.valid)
684 msm_lpm_set_l2_mode(msm_lpm_l2.rs_data.default_value, false);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600685}
686
687static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
688 unsigned long action, void *hcpu)
689{
690 struct msm_lpm_resource *rs = &msm_lpm_l2;
691 switch (action) {
Mahesh Sivasubramanianbfc73942012-10-17 18:04:33 -0600692 case CPU_UP_PREPARE:
693 case CPU_UP_PREPARE_FROZEN:
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700694 rs->rs_data.value = rs->rs_data.default_value;
Mahesh Sivasubramanianbfc73942012-10-17 18:04:33 -0600695 break;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600696 case CPU_ONLINE_FROZEN:
697 case CPU_ONLINE:
698 if (num_online_cpus() > 1)
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700699 rs->rs_data.value = rs->rs_data.default_value;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600700 break;
701 case CPU_DEAD_FROZEN:
702 case CPU_DEAD:
703 if (num_online_cpus() == 1)
Mahesh Sivasubramanianc373eb12012-08-22 11:28:57 -0600704 rs->rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600705 break;
706 }
707 return NOTIFY_OK;
708}
709
710/* RPM CTL */
711static int __devinit msm_lpm_init_rpm_ctl(void)
712{
713 struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
714
715 rs->rs_data.handle = msm_rpm_create_request(
716 MSM_RPM_CTX_ACTIVE_SET,
717 rs->rs_data.type,
718 rs->rs_data.id, 1);
719 if (!rs->rs_data.handle)
720 return -EIO;
721
722 rs->valid = true;
723 return 0;
724}
725
726static int __devinit msm_lpm_resource_sysfs_add(void)
727{
728 struct kobject *module_kobj = NULL;
729 struct kobject *low_power_kobj = NULL;
730 struct kobject *mode_kobj = NULL;
731 int rc = 0;
732
733 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
734 if (!module_kobj) {
735 pr_err("%s: cannot find kobject for module %s\n",
736 __func__, KBUILD_MODNAME);
737 rc = -ENOENT;
738 goto resource_sysfs_add_exit;
739 }
740
741 low_power_kobj = kobject_create_and_add(
742 "enable_low_power", module_kobj);
743 if (!low_power_kobj) {
744 pr_err("%s: cannot create kobject\n", __func__);
745 rc = -ENOMEM;
746 goto resource_sysfs_add_exit;
747 }
748
749 mode_kobj = kobject_create_and_add(
750 "mode", module_kobj);
751 if (!mode_kobj) {
752 pr_err("%s: cannot create kobject\n", __func__);
753 rc = -ENOMEM;
754 goto resource_sysfs_add_exit;
755 }
756
757 rc = sysfs_create_group(low_power_kobj, &msm_lpm_attribute_group);
758 if (rc) {
759 pr_err("%s: cannot create kobject attribute group\n", __func__);
760 goto resource_sysfs_add_exit;
761 }
762
763 rc = sysfs_create_group(mode_kobj, &msm_lpm_rpm_ctl_attr_group);
764 if (rc) {
765 pr_err("%s: cannot create kobject attribute group\n", __func__);
766 goto resource_sysfs_add_exit;
767 }
768
769resource_sysfs_add_exit:
770 if (rc) {
771 if (low_power_kobj)
772 sysfs_remove_group(low_power_kobj,
773 &msm_lpm_attribute_group);
774 kobject_del(low_power_kobj);
775 kobject_del(mode_kobj);
776 }
777
778 return rc;
779}
780
781late_initcall(msm_lpm_resource_sysfs_add);
782
783static int __devinit msm_lpmrs_probe(struct platform_device *pdev)
784{
785 struct device_node *node = NULL;
786 char *key = NULL;
787 int ret = 0;
788
789 for_each_child_of_node(pdev->dev.of_node, node) {
790 struct msm_lpm_resource *rs = NULL;
791 const char *val;
792 int i;
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600793 uint32_t resource_type;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600794
795 key = "qcom,name";
796 ret = of_property_read_string(node, key, &val);
797 if (ret) {
798 pr_err("Cannot read string\n");
799 goto fail;
800 }
801
802 for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
803 char *lpmrs_name = msm_lpm_resources[i]->name;
804 if (!msm_lpm_resources[i]->valid &&
805 !strncmp(val, lpmrs_name, strnlen(lpmrs_name,
806 MAX_RS_NAME))) {
807 rs = msm_lpm_resources[i];
808 break;
809 }
810 }
811
812 if (!rs) {
813 pr_err("LPM resource not found\n");
814 continue;
815 }
816
Mahesh Sivasubramanianb76fb5e2012-11-05 14:40:09 -0700817 key = "qcom,init-value";
818 ret = of_property_read_u32(node, key,
819 &rs->rs_data.default_value);
820 if (ret) {
821 pr_err("%s():Failed to read %s\n", __func__, key);
822 goto fail;
823 }
824
825 rs->rs_data.value = rs->rs_data.default_value;
826
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600827 key = "qcom,resource-type";
828 ret = of_property_read_u32(node, key, &resource_type);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600829 if (ret) {
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600830 pr_err("Failed to read resource-type\n");
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600831 goto fail;
832 }
833
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600834 switch (resource_type) {
835 case MSM_LPM_RPM_RS_TYPE:
836 key = "qcom,type";
837 ret = of_property_read_u32(node, key,
838 &rs->rs_data.type);
839 if (ret) {
840 pr_err("Failed to read type\n");
841 goto fail;
842 }
843
844 key = "qcom,id";
845 ret = of_property_read_u32(node, key, &rs->rs_data.id);
846 if (ret) {
847 pr_err("Failed to read id\n");
848 goto fail;
849 }
850
851 key = "qcom,key";
852 ret = of_property_read_u32(node, key, &rs->rs_data.key);
853 if (ret) {
854 pr_err("Failed to read key\n");
855 goto fail;
856 }
857
858 rs->rs_data.handle = msm_lpm_create_rpm_request(
859 rs->rs_data.type,
860 rs->rs_data.id);
861
862 if (!rs->rs_data.handle) {
863 pr_err("%s: Failed to allocate handle for %s\n",
864 __func__, rs->name);
865 ret = -1;
866 goto fail;
867 }
868 /* fall through */
869
870 case MSM_LPM_LOCAL_RS_TYPE:
871 rs->valid = true;
872 break;
873 default:
874 pr_err("%s: Invalid resource type %d", __func__,
875 resource_type);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600876 goto fail;
877 }
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600878 }
879 msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
880 msm_lpm_init_rpm_ctl();
Girish Mahadevan909dbce2012-09-25 09:38:03 -0600881
882 if (msm_lpm_l2.valid) {
883 register_hotcpu_notifier(&msm_lpm_cpu_nblk);
884 /* For UP mode, set the default to HSFS OPEN*/
885 if (num_possible_cpus() == 1) {
886 msm_lpm_l2.rs_data.default_value =
887 MSM_LPM_L2_CACHE_HSFS_OPEN;
888 msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
889 }
890 msm_pm_set_l2_flush_flag(0);
891 } else
892 msm_pm_set_l2_flush_flag(1);
893
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600894fail:
895 return ret;
896}
897
898static struct of_device_id msm_lpmrs_match_table[] = {
899 {.compatible = "qcom,lpm-resources"},
900 {},
901};
902
903static struct platform_driver msm_lpmrs_driver = {
904 .probe = msm_lpmrs_probe,
905 .driver = {
906 .name = "lpm-resources",
907 .owner = THIS_MODULE,
908 .of_match_table = msm_lpmrs_match_table,
909 },
910};
911
912int __init msm_lpmrs_module_init(void)
913{
914 return platform_driver_register(&msm_lpmrs_driver);
915}