blob: 2eac3c1114b4966d959786b5b5b0d7d32f9be3a4 [file] [log] [blame]
Praveen Chidambaram78499012011-11-01 17:15:17 -06001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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/types.h>
17#include <linux/bug.h>
18#include <linux/completion.h>
19#include <linux/delay.h>
20#include <linux/init.h>
21#include <linux/interrupt.h>
22#include <linux/io.h>
23#include <linux/irq.h>
24#include <linux/list.h>
25#include <linux/mutex.h>
26#include <linux/semaphore.h>
27#include <linux/spinlock.h>
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -060028#include <linux/device.h>
29#include <linux/platform_device.h>
Praveen Chidambaram78499012011-11-01 17:15:17 -060030#include <linux/slab.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031#include <asm/hardware/gic.h>
32#include <mach/msm_iomap.h>
33#include <mach/rpm.h>
34
35/******************************************************************************
36 * Data type and structure definitions
37 *****************************************************************************/
38
39struct msm_rpm_request {
40 struct msm_rpm_iv_pair *req;
41 int count;
42 uint32_t *ctx_mask_ack;
43 uint32_t *sel_masks_ack;
44 struct completion *done;
45};
46
47struct msm_rpm_notif_config {
Praveen Chidambaram78499012011-11-01 17:15:17 -060048 struct msm_rpm_iv_pair iv[SEL_MASK_SIZE * 2];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049};
50
51#define configured_iv(notif_cfg) ((notif_cfg)->iv)
Praveen Chidambaram78499012011-11-01 17:15:17 -060052#define registered_iv(notif_cfg) ((notif_cfg)->iv + msm_rpm_sel_mask_size)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053
Praveen Chidambaram78499012011-11-01 17:15:17 -060054static uint32_t msm_rpm_sel_mask_size;
55static struct msm_rpm_platform_data msm_rpm_data;
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057
58static DEFINE_MUTEX(msm_rpm_mutex);
59static DEFINE_SPINLOCK(msm_rpm_lock);
60static DEFINE_SPINLOCK(msm_rpm_irq_lock);
61
62static struct msm_rpm_request *msm_rpm_request;
63static struct msm_rpm_request msm_rpm_request_irq_mode;
64static struct msm_rpm_request msm_rpm_request_poll_mode;
65
66static LIST_HEAD(msm_rpm_notifications);
67static struct msm_rpm_notif_config msm_rpm_notif_cfgs[MSM_RPM_CTX_SET_COUNT];
68static bool msm_rpm_init_notif_done;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069/******************************************************************************
70 * Internal functions
71 *****************************************************************************/
72
Praveen Chidambaram78499012011-11-01 17:15:17 -060073static inline unsigned int target_enum(unsigned int id)
74{
75 BUG_ON(id >= MSM_RPM_ID_LAST);
76 return msm_rpm_data.target_id[id].id;
77}
78
79static inline unsigned int target_status(unsigned int id)
80{
81 BUG_ON(id >= MSM_RPM_STATUS_ID_LAST);
82 return msm_rpm_data.target_status[id];
83}
84
85static inline unsigned int target_ctrl(unsigned int id)
86{
87 BUG_ON(id >= MSM_RPM_CTRL_LAST);
88 return msm_rpm_data.target_ctrl_id[id];
89}
90
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091static inline uint32_t msm_rpm_read(unsigned int page, unsigned int reg)
92{
Praveen Chidambaram78499012011-11-01 17:15:17 -060093 return __raw_readl(msm_rpm_data.reg_base_addrs[page] + reg * 4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094}
95
96static inline void msm_rpm_write(
97 unsigned int page, unsigned int reg, uint32_t value)
98{
Praveen Chidambaram78499012011-11-01 17:15:17 -060099 __raw_writel(value,
100 msm_rpm_data.reg_base_addrs[page] + reg * 4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101}
102
103static inline void msm_rpm_read_contiguous(
104 unsigned int page, unsigned int reg, uint32_t *values, int count)
105{
106 int i;
107
108 for (i = 0; i < count; i++)
109 values[i] = msm_rpm_read(page, reg + i);
110}
111
112static inline void msm_rpm_write_contiguous(
113 unsigned int page, unsigned int reg, uint32_t *values, int count)
114{
115 int i;
116
117 for (i = 0; i < count; i++)
118 msm_rpm_write(page, reg + i, values[i]);
119}
120
121static inline void msm_rpm_write_contiguous_zeros(
122 unsigned int page, unsigned int reg, int count)
123{
124 int i;
125
126 for (i = 0; i < count; i++)
127 msm_rpm_write(page, reg + i, 0);
128}
129
130static inline uint32_t msm_rpm_map_id_to_sel(uint32_t id)
131{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600132 return (id >= MSM_RPM_ID_LAST) ? msm_rpm_data.sel_last + 1 :
133 msm_rpm_data.target_id[id].sel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134}
135
136/*
137 * Note: the function does not clear the masks before filling them.
138 *
139 * Return value:
140 * 0: success
141 * -EINVAL: invalid id in <req> array
142 */
143static int msm_rpm_fill_sel_masks(
144 uint32_t *sel_masks, struct msm_rpm_iv_pair *req, int count)
145{
146 uint32_t sel;
147 int i;
148
149 for (i = 0; i < count; i++) {
150 sel = msm_rpm_map_id_to_sel(req[i].id);
151
Praveen Chidambaram78499012011-11-01 17:15:17 -0600152 if (sel > msm_rpm_data.sel_last) {
153 pr_err("%s(): RPM ID %d not defined for target\n",
154 __func__, req[i].id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155 return -EINVAL;
Praveen Chidambaram78499012011-11-01 17:15:17 -0600156 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157
158 sel_masks[msm_rpm_get_sel_mask_reg(sel)] |=
159 msm_rpm_get_sel_mask(sel);
160 }
161
162 return 0;
163}
164
165static inline void msm_rpm_send_req_interrupt(void)
166{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600167 __raw_writel(msm_rpm_data.ipc_rpm_val,
168 msm_rpm_data.ipc_rpm_reg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169}
170
171/*
172 * Note: assumes caller has acquired <msm_rpm_irq_lock>.
173 *
174 * Return value:
175 * 0: request acknowledgement
176 * 1: notification
177 * 2: spurious interrupt
178 */
179static int msm_rpm_process_ack_interrupt(void)
180{
181 uint32_t ctx_mask_ack;
Praveen Chidambaram78499012011-11-01 17:15:17 -0600182 uint32_t sel_masks_ack[SEL_MASK_SIZE] = {0};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183
Praveen Chidambaram78499012011-11-01 17:15:17 -0600184 ctx_mask_ack = msm_rpm_read(MSM_RPM_PAGE_CTRL,
185 target_ctrl(MSM_RPM_CTRL_ACK_CTX_0));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 msm_rpm_read_contiguous(MSM_RPM_PAGE_CTRL,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600187 target_ctrl(MSM_RPM_CTRL_ACK_SEL_0),
188 sel_masks_ack, msm_rpm_sel_mask_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189
190 if (ctx_mask_ack & msm_rpm_get_ctx_mask(MSM_RPM_CTX_NOTIFICATION)) {
191 struct msm_rpm_notification *n;
192 int i;
193
194 list_for_each_entry(n, &msm_rpm_notifications, list)
Praveen Chidambaram78499012011-11-01 17:15:17 -0600195 for (i = 0; i < msm_rpm_sel_mask_size; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 if (sel_masks_ack[i] & n->sel_masks[i]) {
197 up(&n->sem);
198 break;
199 }
200
201 msm_rpm_write_contiguous_zeros(MSM_RPM_PAGE_CTRL,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600202 target_ctrl(MSM_RPM_CTRL_ACK_SEL_0),
203 msm_rpm_sel_mask_size);
204 msm_rpm_write(MSM_RPM_PAGE_CTRL,
205 target_ctrl(MSM_RPM_CTRL_ACK_CTX_0), 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 /* Ensure the write is complete before return */
207 mb();
208
209 return 1;
210 }
211
212 if (msm_rpm_request) {
213 int i;
214
215 *(msm_rpm_request->ctx_mask_ack) = ctx_mask_ack;
216 memcpy(msm_rpm_request->sel_masks_ack, sel_masks_ack,
217 sizeof(sel_masks_ack));
218
219 for (i = 0; i < msm_rpm_request->count; i++)
220 msm_rpm_request->req[i].value =
221 msm_rpm_read(MSM_RPM_PAGE_ACK,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600222 target_enum(msm_rpm_request->req[i].id));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223
224 msm_rpm_write_contiguous_zeros(MSM_RPM_PAGE_CTRL,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600225 target_ctrl(MSM_RPM_CTRL_ACK_SEL_0),
226 msm_rpm_sel_mask_size);
227 msm_rpm_write(MSM_RPM_PAGE_CTRL,
228 target_ctrl(MSM_RPM_CTRL_ACK_CTX_0), 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229 /* Ensure the write is complete before return */
230 mb();
231
232 if (msm_rpm_request->done)
233 complete_all(msm_rpm_request->done);
234
235 msm_rpm_request = NULL;
236 return 0;
237 }
238
239 return 2;
240}
241
Stephen Boydf61255e2012-02-24 14:31:09 -0800242static void msm_rpm_err_fatal(void)
243{
244 /* Tell RPM that we're handling the interrupt */
245 __raw_writel(0x1, msm_rpm_data.ipc_rpm_reg);
246 panic("RPM error fataled");
247}
248
249static irqreturn_t msm_rpm_err_interrupt(int irq, void *dev_id)
250{
251 msm_rpm_err_fatal();
252 return IRQ_HANDLED;
253}
254
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255static irqreturn_t msm_rpm_ack_interrupt(int irq, void *dev_id)
256{
257 unsigned long flags;
258 int rc;
259
260 if (dev_id != &msm_rpm_ack_interrupt)
261 return IRQ_NONE;
262
263 spin_lock_irqsave(&msm_rpm_irq_lock, flags);
264 rc = msm_rpm_process_ack_interrupt();
265 spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
266
267 return IRQ_HANDLED;
268}
269
270/*
271 * Note: assumes caller has acquired <msm_rpm_irq_lock>.
272 */
273static void msm_rpm_busy_wait_for_request_completion(
274 bool allow_async_completion)
275{
276 int rc;
277
278 do {
Praveen Chidambaram78499012011-11-01 17:15:17 -0600279 while (!gic_is_spi_pending(msm_rpm_data.irq_ack) &&
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 msm_rpm_request) {
281 if (allow_async_completion)
282 spin_unlock(&msm_rpm_irq_lock);
Stephen Boydf61255e2012-02-24 14:31:09 -0800283 if (gic_is_spi_pending(msm_rpm_data.irq_err))
284 msm_rpm_err_fatal();
285 gic_clear_spi_pending(msm_rpm_data.irq_err);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286 udelay(1);
287 if (allow_async_completion)
288 spin_lock(&msm_rpm_irq_lock);
289 }
290
291 if (!msm_rpm_request)
292 break;
293
294 rc = msm_rpm_process_ack_interrupt();
Praveen Chidambaram78499012011-11-01 17:15:17 -0600295 gic_clear_spi_pending(msm_rpm_data.irq_ack);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 } while (rc);
297}
298
299/* Upon return, the <req> array will contain values from the ack page.
300 *
301 * Note: assumes caller has acquired <msm_rpm_mutex>.
302 *
303 * Return value:
304 * 0: success
305 * -ENOSPC: request rejected
306 */
307static int msm_rpm_set_exclusive(int ctx,
308 uint32_t *sel_masks, struct msm_rpm_iv_pair *req, int count)
309{
310 DECLARE_COMPLETION_ONSTACK(ack);
311 unsigned long flags;
312 uint32_t ctx_mask = msm_rpm_get_ctx_mask(ctx);
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600313 uint32_t ctx_mask_ack = 0;
Praveen Chidambaram78499012011-11-01 17:15:17 -0600314 uint32_t sel_masks_ack[SEL_MASK_SIZE];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 int i;
316
317 msm_rpm_request_irq_mode.req = req;
318 msm_rpm_request_irq_mode.count = count;
319 msm_rpm_request_irq_mode.ctx_mask_ack = &ctx_mask_ack;
320 msm_rpm_request_irq_mode.sel_masks_ack = sel_masks_ack;
321 msm_rpm_request_irq_mode.done = &ack;
322
323 spin_lock_irqsave(&msm_rpm_lock, flags);
324 spin_lock(&msm_rpm_irq_lock);
325
326 BUG_ON(msm_rpm_request);
327 msm_rpm_request = &msm_rpm_request_irq_mode;
328
329 for (i = 0; i < count; i++) {
Praveen Chidambaram78499012011-11-01 17:15:17 -0600330 BUG_ON(target_enum(req[i].id) >= MSM_RPM_ID_LAST);
331 msm_rpm_write(MSM_RPM_PAGE_REQ,
332 target_enum(req[i].id), req[i].value);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333 }
334
335 msm_rpm_write_contiguous(MSM_RPM_PAGE_CTRL,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600336 target_ctrl(MSM_RPM_CTRL_REQ_SEL_0),
337 sel_masks, msm_rpm_sel_mask_size);
338 msm_rpm_write(MSM_RPM_PAGE_CTRL,
339 target_ctrl(MSM_RPM_CTRL_REQ_CTX_0), ctx_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340
341 /* Ensure RPM data is written before sending the interrupt */
342 mb();
343 msm_rpm_send_req_interrupt();
344
345 spin_unlock(&msm_rpm_irq_lock);
346 spin_unlock_irqrestore(&msm_rpm_lock, flags);
347
348 wait_for_completion(&ack);
349
350 BUG_ON((ctx_mask_ack & ~(msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED)))
351 != ctx_mask);
352 BUG_ON(memcmp(sel_masks, sel_masks_ack, sizeof(sel_masks_ack)));
353
354 return (ctx_mask_ack & msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED))
355 ? -ENOSPC : 0;
356}
357
358/* Upon return, the <req> array will contain values from the ack page.
359 *
360 * Note: assumes caller has acquired <msm_rpm_lock>.
361 *
362 * Return value:
363 * 0: success
364 * -ENOSPC: request rejected
365 */
366static int msm_rpm_set_exclusive_noirq(int ctx,
367 uint32_t *sel_masks, struct msm_rpm_iv_pair *req, int count)
368{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600369 unsigned int irq = msm_rpm_data.irq_ack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 unsigned long flags;
371 uint32_t ctx_mask = msm_rpm_get_ctx_mask(ctx);
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600372 uint32_t ctx_mask_ack = 0;
Praveen Chidambaram78499012011-11-01 17:15:17 -0600373 uint32_t sel_masks_ack[SEL_MASK_SIZE];
Stephen Boydf61255e2012-02-24 14:31:09 -0800374 struct irq_chip *irq_chip, *err_chip;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375 int i;
376
377 msm_rpm_request_poll_mode.req = req;
378 msm_rpm_request_poll_mode.count = count;
379 msm_rpm_request_poll_mode.ctx_mask_ack = &ctx_mask_ack;
380 msm_rpm_request_poll_mode.sel_masks_ack = sel_masks_ack;
381 msm_rpm_request_poll_mode.done = NULL;
382
383 spin_lock_irqsave(&msm_rpm_irq_lock, flags);
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600384 irq_chip = irq_get_chip(irq);
385 if (!irq_chip) {
386 spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
387 return -ENOSPC;
388 }
389 irq_chip->irq_mask(irq_get_irq_data(irq));
Stephen Boydf61255e2012-02-24 14:31:09 -0800390 err_chip = irq_get_chip(msm_rpm_data.irq_err);
391 if (!err_chip) {
392 irq_chip->irq_unmask(irq_get_irq_data(irq));
393 spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
394 return -ENOSPC;
395 }
396 err_chip->irq_mask(irq_get_irq_data(msm_rpm_data.irq_err));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397
398 if (msm_rpm_request) {
399 msm_rpm_busy_wait_for_request_completion(true);
400 BUG_ON(msm_rpm_request);
401 }
402
403 msm_rpm_request = &msm_rpm_request_poll_mode;
404
405 for (i = 0; i < count; i++) {
Praveen Chidambaram78499012011-11-01 17:15:17 -0600406 BUG_ON(target_enum(req[i].id) >= MSM_RPM_ID_LAST);
407 msm_rpm_write(MSM_RPM_PAGE_REQ,
408 target_enum(req[i].id), req[i].value);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 }
410
411 msm_rpm_write_contiguous(MSM_RPM_PAGE_CTRL,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600412 target_ctrl(MSM_RPM_CTRL_REQ_SEL_0),
413 sel_masks, msm_rpm_sel_mask_size);
414 msm_rpm_write(MSM_RPM_PAGE_CTRL,
415 target_ctrl(MSM_RPM_CTRL_REQ_CTX_0), ctx_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416
417 /* Ensure RPM data is written before sending the interrupt */
418 mb();
419 msm_rpm_send_req_interrupt();
420
421 msm_rpm_busy_wait_for_request_completion(false);
422 BUG_ON(msm_rpm_request);
423
Stephen Boydf61255e2012-02-24 14:31:09 -0800424 err_chip->irq_unmask(irq_get_irq_data(msm_rpm_data.irq_err));
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600425 irq_chip->irq_unmask(irq_get_irq_data(irq));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426 spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
427
428 BUG_ON((ctx_mask_ack & ~(msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED)))
429 != ctx_mask);
430 BUG_ON(memcmp(sel_masks, sel_masks_ack, sizeof(sel_masks_ack)));
431
432 return (ctx_mask_ack & msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED))
433 ? -ENOSPC : 0;
434}
435
436/* Upon return, the <req> array will contain values from the ack page.
437 *
438 * Return value:
439 * 0: success
440 * -EINTR: interrupted
441 * -EINVAL: invalid <ctx> or invalid id in <req> array
442 * -ENOSPC: request rejected
Mahesh Sivasubramanian0e82fb22011-12-12 12:21:03 -0700443 * -ENODEV: RPM driver not initialized
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700444 */
445static int msm_rpm_set_common(
446 int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq)
447{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600448 uint32_t sel_masks[SEL_MASK_SIZE] = {};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 int rc;
450
451 if (ctx >= MSM_RPM_CTX_SET_COUNT) {
452 rc = -EINVAL;
453 goto set_common_exit;
454 }
455
456 rc = msm_rpm_fill_sel_masks(sel_masks, req, count);
457 if (rc)
458 goto set_common_exit;
459
460 if (noirq) {
461 unsigned long flags;
462
463 spin_lock_irqsave(&msm_rpm_lock, flags);
464 rc = msm_rpm_set_exclusive_noirq(ctx, sel_masks, req, count);
465 spin_unlock_irqrestore(&msm_rpm_lock, flags);
466 } else {
467 rc = mutex_lock_interruptible(&msm_rpm_mutex);
468 if (rc)
469 goto set_common_exit;
470
471 rc = msm_rpm_set_exclusive(ctx, sel_masks, req, count);
472 mutex_unlock(&msm_rpm_mutex);
473 }
474
475set_common_exit:
476 return rc;
477}
478
479/*
480 * Return value:
481 * 0: success
482 * -EINTR: interrupted
483 * -EINVAL: invalid <ctx> or invalid id in <req> array
Mahesh Sivasubramanian0e82fb22011-12-12 12:21:03 -0700484 * -ENODEV: RPM driver not initialized.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700485 */
486static int msm_rpm_clear_common(
487 int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq)
488{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600489 uint32_t sel_masks[SEL_MASK_SIZE] = {};
490 struct msm_rpm_iv_pair r[SEL_MASK_SIZE];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491 int rc;
492 int i;
493
494 if (ctx >= MSM_RPM_CTX_SET_COUNT) {
495 rc = -EINVAL;
496 goto clear_common_exit;
497 }
498
499 rc = msm_rpm_fill_sel_masks(sel_masks, req, count);
500 if (rc)
501 goto clear_common_exit;
502
503 for (i = 0; i < ARRAY_SIZE(r); i++) {
504 r[i].id = MSM_RPM_ID_INVALIDATE_0 + i;
505 r[i].value = sel_masks[i];
506 }
507
508 memset(sel_masks, 0, sizeof(sel_masks));
Praveen Chidambaram78499012011-11-01 17:15:17 -0600509 sel_masks[msm_rpm_get_sel_mask_reg(msm_rpm_data.sel_invalidate)] |=
510 msm_rpm_get_sel_mask(msm_rpm_data.sel_invalidate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511
512 if (noirq) {
513 unsigned long flags;
514
515 spin_lock_irqsave(&msm_rpm_lock, flags);
516 rc = msm_rpm_set_exclusive_noirq(ctx, sel_masks, r,
517 ARRAY_SIZE(r));
518 spin_unlock_irqrestore(&msm_rpm_lock, flags);
519 BUG_ON(rc);
520 } else {
521 rc = mutex_lock_interruptible(&msm_rpm_mutex);
522 if (rc)
523 goto clear_common_exit;
524
525 rc = msm_rpm_set_exclusive(ctx, sel_masks, r, ARRAY_SIZE(r));
526 mutex_unlock(&msm_rpm_mutex);
527 BUG_ON(rc);
528 }
529
530clear_common_exit:
531 return rc;
532}
533
534/*
535 * Note: assumes caller has acquired <msm_rpm_mutex>.
536 */
537static void msm_rpm_update_notification(uint32_t ctx,
538 struct msm_rpm_notif_config *curr_cfg,
539 struct msm_rpm_notif_config *new_cfg)
540{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600541 unsigned int sel_notif = msm_rpm_data.sel_notification;
542
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543 if (memcmp(curr_cfg, new_cfg, sizeof(*new_cfg))) {
Praveen Chidambaram78499012011-11-01 17:15:17 -0600544 uint32_t sel_masks[SEL_MASK_SIZE] = {};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545 int rc;
546
Praveen Chidambaram78499012011-11-01 17:15:17 -0600547 sel_masks[msm_rpm_get_sel_mask_reg(sel_notif)]
548 |= msm_rpm_get_sel_mask(sel_notif);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549
550 rc = msm_rpm_set_exclusive(ctx,
551 sel_masks, new_cfg->iv, ARRAY_SIZE(new_cfg->iv));
552 BUG_ON(rc);
553
554 memcpy(curr_cfg, new_cfg, sizeof(*new_cfg));
555 }
556}
557
558/*
559 * Note: assumes caller has acquired <msm_rpm_mutex>.
560 */
561static void msm_rpm_initialize_notification(void)
562{
563 struct msm_rpm_notif_config cfg;
564 unsigned int ctx;
565 int i;
566
567 for (ctx = MSM_RPM_CTX_SET_0; ctx <= MSM_RPM_CTX_SET_SLEEP; ctx++) {
568 cfg = msm_rpm_notif_cfgs[ctx];
569
Praveen Chidambaram78499012011-11-01 17:15:17 -0600570 for (i = 0; i < msm_rpm_sel_mask_size; i++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 configured_iv(&cfg)[i].id =
572 MSM_RPM_ID_NOTIFICATION_CONFIGURED_0 + i;
573 configured_iv(&cfg)[i].value = ~0UL;
574
575 registered_iv(&cfg)[i].id =
576 MSM_RPM_ID_NOTIFICATION_REGISTERED_0 + i;
577 registered_iv(&cfg)[i].value = 0;
578 }
579
580 msm_rpm_update_notification(ctx,
581 &msm_rpm_notif_cfgs[ctx], &cfg);
582 }
583}
584
585/******************************************************************************
586 * Public functions
587 *****************************************************************************/
588
589int msm_rpm_local_request_is_outstanding(void)
590{
591 unsigned long flags;
592 int outstanding = 0;
593
594 if (!spin_trylock_irqsave(&msm_rpm_lock, flags))
595 goto local_request_is_outstanding_exit;
596
597 if (!spin_trylock(&msm_rpm_irq_lock))
598 goto local_request_is_outstanding_unlock;
599
600 outstanding = (msm_rpm_request != NULL);
601 spin_unlock(&msm_rpm_irq_lock);
602
603local_request_is_outstanding_unlock:
604 spin_unlock_irqrestore(&msm_rpm_lock, flags);
605
606local_request_is_outstanding_exit:
607 return outstanding;
608}
609
610/*
611 * Read the specified status registers and return their values.
612 *
613 * status: array of id-value pairs. Each <id> specifies a status register,
614 * i.e, one of MSM_RPM_STATUS_ID_xxxx. Upon return, each <value> will
615 * contain the value of the status register.
616 * count: number of id-value pairs in the array
617 *
618 * Return value:
619 * 0: success
620 * -EBUSY: RPM is updating the status page; values across different registers
621 * may not be consistent
622 * -EINVAL: invalid id in <status> array
Mahesh Sivasubramanian0e82fb22011-12-12 12:21:03 -0700623 * -ENODEV: RPM driver not initialized
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624 */
625int msm_rpm_get_status(struct msm_rpm_iv_pair *status, int count)
626{
627 uint32_t seq_begin;
628 uint32_t seq_end;
629 int rc;
630 int i;
631
632 seq_begin = msm_rpm_read(MSM_RPM_PAGE_STATUS,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600633 target_status(MSM_RPM_STATUS_ID_SEQUENCE));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634
635 for (i = 0; i < count; i++) {
Praveen Chidambaram78499012011-11-01 17:15:17 -0600636 int target_status_id;
637
638 if (status[i].id >= MSM_RPM_STATUS_ID_LAST) {
639 pr_err("%s(): Status ID beyond limits\n", __func__);
640 rc = -EINVAL;
641 goto get_status_exit;
642 }
643
644 target_status_id = target_status(status[i].id);
645 if (target_status_id >= MSM_RPM_STATUS_ID_LAST) {
646 pr_err("%s(): Status id %d not defined for target\n",
647 __func__,
648 target_status_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 rc = -EINVAL;
650 goto get_status_exit;
651 }
652
653 status[i].value = msm_rpm_read(MSM_RPM_PAGE_STATUS,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600654 target_status_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655 }
656
657 seq_end = msm_rpm_read(MSM_RPM_PAGE_STATUS,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600658 target_status(MSM_RPM_STATUS_ID_SEQUENCE));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700659
660 rc = (seq_begin != seq_end || (seq_begin & 0x01)) ? -EBUSY : 0;
661
662get_status_exit:
663 return rc;
664}
665EXPORT_SYMBOL(msm_rpm_get_status);
666
667/*
668 * Issue a resource request to RPM to set resource values.
669 *
670 * Note: the function may sleep and must be called in a task context.
671 *
672 * ctx: the request's context.
673 * There two contexts that a RPM driver client can use:
674 * MSM_RPM_CTX_SET_0 and MSM_RPM_CTX_SET_SLEEP. For resource values
675 * that are intended to take effect when the CPU is active,
676 * MSM_RPM_CTX_SET_0 should be used. For resource values that are
677 * intended to take effect when the CPU is not active,
678 * MSM_RPM_CTX_SET_SLEEP should be used.
679 * req: array of id-value pairs. Each <id> specifies a RPM resource,
680 * i.e, one of MSM_RPM_ID_xxxx. Each <value> specifies the requested
681 * resource value.
682 * count: number of id-value pairs in the array
683 *
684 * Return value:
685 * 0: success
686 * -EINTR: interrupted
687 * -EINVAL: invalid <ctx> or invalid id in <req> array
688 * -ENOSPC: request rejected
Mahesh Sivasubramanian0e82fb22011-12-12 12:21:03 -0700689 * -ENODEV: RPM driver not initialized
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690 */
691int msm_rpm_set(int ctx, struct msm_rpm_iv_pair *req, int count)
692{
693 return msm_rpm_set_common(ctx, req, count, false);
694}
695EXPORT_SYMBOL(msm_rpm_set);
696
697/*
698 * Issue a resource request to RPM to set resource values.
699 *
700 * Note: the function is similar to msm_rpm_set() except that it must be
701 * called with interrupts masked. If possible, use msm_rpm_set()
702 * instead, to maximize CPU throughput.
703 */
704int msm_rpm_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count)
705{
706 WARN(!irqs_disabled(), "msm_rpm_set_noirq can only be called "
707 "safely when local irqs are disabled. Consider using "
708 "msm_rpm_set or msm_rpm_set_nosleep instead.");
709 return msm_rpm_set_common(ctx, req, count, true);
710}
711EXPORT_SYMBOL(msm_rpm_set_noirq);
712
713/*
714 * Issue a resource request to RPM to clear resource values. Once the
715 * values are cleared, the resources revert back to their default values
716 * for this RPM master.
717 *
718 * Note: the function may sleep and must be called in a task context.
719 *
720 * ctx: the request's context.
721 * req: array of id-value pairs. Each <id> specifies a RPM resource,
722 * i.e, one of MSM_RPM_ID_xxxx. <value>'s are ignored.
723 * count: number of id-value pairs in the array
724 *
725 * Return value:
726 * 0: success
727 * -EINTR: interrupted
728 * -EINVAL: invalid <ctx> or invalid id in <req> array
729 */
730int msm_rpm_clear(int ctx, struct msm_rpm_iv_pair *req, int count)
731{
732 return msm_rpm_clear_common(ctx, req, count, false);
733}
734EXPORT_SYMBOL(msm_rpm_clear);
735
736/*
737 * Issue a resource request to RPM to clear resource values.
738 *
739 * Note: the function is similar to msm_rpm_clear() except that it must be
740 * called with interrupts masked. If possible, use msm_rpm_clear()
741 * instead, to maximize CPU throughput.
742 */
743int msm_rpm_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count)
744{
745 WARN(!irqs_disabled(), "msm_rpm_clear_noirq can only be called "
746 "safely when local irqs are disabled. Consider using "
747 "msm_rpm_clear or msm_rpm_clear_nosleep instead.");
748 return msm_rpm_clear_common(ctx, req, count, true);
749}
750EXPORT_SYMBOL(msm_rpm_clear_noirq);
751
752/*
753 * Register for RPM notification. When the specified resources
754 * change their status on RPM, RPM sends out notifications and the
755 * driver will "up" the semaphore in struct msm_rpm_notification.
756 *
757 * Note: the function may sleep and must be called in a task context.
758 *
759 * Memory for <n> must not be freed until the notification is
760 * unregistered. Memory for <req> can be freed after this
761 * function returns.
762 *
763 * n: the notifcation object. Caller should initialize only the
764 * semaphore field. When a notification arrives later, the
765 * semaphore will be "up"ed.
766 * req: array of id-value pairs. Each <id> specifies a status register,
767 * i.e, one of MSM_RPM_STATUS_ID_xxxx. <value>'s are ignored.
768 * count: number of id-value pairs in the array
769 *
770 * Return value:
771 * 0: success
772 * -EINTR: interrupted
773 * -EINVAL: invalid id in <req> array
Mahesh Sivasubramanian0e82fb22011-12-12 12:21:03 -0700774 * -ENODEV: RPM driver not initialized
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700775 */
776int msm_rpm_register_notification(struct msm_rpm_notification *n,
777 struct msm_rpm_iv_pair *req, int count)
778{
779 unsigned long flags;
780 unsigned int ctx;
781 struct msm_rpm_notif_config cfg;
782 int rc;
783 int i;
784
785 INIT_LIST_HEAD(&n->list);
786 rc = msm_rpm_fill_sel_masks(n->sel_masks, req, count);
787 if (rc)
788 goto register_notification_exit;
789
790 rc = mutex_lock_interruptible(&msm_rpm_mutex);
791 if (rc)
792 goto register_notification_exit;
793
794 if (!msm_rpm_init_notif_done) {
795 msm_rpm_initialize_notification();
796 msm_rpm_init_notif_done = true;
797 }
798
799 spin_lock_irqsave(&msm_rpm_irq_lock, flags);
800 list_add(&n->list, &msm_rpm_notifications);
801 spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
802
803 ctx = MSM_RPM_CTX_SET_0;
804 cfg = msm_rpm_notif_cfgs[ctx];
805
Praveen Chidambaram78499012011-11-01 17:15:17 -0600806 for (i = 0; i < msm_rpm_sel_mask_size; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700807 registered_iv(&cfg)[i].value |= n->sel_masks[i];
808
809 msm_rpm_update_notification(ctx, &msm_rpm_notif_cfgs[ctx], &cfg);
810 mutex_unlock(&msm_rpm_mutex);
811
812register_notification_exit:
813 return rc;
814}
815EXPORT_SYMBOL(msm_rpm_register_notification);
816
817/*
818 * Unregister a notification.
819 *
820 * Note: the function may sleep and must be called in a task context.
821 *
822 * n: the notifcation object that was registered previously.
823 *
824 * Return value:
825 * 0: success
826 * -EINTR: interrupted
Mahesh Sivasubramanian0e82fb22011-12-12 12:21:03 -0700827 * -ENODEV: RPM driver not initialized
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700828 */
829int msm_rpm_unregister_notification(struct msm_rpm_notification *n)
830{
831 unsigned long flags;
832 unsigned int ctx;
833 struct msm_rpm_notif_config cfg;
834 int rc;
835 int i;
836
837 rc = mutex_lock_interruptible(&msm_rpm_mutex);
838 if (rc)
839 goto unregister_notification_exit;
840
841 ctx = MSM_RPM_CTX_SET_0;
842 cfg = msm_rpm_notif_cfgs[ctx];
843
Praveen Chidambaram78499012011-11-01 17:15:17 -0600844 for (i = 0; i < msm_rpm_sel_mask_size; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845 registered_iv(&cfg)[i].value = 0;
846
847 spin_lock_irqsave(&msm_rpm_irq_lock, flags);
848 list_del(&n->list);
849 list_for_each_entry(n, &msm_rpm_notifications, list)
Praveen Chidambaram78499012011-11-01 17:15:17 -0600850 for (i = 0; i < msm_rpm_sel_mask_size; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851 registered_iv(&cfg)[i].value |= n->sel_masks[i];
852 spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
853
854 msm_rpm_update_notification(ctx, &msm_rpm_notif_cfgs[ctx], &cfg);
855 mutex_unlock(&msm_rpm_mutex);
856
857unregister_notification_exit:
858 return rc;
859}
860EXPORT_SYMBOL(msm_rpm_unregister_notification);
861
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600862static uint32_t fw_major, fw_minor, fw_build;
863
864static ssize_t driver_version_show(struct kobject *kobj,
865 struct kobj_attribute *attr, char *buf)
866{
867 return snprintf(buf, PAGE_SIZE, "%u.%u.%u\n",
Praveen Chidambaram78499012011-11-01 17:15:17 -0600868 msm_rpm_data.ver[0], msm_rpm_data.ver[1], msm_rpm_data.ver[2]);
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600869}
870
871static ssize_t fw_version_show(struct kobject *kobj,
872 struct kobj_attribute *attr, char *buf)
873{
874 return snprintf(buf, PAGE_SIZE, "%u.%u.%u\n",
875 fw_major, fw_minor, fw_build);
876}
877
878static struct kobj_attribute driver_version_attr = __ATTR_RO(driver_version);
879static struct kobj_attribute fw_version_attr = __ATTR_RO(fw_version);
880
881static struct attribute *driver_attributes[] = {
882 &driver_version_attr.attr,
883 &fw_version_attr.attr,
884 NULL
885};
886
887static struct attribute_group driver_attr_group = {
888 .attrs = driver_attributes,
889};
890
891static int __devinit msm_rpm_probe(struct platform_device *pdev)
892{
893 return sysfs_create_group(&pdev->dev.kobj, &driver_attr_group);
894}
895
896static int __devexit msm_rpm_remove(struct platform_device *pdev)
897{
898 sysfs_remove_group(&pdev->dev.kobj, &driver_attr_group);
899 return 0;
900}
901
902static struct platform_driver msm_rpm_platform_driver = {
903 .probe = msm_rpm_probe,
904 .remove = __devexit_p(msm_rpm_remove),
905 .driver = {
906 .name = "msm_rpm",
907 .owner = THIS_MODULE,
908 },
909};
910
Praveen Chidambaram78499012011-11-01 17:15:17 -0600911static void __init msm_rpm_populate_map(struct msm_rpm_platform_data *data)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912{
Praveen Chidambaram78499012011-11-01 17:15:17 -0600913 int i, j;
914 struct msm_rpm_map_data *src = NULL;
915 struct msm_rpm_map_data *dst = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916
Praveen Chidambaram78499012011-11-01 17:15:17 -0600917 for (i = 0; i < MSM_RPM_ID_LAST;) {
918 src = &data->target_id[i];
919 dst = &msm_rpm_data.target_id[i];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700920
Praveen Chidambaram78499012011-11-01 17:15:17 -0600921 dst->id = MSM_RPM_ID_LAST;
922 dst->sel = msm_rpm_data.sel_last + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700923
Praveen Chidambaram78499012011-11-01 17:15:17 -0600924 /*
925 * copy the target specific id of the current and also of
926 * all the #count id's that follow the current.
927 * [MSM_RPM_ID_PM8921_S1_0] = { MSM_RPM_8960_ID_PM8921_S1_0,
928 * MSM_RPM_8960_SEL_PM8921_S1,
929 * 2},
930 * [MSM_RPM_ID_PM8921_S1_1] = { 0, 0, 0 },
931 * should translate to
932 * [MSM_RPM_ID_PM8921_S1_0] = { MSM_RPM_8960_ID_PM8921_S1_0,
933 * MSM_RPM_8960_SEL_PM8921,
934 * 2 },
935 * [MSM_RPM_ID_PM8921_S1_1] = { MSM_RPM_8960_ID_PM8921_S1_0 + 1,
936 * MSM_RPM_8960_SEL_PM8921,
937 * 0 },
938 */
939 for (j = 0; j < src->count; j++) {
940 dst = &msm_rpm_data.target_id[i + j];
941 dst->id = src->id + j;
942 dst->sel = src->sel;
943 }
944
945 i += (src->count) ? src->count : 1;
946 }
947
948 for (i = 0; i < MSM_RPM_STATUS_ID_LAST; i++) {
949 if (data->target_status[i] & MSM_RPM_STATUS_ID_VALID)
950 msm_rpm_data.target_status[i] &=
951 ~MSM_RPM_STATUS_ID_VALID;
952 else
953 msm_rpm_data.target_status[i] = MSM_RPM_STATUS_ID_LAST;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954 }
955}
956
957int __init msm_rpm_init(struct msm_rpm_platform_data *data)
958{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 unsigned int irq;
960 int rc;
961
Praveen Chidambaram78499012011-11-01 17:15:17 -0600962 memcpy(&msm_rpm_data, data, sizeof(struct msm_rpm_platform_data));
963 msm_rpm_sel_mask_size = msm_rpm_data.sel_last / 32 + 1;
964 BUG_ON(SEL_MASK_SIZE < msm_rpm_sel_mask_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600966 fw_major = msm_rpm_read(MSM_RPM_PAGE_STATUS,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600967 target_status(MSM_RPM_STATUS_ID_VERSION_MAJOR));
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600968 fw_minor = msm_rpm_read(MSM_RPM_PAGE_STATUS,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600969 target_status(MSM_RPM_STATUS_ID_VERSION_MINOR));
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600970 fw_build = msm_rpm_read(MSM_RPM_PAGE_STATUS,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600971 target_status(MSM_RPM_STATUS_ID_VERSION_BUILD));
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600972 pr_info("%s: RPM firmware %u.%u.%u\n", __func__,
973 fw_major, fw_minor, fw_build);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974
Praveen Chidambaram78499012011-11-01 17:15:17 -0600975 if (fw_major != msm_rpm_data.ver[0]) {
Praveen Chidambaram99a6a5d2011-07-13 10:14:06 -0600976 pr_err("%s: RPM version %u.%u.%u incompatible with "
977 "this driver version %u.%u.%u\n", __func__,
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -0600978 fw_major, fw_minor, fw_build,
Praveen Chidambaram78499012011-11-01 17:15:17 -0600979 msm_rpm_data.ver[0],
980 msm_rpm_data.ver[1],
981 msm_rpm_data.ver[2]);
Praveen Chidambaram99a6a5d2011-07-13 10:14:06 -0600982 return -EFAULT;
983 }
984
Praveen Chidambaram78499012011-11-01 17:15:17 -0600985 msm_rpm_write(MSM_RPM_PAGE_CTRL,
986 target_ctrl(MSM_RPM_CTRL_VERSION_MAJOR), msm_rpm_data.ver[0]);
987 msm_rpm_write(MSM_RPM_PAGE_CTRL,
988 target_ctrl(MSM_RPM_CTRL_VERSION_MINOR), msm_rpm_data.ver[1]);
989 msm_rpm_write(MSM_RPM_PAGE_CTRL,
990 target_ctrl(MSM_RPM_CTRL_VERSION_BUILD), msm_rpm_data.ver[2]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991
Praveen Chidambaram78499012011-11-01 17:15:17 -0600992 irq = data->irq_ack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993
994 rc = request_irq(irq, msm_rpm_ack_interrupt,
995 IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
996 "rpm_drv", msm_rpm_ack_interrupt);
997 if (rc) {
998 pr_err("%s: failed to request irq %d: %d\n",
999 __func__, irq, rc);
1000 return rc;
1001 }
1002
1003 rc = irq_set_irq_wake(irq, 1);
1004 if (rc) {
1005 pr_err("%s: failed to set wakeup irq %u: %d\n",
1006 __func__, irq, rc);
1007 return rc;
1008 }
1009
Stephen Boydf61255e2012-02-24 14:31:09 -08001010 rc = request_irq(data->irq_err, msm_rpm_err_interrupt,
1011 IRQF_TRIGGER_RISING, "rpm_err", NULL);
1012 if (rc) {
1013 pr_err("%s: failed to request error interrupt: %d\n",
1014 __func__, rc);
1015 return rc;
1016 }
1017
Praveen Chidambaram78499012011-11-01 17:15:17 -06001018 msm_rpm_populate_map(data);
Praveen Chidambaram043f4ce2011-08-02 09:37:59 -06001019
1020 return platform_driver_register(&msm_rpm_platform_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001021}