blob: 040e1260489a7960685427be807e93b37af3c16b [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/types.h>
17#include <linux/bitmap.h>
18#include <linux/bitops.h>
19#include <linux/init.h>
20#include <linux/interrupt.h>
21#include <linux/io.h>
22#include <linux/irq.h>
23#include <asm/hardware/gic.h>
24#include <linux/spinlock.h>
25#include <mach/msm_iomap.h>
26#include <mach/gpio.h>
27
28#include "mpm.h"
29
30/******************************************************************************
31 * Debug Definitions
32 *****************************************************************************/
33
34enum {
35 MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0),
36 MSM_MPM_DEBUG_PENDING_IRQ = BIT(1),
37 MSM_MPM_DEBUG_WRITE = BIT(2),
38 MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3),
39};
40
41static int msm_mpm_debug_mask = 1;
42module_param_named(
43 debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
44);
45
46/******************************************************************************
47 * Request and Status Definitions
48 *****************************************************************************/
49
50enum {
51 MSM_MPM_REQUEST_REG_ENABLE,
52 MSM_MPM_REQUEST_REG_DETECT_CTL,
53 MSM_MPM_REQUEST_REG_POLARITY,
54 MSM_MPM_REQUEST_REG_CLEAR,
55};
56
57enum {
58 MSM_MPM_STATUS_REG_PENDING,
59};
60
61/******************************************************************************
62 * IRQ Mapping Definitions
63 *****************************************************************************/
64
65#define MSM_MPM_NR_APPS_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS)
66
67#define MSM_MPM_REG_WIDTH DIV_ROUND_UP(MSM_MPM_NR_MPM_IRQS, 32)
68#define MSM_MPM_IRQ_INDEX(irq) (irq / 32)
69#define MSM_MPM_IRQ_MASK(irq) BIT(irq % 32)
70
71static uint8_t msm_mpm_irqs_a2m[MSM_MPM_NR_APPS_IRQS];
72
73static DEFINE_SPINLOCK(msm_mpm_lock);
74
75/*
76 * Note: the following two bitmaps only mark irqs that are _not_
77 * mappable to MPM.
78 */
79static DECLARE_BITMAP(msm_mpm_enabled_apps_irqs, MSM_MPM_NR_APPS_IRQS);
80static DECLARE_BITMAP(msm_mpm_wake_apps_irqs, MSM_MPM_NR_APPS_IRQS);
81
82static DECLARE_BITMAP(msm_mpm_gpio_irqs_mask, MSM_MPM_NR_APPS_IRQS);
83
84static uint32_t msm_mpm_enabled_irq[MSM_MPM_REG_WIDTH];
85static uint32_t msm_mpm_wake_irq[MSM_MPM_REG_WIDTH];
86static uint32_t msm_mpm_detect_ctl[MSM_MPM_REG_WIDTH];
87static uint32_t msm_mpm_polarity[MSM_MPM_REG_WIDTH];
88
89
90/******************************************************************************
91 * Low Level Functions for Accessing MPM
92 *****************************************************************************/
93
94static inline uint32_t msm_mpm_read(
95 unsigned int reg, unsigned int subreg_index)
96{
97 unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index;
98 return __raw_readl(msm_mpm_dev_data.mpm_status_reg_base + offset * 4);
99}
100
101static inline void msm_mpm_write(
102 unsigned int reg, unsigned int subreg_index, uint32_t value)
103{
104 unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index;
105 __raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4);
106
107 if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask)
108 pr_info("%s: reg %u.%u: 0x%08x\n",
109 __func__, reg, subreg_index, value);
110}
111
112static inline void msm_mpm_send_interrupt(void)
113{
114 __raw_writel(msm_mpm_dev_data.mpm_apps_ipc_val,
115 msm_mpm_dev_data.mpm_apps_ipc_reg);
116 /* Ensure the write is complete before returning. */
117 mb();
118}
119
120static irqreturn_t msm_mpm_irq(int irq, void *dev_id)
121{
122 return IRQ_HANDLED;
123}
124
125/******************************************************************************
126 * MPM Access Functions
127 *****************************************************************************/
128
129static void msm_mpm_set(bool wakeset)
130{
131 uint32_t *irqs;
132 unsigned int reg;
133 int i;
134
135 irqs = wakeset ? msm_mpm_wake_irq : msm_mpm_enabled_irq;
136 for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
137 reg = MSM_MPM_REQUEST_REG_ENABLE;
138 msm_mpm_write(reg, i, irqs[i]);
139
140 reg = MSM_MPM_REQUEST_REG_DETECT_CTL;
141 msm_mpm_write(reg, i, msm_mpm_detect_ctl[i]);
142
143 reg = MSM_MPM_REQUEST_REG_POLARITY;
144 msm_mpm_write(reg, i, msm_mpm_polarity[i]);
145
146 reg = MSM_MPM_REQUEST_REG_CLEAR;
147 msm_mpm_write(reg, i, 0xffffffff);
148 }
149
150 /* Ensure that the set operation is complete before sending the
151 * interrupt
152 */
153 mb();
154 msm_mpm_send_interrupt();
155}
156
157static void msm_mpm_clear(void)
158{
159 int i;
160
161 for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
162 msm_mpm_write(MSM_MPM_REQUEST_REG_ENABLE, i, 0);
163 msm_mpm_write(MSM_MPM_REQUEST_REG_CLEAR, i, 0xffffffff);
164 }
165
166 /* Ensure the clear is complete before sending the interrupt */
167 mb();
168 msm_mpm_send_interrupt();
169}
170
171/******************************************************************************
172 * Interrupt Mapping Functions
173 *****************************************************************************/
174
175static inline bool msm_mpm_is_valid_apps_irq(unsigned int irq)
176{
177 return irq < ARRAY_SIZE(msm_mpm_irqs_a2m);
178}
179
180static inline uint8_t msm_mpm_get_irq_a2m(unsigned int irq)
181{
182 return msm_mpm_irqs_a2m[irq];
183}
184
185static inline void msm_mpm_set_irq_a2m(unsigned int apps_irq,
186 unsigned int mpm_irq)
187{
188 msm_mpm_irqs_a2m[apps_irq] = (uint8_t) mpm_irq;
189}
190
191static inline bool msm_mpm_is_valid_mpm_irq(unsigned int irq)
192{
193 return irq < msm_mpm_dev_data.irqs_m2a_size;
194}
195
196static inline uint16_t msm_mpm_get_irq_m2a(unsigned int irq)
197{
198 return msm_mpm_dev_data.irqs_m2a[irq];
199}
200
201static bool msm_mpm_bypass_apps_irq(unsigned int irq)
202{
203 int i;
204
205 for (i = 0; i < msm_mpm_dev_data.bypassed_apps_irqs_size; i++)
206 if (irq == msm_mpm_dev_data.bypassed_apps_irqs[i])
207 return true;
208
209 return false;
210}
211
212static int msm_mpm_enable_irq_exclusive(
213 unsigned int irq, bool enable, bool wakeset)
214{
215 uint32_t mpm_irq;
216
217 if (!msm_mpm_is_valid_apps_irq(irq))
218 return -EINVAL;
219
220 if (msm_mpm_bypass_apps_irq(irq))
221 return 0;
222
223 mpm_irq = msm_mpm_get_irq_a2m(irq);
224 if (mpm_irq) {
225 uint32_t *mpm_irq_masks = wakeset ?
226 msm_mpm_wake_irq : msm_mpm_enabled_irq;
227 uint32_t index = MSM_MPM_IRQ_INDEX(mpm_irq);
228 uint32_t mask = MSM_MPM_IRQ_MASK(mpm_irq);
229
230 if (enable)
231 mpm_irq_masks[index] |= mask;
232 else
233 mpm_irq_masks[index] &= ~mask;
234 } else {
235 unsigned long *apps_irq_bitmap = wakeset ?
236 msm_mpm_wake_apps_irqs : msm_mpm_enabled_apps_irqs;
237
238 if (enable)
239 __set_bit(irq, apps_irq_bitmap);
240 else
241 __clear_bit(irq, apps_irq_bitmap);
242 }
243
244 return 0;
245}
246
247static int msm_mpm_set_irq_type_exclusive(
248 unsigned int irq, unsigned int flow_type)
249{
250 uint32_t mpm_irq;
251
252 if (!msm_mpm_is_valid_apps_irq(irq))
253 return -EINVAL;
254
255 if (msm_mpm_bypass_apps_irq(irq))
256 return 0;
257
258 mpm_irq = msm_mpm_get_irq_a2m(irq);
259 if (mpm_irq) {
260 uint32_t index = MSM_MPM_IRQ_INDEX(mpm_irq);
261 uint32_t mask = MSM_MPM_IRQ_MASK(mpm_irq);
262
263 if (flow_type & IRQ_TYPE_EDGE_BOTH)
264 msm_mpm_detect_ctl[index] |= mask;
265 else
266 msm_mpm_detect_ctl[index] &= ~mask;
267
268 if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
269 msm_mpm_polarity[index] |= mask;
270 else
271 msm_mpm_polarity[index] &= ~mask;
272 }
273
274 return 0;
275}
276
277static int __msm_mpm_enable_irq(unsigned int irq, unsigned int enable)
278{
279 unsigned long flags;
280 int rc;
281
282 spin_lock_irqsave(&msm_mpm_lock, flags);
283 rc = msm_mpm_enable_irq_exclusive(irq, (bool)enable, false);
284 spin_unlock_irqrestore(&msm_mpm_lock, flags);
285
286 return rc;
287}
288
289static void msm_mpm_enable_irq(struct irq_data *d)
290{
291 __msm_mpm_enable_irq(d->irq, 1);
292}
293
294static void msm_mpm_disable_irq(struct irq_data *d)
295{
296 __msm_mpm_enable_irq(d->irq, 0);
297}
298
299static int msm_mpm_set_irq_wake(struct irq_data *d, unsigned int on)
300{
301 unsigned long flags;
302 int rc;
303
304 spin_lock_irqsave(&msm_mpm_lock, flags);
305 rc = msm_mpm_enable_irq_exclusive(d->irq, (bool)on, true);
306 spin_unlock_irqrestore(&msm_mpm_lock, flags);
307
308 return rc;
309}
310
311static int msm_mpm_set_irq_type(struct irq_data *d, unsigned int flow_type)
312{
313 unsigned long flags;
314 int rc;
315
316 spin_lock_irqsave(&msm_mpm_lock, flags);
317 rc = msm_mpm_set_irq_type_exclusive(d->irq, flow_type);
318 spin_unlock_irqrestore(&msm_mpm_lock, flags);
319
320 return rc;
321}
322
323/******************************************************************************
324 * Public functions
325 *****************************************************************************/
326int msm_mpm_enable_pin(enum msm_mpm_pin pin, unsigned int enable)
327{
328 uint32_t index = MSM_MPM_IRQ_INDEX(pin);
329 uint32_t mask = MSM_MPM_IRQ_MASK(pin);
330 unsigned long flags;
331
332 spin_lock_irqsave(&msm_mpm_lock, flags);
333
334 if (enable)
335 msm_mpm_enabled_irq[index] |= mask;
336 else
337 msm_mpm_enabled_irq[index] &= ~mask;
338
339 spin_unlock_irqrestore(&msm_mpm_lock, flags);
340 return 0;
341}
342
343int msm_mpm_set_pin_wake(enum msm_mpm_pin pin, unsigned int on)
344{
345 uint32_t index = MSM_MPM_IRQ_INDEX(pin);
346 uint32_t mask = MSM_MPM_IRQ_MASK(pin);
347 unsigned long flags;
348
349 spin_lock_irqsave(&msm_mpm_lock, flags);
350
351 if (on)
352 msm_mpm_wake_irq[index] |= mask;
353 else
354 msm_mpm_wake_irq[index] &= ~mask;
355
356 spin_unlock_irqrestore(&msm_mpm_lock, flags);
357 return 0;
358}
359
360int msm_mpm_set_pin_type(enum msm_mpm_pin pin, unsigned int flow_type)
361{
362 uint32_t index = MSM_MPM_IRQ_INDEX(pin);
363 uint32_t mask = MSM_MPM_IRQ_MASK(pin);
364 unsigned long flags;
365
366 spin_lock_irqsave(&msm_mpm_lock, flags);
367
368 if (flow_type & IRQ_TYPE_EDGE_BOTH)
369 msm_mpm_detect_ctl[index] |= mask;
370 else
371 msm_mpm_detect_ctl[index] &= ~mask;
372
373 if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
374 msm_mpm_polarity[index] |= mask;
375 else
376 msm_mpm_polarity[index] &= ~mask;
377
378 spin_unlock_irqrestore(&msm_mpm_lock, flags);
379 return 0;
380}
381
382bool msm_mpm_irqs_detectable(bool from_idle)
383{
384 unsigned long *apps_irq_bitmap;
385 int debug_mask;
386
387 if (from_idle) {
388 apps_irq_bitmap = msm_mpm_enabled_apps_irqs;
389 debug_mask = msm_mpm_debug_mask &
390 MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE;
391 } else {
392 apps_irq_bitmap = msm_mpm_wake_apps_irqs;
393 debug_mask = msm_mpm_debug_mask &
394 MSM_MPM_DEBUG_NON_DETECTABLE_IRQ;
395 }
396
397 if (debug_mask) {
398 static char buf[DIV_ROUND_UP(MSM_MPM_NR_APPS_IRQS, 32)*9+1];
399
400 bitmap_scnprintf(buf, sizeof(buf), apps_irq_bitmap,
401 MSM_MPM_NR_APPS_IRQS);
402 buf[sizeof(buf) - 1] = '\0';
403
404 pr_info("%s: cannot monitor %s", __func__, buf);
405 }
406
407 return (bool)__bitmap_empty(apps_irq_bitmap, MSM_MPM_NR_APPS_IRQS);
408}
409
410bool msm_mpm_gpio_irqs_detectable(bool from_idle)
411{
412 unsigned long *apps_irq_bitmap = from_idle ?
413 msm_mpm_enabled_apps_irqs : msm_mpm_wake_apps_irqs;
414
415 return !__bitmap_intersects(msm_mpm_gpio_irqs_mask, apps_irq_bitmap,
416 MSM_MPM_NR_APPS_IRQS);
417}
418
419void msm_mpm_enter_sleep(bool from_idle)
420{
421 msm_mpm_set(!from_idle);
422}
423
424void msm_mpm_exit_sleep(bool from_idle)
425{
426 unsigned long pending;
427 int i;
428 int k;
429
430 for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
431 pending = msm_mpm_read(MSM_MPM_STATUS_REG_PENDING, i);
432
433 if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
434 pr_info("%s: pending.%d: 0x%08lx", __func__,
435 i, pending);
436
437 k = find_first_bit(&pending, 32);
438 while (k < 32) {
439 unsigned int mpm_irq = 32 * i + k;
440 unsigned int apps_irq = msm_mpm_get_irq_m2a(mpm_irq);
441 struct irq_desc *desc = apps_irq ?
442 irq_to_desc(apps_irq) : NULL;
443
444 if (desc && !irqd_is_level_type(&desc->irq_data)) {
445 irq_set_pending(apps_irq);
446 if (from_idle)
447 check_irq_resend(desc, apps_irq);
448 }
449
450 k = find_next_bit(&pending, 32, k + 1);
451 }
452 }
453
454 msm_mpm_clear();
455}
456
457static int __init msm_mpm_early_init(void)
458{
459 uint8_t mpm_irq;
460 uint16_t apps_irq;
461
462 for (mpm_irq = 0; msm_mpm_is_valid_mpm_irq(mpm_irq); mpm_irq++) {
463 apps_irq = msm_mpm_get_irq_m2a(mpm_irq);
464 if (apps_irq && msm_mpm_is_valid_apps_irq(apps_irq))
465 msm_mpm_set_irq_a2m(apps_irq, mpm_irq);
466 }
467
468 return 0;
469}
470core_initcall(msm_mpm_early_init);
471
472void msm_mpm_irq_extn_init(void)
473{
474 gic_arch_extn.irq_mask = msm_mpm_disable_irq;
475 gic_arch_extn.irq_unmask = msm_mpm_enable_irq;
476 gic_arch_extn.irq_disable = msm_mpm_disable_irq;
477 gic_arch_extn.irq_set_type = msm_mpm_set_irq_type;
478 gic_arch_extn.irq_set_wake = msm_mpm_set_irq_wake;
479
480 msm_gpio_irq_extn.irq_mask = msm_mpm_disable_irq;
481 msm_gpio_irq_extn.irq_unmask = msm_mpm_enable_irq;
482 msm_gpio_irq_extn.irq_disable = msm_mpm_disable_irq;
483 msm_gpio_irq_extn.irq_set_type = msm_mpm_set_irq_type;
484 msm_gpio_irq_extn.irq_set_wake = msm_mpm_set_irq_wake;
485
486 bitmap_set(msm_mpm_gpio_irqs_mask, NR_MSM_IRQS, NR_GPIO_IRQS);
487}
488
489static int __init msm_mpm_init(void)
490{
491 unsigned int irq = msm_mpm_dev_data.mpm_ipc_irq;
492 int rc;
493
494 rc = request_irq(irq, msm_mpm_irq,
495 IRQF_TRIGGER_RISING, "mpm_drv", msm_mpm_irq);
496
497 if (rc) {
498 pr_err("%s: failed to request irq %u: %d\n",
499 __func__, irq, rc);
500 goto init_bail;
501 }
502
503 rc = irq_set_irq_wake(irq, 1);
504 if (rc) {
505 pr_err("%s: failed to set wakeup irq %u: %d\n",
506 __func__, irq, rc);
507 goto init_free_bail;
508 }
509
510 return 0;
511
512init_free_bail:
513 free_irq(irq, msm_mpm_irq);
514
515init_bail:
516 return rc;
517}
518device_initcall(msm_mpm_init);