diff options
author | Naga Venkata Srikanth V <vnv.srikanth@samsung.com> | 2013-07-25 09:15:47 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-08-20 04:19:02 -0400 |
commit | 87343e534117c2932adfb394351dc83d7c378af6 (patch) | |
tree | 22b8fe14d00d4662fed82f9fb1ea6ccd78bd8837 /drivers/mfd/twl6030-irq.c | |
parent | cc01b4639c94b1732995a9909a8973bfed67db2b (diff) |
mfd: twl6030-irq: Migrate to IRQ threaded handler
1) Removed request_irq() and replaced it with request_threaded_irq().
2) Removed generic_handle_irq() and replaced it with
handle_nested_irq().
Handling of these interrupts is nested, as we are handling an
interrupt (for e.g rtc, mmc1) when we are still servicing TWL irq.
3) Removed I2C read-retry logic for the case when twl_i2c_read() is
failed inside IRQ handler - there is no sense to do that, so just report
an error and return.
4) Each nested IRQ is configured with corresponding parent_irq,
which need to be retriggered in case if nested IRQ is marked
as IRQS_PENDING.
Signed-off-by: Naga Venkata Srikanth V <vnv.srikanth@samsung.com>
Signed-off-by: Oleg_Kosheliev <oleg.kosheliev@ti.com>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Acked-by: Graeme Gregory <gg@slimlogic.co.uk>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/twl6030-irq.c')
-rw-r--r-- | drivers/mfd/twl6030-irq.c | 150 |
1 files changed, 50 insertions, 100 deletions
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 277a8dba42d5..1606cedbbff4 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c | |||
@@ -90,7 +90,6 @@ static unsigned twl6030_irq_base; | |||
90 | static int twl_irq; | 90 | static int twl_irq; |
91 | static bool twl_irq_wake_enabled; | 91 | static bool twl_irq_wake_enabled; |
92 | 92 | ||
93 | static struct completion irq_event; | ||
94 | static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); | 93 | static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); |
95 | 94 | ||
96 | static int twl6030_irq_pm_notifier(struct notifier_block *notifier, | 95 | static int twl6030_irq_pm_notifier(struct notifier_block *notifier, |
@@ -131,95 +130,57 @@ static struct notifier_block twl6030_irq_pm_notifier_block = { | |||
131 | }; | 130 | }; |
132 | 131 | ||
133 | /* | 132 | /* |
134 | * This thread processes interrupts reported by the Primary Interrupt Handler. | 133 | * Threaded irq handler for the twl6030 interrupt. |
135 | */ | 134 | * We query the interrupt controller in the twl6030 to determine |
136 | static int twl6030_irq_thread(void *data) | 135 | * which module is generating the interrupt request and call |
136 | * handle_nested_irq for that module. | ||
137 | */ | ||
138 | static irqreturn_t twl6030_irq_thread(int irq, void *data) | ||
137 | { | 139 | { |
138 | long irq = (long)data; | 140 | int i, ret; |
139 | static unsigned i2c_errors; | 141 | union { |
140 | static const unsigned max_i2c_errors = 100; | ||
141 | int ret; | ||
142 | |||
143 | while (!kthread_should_stop()) { | ||
144 | int i; | ||
145 | union { | ||
146 | u8 bytes[4]; | 142 | u8 bytes[4]; |
147 | u32 int_sts; | 143 | u32 int_sts; |
148 | } sts; | 144 | } sts; |
149 | |||
150 | /* Wait for IRQ, then read PIH irq status (also blocking) */ | ||
151 | wait_for_completion_interruptible(&irq_event); | ||
152 | |||
153 | /* read INT_STS_A, B and C in one shot using a burst read */ | ||
154 | ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, | ||
155 | REG_INT_STS_A, 3); | ||
156 | if (ret) { | ||
157 | pr_warning("twl6030: I2C error %d reading PIH ISR\n", | ||
158 | ret); | ||
159 | if (++i2c_errors >= max_i2c_errors) { | ||
160 | printk(KERN_ERR "Maximum I2C error count" | ||
161 | " exceeded. Terminating %s.\n", | ||
162 | __func__); | ||
163 | break; | ||
164 | } | ||
165 | complete(&irq_event); | ||
166 | continue; | ||
167 | } | ||
168 | |||
169 | 145 | ||
146 | /* read INT_STS_A, B and C in one shot using a burst read */ | ||
147 | ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3); | ||
148 | if (ret) { | ||
149 | pr_warn("twl6030_irq: I2C error %d reading PIH ISR\n", ret); | ||
150 | return IRQ_HANDLED; | ||
151 | } | ||
170 | 152 | ||
171 | sts.bytes[3] = 0; /* Only 24 bits are valid*/ | 153 | sts.bytes[3] = 0; /* Only 24 bits are valid*/ |
172 | 154 | ||
173 | /* | 155 | /* |
174 | * Since VBUS status bit is not reliable for VBUS disconnect | 156 | * Since VBUS status bit is not reliable for VBUS disconnect |
175 | * use CHARGER VBUS detection status bit instead. | 157 | * use CHARGER VBUS detection status bit instead. |
176 | */ | 158 | */ |
177 | if (sts.bytes[2] & 0x10) | 159 | if (sts.bytes[2] & 0x10) |
178 | sts.bytes[2] |= 0x08; | 160 | sts.bytes[2] |= 0x08; |
179 | 161 | ||
180 | for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) { | 162 | for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) |
181 | local_irq_disable(); | 163 | if (sts.int_sts & 0x1) { |
182 | if (sts.int_sts & 0x1) { | 164 | int module_irq = twl6030_irq_base + |
183 | int module_irq = twl6030_irq_base + | ||
184 | twl6030_interrupt_mapping[i]; | 165 | twl6030_interrupt_mapping[i]; |
185 | generic_handle_irq(module_irq); | 166 | handle_nested_irq(module_irq); |
186 | 167 | pr_debug("twl6030_irq: PIH ISR %u, virq%u\n", | |
187 | } | 168 | i, module_irq); |
188 | local_irq_enable(); | ||
189 | } | 169 | } |
190 | 170 | ||
191 | /* | 171 | /* |
192 | * NOTE: | 172 | * NOTE: |
193 | * Simulation confirms that documentation is wrong w.r.t the | 173 | * Simulation confirms that documentation is wrong w.r.t the |
194 | * interrupt status clear operation. A single *byte* write to | 174 | * interrupt status clear operation. A single *byte* write to |
195 | * any one of STS_A to STS_C register results in all three | 175 | * any one of STS_A to STS_C register results in all three |
196 | * STS registers being reset. Since it does not matter which | 176 | * STS registers being reset. Since it does not matter which |
197 | * value is written, all three registers are cleared on a | 177 | * value is written, all three registers are cleared on a |
198 | * single byte write, so we just use 0x0 to clear. | 178 | * single byte write, so we just use 0x0 to clear. |
199 | */ | 179 | */ |
200 | ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A); | 180 | ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A); |
201 | if (ret) | 181 | if (ret) |
202 | pr_warning("twl6030: I2C error in clearing PIH ISR\n"); | 182 | pr_warn("twl6030_irq: I2C error in clearing PIH ISR\n"); |
203 | |||
204 | enable_irq(irq); | ||
205 | } | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | 183 | ||
210 | /* | ||
211 | * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt. | ||
212 | * This is a chained interrupt, so there is no desc->action method for it. | ||
213 | * Now we need to query the interrupt controller in the twl6030 to determine | ||
214 | * which module is generating the interrupt request. However, we can't do i2c | ||
215 | * transactions in interrupt context, so we must defer that work to a kernel | ||
216 | * thread. All we do here is acknowledge and mask the interrupt and wakeup | ||
217 | * the kernel thread. | ||
218 | */ | ||
219 | static irqreturn_t handle_twl6030_pih(int irq, void *devid) | ||
220 | { | ||
221 | disable_irq_nosync(irq); | ||
222 | complete(devid); | ||
223 | return IRQ_HANDLED; | 184 | return IRQ_HANDLED; |
224 | } | 185 | } |
225 | 186 | ||
@@ -351,7 +312,6 @@ int twl6030_init_irq(struct device *dev, int irq_num) | |||
351 | { | 312 | { |
352 | struct device_node *node = dev->of_node; | 313 | struct device_node *node = dev->of_node; |
353 | int nr_irqs, irq_base, irq_end; | 314 | int nr_irqs, irq_base, irq_end; |
354 | struct task_struct *task; | ||
355 | static struct irq_chip twl6030_irq_chip; | 315 | static struct irq_chip twl6030_irq_chip; |
356 | int status = 0; | 316 | int status = 0; |
357 | int i; | 317 | int i; |
@@ -396,36 +356,26 @@ int twl6030_init_irq(struct device *dev, int irq_num) | |||
396 | irq_set_chip_and_handler(i, &twl6030_irq_chip, | 356 | irq_set_chip_and_handler(i, &twl6030_irq_chip, |
397 | handle_simple_irq); | 357 | handle_simple_irq); |
398 | irq_set_chip_data(i, (void *)irq_num); | 358 | irq_set_chip_data(i, (void *)irq_num); |
359 | irq_set_nested_thread(i, true); | ||
360 | irq_set_parent(i, irq_num); | ||
399 | activate_irq(i); | 361 | activate_irq(i); |
400 | } | 362 | } |
401 | 363 | ||
402 | dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n", | 364 | dev_info(dev, "PIH (irq %d) nested IRQs %d..%d\n", |
403 | irq_num, irq_base, irq_end); | 365 | irq_num, irq_base, irq_end); |
404 | 366 | ||
405 | /* install an irq handler to demultiplex the TWL6030 interrupt */ | 367 | /* install an irq handler to demultiplex the TWL6030 interrupt */ |
406 | init_completion(&irq_event); | 368 | status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread, |
407 | 369 | IRQF_ONESHOT, "TWL6030-PIH", NULL); | |
408 | status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH", | ||
409 | &irq_event); | ||
410 | if (status < 0) { | 370 | if (status < 0) { |
411 | dev_err(dev, "could not claim irq %d: %d\n", irq_num, status); | 371 | dev_err(dev, "could not claim irq %d: %d\n", irq_num, status); |
412 | goto fail_irq; | 372 | goto fail_irq; |
413 | } | 373 | } |
414 | 374 | ||
415 | task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); | ||
416 | if (IS_ERR(task)) { | ||
417 | dev_err(dev, "could not create irq %d thread!\n", irq_num); | ||
418 | status = PTR_ERR(task); | ||
419 | goto fail_kthread; | ||
420 | } | ||
421 | |||
422 | twl_irq = irq_num; | 375 | twl_irq = irq_num; |
423 | register_pm_notifier(&twl6030_irq_pm_notifier_block); | 376 | register_pm_notifier(&twl6030_irq_pm_notifier_block); |
424 | return irq_base; | 377 | return irq_base; |
425 | 378 | ||
426 | fail_kthread: | ||
427 | free_irq(irq_num, &irq_event); | ||
428 | |||
429 | fail_irq: | 379 | fail_irq: |
430 | for (i = irq_base; i < irq_end; i++) | 380 | for (i = irq_base; i < irq_end; i++) |
431 | irq_set_chip_and_handler(i, NULL, NULL); | 381 | irq_set_chip_and_handler(i, NULL, NULL); |
@@ -435,12 +385,12 @@ fail_irq: | |||
435 | 385 | ||
436 | int twl6030_exit_irq(void) | 386 | int twl6030_exit_irq(void) |
437 | { | 387 | { |
438 | unregister_pm_notifier(&twl6030_irq_pm_notifier_block); | ||
439 | 388 | ||
440 | if (twl6030_irq_base) { | 389 | if (twl_irq) { |
441 | pr_err("twl6030: can't yet clean up IRQs?\n"); | 390 | unregister_pm_notifier(&twl6030_irq_pm_notifier_block); |
442 | return -ENOSYS; | 391 | free_irq(twl_irq, NULL); |
443 | } | 392 | } |
393 | |||
444 | return 0; | 394 | return 0; |
445 | } | 395 | } |
446 | 396 | ||