diff options
Diffstat (limited to 'drivers/mfd/twl6030-irq.c')
-rw-r--r-- | drivers/mfd/twl6030-irq.c | 101 |
1 files changed, 59 insertions, 42 deletions
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 1b03ce9e9f15..e3c54f80b9f0 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c | |||
@@ -86,36 +86,44 @@ static int twl6030_interrupt_mapping[24] = { | |||
86 | }; | 86 | }; |
87 | /*----------------------------------------------------------------------*/ | 87 | /*----------------------------------------------------------------------*/ |
88 | 88 | ||
89 | static int twl_irq; | 89 | struct twl6030_irq { |
90 | static bool twl_irq_wake_enabled; | 90 | unsigned int irq_base; |
91 | int twl_irq; | ||
92 | bool irq_wake_enabled; | ||
93 | atomic_t wakeirqs; | ||
94 | struct notifier_block pm_nb; | ||
95 | struct irq_chip irq_chip; | ||
96 | struct irq_domain *irq_domain; | ||
97 | }; | ||
91 | 98 | ||
92 | static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); | 99 | static struct twl6030_irq *twl6030_irq; |
93 | struct irq_domain *irq_domain; | ||
94 | 100 | ||
95 | static int twl6030_irq_pm_notifier(struct notifier_block *notifier, | 101 | static int twl6030_irq_pm_notifier(struct notifier_block *notifier, |
96 | unsigned long pm_event, void *unused) | 102 | unsigned long pm_event, void *unused) |
97 | { | 103 | { |
98 | int chained_wakeups; | 104 | int chained_wakeups; |
105 | struct twl6030_irq *pdata = container_of(notifier, struct twl6030_irq, | ||
106 | pm_nb); | ||
99 | 107 | ||
100 | switch (pm_event) { | 108 | switch (pm_event) { |
101 | case PM_SUSPEND_PREPARE: | 109 | case PM_SUSPEND_PREPARE: |
102 | chained_wakeups = atomic_read(&twl6030_wakeirqs); | 110 | chained_wakeups = atomic_read(&pdata->wakeirqs); |
103 | 111 | ||
104 | if (chained_wakeups && !twl_irq_wake_enabled) { | 112 | if (chained_wakeups && !pdata->irq_wake_enabled) { |
105 | if (enable_irq_wake(twl_irq)) | 113 | if (enable_irq_wake(pdata->twl_irq)) |
106 | pr_err("twl6030 IRQ wake enable failed\n"); | 114 | pr_err("twl6030 IRQ wake enable failed\n"); |
107 | else | 115 | else |
108 | twl_irq_wake_enabled = true; | 116 | pdata->irq_wake_enabled = true; |
109 | } else if (!chained_wakeups && twl_irq_wake_enabled) { | 117 | } else if (!chained_wakeups && pdata->irq_wake_enabled) { |
110 | disable_irq_wake(twl_irq); | 118 | disable_irq_wake(pdata->twl_irq); |
111 | twl_irq_wake_enabled = false; | 119 | pdata->irq_wake_enabled = false; |
112 | } | 120 | } |
113 | 121 | ||
114 | disable_irq(twl_irq); | 122 | disable_irq(pdata->twl_irq); |
115 | break; | 123 | break; |
116 | 124 | ||
117 | case PM_POST_SUSPEND: | 125 | case PM_POST_SUSPEND: |
118 | enable_irq(twl_irq); | 126 | enable_irq(pdata->twl_irq); |
119 | break; | 127 | break; |
120 | 128 | ||
121 | default: | 129 | default: |
@@ -125,10 +133,6 @@ static int twl6030_irq_pm_notifier(struct notifier_block *notifier, | |||
125 | return NOTIFY_DONE; | 133 | return NOTIFY_DONE; |
126 | } | 134 | } |
127 | 135 | ||
128 | static struct notifier_block twl6030_irq_pm_notifier_block = { | ||
129 | .notifier_call = twl6030_irq_pm_notifier, | ||
130 | }; | ||
131 | |||
132 | /* | 136 | /* |
133 | * Threaded irq handler for the twl6030 interrupt. | 137 | * Threaded irq handler for the twl6030 interrupt. |
134 | * We query the interrupt controller in the twl6030 to determine | 138 | * We query the interrupt controller in the twl6030 to determine |
@@ -138,11 +142,11 @@ static struct notifier_block twl6030_irq_pm_notifier_block = { | |||
138 | static irqreturn_t twl6030_irq_thread(int irq, void *data) | 142 | static irqreturn_t twl6030_irq_thread(int irq, void *data) |
139 | { | 143 | { |
140 | int i, ret; | 144 | int i, ret; |
141 | struct irq_domain *irq_domain = (struct irq_domain *)data; | ||
142 | union { | 145 | union { |
143 | u8 bytes[4]; | 146 | u8 bytes[4]; |
144 | u32 int_sts; | 147 | u32 int_sts; |
145 | } sts; | 148 | } sts; |
149 | struct twl6030_irq *pdata = data; | ||
146 | 150 | ||
147 | /* read INT_STS_A, B and C in one shot using a burst read */ | 151 | /* read INT_STS_A, B and C in one shot using a burst read */ |
148 | ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3); | 152 | ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3); |
@@ -163,7 +167,7 @@ static irqreturn_t twl6030_irq_thread(int irq, void *data) | |||
163 | for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) | 167 | for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) |
164 | if (sts.int_sts & 0x1) { | 168 | if (sts.int_sts & 0x1) { |
165 | int module_irq = | 169 | int module_irq = |
166 | irq_find_mapping(irq_domain, | 170 | irq_find_mapping(pdata->irq_domain, |
167 | twl6030_interrupt_mapping[i]); | 171 | twl6030_interrupt_mapping[i]); |
168 | if (module_irq) | 172 | if (module_irq) |
169 | handle_nested_irq(module_irq); | 173 | handle_nested_irq(module_irq); |
@@ -194,10 +198,12 @@ static irqreturn_t twl6030_irq_thread(int irq, void *data) | |||
194 | 198 | ||
195 | static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) | 199 | static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) |
196 | { | 200 | { |
201 | struct twl6030_irq *pdata = irq_get_chip_data(d->irq); | ||
202 | |||
197 | if (on) | 203 | if (on) |
198 | atomic_inc(&twl6030_wakeirqs); | 204 | atomic_inc(&pdata->wakeirqs); |
199 | else | 205 | else |
200 | atomic_dec(&twl6030_wakeirqs); | 206 | atomic_dec(&pdata->wakeirqs); |
201 | 207 | ||
202 | return 0; | 208 | return 0; |
203 | } | 209 | } |
@@ -272,7 +278,8 @@ int twl6030_mmc_card_detect_config(void) | |||
272 | return ret; | 278 | return ret; |
273 | } | 279 | } |
274 | 280 | ||
275 | return irq_find_mapping(irq_domain, MMCDETECT_INTR_OFFSET); | 281 | return irq_find_mapping(twl6030_irq->irq_domain, |
282 | MMCDETECT_INTR_OFFSET); | ||
276 | } | 283 | } |
277 | EXPORT_SYMBOL(twl6030_mmc_card_detect_config); | 284 | EXPORT_SYMBOL(twl6030_mmc_card_detect_config); |
278 | 285 | ||
@@ -301,15 +308,15 @@ int twl6030_mmc_card_detect(struct device *dev, int slot) | |||
301 | } | 308 | } |
302 | EXPORT_SYMBOL(twl6030_mmc_card_detect); | 309 | EXPORT_SYMBOL(twl6030_mmc_card_detect); |
303 | 310 | ||
304 | static struct irq_chip twl6030_irq_chip; | ||
305 | |||
306 | static int twl6030_irq_map(struct irq_domain *d, unsigned int virq, | 311 | static int twl6030_irq_map(struct irq_domain *d, unsigned int virq, |
307 | irq_hw_number_t hwirq) | 312 | irq_hw_number_t hwirq) |
308 | { | 313 | { |
309 | irq_set_chip_data(virq, &twl6030_irq_chip); | 314 | struct twl6030_irq *pdata = d->host_data; |
310 | irq_set_chip_and_handler(virq, &twl6030_irq_chip, handle_simple_irq); | 315 | |
316 | irq_set_chip_data(virq, pdata); | ||
317 | irq_set_chip_and_handler(virq, &pdata->irq_chip, handle_simple_irq); | ||
311 | irq_set_nested_thread(virq, true); | 318 | irq_set_nested_thread(virq, true); |
312 | irq_set_parent(virq, twl_irq); | 319 | irq_set_parent(virq, pdata->twl_irq); |
313 | 320 | ||
314 | #ifdef CONFIG_ARM | 321 | #ifdef CONFIG_ARM |
315 | /* | 322 | /* |
@@ -349,6 +356,12 @@ int twl6030_init_irq(struct device *dev, int irq_num) | |||
349 | 356 | ||
350 | nr_irqs = TWL6030_NR_IRQS; | 357 | nr_irqs = TWL6030_NR_IRQS; |
351 | 358 | ||
359 | twl6030_irq = devm_kzalloc(dev, sizeof(*twl6030_irq), GFP_KERNEL); | ||
360 | if (!twl6030_irq) { | ||
361 | dev_err(dev, "twl6030_irq: Memory allocation failed\n"); | ||
362 | return -ENOMEM; | ||
363 | } | ||
364 | |||
352 | mask[0] = 0xFF; | 365 | mask[0] = 0xFF; |
353 | mask[1] = 0xFF; | 366 | mask[1] = 0xFF; |
354 | mask[2] = 0xFF; | 367 | mask[2] = 0xFF; |
@@ -369,14 +382,18 @@ int twl6030_init_irq(struct device *dev, int irq_num) | |||
369 | * install an irq handler for each of the modules; | 382 | * install an irq handler for each of the modules; |
370 | * clone dummy irq_chip since PIH can't *do* anything | 383 | * clone dummy irq_chip since PIH can't *do* anything |
371 | */ | 384 | */ |
372 | twl6030_irq_chip = dummy_irq_chip; | 385 | twl6030_irq->irq_chip = dummy_irq_chip; |
373 | twl6030_irq_chip.name = "twl6030"; | 386 | twl6030_irq->irq_chip.name = "twl6030"; |
374 | twl6030_irq_chip.irq_set_type = NULL; | 387 | twl6030_irq->irq_chip.irq_set_type = NULL; |
375 | twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake; | 388 | twl6030_irq->irq_chip.irq_set_wake = twl6030_irq_set_wake; |
376 | 389 | ||
377 | irq_domain = irq_domain_add_linear(node, nr_irqs, | 390 | twl6030_irq->pm_nb.notifier_call = twl6030_irq_pm_notifier; |
378 | &twl6030_irq_domain_ops, NULL); | 391 | atomic_set(&twl6030_irq->wakeirqs, 0); |
379 | if (!irq_domain) { | 392 | |
393 | twl6030_irq->irq_domain = | ||
394 | irq_domain_add_linear(node, nr_irqs, | ||
395 | &twl6030_irq_domain_ops, twl6030_irq); | ||
396 | if (!twl6030_irq->irq_domain) { | ||
380 | dev_err(dev, "Can't add irq_domain\n"); | 397 | dev_err(dev, "Can't add irq_domain\n"); |
381 | return -ENOMEM; | 398 | return -ENOMEM; |
382 | } | 399 | } |
@@ -385,26 +402,26 @@ int twl6030_init_irq(struct device *dev, int irq_num) | |||
385 | 402 | ||
386 | /* install an irq handler to demultiplex the TWL6030 interrupt */ | 403 | /* install an irq handler to demultiplex the TWL6030 interrupt */ |
387 | status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread, | 404 | status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread, |
388 | IRQF_ONESHOT, "TWL6030-PIH", irq_domain); | 405 | IRQF_ONESHOT, "TWL6030-PIH", twl6030_irq); |
389 | if (status < 0) { | 406 | if (status < 0) { |
390 | dev_err(dev, "could not claim irq %d: %d\n", irq_num, status); | 407 | dev_err(dev, "could not claim irq %d: %d\n", irq_num, status); |
391 | goto fail_irq; | 408 | goto fail_irq; |
392 | } | 409 | } |
393 | 410 | ||
394 | twl_irq = irq_num; | 411 | twl6030_irq->twl_irq = irq_num; |
395 | register_pm_notifier(&twl6030_irq_pm_notifier_block); | 412 | register_pm_notifier(&twl6030_irq->pm_nb); |
396 | return 0; | 413 | return 0; |
397 | 414 | ||
398 | fail_irq: | 415 | fail_irq: |
399 | irq_domain_remove(irq_domain); | 416 | irq_domain_remove(twl6030_irq->irq_domain); |
400 | return status; | 417 | return status; |
401 | } | 418 | } |
402 | 419 | ||
403 | int twl6030_exit_irq(void) | 420 | int twl6030_exit_irq(void) |
404 | { | 421 | { |
405 | if (twl_irq) { | 422 | if (twl6030_irq && twl6030_irq->twl_irq) { |
406 | unregister_pm_notifier(&twl6030_irq_pm_notifier_block); | 423 | unregister_pm_notifier(&twl6030_irq->pm_nb); |
407 | free_irq(twl_irq, NULL); | 424 | free_irq(twl6030_irq->twl_irq, NULL); |
408 | /* | 425 | /* |
409 | * TODO: IRQ domain and allocated nested IRQ descriptors | 426 | * TODO: IRQ domain and allocated nested IRQ descriptors |
410 | * should be freed somehow here. Now It can't be done, because | 427 | * should be freed somehow here. Now It can't be done, because |