[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