aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/twl6030-irq.c
diff options
context:
space:
mode:
authorNaga Venkata Srikanth V <vnv.srikanth@samsung.com>2013-07-25 09:15:47 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2013-08-20 04:19:02 -0400
commit87343e534117c2932adfb394351dc83d7c378af6 (patch)
tree22b8fe14d00d4662fed82f9fb1ea6ccd78bd8837 /drivers/mfd/twl6030-irq.c
parentcc01b4639c94b1732995a9909a8973bfed67db2b (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.c150
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;
90static int twl_irq; 90static int twl_irq;
91static bool twl_irq_wake_enabled; 91static bool twl_irq_wake_enabled;
92 92
93static struct completion irq_event;
94static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); 93static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
95 94
96static int twl6030_irq_pm_notifier(struct notifier_block *notifier, 95static 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
136static int twl6030_irq_thread(void *data) 135* which module is generating the interrupt request and call
136* handle_nested_irq for that module.
137*/
138static 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 */
219static 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
426fail_kthread:
427 free_irq(irq_num, &irq_event);
428
429fail_irq: 379fail_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
436int twl6030_exit_irq(void) 386int 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