blob: 70ee39bc4d4689f6c1ea5c95fa53e77bc8843922 [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
Praveen Chidambaramfdaef162011-09-28 08:40:05 -0600263 if (index >= MSM_MPM_REG_WIDTH)
264 return -EFAULT;
265
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266 if (flow_type & IRQ_TYPE_EDGE_BOTH)
267 msm_mpm_detect_ctl[index] |= mask;
268 else
269 msm_mpm_detect_ctl[index] &= ~mask;
270
271 if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
272 msm_mpm_polarity[index] |= mask;
273 else
274 msm_mpm_polarity[index] &= ~mask;
275 }
276
277 return 0;
278}
279
280static int __msm_mpm_enable_irq(unsigned int irq, unsigned int enable)
281{
282 unsigned long flags;
283 int rc;
284
285 spin_lock_irqsave(&msm_mpm_lock, flags);
286 rc = msm_mpm_enable_irq_exclusive(irq, (bool)enable, false);
287 spin_unlock_irqrestore(&msm_mpm_lock, flags);
288
289 return rc;
290}
291
292static void msm_mpm_enable_irq(struct irq_data *d)
293{
294 __msm_mpm_enable_irq(d->irq, 1);
295}
296
297static void msm_mpm_disable_irq(struct irq_data *d)
298{
299 __msm_mpm_enable_irq(d->irq, 0);
300}
301
302static int msm_mpm_set_irq_wake(struct irq_data *d, unsigned int on)
303{
304 unsigned long flags;
305 int rc;
306
307 spin_lock_irqsave(&msm_mpm_lock, flags);
308 rc = msm_mpm_enable_irq_exclusive(d->irq, (bool)on, true);
309 spin_unlock_irqrestore(&msm_mpm_lock, flags);
310
311 return rc;
312}
313
314static int msm_mpm_set_irq_type(struct irq_data *d, unsigned int flow_type)
315{
316 unsigned long flags;
317 int rc;
318
319 spin_lock_irqsave(&msm_mpm_lock, flags);
320 rc = msm_mpm_set_irq_type_exclusive(d->irq, flow_type);
321 spin_unlock_irqrestore(&msm_mpm_lock, flags);
322
323 return rc;
324}
325
326/******************************************************************************
327 * Public functions
328 *****************************************************************************/
329int msm_mpm_enable_pin(enum msm_mpm_pin pin, unsigned int enable)
330{
331 uint32_t index = MSM_MPM_IRQ_INDEX(pin);
332 uint32_t mask = MSM_MPM_IRQ_MASK(pin);
333 unsigned long flags;
334
335 spin_lock_irqsave(&msm_mpm_lock, flags);
336
337 if (enable)
338 msm_mpm_enabled_irq[index] |= mask;
339 else
340 msm_mpm_enabled_irq[index] &= ~mask;
341
342 spin_unlock_irqrestore(&msm_mpm_lock, flags);
343 return 0;
344}
345
346int msm_mpm_set_pin_wake(enum msm_mpm_pin pin, unsigned int on)
347{
348 uint32_t index = MSM_MPM_IRQ_INDEX(pin);
349 uint32_t mask = MSM_MPM_IRQ_MASK(pin);
350 unsigned long flags;
351
352 spin_lock_irqsave(&msm_mpm_lock, flags);
353
354 if (on)
355 msm_mpm_wake_irq[index] |= mask;
356 else
357 msm_mpm_wake_irq[index] &= ~mask;
358
359 spin_unlock_irqrestore(&msm_mpm_lock, flags);
360 return 0;
361}
362
363int msm_mpm_set_pin_type(enum msm_mpm_pin pin, unsigned int flow_type)
364{
365 uint32_t index = MSM_MPM_IRQ_INDEX(pin);
366 uint32_t mask = MSM_MPM_IRQ_MASK(pin);
367 unsigned long flags;
368
369 spin_lock_irqsave(&msm_mpm_lock, flags);
370
371 if (flow_type & IRQ_TYPE_EDGE_BOTH)
372 msm_mpm_detect_ctl[index] |= mask;
373 else
374 msm_mpm_detect_ctl[index] &= ~mask;
375
376 if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
377 msm_mpm_polarity[index] |= mask;
378 else
379 msm_mpm_polarity[index] &= ~mask;
380
381 spin_unlock_irqrestore(&msm_mpm_lock, flags);
382 return 0;
383}
384
385bool msm_mpm_irqs_detectable(bool from_idle)
386{
387 unsigned long *apps_irq_bitmap;
388 int debug_mask;
389
390 if (from_idle) {
391 apps_irq_bitmap = msm_mpm_enabled_apps_irqs;
392 debug_mask = msm_mpm_debug_mask &
393 MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE;
394 } else {
395 apps_irq_bitmap = msm_mpm_wake_apps_irqs;
396 debug_mask = msm_mpm_debug_mask &
397 MSM_MPM_DEBUG_NON_DETECTABLE_IRQ;
398 }
399
400 if (debug_mask) {
401 static char buf[DIV_ROUND_UP(MSM_MPM_NR_APPS_IRQS, 32)*9+1];
402
403 bitmap_scnprintf(buf, sizeof(buf), apps_irq_bitmap,
404 MSM_MPM_NR_APPS_IRQS);
405 buf[sizeof(buf) - 1] = '\0';
406
407 pr_info("%s: cannot monitor %s", __func__, buf);
408 }
409
410 return (bool)__bitmap_empty(apps_irq_bitmap, MSM_MPM_NR_APPS_IRQS);
411}
412
413bool msm_mpm_gpio_irqs_detectable(bool from_idle)
414{
415 unsigned long *apps_irq_bitmap = from_idle ?
416 msm_mpm_enabled_apps_irqs : msm_mpm_wake_apps_irqs;
417
418 return !__bitmap_intersects(msm_mpm_gpio_irqs_mask, apps_irq_bitmap,
419 MSM_MPM_NR_APPS_IRQS);
420}
421
422void msm_mpm_enter_sleep(bool from_idle)
423{
424 msm_mpm_set(!from_idle);
425}
426
427void msm_mpm_exit_sleep(bool from_idle)
428{
429 unsigned long pending;
430 int i;
431 int k;
432
433 for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
434 pending = msm_mpm_read(MSM_MPM_STATUS_REG_PENDING, i);
435
436 if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
437 pr_info("%s: pending.%d: 0x%08lx", __func__,
438 i, pending);
439
440 k = find_first_bit(&pending, 32);
441 while (k < 32) {
442 unsigned int mpm_irq = 32 * i + k;
443 unsigned int apps_irq = msm_mpm_get_irq_m2a(mpm_irq);
444 struct irq_desc *desc = apps_irq ?
445 irq_to_desc(apps_irq) : NULL;
446
447 if (desc && !irqd_is_level_type(&desc->irq_data)) {
448 irq_set_pending(apps_irq);
449 if (from_idle)
450 check_irq_resend(desc, apps_irq);
451 }
452
453 k = find_next_bit(&pending, 32, k + 1);
454 }
455 }
456
457 msm_mpm_clear();
458}
459
460static int __init msm_mpm_early_init(void)
461{
462 uint8_t mpm_irq;
463 uint16_t apps_irq;
464
465 for (mpm_irq = 0; msm_mpm_is_valid_mpm_irq(mpm_irq); mpm_irq++) {
466 apps_irq = msm_mpm_get_irq_m2a(mpm_irq);
467 if (apps_irq && msm_mpm_is_valid_apps_irq(apps_irq))
468 msm_mpm_set_irq_a2m(apps_irq, mpm_irq);
469 }
470
471 return 0;
472}
473core_initcall(msm_mpm_early_init);
474
475void msm_mpm_irq_extn_init(void)
476{
477 gic_arch_extn.irq_mask = msm_mpm_disable_irq;
478 gic_arch_extn.irq_unmask = msm_mpm_enable_irq;
479 gic_arch_extn.irq_disable = msm_mpm_disable_irq;
480 gic_arch_extn.irq_set_type = msm_mpm_set_irq_type;
481 gic_arch_extn.irq_set_wake = msm_mpm_set_irq_wake;
482
483 msm_gpio_irq_extn.irq_mask = msm_mpm_disable_irq;
484 msm_gpio_irq_extn.irq_unmask = msm_mpm_enable_irq;
485 msm_gpio_irq_extn.irq_disable = msm_mpm_disable_irq;
486 msm_gpio_irq_extn.irq_set_type = msm_mpm_set_irq_type;
487 msm_gpio_irq_extn.irq_set_wake = msm_mpm_set_irq_wake;
488
489 bitmap_set(msm_mpm_gpio_irqs_mask, NR_MSM_IRQS, NR_GPIO_IRQS);
490}
491
492static int __init msm_mpm_init(void)
493{
494 unsigned int irq = msm_mpm_dev_data.mpm_ipc_irq;
495 int rc;
496
497 rc = request_irq(irq, msm_mpm_irq,
498 IRQF_TRIGGER_RISING, "mpm_drv", msm_mpm_irq);
499
500 if (rc) {
501 pr_err("%s: failed to request irq %u: %d\n",
502 __func__, irq, rc);
503 goto init_bail;
504 }
505
506 rc = irq_set_irq_wake(irq, 1);
507 if (rc) {
508 pr_err("%s: failed to set wakeup irq %u: %d\n",
509 __func__, irq, rc);
510 goto init_free_bail;
511 }
512
513 return 0;
514
515init_free_bail:
516 free_irq(irq, msm_mpm_irq);
517
518init_bail:
519 return rc;
520}
521device_initcall(msm_mpm_init);