blob: f2b3e88751c588472209146a786eb93d24bf6218 [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 -070067static void *msm_rpmrs_l2_counter_addr;
68static int msm_rpmrs_l2_reset_count;
69#define L2_PC_COUNTER_ADDR 0x660
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070
71#define MSM_RPMRS_MAX_RS_REGISTER_COUNT 2
72
Praveen Chidambaram66775c62011-08-04 16:59:24 -060073#define RPMRS_ATTR(_name) \
74 __ATTR(_name, S_IRUGO|S_IWUSR, \
75 msm_rpmrs_resource_attr_show, msm_rpmrs_resource_attr_store)
76
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077struct msm_rpmrs_resource {
78 struct msm_rpm_iv_pair rs[MSM_RPMRS_MAX_RS_REGISTER_COUNT];
79 uint32_t size;
80 char *name;
81
82 uint32_t enable_low_power;
83
84 bool (*beyond_limits)(struct msm_rpmrs_limits *limits);
85 void (*aggregate)(struct msm_rpmrs_limits *limits);
86 void (*restore)(void);
Praveen Chidambaram66775c62011-08-04 16:59:24 -060087
88 struct kobj_attribute ko_attr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089};
90
91static struct msm_rpmrs_resource msm_rpmrs_pxo = {
92 .rs[0].id = MSM_RPMRS_ID_PXO_CLK,
93 .size = 1,
94 .name = "pxo",
95 .beyond_limits = msm_rpmrs_pxo_beyond_limits,
96 .aggregate = msm_rpmrs_aggregate_pxo,
97 .restore = msm_rpmrs_restore_pxo,
Praveen Chidambaram66775c62011-08-04 16:59:24 -060098 .ko_attr = RPMRS_ATTR(pxo),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099};
100
101static struct msm_rpmrs_resource msm_rpmrs_l2_cache = {
102 .rs[0].id = MSM_RPMRS_ID_APPS_L2_CACHE_CTL,
103 .size = 1,
104 .name = "L2_cache",
105 .beyond_limits = msm_rpmrs_l2_cache_beyond_limits,
106 .aggregate = msm_rpmrs_aggregate_l2_cache,
107 .restore = msm_rpmrs_restore_l2_cache,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600108 .ko_attr = RPMRS_ATTR(L2_cache),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109};
110
111static struct msm_rpmrs_resource msm_rpmrs_vdd_mem = {
112 .rs[0].id = MSM_RPMRS_ID_VDD_MEM_0,
113 .rs[1].id = MSM_RPMRS_ID_VDD_MEM_1,
114 .size = 2,
115 .name = "vdd_mem",
116 .beyond_limits = msm_rpmrs_vdd_mem_beyond_limits,
117 .aggregate = msm_rpmrs_aggregate_vdd_mem,
118 .restore = msm_rpmrs_restore_vdd_mem,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600119 .ko_attr = RPMRS_ATTR(vdd_mem),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120};
121
122static struct msm_rpmrs_resource msm_rpmrs_vdd_dig = {
123 .rs[0].id = MSM_RPMRS_ID_VDD_DIG_0,
124 .rs[1].id = MSM_RPMRS_ID_VDD_DIG_1,
125 .size = 2,
126 .name = "vdd_dig",
127 .beyond_limits = msm_rpmrs_vdd_dig_beyond_limits,
128 .aggregate = msm_rpmrs_aggregate_vdd_dig,
129 .restore = msm_rpmrs_restore_vdd_dig,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600130 .ko_attr = RPMRS_ATTR(vdd_dig),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131};
132
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600133static struct msm_rpmrs_resource msm_rpmrs_rpm_ctl = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 .rs[0].id = MSM_RPMRS_ID_RPM_CTL,
135 .size = 1,
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600136 .name = "rpm_ctl",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137 .beyond_limits = NULL,
Eugene Seah78aa5e72011-07-18 18:28:37 -0600138 .aggregate = NULL,
139 .restore = NULL,
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600140 .ko_attr = RPMRS_ATTR(rpm_ctl),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141};
142
143static struct msm_rpmrs_resource *msm_rpmrs_resources[] = {
144 &msm_rpmrs_pxo,
145 &msm_rpmrs_l2_cache,
146 &msm_rpmrs_vdd_mem,
147 &msm_rpmrs_vdd_dig,
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600148 &msm_rpmrs_rpm_ctl,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149};
150
151static uint32_t msm_rpmrs_buffer[MSM_RPM_ID_LAST + 1];
152static DECLARE_BITMAP(msm_rpmrs_buffered, MSM_RPM_ID_LAST + 1);
153static DECLARE_BITMAP(msm_rpmrs_listed, MSM_RPM_ID_LAST + 1);
154static DEFINE_SPINLOCK(msm_rpmrs_lock);
155
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156#define MSM_RPMRS_VDD(v) ((v) & (MSM_RPMRS_VDD_MASK))
157
158/******************************************************************************
159 * Attribute Definitions
160 *****************************************************************************/
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600161static struct attribute *msm_rpmrs_attributes[] = {
162 &msm_rpmrs_pxo.ko_attr.attr,
163 &msm_rpmrs_l2_cache.ko_attr.attr,
164 &msm_rpmrs_vdd_mem.ko_attr.attr,
165 &msm_rpmrs_vdd_dig.ko_attr.attr,
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600166 NULL,
167};
168static struct attribute *msm_rpmrs_mode_attributes[] = {
169 &msm_rpmrs_rpm_ctl.ko_attr.attr,
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600170 NULL,
171};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600173static struct attribute_group msm_rpmrs_attribute_group = {
174 .attrs = msm_rpmrs_attributes,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175};
176
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600177static struct attribute_group msm_rpmrs_mode_attribute_group = {
178 .attrs = msm_rpmrs_mode_attributes,
179};
180
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181#define GET_RS_FROM_ATTR(attr) \
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600182 (container_of(attr, struct msm_rpmrs_resource, ko_attr))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184
185/******************************************************************************
186 * Resource Specific Functions
187 *****************************************************************************/
188
189static void msm_rpmrs_aggregate_sclk(uint32_t sclk_count)
190{
191 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_TO] = 0;
192 set_bit(MSM_RPM_ID_TRIGGER_TIMED_TO, msm_rpmrs_buffered);
193 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT] = sclk_count;
194 set_bit(MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, msm_rpmrs_buffered);
195}
196
197static void msm_rpmrs_restore_sclk(void)
198{
199 clear_bit(MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, msm_rpmrs_buffered);
200 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT] = 0;
201 clear_bit(MSM_RPM_ID_TRIGGER_TIMED_TO, msm_rpmrs_buffered);
202 msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_TO] = 0;
203}
204
205static bool msm_rpmrs_pxo_beyond_limits(struct msm_rpmrs_limits *limits)
206{
207 struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo;
208 uint32_t pxo;
209
210 if (rs->enable_low_power && test_bit(rs->rs[0].id, msm_rpmrs_buffered))
211 pxo = msm_rpmrs_buffer[rs->rs[0].id];
212 else
213 pxo = MSM_RPMRS_PXO_ON;
214
215 return pxo > limits->pxo;
216}
217
218static void msm_rpmrs_aggregate_pxo(struct msm_rpmrs_limits *limits)
219{
220 struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo;
221 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
222
223 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
224 rs->rs[0].value = *buf;
225 if (limits->pxo > *buf)
226 *buf = limits->pxo;
227 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
228 pr_info("%s: %d (0x%x)\n", __func__, *buf, *buf);
229 }
230}
231
232static void msm_rpmrs_restore_pxo(void)
233{
234 struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo;
235
236 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
237 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
238}
239
240static bool msm_rpmrs_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits)
241{
242 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
243 uint32_t l2_cache;
244
245 if (rs->enable_low_power && test_bit(rs->rs[0].id, msm_rpmrs_buffered))
246 l2_cache = msm_rpmrs_buffer[rs->rs[0].id];
247 else
248 l2_cache = MSM_RPMRS_L2_CACHE_ACTIVE;
249
250 return l2_cache > limits->l2_cache;
251}
252
253static void msm_rpmrs_aggregate_l2_cache(struct msm_rpmrs_limits *limits)
254{
255 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
256 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
257
258 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
259 rs->rs[0].value = *buf;
260 if (limits->l2_cache > *buf)
261 *buf = limits->l2_cache;
262
263 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
264 pr_info("%s: %d (0x%x)\n", __func__, *buf, *buf);
265 }
266}
267
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268static bool msm_spm_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits)
269{
270 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
271 uint32_t l2_cache = rs->rs[0].value;
272
273 if (!rs->enable_low_power)
274 l2_cache = MSM_RPMRS_L2_CACHE_ACTIVE;
275
276 return l2_cache > limits->l2_cache;
277}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278
279static void msm_rpmrs_restore_l2_cache(void)
280{
281 struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache;
282
283 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
284 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
285}
286
287static bool msm_rpmrs_vdd_mem_beyond_limits(struct msm_rpmrs_limits *limits)
288{
289 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem;
290 uint32_t vdd_mem;
291
292 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
293 uint32_t buffered_value = msm_rpmrs_buffer[rs->rs[0].id];
294
295 if (rs->enable_low_power == 0)
296 vdd_mem = MSM_RPMRS_VDD_MEM_ACTIVE;
297 else if (rs->enable_low_power == 1)
298 vdd_mem = MSM_RPMRS_VDD_MEM_RET_HIGH;
299 else
300 vdd_mem = MSM_RPMRS_VDD_MEM_RET_LOW;
301
302 if (MSM_RPMRS_VDD(buffered_value) > MSM_RPMRS_VDD(vdd_mem))
303 vdd_mem = buffered_value;
304 } else {
305 vdd_mem = MSM_RPMRS_VDD_MEM_ACTIVE;
306 }
307
308 return MSM_RPMRS_VDD(vdd_mem) >=
309 MSM_RPMRS_VDD(limits->vdd_mem_upper_bound);
310}
311
312static void msm_rpmrs_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
313{
314 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem;
315 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
316
317 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
318 rs->rs[0].value = *buf;
319 if (MSM_RPMRS_VDD(limits->vdd_mem) > MSM_RPMRS_VDD(*buf)) {
320 *buf &= ~MSM_RPMRS_VDD_MASK;
321 *buf |= MSM_RPMRS_VDD(limits->vdd_mem);
322 }
323
324 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
325 pr_info("%s: vdd %d (0x%x)\n", __func__,
326 MSM_RPMRS_VDD(*buf), MSM_RPMRS_VDD(*buf));
327 }
328}
329
330static void msm_rpmrs_restore_vdd_mem(void)
331{
332 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem;
333
334 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
335 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
336}
337
338static bool msm_rpmrs_vdd_dig_beyond_limits(struct msm_rpmrs_limits *limits)
339{
340 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig;
341 uint32_t vdd_dig;
342
343 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
344 uint32_t buffered_value = msm_rpmrs_buffer[rs->rs[0].id];
345
346 if (rs->enable_low_power == 0)
347 vdd_dig = MSM_RPMRS_VDD_DIG_ACTIVE;
348 else if (rs->enable_low_power == 1)
349 vdd_dig = MSM_RPMRS_VDD_DIG_RET_HIGH;
350 else
351 vdd_dig = MSM_RPMRS_VDD_DIG_RET_LOW;
352
353 if (MSM_RPMRS_VDD(buffered_value) > MSM_RPMRS_VDD(vdd_dig))
354 vdd_dig = buffered_value;
355 } else {
356 vdd_dig = MSM_RPMRS_VDD_DIG_ACTIVE;
357 }
358
359 return MSM_RPMRS_VDD(vdd_dig) >=
360 MSM_RPMRS_VDD(limits->vdd_dig_upper_bound);
361}
362
363static void msm_rpmrs_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
364{
365 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig;
366 uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id];
367
368 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) {
369 rs->rs[0].value = *buf;
370 if (MSM_RPMRS_VDD(limits->vdd_dig) > MSM_RPMRS_VDD(*buf)) {
371 *buf &= ~MSM_RPMRS_VDD_MASK;
372 *buf |= MSM_RPMRS_VDD(limits->vdd_dig);
373 }
374
375
376 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
377 pr_info("%s: vdd %d (0x%x)\n", __func__,
378 MSM_RPMRS_VDD(*buf), MSM_RPMRS_VDD(*buf));
379 }
380}
381
382static void msm_rpmrs_restore_vdd_dig(void)
383{
384 struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig;
385
386 if (test_bit(rs->rs[0].id, msm_rpmrs_buffered))
387 msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value;
388}
389
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390/******************************************************************************
391 * Buffering Functions
392 *****************************************************************************/
393
394static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits,
395 bool irqs_detect, bool gpio_detect)
396{
397
398 if (limits->vdd_dig <= MSM_RPMRS_VDD_DIG_RET_HIGH)
399 return irqs_detect;
400
401 if (limits->pxo == MSM_RPMRS_PXO_OFF)
402 return gpio_detect;
403
404 return true;
405}
406
407static bool msm_rpmrs_use_mpm(struct msm_rpmrs_limits *limits)
408{
409 return (limits->pxo == MSM_RPMRS_PXO_OFF) ||
410 (limits->vdd_dig <= MSM_RPMRS_VDD_DIG_RET_HIGH);
411}
412
413static void msm_rpmrs_update_levels(void)
414{
415 int i, k;
416
417 for (i = 0; i < msm_rpmrs_level_count; i++) {
418 struct msm_rpmrs_level *level = &msm_rpmrs_levels[i];
419
420 if (level->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
421 continue;
422
423 level->available = true;
424
425 for (k = 0; k < ARRAY_SIZE(msm_rpmrs_resources); k++) {
426 struct msm_rpmrs_resource *rs = msm_rpmrs_resources[k];
427
428 if (rs->beyond_limits &&
429 rs->beyond_limits(&level->rs_limits)) {
430 level->available = false;
431 break;
432 }
433 }
434 }
435}
436
437/*
438 * Return value:
439 * 0: no entries in <req> is on our resource list
440 * 1: one or more entries in <req> is on our resource list
441 * -EINVAL: invalid id in <req> array
442 */
443static int msm_rpmrs_buffer_request(struct msm_rpm_iv_pair *req, int count)
444{
445 bool listed;
446 int i;
447
448 for (i = 0; i < count; i++)
449 if (req[i].id > MSM_RPM_ID_LAST)
450 return -EINVAL;
451
452 for (i = 0, listed = false; i < count; i++) {
453 msm_rpmrs_buffer[req[i].id] = req[i].value;
454 set_bit(req[i].id, msm_rpmrs_buffered);
455
456 if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask)
457 pr_info("%s: reg %d: 0x%x\n",
458 __func__, req[i].id, req[i].value);
459
460 if (listed)
461 continue;
462
463 if (test_bit(req[i].id, msm_rpmrs_listed))
464 listed = true;
465 }
466
467 return listed ? 1 : 0;
468}
469
470/*
471 * Return value:
472 * 0: no entries in <req> is on our resource list
473 * 1: one or more entries in <req> is on our resource list
474 * -EINVAL: invalid id in <req> array
475 */
476static int msm_rpmrs_clear_buffer(struct msm_rpm_iv_pair *req, int count)
477{
478 bool listed;
479 int i;
480
481 for (i = 0; i < count; i++)
482 if (req[i].id > MSM_RPM_ID_LAST)
483 return -EINVAL;
484
485 for (i = 0, listed = false; i < count; i++) {
486 msm_rpmrs_buffer[req[i].id] = 0;
487 clear_bit(req[i].id, msm_rpmrs_buffered);
488
489 if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask)
490 pr_info("%s: reg %d\n", __func__, req[i].id);
491
492 if (listed)
493 continue;
494
495 if (test_bit(req[i].id, msm_rpmrs_listed))
496 listed = true;
497 }
498
499 return listed ? 1 : 0;
500}
501
502#ifdef CONFIG_MSM_L2_SPM
503static int msm_rpmrs_flush_L2(struct msm_rpmrs_limits *limits, int notify_rpm)
504{
505 int rc = 0;
506 int lpm;
507
508 switch (limits->l2_cache) {
509 case MSM_RPMRS_L2_CACHE_HSFS_OPEN:
510 lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
511 /* Increment the counter for TZ to init L2 on warmboot */
512 /* Barrier in msm_spm_l2_set_low_power_mode */
513 BUG_ON(!msm_rpmrs_l2_counter_addr);
514 writel_relaxed(++msm_rpmrs_l2_reset_count,
515 msm_rpmrs_l2_counter_addr);
516 break;
517 case MSM_RPMRS_L2_CACHE_GDHS:
518 lpm = MSM_SPM_L2_MODE_GDHS;
519 break;
520 case MSM_RPMRS_L2_CACHE_RETENTION:
521 lpm = MSM_SPM_L2_MODE_RETENTION;
522 break;
523 default:
524 case MSM_RPMRS_L2_CACHE_ACTIVE:
525 lpm = MSM_SPM_L2_MODE_DISABLED;
526 break;
527 }
528
529 rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
530 if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask)
531 pr_info("%s: Requesting low power mode %d returned %d\n",
532 __func__, lpm, rc);
533
534 return rc;
535}
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600536static void msm_rpmrs_L2_restore(struct msm_rpmrs_limits *limits,
537 bool notify_rpm, bool collapsed)
538{
539 msm_spm_l2_set_low_power_mode(MSM_SPM_MODE_DISABLED, notify_rpm);
540 if (!collapsed && (limits->l2_cache == MSM_RPMRS_L2_CACHE_HSFS_OPEN))
541 writel_relaxed(--msm_rpmrs_l2_reset_count,
542 msm_rpmrs_l2_counter_addr);
543}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544#else
545static int msm_rpmrs_flush_L2(struct msm_rpmrs_limits *limits, int notify_rpm)
546{
547 return 0;
548}
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600549static void msm_rpmrs_L2_restore(struct msm_rpmrs_limits *limits,
550 bool notify_rpm, bool collapsed)
551{
552}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553#endif
554
555static int msm_rpmrs_flush_buffer(
556 uint32_t sclk_count, struct msm_rpmrs_limits *limits, int from_idle)
557{
558 struct msm_rpm_iv_pair *req;
559 int count;
560 int rc;
561 int i;
562
563 msm_rpmrs_aggregate_sclk(sclk_count);
564 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
565 if (msm_rpmrs_resources[i]->aggregate)
566 msm_rpmrs_resources[i]->aggregate(limits);
567 }
568
569 count = bitmap_weight(msm_rpmrs_buffered, MSM_RPM_ID_LAST + 1);
570
571 req = kmalloc(sizeof(*req) * count, GFP_ATOMIC);
572 if (!req) {
573 rc = -ENOMEM;
574 goto flush_buffer_restore;
575 }
576
577 count = 0;
578 i = find_first_bit(msm_rpmrs_buffered, MSM_RPM_ID_LAST + 1);
579
580 while (i < MSM_RPM_ID_LAST + 1) {
581 if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask)
582 pr_info("%s: reg %d: 0x%x\n",
583 __func__, i, msm_rpmrs_buffer[i]);
584
585 req[count].id = i;
586 req[count].value = msm_rpmrs_buffer[i];
587 count++;
588
589 i = find_next_bit(msm_rpmrs_buffered, MSM_RPM_ID_LAST+1, i+1);
590 }
591
592 rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_SLEEP, req, count);
593 kfree(req);
594
595 if (rc)
596 goto flush_buffer_restore;
597
598 bitmap_and(msm_rpmrs_buffered,
599 msm_rpmrs_buffered, msm_rpmrs_listed, MSM_RPM_ID_LAST + 1);
600
601flush_buffer_restore:
602 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
603 if (msm_rpmrs_resources[i]->restore)
604 msm_rpmrs_resources[i]->restore();
605 }
606 msm_rpmrs_restore_sclk();
607
608 if (rc)
609 pr_err("%s: failed: %d\n", __func__, rc);
610 return rc;
611}
612
613static int msm_rpmrs_set_common(
614 int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq)
615{
616 if (ctx == MSM_RPM_CTX_SET_SLEEP) {
617 unsigned long flags;
618 int rc;
619
620 spin_lock_irqsave(&msm_rpmrs_lock, flags);
621 rc = msm_rpmrs_buffer_request(req, count);
622 if (rc > 0) {
623 msm_rpmrs_update_levels();
624 rc = 0;
625 }
626 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
627
628 return rc;
629 }
630
631 if (noirq)
632 return msm_rpm_set_noirq(ctx, req, count);
633 else
634 return msm_rpm_set(ctx, req, count);
635}
636
637static int msm_rpmrs_clear_common(
638 int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq)
639{
640 if (ctx == MSM_RPM_CTX_SET_SLEEP) {
641 unsigned long flags;
642 int rc;
643
644 spin_lock_irqsave(&msm_rpmrs_lock, flags);
645 rc = msm_rpmrs_clear_buffer(req, count);
646 if (rc > 0) {
647 msm_rpmrs_update_levels();
648 rc = 0;
649 }
650 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
651
652 if (rc < 0)
653 return rc;
654 }
655
656 if (noirq)
657 return msm_rpm_clear_noirq(ctx, req, count);
658 else
659 return msm_rpm_clear(ctx, req, count);
660}
661
662/******************************************************************************
663 * Attribute Functions
664 *****************************************************************************/
665
666static ssize_t msm_rpmrs_resource_attr_show(
667 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
668{
669 struct kernel_param kp;
670 unsigned long flags;
671 unsigned int temp;
672 int rc;
673
674 spin_lock_irqsave(&msm_rpmrs_lock, flags);
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600675 /* special case active-set signal for MSM_RPMRS_ID_RPM_CTL */
676 if (GET_RS_FROM_ATTR(attr)->rs[0].id == MSM_RPMRS_ID_RPM_CTL)
677 temp = GET_RS_FROM_ATTR(attr)->rs[0].value;
678 else
679 temp = GET_RS_FROM_ATTR(attr)->enable_low_power;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
681
682 kp.arg = &temp;
683 rc = param_get_uint(buf, &kp);
684
685 if (rc > 0) {
686 strcat(buf, "\n");
687 rc++;
688 }
689
690 return rc;
691}
692
693static ssize_t msm_rpmrs_resource_attr_store(struct kobject *kobj,
694 struct kobj_attribute *attr, const char *buf, size_t count)
695{
696 struct kernel_param kp;
697 unsigned long flags;
698 unsigned int temp;
699 int rc;
700
701 kp.arg = &temp;
702 rc = param_set_uint(buf, &kp);
703 if (rc)
704 return rc;
705
706 spin_lock_irqsave(&msm_rpmrs_lock, flags);
707 GET_RS_FROM_ATTR(attr)->enable_low_power = temp;
Eugene Seah78aa5e72011-07-18 18:28:37 -0600708
709 /* special case active-set signal for MSM_RPMRS_ID_RPM_CTL */
710 if (GET_RS_FROM_ATTR(attr)->rs[0].id == MSM_RPMRS_ID_RPM_CTL) {
711 struct msm_rpm_iv_pair req;
712 req.id = MSM_RPMRS_ID_RPM_CTL;
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600713 req.value = GET_RS_FROM_ATTR(attr)->enable_low_power;
714 GET_RS_FROM_ATTR(attr)->rs[0].value = req.value;
Eugene Seah78aa5e72011-07-18 18:28:37 -0600715
716 rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &req, 1);
717 if (rc) {
718 pr_err("%s: failed to request RPM_CTL to %d: %d\n",
719 __func__, req.value, rc);
720 }
721 }
722
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723 msm_rpmrs_update_levels();
724 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
725
726 return count;
727}
728
729static int __init msm_rpmrs_resource_sysfs_add(void)
730{
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600731 struct kobject *module_kobj = NULL;
732 struct kobject *low_power_kobj = NULL;
733 struct kobject *mode_kobj = NULL;
734 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700735
736 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
737 if (!module_kobj) {
738 pr_err("%s: cannot find kobject for module %s\n",
739 __func__, KBUILD_MODNAME);
740 rc = -ENOENT;
741 goto resource_sysfs_add_exit;
742 }
743
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600744 low_power_kobj = kobject_create_and_add(
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700745 "enable_low_power", module_kobj);
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600746 if (!low_power_kobj) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747 pr_err("%s: cannot create kobject\n", __func__);
748 rc = -ENOMEM;
749 goto resource_sysfs_add_exit;
750 }
751
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600752 mode_kobj = kobject_create_and_add(
753 "mode", module_kobj);
754 if (!mode_kobj) {
755 pr_err("%s: cannot create kobject\n", __func__);
756 rc = -ENOMEM;
757 goto resource_sysfs_add_exit;
758 }
759
760 rc = sysfs_create_group(low_power_kobj, &msm_rpmrs_attribute_group);
Praveen Chidambaram66775c62011-08-04 16:59:24 -0600761 if (rc) {
762 pr_err("%s: cannot create kobject attribute group\n", __func__);
763 goto resource_sysfs_add_exit;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700764 }
765
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600766 rc = sysfs_create_group(mode_kobj, &msm_rpmrs_mode_attribute_group);
767 if (rc) {
768 pr_err("%s: cannot create kobject attribute group\n", __func__);
769 goto resource_sysfs_add_exit;
770 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700771
772resource_sysfs_add_exit:
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600773 if (rc) {
774 if (low_power_kobj)
775 sysfs_remove_group(low_power_kobj,
776 &msm_rpmrs_attribute_group);
777 kobject_del(low_power_kobj);
778 kobject_del(mode_kobj);
779 }
780
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700781 return rc;
782}
783
784/******************************************************************************
785 * Public Functions
786 *****************************************************************************/
787
788int msm_rpmrs_set(int ctx, struct msm_rpm_iv_pair *req, int count)
789{
790 return msm_rpmrs_set_common(ctx, req, count, false);
791}
792
793int msm_rpmrs_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count)
794{
795 WARN(!irqs_disabled(), "msm_rpmrs_set_noirq can only be called "
796 "safely when local irqs are disabled. Consider using "
797 "msm_rpmrs_set or msm_rpmrs_set_nosleep instead.");
798 return msm_rpmrs_set_common(ctx, req, count, true);
799}
800
Praveen Chidambaram9dfa8712011-09-14 16:25:01 -0600801/* Allow individual bits of an rpm resource be set, currently used only for
802 * active context resource viz. RPM_CTL. The API is generic enough to possibly
803 * extend it to other resources as well in the future.
804 */
805int msm_rpmrs_set_bits_noirq(int ctx, struct msm_rpm_iv_pair *req, int count,
806 int *mask)
807{
808 unsigned long flags;
809 int i, j;
810 int rc = -1;
811 struct msm_rpmrs_resource *rs;
812
813 if (ctx != MSM_RPM_CTX_SET_0)
814 return -ENOSYS;
815
816 spin_lock_irqsave(&msm_rpmrs_lock, flags);
817 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
818 rs = msm_rpmrs_resources[i];
819 if (rs->rs[0].id == req[0].id && rs->size == count) {
820 for (j = 0; j < rs->size; j++) {
821 rs->rs[j].value &= ~mask[j];
822 rs->rs[j].value |= req[j].value & mask[j];
823 }
824 break;
825 }
826 }
827
828 if (i != ARRAY_SIZE(msm_rpmrs_resources)) {
829 rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &rs->rs[0], rs->size);
830 if (rc) {
831 for (j = 0; j < rs->size; j++) {
832 pr_err("%s: failed to request %d to %d: %d\n",
833 __func__,
834 rs->rs[j].id, rs->rs[j].value, rc);
835 }
836 }
837 }
838 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
839
840 return rc;
841
842}
843
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844int msm_rpmrs_clear(int ctx, struct msm_rpm_iv_pair *req, int count)
845{
846 return msm_rpmrs_clear_common(ctx, req, count, false);
847}
848
849int msm_rpmrs_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count)
850{
851 WARN(!irqs_disabled(), "msm_rpmrs_clear_noirq can only be called "
852 "safely when local irqs are disabled. Consider using "
853 "msm_rpmrs_clear or msm_rpmrs_clear_nosleep instead.");
854 return msm_rpmrs_clear_common(ctx, req, count, true);
855}
856
857void msm_rpmrs_show_resources(void)
858{
859 struct msm_rpmrs_resource *rs;
860 unsigned long flags;
861 int i;
862
863 spin_lock_irqsave(&msm_rpmrs_lock, flags);
864 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
865 rs = msm_rpmrs_resources[i];
866 if (rs->rs[0].id < MSM_RPM_ID_LAST + 1)
867 pr_info("%s: resource %s: buffered %d, value 0x%x\n",
868 __func__, rs->name,
869 test_bit(rs->rs[0].id, msm_rpmrs_buffered),
870 msm_rpmrs_buffer[rs->rs[0].id]);
871 else
872 pr_info("%s: resource %s: value %d\n",
873 __func__, rs->name, rs->rs[0].value);
874 }
875 spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
876}
877
878struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
879 bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
880 uint32_t sleep_us)
881{
882 unsigned int cpu = smp_processor_id();
883 struct msm_rpmrs_level *best_level = NULL;
884 bool irqs_detectable = false;
885 bool gpio_detectable = false;
886 int i;
887
888 if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
889 irqs_detectable = msm_mpm_irqs_detectable(from_idle);
890 gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
891 }
892
893 for (i = 0; i < msm_rpmrs_level_count; i++) {
894 struct msm_rpmrs_level *level = &msm_rpmrs_levels[i];
895 uint32_t power;
896
897 if (!level->available)
898 continue;
899
900 if (sleep_mode != level->sleep_mode)
901 continue;
902
903 if (latency_us < level->latency_us)
904 continue;
905
906 if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
907 irqs_detectable, gpio_detectable))
908 continue;
909
910 if (sleep_us <= 1) {
911 power = level->energy_overhead;
912 } else if (sleep_us <= level->time_overhead_us) {
913 power = level->energy_overhead / sleep_us;
914 } else if ((sleep_us >> 10) > level->time_overhead_us) {
915 power = level->steady_state_power;
916 } else {
Maheshkumar Sivasubramaniand2239ad2011-10-04 09:07:51 -0600917 power = level->steady_state_power;
918 power -= (level->time_overhead_us *
919 level->steady_state_power)/sleep_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700920 power += level->energy_overhead / sleep_us;
921 }
922
923 if (!best_level ||
924 best_level->rs_limits.power[cpu] >= power) {
925 level->rs_limits.latency_us[cpu] = level->latency_us;
926 level->rs_limits.power[cpu] = power;
927 best_level = level;
928 }
929 }
930
931 return best_level ? &best_level->rs_limits : NULL;
932}
933
934int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
935 bool from_idle, bool notify_rpm)
936{
937 int rc = 0;
938
939 rc = msm_rpmrs_flush_L2(limits, notify_rpm);
940 if (rc)
941 return rc;
942
943 if (notify_rpm) {
944 rc = msm_rpmrs_flush_buffer(sclk_count, limits, from_idle);
945 if (rc)
946 return rc;
947
948 if (msm_rpmrs_use_mpm(limits))
949 msm_mpm_enter_sleep(from_idle);
950 }
951
952 return rc;
953}
954
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600955void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
956 bool notify_rpm, bool collapsed)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957{
958
959 /* Disable L2 for now, we dont want L2 to do retention by default */
Maheshkumar Sivasubramaniandd93ecf2011-09-15 19:39:14 -0600960 msm_rpmrs_L2_restore(limits, notify_rpm, collapsed);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961
962 if (msm_rpmrs_use_mpm(limits))
963 msm_mpm_exit_sleep(from_idle);
964}
965
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700966static int rpmrs_cpu_callback(struct notifier_block *nfb,
967 unsigned long action, void *hcpu)
968{
969 switch (action) {
970 case CPU_ONLINE_FROZEN:
971 case CPU_ONLINE:
972 if (num_online_cpus() > 1)
973 msm_rpmrs_l2_cache.rs[0].value =
974 MSM_RPMRS_L2_CACHE_ACTIVE;
975 break;
976 case CPU_DEAD_FROZEN:
977 case CPU_DEAD:
978 if (num_online_cpus() == 1)
979 msm_rpmrs_l2_cache.rs[0].value =
980 MSM_RPMRS_L2_CACHE_HSFS_OPEN;
981 break;
982 }
983
984 msm_rpmrs_update_levels();
985 return NOTIFY_OK;
986}
987
988static struct notifier_block __refdata rpmrs_cpu_notifier = {
989 .notifier_call = rpmrs_cpu_callback,
990};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991
992int __init msm_rpmrs_levels_init(struct msm_rpmrs_level *levels, int size)
993{
994 msm_rpmrs_levels = kzalloc(sizeof(struct msm_rpmrs_level) * size,
995 GFP_KERNEL);
996 if (!msm_rpmrs_levels)
997 return -ENOMEM;
998 msm_rpmrs_level_count = size;
999 memcpy(msm_rpmrs_levels, levels, size * sizeof(struct msm_rpmrs_level));
1000
1001 return 0;
1002}
1003
1004static int __init msm_rpmrs_init(void)
1005{
1006 struct msm_rpm_iv_pair req;
1007 int rc;
1008
Stepan Moskovchenko0302fbc2011-08-05 18:06:13 -07001009 if (cpu_is_apq8064())
1010 return -ENODEV;
1011
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001012 BUG_ON(!msm_rpmrs_levels);
1013
Praveen Chidambaram841d46c2011-08-04 09:07:53 -06001014 if (cpu_is_msm8x60()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001015 req.id = MSM_RPMRS_ID_APPS_L2_CACHE_CTL;
1016 req.value = 1;
1017
1018 rc = msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
1019 if (rc) {
1020 pr_err("%s: failed to request L2 cache: %d\n",
1021 __func__, rc);
1022 goto init_exit;
1023 }
1024
1025 req.id = MSM_RPMRS_ID_APPS_L2_CACHE_CTL;
1026 req.value = 0;
1027
1028 rc = msm_rpmrs_set(MSM_RPM_CTX_SET_SLEEP, &req, 1);
1029 if (rc) {
1030 pr_err("%s: failed to initialize L2 cache for sleep: "
1031 "%d\n", __func__, rc);
1032 goto init_exit;
1033 }
1034 }
1035
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036 rc = msm_rpmrs_resource_sysfs_add();
1037
1038init_exit:
1039 return rc;
1040}
1041device_initcall(msm_rpmrs_init);
1042
1043static int __init msm_rpmrs_early_init(void)
1044{
1045 int i, k;
1046
1047 /* Initialize listed bitmap for valid resource IDs */
1048 for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) {
1049 for (k = 0; k < msm_rpmrs_resources[i]->size; k++)
1050 set_bit(msm_rpmrs_resources[i]->rs[k].id,
1051 msm_rpmrs_listed);
1052 }
1053
1054 return 0;
1055}
1056early_initcall(msm_rpmrs_early_init);
1057
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001058static int __init msm_rpmrs_l2_counter_init(void)
1059{
Maheshkumar Sivasubramanian07c363f2011-10-18 09:52:33 -06001060 if (cpu_is_msm8960()) {
1061 msm_rpmrs_l2_counter_addr = MSM_IMEM_BASE + L2_PC_COUNTER_ADDR;
1062 writel_relaxed(msm_rpmrs_l2_reset_count,
1063 msm_rpmrs_l2_counter_addr);
1064 mb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001065
Maheshkumar Sivasubramanian07c363f2011-10-18 09:52:33 -06001066 msm_rpmrs_l2_cache.beyond_limits =
1067 msm_spm_l2_cache_beyond_limits;
1068 msm_rpmrs_l2_cache.aggregate = NULL;
1069 msm_rpmrs_l2_cache.restore = NULL;
1070 register_hotcpu_notifier(&rpmrs_cpu_notifier);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001071
Maheshkumar Sivasubramanian07c363f2011-10-18 09:52:33 -06001072 } else if (cpu_is_msm9615()) {
1073 msm_rpmrs_l2_cache.beyond_limits = NULL;
1074 msm_rpmrs_l2_cache.aggregate = NULL;
1075 msm_rpmrs_l2_cache.restore = NULL;
1076 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001077 return 0;
1078}
1079early_initcall(msm_rpmrs_l2_counter_init);