diff options
Diffstat (limited to 'drivers/mfd/twl6030-irq.c')
-rw-r--r-- | drivers/mfd/twl6030-irq.c | 75 |
1 files changed, 67 insertions, 8 deletions
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index eb3b5f88e566..deec3ec858bf 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/kthread.h> | 37 | #include <linux/kthread.h> |
38 | #include <linux/i2c/twl.h> | 38 | #include <linux/i2c/twl.h> |
39 | #include <linux/platform_device.h> | 39 | #include <linux/platform_device.h> |
40 | #include <linux/suspend.h> | ||
40 | 41 | ||
41 | #include "twl-core.h" | 42 | #include "twl-core.h" |
42 | 43 | ||
@@ -83,8 +84,48 @@ static int twl6030_interrupt_mapping[24] = { | |||
83 | /*----------------------------------------------------------------------*/ | 84 | /*----------------------------------------------------------------------*/ |
84 | 85 | ||
85 | static unsigned twl6030_irq_base; | 86 | static unsigned twl6030_irq_base; |
87 | static int twl_irq; | ||
88 | static bool twl_irq_wake_enabled; | ||
86 | 89 | ||
87 | static struct completion irq_event; | 90 | static struct completion irq_event; |
91 | static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); | ||
92 | |||
93 | static int twl6030_irq_pm_notifier(struct notifier_block *notifier, | ||
94 | unsigned long pm_event, void *unused) | ||
95 | { | ||
96 | int chained_wakeups; | ||
97 | |||
98 | switch (pm_event) { | ||
99 | case PM_SUSPEND_PREPARE: | ||
100 | chained_wakeups = atomic_read(&twl6030_wakeirqs); | ||
101 | |||
102 | if (chained_wakeups && !twl_irq_wake_enabled) { | ||
103 | if (enable_irq_wake(twl_irq)) | ||
104 | pr_err("twl6030 IRQ wake enable failed\n"); | ||
105 | else | ||
106 | twl_irq_wake_enabled = true; | ||
107 | } else if (!chained_wakeups && twl_irq_wake_enabled) { | ||
108 | disable_irq_wake(twl_irq); | ||
109 | twl_irq_wake_enabled = false; | ||
110 | } | ||
111 | |||
112 | disable_irq(twl_irq); | ||
113 | break; | ||
114 | |||
115 | case PM_POST_SUSPEND: | ||
116 | enable_irq(twl_irq); | ||
117 | break; | ||
118 | |||
119 | default: | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | return NOTIFY_DONE; | ||
124 | } | ||
125 | |||
126 | static struct notifier_block twl6030_irq_pm_notifier_block = { | ||
127 | .notifier_call = twl6030_irq_pm_notifier, | ||
128 | }; | ||
88 | 129 | ||
89 | /* | 130 | /* |
90 | * This thread processes interrupts reported by the Primary Interrupt Handler. | 131 | * This thread processes interrupts reported by the Primary Interrupt Handler. |
@@ -187,6 +228,16 @@ static inline void activate_irq(int irq) | |||
187 | #endif | 228 | #endif |
188 | } | 229 | } |
189 | 230 | ||
231 | int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) | ||
232 | { | ||
233 | if (on) | ||
234 | atomic_inc(&twl6030_wakeirqs); | ||
235 | else | ||
236 | atomic_dec(&twl6030_wakeirqs); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
190 | /*----------------------------------------------------------------------*/ | 241 | /*----------------------------------------------------------------------*/ |
191 | 242 | ||
192 | static unsigned twl6030_irq_next; | 243 | static unsigned twl6030_irq_next; |
@@ -318,10 +369,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
318 | twl6030_irq_chip = dummy_irq_chip; | 369 | twl6030_irq_chip = dummy_irq_chip; |
319 | twl6030_irq_chip.name = "twl6030"; | 370 | twl6030_irq_chip.name = "twl6030"; |
320 | twl6030_irq_chip.irq_set_type = NULL; | 371 | twl6030_irq_chip.irq_set_type = NULL; |
372 | twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake; | ||
321 | 373 | ||
322 | for (i = irq_base; i < irq_end; i++) { | 374 | for (i = irq_base; i < irq_end; i++) { |
323 | irq_set_chip_and_handler(i, &twl6030_irq_chip, | 375 | irq_set_chip_and_handler(i, &twl6030_irq_chip, |
324 | handle_simple_irq); | 376 | handle_simple_irq); |
377 | irq_set_chip_data(i, (void *)irq_num); | ||
325 | activate_irq(i); | 378 | activate_irq(i); |
326 | } | 379 | } |
327 | 380 | ||
@@ -331,6 +384,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
331 | 384 | ||
332 | /* install an irq handler to demultiplex the TWL6030 interrupt */ | 385 | /* install an irq handler to demultiplex the TWL6030 interrupt */ |
333 | init_completion(&irq_event); | 386 | init_completion(&irq_event); |
387 | |||
388 | status = request_irq(irq_num, handle_twl6030_pih, 0, | ||
389 | "TWL6030-PIH", &irq_event); | ||
390 | if (status < 0) { | ||
391 | pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); | ||
392 | goto fail_irq; | ||
393 | } | ||
394 | |||
334 | task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); | 395 | task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); |
335 | if (IS_ERR(task)) { | 396 | if (IS_ERR(task)) { |
336 | pr_err("twl6030: could not create irq %d thread!\n", irq_num); | 397 | pr_err("twl6030: could not create irq %d thread!\n", irq_num); |
@@ -338,17 +399,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
338 | goto fail_kthread; | 399 | goto fail_kthread; |
339 | } | 400 | } |
340 | 401 | ||
341 | status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED, | 402 | twl_irq = irq_num; |
342 | "TWL6030-PIH", &irq_event); | 403 | register_pm_notifier(&twl6030_irq_pm_notifier_block); |
343 | if (status < 0) { | ||
344 | pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); | ||
345 | goto fail_irq; | ||
346 | } | ||
347 | return status; | 404 | return status; |
348 | fail_irq: | ||
349 | free_irq(irq_num, &irq_event); | ||
350 | 405 | ||
351 | fail_kthread: | 406 | fail_kthread: |
407 | free_irq(irq_num, &irq_event); | ||
408 | |||
409 | fail_irq: | ||
352 | for (i = irq_base; i < irq_end; i++) | 410 | for (i = irq_base; i < irq_end; i++) |
353 | irq_set_chip_and_handler(i, NULL, NULL); | 411 | irq_set_chip_and_handler(i, NULL, NULL); |
354 | return status; | 412 | return status; |
@@ -356,6 +414,7 @@ fail_kthread: | |||
356 | 414 | ||
357 | int twl6030_exit_irq(void) | 415 | int twl6030_exit_irq(void) |
358 | { | 416 | { |
417 | unregister_pm_notifier(&twl6030_irq_pm_notifier_block); | ||
359 | 418 | ||
360 | if (twl6030_irq_base) { | 419 | if (twl6030_irq_base) { |
361 | pr_err("twl6030: can't yet clean up IRQs?\n"); | 420 | pr_err("twl6030: can't yet clean up IRQs?\n"); |