blob: b7508f796a08de4fddf9baa84b8d61714258c7ec [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, 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/types.h>
18#include <linux/bug.h>
19#include <linux/mutex.h>
20#include <linux/proc_fs.h>
21#include <linux/spinlock.h>
22#include <linux/cpu.h>
23#include <mach/rpm.h>
24#include <mach/msm_iomap.h>
25#include <asm/mach-types.h>
26#include <linux/io.h>
Praveen Chidambaram841d46c2011-08-04 09:07:53 -060027#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include "mpm.h"
29#include "rpm_resources.h"
30#include "spm.h"
31
32/******************************************************************************
33 * Debug Definitions
34 *****************************************************************************/
35
36enum {
37 MSM_RPMRS_DEBUG_OUTPUT = BIT(0),
38 MSM_RPMRS_DEBUG_BUFFER = BIT(1),
39};
40
41static int msm_rpmrs_debug_mask;
42module_param_named(
43 debug_mask, msm_rpmrs_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
44);
45
46static struct msm_rpmrs_level *msm_rpmrs_levels;
47static int msm_rpmrs_level_count;
48
49static bool msm_rpmrs_pxo_beyond_limits(struct msm_rpmrs_limits *limits);
50static void msm_rpmrs_aggregate_pxo(struct msm_rpmrs_limits *limits);
51static void msm_rpmrs_restore_pxo(void);
52static bool msm_rpmrs_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits);
53static void msm_rpmrs_aggregate_l2_cache(struct msm_rpmrs_limits *limits);
54static void msm_rpmrs_restore_l2_cache(void);
55static bool msm_rpmrs_vdd_mem_beyond_limits(struct msm_rpmrs_limits *limits);
56static void msm_rpmrs_aggregate_vdd_mem(struct msm_rpmrs_limits *limits);
57static void msm_rpmrs_restore_vdd_mem(void);
58static bool msm_rpmrs_vdd_dig_beyond_limits(struct msm_rpmrs_limits *limits);
59static void msm_rpmrs_aggregate_vdd_dig(struct msm_rpmrs_limits *limits);
60static void msm_rpmrs_restore_vdd_dig(void);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061
Praveen Chidambaram66775c62011-08-04 16:59:24 -060062static ssize_t msm_rpmrs_resource_attr_show(
63 struct kobject *kobj, struct kobj_attribute *attr, char *buf);
64static ssize_t msm_rpmrs_resource_attr_store(struct kobject *kobj,
65 struct kobj_attribute *attr, const char *buf, size_t count);
66
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067#ifdef CONFIG_MSM_L2_SPM
68static void *msm_rpmrs_l2_counter_addr;
69static int msm_rpmrs_l2_reset_count;
70#define L2_PC_COUNTER_ADDR 0x660
71#endif
72
73#define MSM_RPMRS_MAX_RS_REGISTER_COUNT 2
74
Praveen Chidambaram66775c62011-08-04 16:59:24 -060075#define RPMRS_ATTR(_name) \
76 __ATTR(_name, S_IRUGO|S_IWUSR, \
77 msm_rpmrs_resource_attr_show, msm_rpmrs_resource_attr_store)
78
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079struct msm_rpmrs_resource {
80 struct msm_rpm_iv_pair rs[MSM_RPMRS_MAX_RS_REGISTER_COUNT];
81 uint32_t size;
82 char *name;
83
84 uint32_t enable_low_power;
85
86 bool (*beyond_limits)(struct msm_rpmrs_limits *limits);
87 void (*aggregate)(struct msm_rpmrs_limits *limits);
88 void (*restore)(void);
Praveen Chidambaram66775c62011-08-04 16:59:24 -060089
90 struct kobj_attribute ko_attr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091};
92
93static struct msm_rpmrs_resource msm_rpmrs_pxo = {
94 .rs[0].id = MSM_RPMRS_ID_PXO_CLK,
95 .size = 1,
96 .name = "pxo",
97 .beyond_limits = msm_rpmrs_pxo_beyond_limits,
98 .aggregate = msm_rpmrs_aggregate_pxo,
99 .restore = msm_rpmrs_restore_pxo,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600100 .ko_attr = RPMRS_ATTR(pxo),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101};
102
103static struct msm_rpmrs_resource msm_rpmrs_l2_cache = {
104 .rs[0].id = MSM_RPMRS_ID_APPS_L2_CACHE_CTL,
105 .size = 1,
106 .name = "L2_cache",
107 .beyond_limits = msm_rpmrs_l2_cache_beyond_limits,
108 .aggregate = msm_rpmrs_aggregate_l2_cache,
109 .restore = msm_rpmrs_restore_l2_cache,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600110 .ko_attr = RPMRS_ATTR(L2_cache),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111};
112
113static struct msm_rpmrs_resource msm_rpmrs_vdd_mem = {
114 .rs[0].id = MSM_RPMRS_ID_VDD_MEM_0,
115 .rs[1].id = MSM_RPMRS_ID_VDD_MEM_1,
116 .size = 2,
117 .name = "vdd_mem",
118 .beyond_limits = msm_rpmrs_vdd_mem_beyond_limits,
119 .aggregate = msm_rpmrs_aggregate_vdd_mem,
120 .restore = msm_rpmrs_restore_vdd_mem,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600121 .ko_attr = RPMRS_ATTR(vdd_mem),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122};
123
124static struct msm_rpmrs_resource msm_rpmrs_vdd_dig = {
125 .rs[0].id = MSM_RPMRS_ID_VDD_DIG_0,
126 .rs[1].id = MSM_RPMRS_ID_VDD_DIG_1,
127 .size = 2,
128 .name = "vdd_dig",
129 .beyond_limits = msm_rpmrs_vdd_dig_beyond_limits,
130 .aggregate = msm_rpmrs_aggregate_vdd_dig,
131 .restore = msm_rpmrs_restore_vdd_dig,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600132 .ko_attr = RPMRS_ATTR(vdd_dig),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133};
134
135static struct msm_rpmrs_resource msm_rpmrs_rpm_cpu = {
136 .rs[0].id = MSM_RPMRS_ID_RPM_CTL,
137 .size = 1,
138 .name = "rpm_cpu",
139 .beyond_limits = NULL,
Eugene Seah78aa5e72011-07-18 18:28:37 -0600140 .aggregate = NULL,
141 .restore = NULL,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600142 .ko_attr = RPMRS_ATTR(rpm_cpu),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143};
144
145static struct msm_rpmrs_resource *msm_rpmrs_resources[] = {
146 &msm_rpmrs_pxo,
147 &msm_rpmrs_l2_cache,
148 &msm_rpmrs_vdd_mem,
149 &msm_rpmrs_vdd_dig,
150 &msm_rpmrs_rpm_cpu,
151};
152
153static uint32_t msm_rpmrs_buffer[MSM_RPM_ID_LAST + 1];
154static DECLARE_BITMAP(msm_rpmrs_buffered, MSM_RPM_ID_LAST + 1);
155static DECLARE_BITMAP(msm_rpmrs_listed, MSM_RPM_ID_LAST + 1);
156static DEFINE_SPINLOCK(msm_rpmrs_lock);
157
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158#define MSM_RPMRS_VDD(v) ((v) & (MSM_RPMRS_VDD_MASK))
159
160/******************************************************************************
161 * Attribute Definitions
162 *****************************************************************************/
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600163static struct attribute *msm_rpmrs_attributes[] = {
164 &msm_rpmrs_pxo.ko_attr.attr,
165 &msm_rpmrs_l2_cache.ko_attr.attr,
166 &msm_rpmrs_vdd_mem.ko_attr.attr,
167 &msm_rpmrs_vdd_dig.ko_attr.attr,
168 &msm_rpmrs_rpm_cpu.ko_attr.attr,
169 NULL,
170};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600172static struct attribute_group msm_rpmrs_attribute_group = {
173 .attrs = msm_rpmrs_attributes,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174};
175
176#define GET_RS_FROM_ATTR(attr) \
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600177 (container_of(attr, struct msm_rpmrs_resource, ko_attr))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179
180/******************************************************************************
181 * Resource Specific Functions
182 *****************************************************************************/
183
184static void msm_rpmrs_aggregate_sclk(uint32_t sclk_count)
185{
186 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_TO] = 0;
187 set_bit(MSM_RPM_ID_TRIGGER_TIMED_TO, msm_rpmrs_buffered);
188 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT] = sclk_count;
189 set_bit(MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, msm_rpmrs_buffered);
190}
191
192static void msm_rpmrs_restore_sclk(void)
193{
194 clear_bit(MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, msm_rpmrs_buffered);
195 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT] = 0;
196 clear_bit(MSM_RPM_ID_TRIGGER_TIMED_TO, msm_rpmrs_buffered);
197 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_TO] = 0;
198}
199
200static bool msm_rpmrs_pxo_beyond_limits(struct msm_rpmrs_limits *limits)
201{
202 struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo;
203 uint32_t pxo;
204
205 if (rs->enable_low_power && test_bit(rs->rs[0].id, msm_rpmrs_buffered))
206 pxo = msm_rpmrs_buffer[rs->rs[0].id];
207 else
208 pxo = MSM_RPMRS_PXO_ON;
209
210 return pxo > limits->pxo;
211}
212
213static void msm_rpmrs_aggregate_pxo(struct msm_rpmrs_limits *limits)
214{
215 struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo;
216 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
217
218 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
219 rs->rs[0].value = *buf;
220 if (limits->pxo > *buf)
221 *buf = limits->pxo;
222 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
223 pr_info("%s: %d (0x%x)\n", __func__, *buf, *buf);
224 }
225}
226
227static void msm_rpmrs_restore_pxo(void)
228{
229 struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo;
230
231 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
232 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
233}
234
235static bool msm_rpmrs_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits)
236{
237 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
238 uint32_t l2_cache;
239
240 if (rs->enable_low_power && test_bit(rs->rs[0].id, msm_rpmrs_buffered))
241 l2_cache = msm_rpmrs_buffer[rs->rs[0].id];
242 else
243 l2_cache = MSM_RPMRS_L2_CACHE_ACTIVE;
244
245 return l2_cache > limits->l2_cache;
246}
247
248static void msm_rpmrs_aggregate_l2_cache(struct msm_rpmrs_limits *limits)
249{
250 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
251 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
252
253 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
254 rs->rs[0].value = *buf;
255 if (limits->l2_cache > *buf)
256 *buf = limits->l2_cache;
257
258 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
259 pr_info("%s: %d (0x%x)\n", __func__, *buf, *buf);
260 }
261}
262
263#ifdef CONFIG_MSM_L2_SPM
264static bool msm_spm_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits)
265{
266 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
267 uint32_t l2_cache = rs->rs[0].value;
268
269 if (!rs->enable_low_power)
270 l2_cache = MSM_RPMRS_L2_CACHE_ACTIVE;
271
272 return l2_cache > limits->l2_cache;
273}
274#endif
275
276static void msm_rpmrs_restore_l2_cache(void)
277{
278 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
279
280 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
281 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
282}
283
284static bool msm_rpmrs_vdd_mem_beyond_limits(struct msm_rpmrs_limits *limits)
285{
286 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem;
287 uint32_t vdd_mem;
288
289 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
290 uint32_t buffered_value = msm_rpmrs_buffer[rs->rs[0].id];
291
292 if (rs->enable_low_power == 0)
293 vdd_mem = MSM_RPMRS_VDD_MEM_ACTIVE;
294 else if (rs->enable_low_power == 1)
295 vdd_mem = MSM_RPMRS_VDD_MEM_RET_HIGH;
296 else
297 vdd_mem = MSM_RPMRS_VDD_MEM_RET_LOW;
298
299 if (MSM_RPMRS_VDD(buffered_value) > MSM_RPMRS_VDD(vdd_mem))
300 vdd_mem = buffered_value;
301 } else {
302 vdd_mem = MSM_RPMRS_VDD_MEM_ACTIVE;
303 }
304
305 return MSM_RPMRS_VDD(vdd_mem) >=
306 MSM_RPMRS_VDD(limits->vdd_mem_upper_bound);
307}
308
309static void msm_rpmrs_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
310{
311 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem;
312 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
313
314 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
315 rs->rs[0].value = *buf;
316 if (MSM_RPMRS_VDD(limits->vdd_mem) > MSM_RPMRS_VDD(*buf)) {
317 *buf &= ~MSM_RPMRS_VDD_MASK;
318 *buf |= MSM_RPMRS_VDD(limits->vdd_mem);
319 }
320
321 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
322 pr_info("%s: vdd %d (0x%x)\n", __func__,
323 MSM_RPMRS_VDD(*buf), MSM_RPMRS_VDD(*buf));
324 }
325}
326
327static void msm_rpmrs_restore_vdd_mem(void)
328{
329 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem;
330
331 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
332 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
333}
334
335static bool msm_rpmrs_vdd_dig_beyond_limits(struct msm_rpmrs_limits *limits)
336{
337 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig;
338 uint32_t vdd_dig;
339
340 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
341 uint32_t buffered_value = msm_rpmrs_buffer[rs->rs[0].id];
342
343 if (rs->enable_low_power == 0)
344 vdd_dig = MSM_RPMRS_VDD_DIG_ACTIVE;
345 else if (rs->enable_low_power == 1)
346 vdd_dig = MSM_RPMRS_VDD_DIG_RET_HIGH;
347 else
348 vdd_dig = MSM_RPMRS_VDD_DIG_RET_LOW;
349
350 if (MSM_RPMRS_VDD(buffered_value) > MSM_RPMRS_VDD(vdd_dig))
351 vdd_dig = buffered_value;
352 } else {
353 vdd_dig = MSM_RPMRS_VDD_DIG_ACTIVE;
354 }
355
356 return MSM_RPMRS_VDD(vdd_dig) >=
357 MSM_RPMRS_VDD(limits->vdd_dig_upper_bound);
358}
359
360static void msm_rpmrs_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
361{
362 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig;
363 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
364
365 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
366 rs->rs[0].value = *buf;
367 if (MSM_RPMRS_VDD(limits->vdd_dig) > MSM_RPMRS_VDD(*buf)) {
368 *buf &= ~MSM_RPMRS_VDD_MASK;
369 *buf |= MSM_RPMRS_VDD(limits->vdd_dig);
370 }
371
372
373 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
374 pr_info("%s: vdd %d (0x%x)\n", __func__,
375 MSM_RPMRS_VDD(*buf), MSM_RPMRS_VDD(*buf));
376 }
377}
378
379static void msm_rpmrs_restore_vdd_dig(void)
380{
381 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig;
382
383 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
384 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
385}
386
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387/******************************************************************************
388 * Buffering Functions
389 *****************************************************************************/
390
391static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits,
392 bool irqs_detect, bool gpio_detect)
393{
394
395 if (limits->vdd_dig <= MSM_RPMRS_VDD_DIG_RET_HIGH)
396 return irqs_detect;
397
398 if (limits->pxo == MSM_RPMRS_PXO_OFF)
399 return gpio_detect;
400
401 return true;
402}
403
404static bool msm_rpmrs_use_mpm(struct msm_rpmrs_limits *limits)
405{
406 return (limits->pxo == MSM_RPMRS_PXO_OFF) ||
407 (limits->vdd_dig <= MSM_RPMRS_VDD_DIG_RET_HIGH);
408}
409
410static void msm_rpmrs_update_levels(void)
411{
412 int i, k;
413
414 for (i = 0; i < msm_rpmrs_level_count; i++) {
415 struct msm_rpmrs_level *level = &msm_rpmrs_levels[i];
416
417 if (level->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
418 continue;
419
420 level->available = true;
421
422 for (k = 0; k < ARRAY_SIZE(msm_rpmrs_resources); k++) {
423 struct msm_rpmrs_resource *rs = msm_rpmrs_resources[k];
424
425 if (rs->beyond_limits &&
426 rs->beyond_limits(&level->rs_limits)) {
427 level->available = false;
428 break;
429 }
430 }
431 }
432}
433
434/*
435 * Return value:
436 * 0: no entries in <req> is on our resource list
437 * 1: one or more entries in <req> is on our resource list
438 * -EINVAL: invalid id in <req> array
439 */
440static int msm_rpmrs_buffer_request(struct msm_rpm_iv_pair *req, int count)
441{
442 bool listed;
443 int i;
444
445 for (i = 0; i < count; i++)
446 if (req[i].id > MSM_RPM_ID_LAST)
447 return -EINVAL;
448
449 for (i = 0, listed = false; i < count; i++) {
450 msm_rpmrs_buffer[req[i].id] = req[i].value;
451 set_bit(req[i].id, msm_rpmrs_buffered);
452
453 if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask)
454 pr_info("%s: reg %d: 0x%x\n",
455 __func__, req[i].id, req[i].value);
456
457 if (listed)
458 continue;
459
460 if (test_bit(req[i].id, msm_rpmrs_listed))
461 listed = true;
462 }
463
464 return listed ? 1 : 0;
465}
466
467/*
468 * Return value:
469 * 0: no entries in <req> is on our resource list
470 * 1: one or more entries in <req> is on our resource list
471 * -EINVAL: invalid id in <req> array
472 */
473static int msm_rpmrs_clear_buffer(struct msm_rpm_iv_pair *req, int count)
474{
475 bool listed;
476 int i;
477
478 for (i = 0; i < count; i++)
479 if (req[i].id > MSM_RPM_ID_LAST)
480 return -EINVAL;
481
482 for (i = 0, listed = false; i < count; i++) {
483 msm_rpmrs_buffer[req[i].id] = 0;
484 clear_bit(req[i].id, msm_rpmrs_buffered);
485
486 if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask)
487 pr_info("%s: reg %d\n", __func__, req[i].id);
488
489 if (listed)
490 continue;
491
492 if (test_bit(req[i].id, msm_rpmrs_listed))
493 listed = true;
494 }
495
496 return listed ? 1 : 0;
497}
498
499#ifdef CONFIG_MSM_L2_SPM
500static int msm_rpmrs_flush_L2(struct msm_rpmrs_limits *limits, int notify_rpm)
501{
502 int rc = 0;
503 int lpm;
504
505 switch (limits->l2_cache) {
506 case MSM_RPMRS_L2_CACHE_HSFS_OPEN:
507 lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
508 /* Increment the counter for TZ to init L2 on warmboot */
509 /* Barrier in msm_spm_l2_set_low_power_mode */
510 BUG_ON(!msm_rpmrs_l2_counter_addr);
511 writel_relaxed(++msm_rpmrs_l2_reset_count,
512 msm_rpmrs_l2_counter_addr);
513 break;
514 case MSM_RPMRS_L2_CACHE_GDHS:
515 lpm = MSM_SPM_L2_MODE_GDHS;
516 break;
517 case MSM_RPMRS_L2_CACHE_RETENTION:
518 lpm = MSM_SPM_L2_MODE_RETENTION;
519 break;
520 default:
521 case MSM_RPMRS_L2_CACHE_ACTIVE:
522 lpm = MSM_SPM_L2_MODE_DISABLED;
523 break;
524 }
525
526 rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
527 if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask)
528 pr_info("%s: Requesting low power mode %d returned %d\n",
529 __func__, lpm, rc);
530
531 return rc;
532}
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600533static void msm_rpmrs_L2_restore(struct msm_rpmrs_limits *limits,
534 bool notify_rpm, bool collapsed)
535{
536 msm_spm_l2_set_low_power_mode(MSM_SPM_MODE_DISABLED, notify_rpm);
537 if (!collapsed && (limits->l2_cache == MSM_RPMRS_L2_CACHE_HSFS_OPEN))
538 writel_relaxed(--msm_rpmrs_l2_reset_count,
539 msm_rpmrs_l2_counter_addr);
540}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541#else
542static int msm_rpmrs_flush_L2(struct msm_rpmrs_limits *limits, int notify_rpm)
543{
544 return 0;
545}
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600546static void msm_rpmrs_L2_restore(struct msm_rpmrs_limits *limits,
547 bool notify_rpm, bool collapsed)
548{
549}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550#endif
551
552static int msm_rpmrs_flush_buffer(
553 uint32_t sclk_count, struct msm_rpmrs_limits *limits, int from_idle)
554{
555 struct msm_rpm_iv_pair *req;
556 int count;
557 int rc;
558 int i;
559
560 msm_rpmrs_aggregate_sclk(sclk_count);
561 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
562 if (msm_rpmrs_resources[i]->aggregate)
563 msm_rpmrs_resources[i]->aggregate(limits);
564 }
565
566 count = bitmap_weight(msm_rpmrs_buffered, MSM_RPM_ID_LAST + 1);
567
568 req = kmalloc(sizeof(*req) * count, GFP_ATOMIC);
569 if (!req) {
570 rc = -ENOMEM;
571 goto flush_buffer_restore;
572 }
573
574 count = 0;
575 i = find_first_bit(msm_rpmrs_buffered, MSM_RPM_ID_LAST + 1);
576
577 while (i < MSM_RPM_ID_LAST + 1) {
578 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
579 pr_info("%s: reg %d: 0x%x\n",
580 __func__, i, msm_rpmrs_buffer[i]);
581
582 req[count].id = i;
583 req[count].value = msm_rpmrs_buffer[i];
584 count++;
585
586 i = find_next_bit(msm_rpmrs_buffered, MSM_RPM_ID_LAST+1, i+1);
587 }
588
589 rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_SLEEP, req, count);
590 kfree(req);
591
592 if (rc)
593 goto flush_buffer_restore;
594
595 bitmap_and(msm_rpmrs_buffered,
596 msm_rpmrs_buffered, msm_rpmrs_listed, MSM_RPM_ID_LAST + 1);
597
598flush_buffer_restore:
599 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
600 if (msm_rpmrs_resources[i]->restore)
601 msm_rpmrs_resources[i]->restore();
602 }
603 msm_rpmrs_restore_sclk();
604
605 if (rc)
606 pr_err("%s: failed: %d\n", __func__, rc);
607 return rc;
608}
609
610static int msm_rpmrs_set_common(
611 int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq)
612{
613 if (ctx == MSM_RPM_CTX_SET_SLEEP) {
614 unsigned long flags;
615 int rc;
616
617 spin_lock_irqsave(&msm_rpmrs_lock, flags);
618 rc = msm_rpmrs_buffer_request(req, count);
619 if (rc > 0) {
620 msm_rpmrs_update_levels();
621 rc = 0;
622 }
623 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
624
625 return rc;
626 }
627
628 if (noirq)
629 return msm_rpm_set_noirq(ctx, req, count);
630 else
631 return msm_rpm_set(ctx, req, count);
632}
633
634static int msm_rpmrs_clear_common(
635 int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq)
636{
637 if (ctx == MSM_RPM_CTX_SET_SLEEP) {
638 unsigned long flags;
639 int rc;
640
641 spin_lock_irqsave(&msm_rpmrs_lock, flags);
642 rc = msm_rpmrs_clear_buffer(req, count);
643 if (rc > 0) {
644 msm_rpmrs_update_levels();
645 rc = 0;
646 }
647 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
648
649 if (rc < 0)
650 return rc;
651 }
652
653 if (noirq)
654 return msm_rpm_clear_noirq(ctx, req, count);
655 else
656 return msm_rpm_clear(ctx, req, count);
657}
658
659/******************************************************************************
660 * Attribute Functions
661 *****************************************************************************/
662
663static ssize_t msm_rpmrs_resource_attr_show(
664 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
665{
666 struct kernel_param kp;
667 unsigned long flags;
668 unsigned int temp;
669 int rc;
670
671 spin_lock_irqsave(&msm_rpmrs_lock, flags);
672 temp = GET_RS_FROM_ATTR(attr)->enable_low_power;
673 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
674
675 kp.arg = &temp;
676 rc = param_get_uint(buf, &kp);
677
678 if (rc > 0) {
679 strcat(buf, "\n");
680 rc++;
681 }
682
683 return rc;
684}
685
686static ssize_t msm_rpmrs_resource_attr_store(struct kobject *kobj,
687 struct kobj_attribute *attr, const char *buf, size_t count)
688{
689 struct kernel_param kp;
690 unsigned long flags;
691 unsigned int temp;
692 int rc;
693
694 kp.arg = &temp;
695 rc = param_set_uint(buf, &kp);
696 if (rc)
697 return rc;
698
699 spin_lock_irqsave(&msm_rpmrs_lock, flags);
700 GET_RS_FROM_ATTR(attr)->enable_low_power = temp;
Eugene Seah78aa5e72011-07-18 18:28:37 -0600701
702 /* special case active-set signal for MSM_RPMRS_ID_RPM_CTL */
703 if (GET_RS_FROM_ATTR(attr)->rs[0].id == MSM_RPMRS_ID_RPM_CTL) {
704 struct msm_rpm_iv_pair req;
705 req.id = MSM_RPMRS_ID_RPM_CTL;
706 req.value = GET_RS_FROM_ATTR(attr)->enable_low_power ? 0 : 1;
707
708 rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &req, 1);
709 if (rc) {
710 pr_err("%s: failed to request RPM_CTL to %d: %d\n",
711 __func__, req.value, rc);
712 }
713 }
714
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715 msm_rpmrs_update_levels();
716 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
717
718 return count;
719}
720
721static int __init msm_rpmrs_resource_sysfs_add(void)
722{
723 struct kobject *module_kobj;
724 struct kobject *low_power_kboj;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725 int rc;
726
727 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
728 if (!module_kobj) {
729 pr_err("%s: cannot find kobject for module %s\n",
730 __func__, KBUILD_MODNAME);
731 rc = -ENOENT;
732 goto resource_sysfs_add_exit;
733 }
734
735 low_power_kboj = kobject_create_and_add(
736 "enable_low_power", module_kobj);
737 if (!low_power_kboj) {
738 pr_err("%s: cannot create kobject\n", __func__);
739 rc = -ENOMEM;
740 goto resource_sysfs_add_exit;
741 }
742
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600743 rc = sysfs_create_group(low_power_kboj, &msm_rpmrs_attribute_group);
744 if (rc) {
745 pr_err("%s: cannot create kobject attribute group\n", __func__);
746 goto resource_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747 }
748
749 rc = 0;
750
751resource_sysfs_add_exit:
752 return rc;
753}
754
755/******************************************************************************
756 * Public Functions
757 *****************************************************************************/
758
759int msm_rpmrs_set(int ctx, struct msm_rpm_iv_pair *req, int count)
760{
761 return msm_rpmrs_set_common(ctx, req, count, false);
762}
763
764int msm_rpmrs_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count)
765{
766 WARN(!irqs_disabled(), "msm_rpmrs_set_noirq can only be called "
767 "safely when local irqs are disabled. Consider using "
768 "msm_rpmrs_set or msm_rpmrs_set_nosleep instead.");
769 return msm_rpmrs_set_common(ctx, req, count, true);
770}
771
772int msm_rpmrs_clear(int ctx, struct msm_rpm_iv_pair *req, int count)
773{
774 return msm_rpmrs_clear_common(ctx, req, count, false);
775}
776
777int msm_rpmrs_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count)
778{
779 WARN(!irqs_disabled(), "msm_rpmrs_clear_noirq can only be called "
780 "safely when local irqs are disabled. Consider using "
781 "msm_rpmrs_clear or msm_rpmrs_clear_nosleep instead.");
782 return msm_rpmrs_clear_common(ctx, req, count, true);
783}
784
785void msm_rpmrs_show_resources(void)
786{
787 struct msm_rpmrs_resource *rs;
788 unsigned long flags;
789 int i;
790
791 spin_lock_irqsave(&msm_rpmrs_lock, flags);
792 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
793 rs = msm_rpmrs_resources[i];
794 if (rs->rs[0].id < MSM_RPM_ID_LAST + 1)
795 pr_info("%s: resource %s: buffered %d, value 0x%x\n",
796 __func__, rs->name,
797 test_bit(rs->rs[0].id, msm_rpmrs_buffered),
798 msm_rpmrs_buffer[rs->rs[0].id]);
799 else
800 pr_info("%s: resource %s: value %d\n",
801 __func__, rs->name, rs->rs[0].value);
802 }
803 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
804}
805
806struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
807 bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
808 uint32_t sleep_us)
809{
810 unsigned int cpu = smp_processor_id();
811 struct msm_rpmrs_level *best_level = NULL;
812 bool irqs_detectable = false;
813 bool gpio_detectable = false;
814 int i;
815
816 if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
817 irqs_detectable = msm_mpm_irqs_detectable(from_idle);
818 gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
819 }
820
821 for (i = 0; i < msm_rpmrs_level_count; i++) {
822 struct msm_rpmrs_level *level = &msm_rpmrs_levels[i];
823 uint32_t power;
824
825 if (!level->available)
826 continue;
827
828 if (sleep_mode != level->sleep_mode)
829 continue;
830
831 if (latency_us < level->latency_us)
832 continue;
833
834 if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
835 irqs_detectable, gpio_detectable))
836 continue;
837
838 if (sleep_us <= 1) {
839 power = level->energy_overhead;
840 } else if (sleep_us <= level->time_overhead_us) {
841 power = level->energy_overhead / sleep_us;
842 } else if ((sleep_us >> 10) > level->time_overhead_us) {
843 power = level->steady_state_power;
844 } else {
845 power = (sleep_us - level->time_overhead_us);
846 power *= level->steady_state_power;
847 power /= sleep_us;
848 power += level->energy_overhead / sleep_us;
849 }
850
851 if (!best_level ||
852 best_level->rs_limits.power[cpu] >= power) {
853 level->rs_limits.latency_us[cpu] = level->latency_us;
854 level->rs_limits.power[cpu] = power;
855 best_level = level;
856 }
857 }
858
859 return best_level ? &best_level->rs_limits : NULL;
860}
861
862int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
863 bool from_idle, bool notify_rpm)
864{
865 int rc = 0;
866
867 rc = msm_rpmrs_flush_L2(limits, notify_rpm);
868 if (rc)
869 return rc;
870
871 if (notify_rpm) {
872 rc = msm_rpmrs_flush_buffer(sclk_count, limits, from_idle);
873 if (rc)
874 return rc;
875
876 if (msm_rpmrs_use_mpm(limits))
877 msm_mpm_enter_sleep(from_idle);
878 }
879
880 return rc;
881}
882
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600883void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
884 bool notify_rpm, bool collapsed)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700885{
886
887 /* Disable L2 for now, we dont want L2 to do retention by default */
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600888 msm_rpmrs_L2_restore(limits, notify_rpm, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700889
890 if (msm_rpmrs_use_mpm(limits))
891 msm_mpm_exit_sleep(from_idle);
892}
893
894#ifdef CONFIG_MSM_L2_SPM
895static int rpmrs_cpu_callback(struct notifier_block *nfb,
896 unsigned long action, void *hcpu)
897{
898 switch (action) {
899 case CPU_ONLINE_FROZEN:
900 case CPU_ONLINE:
901 if (num_online_cpus() > 1)
902 msm_rpmrs_l2_cache.rs[0].value =
903 MSM_RPMRS_L2_CACHE_ACTIVE;
904 break;
905 case CPU_DEAD_FROZEN:
906 case CPU_DEAD:
907 if (num_online_cpus() == 1)
908 msm_rpmrs_l2_cache.rs[0].value =
909 MSM_RPMRS_L2_CACHE_HSFS_OPEN;
910 break;
911 }
912
913 msm_rpmrs_update_levels();
914 return NOTIFY_OK;
915}
916
917static struct notifier_block __refdata rpmrs_cpu_notifier = {
918 .notifier_call = rpmrs_cpu_callback,
919};
920#endif
921
922int __init msm_rpmrs_levels_init(struct msm_rpmrs_level *levels, int size)
923{
924 msm_rpmrs_levels = kzalloc(sizeof(struct msm_rpmrs_level) * size,
925 GFP_KERNEL);
926 if (!msm_rpmrs_levels)
927 return -ENOMEM;
928 msm_rpmrs_level_count = size;
929 memcpy(msm_rpmrs_levels, levels, size * sizeof(struct msm_rpmrs_level));
930
931 return 0;
932}
933
934static int __init msm_rpmrs_init(void)
935{
936 struct msm_rpm_iv_pair req;
937 int rc;
938
Stepan Moskovchenko0302fbc2011-08-05 18:06:13 -0700939 if (cpu_is_apq8064())
940 return -ENODEV;
941
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942 BUG_ON(!msm_rpmrs_levels);
943
Praveen Chidambaram841d46c2011-08-04 09:07:53 -0600944 if (cpu_is_msm8x60()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 req.id = MSM_RPMRS_ID_APPS_L2_CACHE_CTL;
946 req.value = 1;
947
948 rc = msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
949 if (rc) {
950 pr_err("%s: failed to request L2 cache: %d\n",
951 __func__, rc);
952 goto init_exit;
953 }
954
955 req.id = MSM_RPMRS_ID_APPS_L2_CACHE_CTL;
956 req.value = 0;
957
958 rc = msm_rpmrs_set(MSM_RPM_CTX_SET_SLEEP, &req, 1);
959 if (rc) {
960 pr_err("%s: failed to initialize L2 cache for sleep: "
961 "%d\n", __func__, rc);
962 goto init_exit;
963 }
964 }
965
Eugene Seah78aa5e72011-07-18 18:28:37 -0600966 /* Enable RPM SWFI on Apps initialization */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700967 req.id = MSM_RPMRS_ID_RPM_CTL;
968 req.value = 0;
969
Eugene Seah78aa5e72011-07-18 18:28:37 -0600970 rc = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &req, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700971 if (rc) {
Eugene Seah78aa5e72011-07-18 18:28:37 -0600972 pr_err("%s: failed to initialize RPM halt: "
973 "%d\n", __func__, rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974 goto init_exit;
975 }
976
977 rc = msm_rpmrs_resource_sysfs_add();
978
979init_exit:
980 return rc;
981}
982device_initcall(msm_rpmrs_init);
983
984static int __init msm_rpmrs_early_init(void)
985{
986 int i, k;
987
988 /* Initialize listed bitmap for valid resource IDs */
989 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
990 for (k = 0; k < msm_rpmrs_resources[i]->size; k++)
991 set_bit(msm_rpmrs_resources[i]->rs[k].id,
992 msm_rpmrs_listed);
993 }
994
995 return 0;
996}
997early_initcall(msm_rpmrs_early_init);
998
999#ifdef CONFIG_MSM_L2_SPM
1000static int __init msm_rpmrs_l2_counter_init(void)
1001{
1002 msm_rpmrs_l2_counter_addr = MSM_IMEM_BASE + L2_PC_COUNTER_ADDR;
1003 writel_relaxed(msm_rpmrs_l2_reset_count, msm_rpmrs_l2_counter_addr);
1004 mb();
1005
1006 msm_rpmrs_l2_cache.beyond_limits = msm_spm_l2_cache_beyond_limits;
1007 msm_rpmrs_l2_cache.aggregate = NULL;
1008 msm_rpmrs_l2_cache.restore = NULL;
1009
1010 register_hotcpu_notifier(&rpmrs_cpu_notifier);
1011
1012 return 0;
1013}
1014early_initcall(msm_rpmrs_l2_counter_init);
1015#endif