From patchwork Mon Mar 25 13:51:42 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bastian Hecht X-Patchwork-Id: 2331391 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id CD7FC3FC54 for ; Mon, 25 Mar 2013 13:51:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757953Ab3CYNvw (ORCPT ); Mon, 25 Mar 2013 09:51:52 -0400 Received: from mail-ea0-f181.google.com ([209.85.215.181]:54993 "EHLO mail-ea0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757950Ab3CYNvv (ORCPT ); Mon, 25 Mar 2013 09:51:51 -0400 Received: by mail-ea0-f181.google.com with SMTP id z10so2298891ead.26 for ; Mon, 25 Mar 2013 06:51:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer; bh=I9It5Q7jTp/JYn8EMz8Q/ajItNBLD5pHp6zG/Plr2SU=; b=ZIdW6cQf8n0LkARhxNc1LumLMywRH9UJtZERwfpNhb9YVG7zBzrGUCPrinSSBiacOk AjuyIqE6AD9qB8WJfMBnz5PuKbNkLxV8GdCdl1XxMxDzWCDdYy1wJA9SdhuOZVagsTyn i1+R7P/Nypr+I0E5tlkRzrYgXUatDscjJuOt/M65X5zhn6QNsM3HsHUSPzzBPmiqND6n VlgxbBiTC8i+VzEStsUQdqqtvOX/9LCICDuk3kDL664AoIHGR0B8488RFgUET71Q0u/l auM4MoJzxXMndegG7HRD16FPLSv6jTqsrgrLgmDLxfnXMth5rmzaU2iy9XoLf/Kd8mQz eArw== X-Received: by 10.15.36.67 with SMTP id h43mr33745887eev.5.1364219510571; Mon, 25 Mar 2013 06:51:50 -0700 (PDT) Received: from localhost.localdomain (p4FD22CC3.dip.t-dialin.net. [79.210.44.195]) by mx.google.com with ESMTPS id s3sm19203365eem.4.2013.03.25.06.51.48 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 25 Mar 2013 06:51:50 -0700 (PDT) From: Bastian Hecht To: linux-sh@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, Magnus Damm , Simon Horman Subject: [PATCH v2 1/2] irqchip: intc-irqpin: Add support for shared interrupt lines Date: Mon, 25 Mar 2013 14:51:42 +0100 Message-Id: <1364219503-7738-1-git-send-email-hechtb+renesas@gmail.com> X-Mailer: git-send-email 1.7.9.5 Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org 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. If you need multiple shared intc_irqpin device instances, be sure to mask out all interrupts on the INTC that share the one line before you start to register them. Else you run into IRQ floods that would be caused by interrupts for which no handler has been set up yet when the first intc_irqpin device is registered. Signed-off-by: Bastian Hecht Acked-by: Magnus Damm --- 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. drivers/irqchip/irq-renesas-intc-irqpin.c | 90 ++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index fd5dabc..5a68e5a 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 { struct platform_device *pdev; struct irq_chip irq_chip; struct irq_domain *irq_domain; + bool shared_irqs; + u8 shared_irq_mask; }; static unsigned long intc_irqpin_read32(void __iomem *iomem) @@ -193,6 +195,28 @@ static void intc_irqpin_irq_disable(struct irq_data *d) intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); } +static void intc_irqpin_shared_irq_enable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); + + p->shared_irq_mask &= ~BIT(hw_irq); +} + +static void intc_irqpin_shared_irq_disable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "shared 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) { 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) 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) { @@ -292,6 +335,7 @@ 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 ref_irq; int ret; int k; @@ -372,13 +416,29 @@ static int intc_irqpin_probe(struct platform_device *pdev) 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); + + /* scan for shared interrupt lines */ + ref_irq = p->irq[0].requested_irq; + p->shared_irqs = true; + for (k = 1; k < p->number_of_irqs; k++) { + if (ref_irq != p->irq[k].requested_irq) { + p->shared_irqs = false; + break; + } + } + /* use more severe masking method if requested */ if (p->config.control_parent) { enable_fn = intc_irqpin_irq_enable_force; disable_fn = intc_irqpin_irq_disable_force; - } else { + } else if (!p->shared_irqs) { enable_fn = intc_irqpin_irq_enable; disable_fn = intc_irqpin_irq_disable; + } else { + enable_fn = intc_irqpin_shared_irq_enable; + disable_fn = intc_irqpin_shared_irq_disable; } irq_chip = &p->irq_chip; @@ -400,18 +460,34 @@ 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])) { + if (p->shared_irqs) { + /* request one shared interrupt */ + 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); + } else { + /* 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; + } + } } + /* 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 */