[PATCH] irqchip: intc-irqpin: Add support for shared interrupt lines

Bastian Hecht hechtb at gmail.com
Mon Mar 25 09:51:20 EDT 2013


Hi,

I'll post a follow-up. The changelog:

v2:

Nothing fundamentally changed but several balancing issues:

We don't check for mixing single and shared IRQs but expect the user not to
do this. Code gets a good bit shorter.

Same for unmasked IRQs. We don't check it but assume the user to do it.

Add dis-/enable IRQ functions for the shared case. While we add verbosity,
we unburden the normal case hotpath from the extra read/modify/write step
to update the shared_irq_mask.


2013/3/24 Bastian Hecht <hechtb at gmail.com>:
> On some hardware we don't have a 1-1 mapping from the external
> interrupts coming from INTC to the GIC SPI pins. We can however
> share lines to demux incoming IRQs on these SoCs.
>
> This patch enables the intc_irqpin driver to detect requests for shared
> interrupt lines and demuxes them properly by querying the INTC INTREQx0A
> registers.
>
> As multiple driver instances can use the same shared interrupt line, we
> need to pay extra attention during the device probing to exclude IRQ
> floods that would be caused by interrupts for which no handler has been
> set up yet.
>
> Signed-off-by: Bastian Hecht <hechtb+renesas at gmail.com>
> ---
>  drivers/irqchip/irq-renesas-intc-irqpin.c |  110 ++++++++++++++++++++++++++---
>  1 file changed, 101 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
> index fd5dabc..b6fc6a6 100644
> --- a/drivers/irqchip/irq-renesas-intc-irqpin.c
> +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
> @@ -74,8 +74,11 @@ struct intc_irqpin_priv {
>         struct platform_device *pdev;
>         struct irq_chip irq_chip;
>         struct irq_domain *irq_domain;
> +       u8 shared_irq_mask;
>  };
>
> +enum { IRQ_SCAN_START, IRQ_SCAN_SHARED, IRQ_SCAN_SINGLE, IRQ_SCAN_ERROR };
> +
>  static unsigned long intc_irqpin_read32(void __iomem *iomem)
>  {
>         return ioread32(iomem);
> @@ -182,6 +185,8 @@ static void intc_irqpin_irq_enable(struct irq_data *d)
>
>         intc_irqpin_dbg(&p->irq[hw_irq], "enable");
>         intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq);
> +
> +       p->shared_irq_mask &= ~BIT(hw_irq);
>  }
>
>  static void intc_irqpin_irq_disable(struct irq_data *d)
> @@ -191,6 +196,8 @@ static void intc_irqpin_irq_disable(struct irq_data *d)
>
>         intc_irqpin_dbg(&p->irq[hw_irq], "disable");
>         intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq);
> +
> +       p->shared_irq_mask |= BIT(hw_irq);
>  }
>
>  static void intc_irqpin_irq_enable_force(struct irq_data *d)
> @@ -261,8 +268,27 @@ static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id)
>         return IRQ_NONE;
>  }
>
> +static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id)
> +{
> +       struct intc_irqpin_priv *p = dev_id;
> +       unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE);
> +       irqreturn_t status = IRQ_NONE;
> +       int k;
> +
> +       for (k = 0; k < 8; k++) {
> +               if (reg_source & BIT(7 - k)) {
> +                       if (BIT(k) & p->shared_irq_mask)
> +                               continue;
> +
> +                       status |= intc_irqpin_irq_handler(irq, &p->irq[k]);
> +               }
> +       }
> +
> +       return status;
> +}
> +
>  static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
> -                                     irq_hw_number_t hw)
> +                                       irq_hw_number_t hw)
>  {
>         struct intc_irqpin_priv *p = h->host_data;
>
> @@ -278,7 +304,7 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
>
>  static struct irq_domain_ops intc_irqpin_irq_domain_ops = {
>         .map    = intc_irqpin_irq_domain_map,
> -       .xlate  = irq_domain_xlate_twocell,
> +       .xlate  = irq_domain_xlate_twocell,
>  };
>
>  static int intc_irqpin_probe(struct platform_device *pdev)
> @@ -292,6 +318,8 @@ static int intc_irqpin_probe(struct platform_device *pdev)
>         void (*enable_fn)(struct irq_data *d);
>         void (*disable_fn)(struct irq_data *d);
>         const char *name = dev_name(&pdev->dev);
> +       int scan_status;
> +       int reg_prio;
>         int ret;
>         int k;
>
> @@ -369,9 +397,13 @@ static int intc_irqpin_probe(struct platform_device *pdev)
>         }
>
>         /* mask all interrupts using priority */
> +       reg_prio = intc_irqpin_read(p, INTC_IRQPIN_REG_PRIO);
>         for (k = 0; k < p->number_of_irqs; k++)
>                 intc_irqpin_mask_unmask_prio(p, k, 1);
>
> +       /* clear all pending interrupts */
> +       intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0);
> +
>         /* use more severe masking method if requested */
>         if (p->config.control_parent) {
>                 enable_fn = intc_irqpin_irq_enable_force;
> @@ -400,18 +432,78 @@ static int intc_irqpin_probe(struct platform_device *pdev)
>                 goto err0;
>         }
>
> -       /* request and set priority on interrupts one by one */
> -       for (k = 0; k < p->number_of_irqs; k++) {
> -               if (devm_request_irq(&pdev->dev, p->irq[k].requested_irq,
> -                                    intc_irqpin_irq_handler,
> -                                    0, name, &p->irq[k])) {
> +       /* scan for shared interrupt lines */
> +       scan_status = IRQ_SCAN_START;
> +       for (k = 1; k < p->number_of_irqs; k++) {
> +               if (p->irq[k].requested_irq == p->irq[0].requested_irq) {
> +                       if (scan_status == IRQ_SCAN_SINGLE) {
> +                               scan_status = IRQ_SCAN_ERROR;
> +                               break;
> +                       }
> +                       scan_status = IRQ_SCAN_SHARED;
> +               } else {
> +                       if (scan_status == IRQ_SCAN_SHARED) {
> +                               scan_status = IRQ_SCAN_ERROR;
> +                               break;
> +                       }
> +                       scan_status = IRQ_SCAN_SINGLE;
> +               }
> +       }
> +
> +       switch (scan_status) {
> +       case IRQ_SCAN_START:    /* means we got only 1 interrupt */
> +       case IRQ_SCAN_SINGLE:
> +               /* request interrupts one by one */
> +               for (k = 0; k < p->number_of_irqs; k++) {
> +                       if (devm_request_irq(&pdev->dev,
> +                                       p->irq[k].requested_irq,
> +                                       intc_irqpin_irq_handler,
> +                                       0, name, &p->irq[k])) {
> +                               dev_err(&pdev->dev,
> +                                       "failed to request low IRQ\n");
> +                               ret = -ENOENT;
> +                               goto err1;
> +                       }
> +               }
> +               break;
> +
> +       case IRQ_SCAN_SHARED:
> +               /* request one shared interrupt
> +                *
> +                * On some hardware (like r8a7740) the shared interrupt only
> +                * is silent if we mask out REG_PRIO *and* REG_MASK. To save
> +                * the user from an IRQ-flood when using multiple intc_irqpin
> +                * instances in a shared fashion, we enforce this masking a
> +                * priori.
> +                */
> +               p->shared_irq_mask = intc_irqpin_read(p, INTC_IRQPIN_REG_MASK);
> +               if (p->shared_irq_mask != 0xff || reg_prio != 0x00) {
> +                       dev_err(&pdev->dev,
> +                               "expected all interrupts to be masked out "\
> +                               "when using shared interrupts\n");
> +                       ret = -EINVAL;
> +                       goto err1;
> +               }
> +               if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq,
> +                               intc_irqpin_shared_irq_handler,
> +                               IRQF_SHARED, name, p)) {
>                         dev_err(&pdev->dev, "failed to request low IRQ\n");
>                         ret = -ENOENT;
>                         goto err1;
>                 }
> -               intc_irqpin_mask_unmask_prio(p, k, 0);
> +               break;
> +
> +       case IRQ_SCAN_ERROR:
> +               dev_err(&pdev->dev,
> +                       "mixing single and shared IRQ lines not supported\n");
> +               ret = -EINVAL;
> +               goto err0;
>         }
>
> +       /* unmask all interrupts on prio level */
> +       for (k = 0; k < p->number_of_irqs; k++)
> +               intc_irqpin_mask_unmask_prio(p, k, 0);
> +
>         dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
>
>         /* warn in case of mismatch if irq base is specified */
> @@ -450,7 +542,7 @@ static struct platform_driver intc_irqpin_device_driver = {
>         .driver         = {
>                 .name   = "renesas_intc_irqpin",
>                 .of_match_table = intc_irqpin_dt_ids,
> -               .owner  = THIS_MODULE,
> +               .owner  = THIS_MODULE,
>         }
>  };
>
> --
> 1.7.9.5
>



More information about the linux-arm-kernel mailing list