aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2013-04-08 11:52:35 -0400
committerArnd Bergmann <arnd@arndb.de>2013-04-08 11:52:35 -0400
commit4680ebc2c90f663ba70c6bb3d8596b0f2c4dfa9e (patch)
treeb4db4387357dc651389d6d223c1d3a4e13f7f64d
parent0dfca419b5448082b7e2f7db4de2e4643dfbfe35 (diff)
parent427cc720277c140e6a63a03237f9bf37d8076ac3 (diff)
Merge tag 'renesas-intc-external-irq2-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas into next/drivers
From Simon Horman <horms+renesas@verge.net.au>: Update for Renesas INTC External IRQ pin driver for v3.10 This adds support for shared interrupt lines to the Renesas INTC External IRQ pin driver which has already been queued up for v3.10 (tag renesas-intc-external-irq-for-v3.10). * tag 'renesas-intc-external-irq2-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas: irqchip: intc-irqpin: Add support for shared interrupt lines Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c90
1 files changed, 83 insertions, 7 deletions
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index fd5dabc2235d..5a68e5accec1 100644
--- a/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -74,6 +74,8 @@ struct intc_irqpin_priv {
74 struct platform_device *pdev; 74 struct platform_device *pdev;
75 struct irq_chip irq_chip; 75 struct irq_chip irq_chip;
76 struct irq_domain *irq_domain; 76 struct irq_domain *irq_domain;
77 bool shared_irqs;
78 u8 shared_irq_mask;
77}; 79};
78 80
79static unsigned long intc_irqpin_read32(void __iomem *iomem) 81static unsigned long intc_irqpin_read32(void __iomem *iomem)
@@ -193,6 +195,28 @@ static void intc_irqpin_irq_disable(struct irq_data *d)
193 intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); 195 intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq);
194} 196}
195 197
198static void intc_irqpin_shared_irq_enable(struct irq_data *d)
199{
200 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
201 int hw_irq = irqd_to_hwirq(d);
202
203 intc_irqpin_dbg(&p->irq[hw_irq], "shared enable");
204 intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq);
205
206 p->shared_irq_mask &= ~BIT(hw_irq);
207}
208
209static void intc_irqpin_shared_irq_disable(struct irq_data *d)
210{
211 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
212 int hw_irq = irqd_to_hwirq(d);
213
214 intc_irqpin_dbg(&p->irq[hw_irq], "shared disable");
215 intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq);
216
217 p->shared_irq_mask |= BIT(hw_irq);
218}
219
196static void intc_irqpin_irq_enable_force(struct irq_data *d) 220static void intc_irqpin_irq_enable_force(struct irq_data *d)
197{ 221{
198 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 222 struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
@@ -261,6 +285,25 @@ static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id)
261 return IRQ_NONE; 285 return IRQ_NONE;
262} 286}
263 287
288static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id)
289{
290 struct intc_irqpin_priv *p = dev_id;
291 unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE);
292 irqreturn_t status = IRQ_NONE;
293 int k;
294
295 for (k = 0; k < 8; k++) {
296 if (reg_source & BIT(7 - k)) {
297 if (BIT(k) & p->shared_irq_mask)
298 continue;
299
300 status |= intc_irqpin_irq_handler(irq, &p->irq[k]);
301 }
302 }
303
304 return status;
305}
306
264static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, 307static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
265 irq_hw_number_t hw) 308 irq_hw_number_t hw)
266{ 309{
@@ -292,6 +335,7 @@ static int intc_irqpin_probe(struct platform_device *pdev)
292 void (*enable_fn)(struct irq_data *d); 335 void (*enable_fn)(struct irq_data *d);
293 void (*disable_fn)(struct irq_data *d); 336 void (*disable_fn)(struct irq_data *d);
294 const char *name = dev_name(&pdev->dev); 337 const char *name = dev_name(&pdev->dev);
338 int ref_irq;
295 int ret; 339 int ret;
296 int k; 340 int k;
297 341
@@ -372,13 +416,29 @@ static int intc_irqpin_probe(struct platform_device *pdev)
372 for (k = 0; k < p->number_of_irqs; k++) 416 for (k = 0; k < p->number_of_irqs; k++)
373 intc_irqpin_mask_unmask_prio(p, k, 1); 417 intc_irqpin_mask_unmask_prio(p, k, 1);
374 418
419 /* clear all pending interrupts */
420 intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0);
421
422 /* scan for shared interrupt lines */
423 ref_irq = p->irq[0].requested_irq;
424 p->shared_irqs = true;
425 for (k = 1; k < p->number_of_irqs; k++) {
426 if (ref_irq != p->irq[k].requested_irq) {
427 p->shared_irqs = false;
428 break;
429 }
430 }
431
375 /* use more severe masking method if requested */ 432 /* use more severe masking method if requested */
376 if (p->config.control_parent) { 433 if (p->config.control_parent) {
377 enable_fn = intc_irqpin_irq_enable_force; 434 enable_fn = intc_irqpin_irq_enable_force;
378 disable_fn = intc_irqpin_irq_disable_force; 435 disable_fn = intc_irqpin_irq_disable_force;
379 } else { 436 } else if (!p->shared_irqs) {
380 enable_fn = intc_irqpin_irq_enable; 437 enable_fn = intc_irqpin_irq_enable;
381 disable_fn = intc_irqpin_irq_disable; 438 disable_fn = intc_irqpin_irq_disable;
439 } else {
440 enable_fn = intc_irqpin_shared_irq_enable;
441 disable_fn = intc_irqpin_shared_irq_disable;
382 } 442 }
383 443
384 irq_chip = &p->irq_chip; 444 irq_chip = &p->irq_chip;
@@ -400,18 +460,34 @@ static int intc_irqpin_probe(struct platform_device *pdev)
400 goto err0; 460 goto err0;
401 } 461 }
402 462
403 /* request and set priority on interrupts one by one */ 463 if (p->shared_irqs) {
404 for (k = 0; k < p->number_of_irqs; k++) { 464 /* request one shared interrupt */
405 if (devm_request_irq(&pdev->dev, p->irq[k].requested_irq, 465 if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq,
406 intc_irqpin_irq_handler, 466 intc_irqpin_shared_irq_handler,
407 0, name, &p->irq[k])) { 467 IRQF_SHARED, name, p)) {
408 dev_err(&pdev->dev, "failed to request low IRQ\n"); 468 dev_err(&pdev->dev, "failed to request low IRQ\n");
409 ret = -ENOENT; 469 ret = -ENOENT;
410 goto err1; 470 goto err1;
411 } 471 }
412 intc_irqpin_mask_unmask_prio(p, k, 0); 472 } else {
473 /* request interrupts one by one */
474 for (k = 0; k < p->number_of_irqs; k++) {
475 if (devm_request_irq(&pdev->dev,
476 p->irq[k].requested_irq,
477 intc_irqpin_irq_handler,
478 0, name, &p->irq[k])) {
479 dev_err(&pdev->dev,
480 "failed to request low IRQ\n");
481 ret = -ENOENT;
482 goto err1;
483 }
484 }
413 } 485 }
414 486
487 /* unmask all interrupts on prio level */
488 for (k = 0; k < p->number_of_irqs; k++)
489 intc_irqpin_mask_unmask_prio(p, k, 0);
490
415 dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); 491 dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
416 492
417 /* warn in case of mismatch if irq base is specified */ 493 /* warn in case of mismatch if irq base is specified */