From patchwork Thu Oct 17 06:37:33 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sebastian Hesselbarth X-Patchwork-Id: 3059031 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 14DB29F243 for ; Thu, 17 Oct 2013 06:38:28 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B62DA203AE for ; Thu, 17 Oct 2013 06:38:26 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7551E203AD for ; Thu, 17 Oct 2013 06:38:25 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VWhDt-0002k6-F6; Thu, 17 Oct 2013 06:38:17 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VWhDr-0002io-3m; Thu, 17 Oct 2013 06:38:15 +0000 Received: from mail-ea0-x229.google.com ([2a00:1450:4013:c01::229]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VWhDn-0002hY-SI for linux-arm-kernel@lists.infradead.org; Thu, 17 Oct 2013 06:38:13 +0000 Received: by mail-ea0-f169.google.com with SMTP id k11so830831eaj.28 for ; Wed, 16 Oct 2013 23:37:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=M4UVvOpj/nMrLNVskk3QI40QhP8zab2e54gZj0C/qRY=; b=JgbX+P8oXDcZ3dBGOq3baoYScl89NlsfrAKTFrqcy/Xv8e6JZE3pWbkmE/btsPNlcz izvwTAFJQjTohudnKIpUGmIVGXM2XIYp6pvxkFejdVP2QlgnUyOKoIWQeenOpqJB1v25 F2ZHfNTaZu5WA3EaNOuuP+I0HVPTp9j2Kq16t2RB7HMBXeI1JcuFJg9Tpn0gvzparElP RoW8U0As9eFeWrlYsiDxO8Y4bR/ex2sW8k2ZCv2Bt0S/NFsH/A+jEAIP8qNml4WBiYLM oAz71WTmsXe4t09JWzd5IeTkUKTa7It/LFK88goCsvto37UiT0Pp6kf6bOAtOrMEk4xa tkkA== X-Received: by 10.15.65.14 with SMTP id p14mr280076eex.96.1381991869654; Wed, 16 Oct 2013 23:37:49 -0700 (PDT) Received: from topkick.lan (dslc-082-083-247-252.pools.arcor-ip.net. [82.83.247.252]) by mx.google.com with ESMTPSA id a43sm188865484eep.9.1969.12.31.16.00.00 (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 16 Oct 2013 23:37:48 -0700 (PDT) From: Sebastian Hesselbarth To: Sebastian Hesselbarth Subject: [PATCH v2 1/8] irqchip: add DesignWare APB ICTL interrupt controller Date: Thu, 17 Oct 2013 08:37:33 +0200 Message-Id: <1381991853-8131-1-git-send-email-sebastian.hesselbarth@gmail.com> In-Reply-To: <1381235073-17134-2-git-send-email-sebastian.hesselbarth@gmail.com> References: <1381235073-17134-2-git-send-email-sebastian.hesselbarth@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131017_023812_203594_D9242225 X-CRM114-Status: GOOD ( 23.22 ) X-Spam-Score: 1.0 (+) Cc: Thomas Petazzoni , Mark Rutland , Jason Cooper , Arnd Bergmann , devicetree@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Jisheng Zhang , Thomas Gleixner , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-3.5 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, KHOP_BIG_TO_CC, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This adds an irqchip driver and corresponding devicetree binding for the secondary interrupt controllers based on Synopsys DesignWare IP dw_apb_ictl. Signed-off-by: Sebastian Hesselbarth Reviewed-by: Mark Rutland Reviewed-by: Jisheng Zhang --- Changelog: v1->v2: - reword binding docu for reg property and add FIQ decoding note (Suggested by Mark Rutland) - add cleanup on error path (Reported by Jisheng Zhang) RFCv1->RFCv2: - added copyright reference Cc: Jason Cooper Cc: Thomas Petazzoni Cc: Arnd Bergmann Cc: Thomas Gleixner Cc: Mark Rutland Cc: Jisheng Zhang Cc: devicetree@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org --- .../interrupt-controller/snps,dw-apb-ictl.txt | 32 +++++ drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-dw-apb-ictl.c | 150 ++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/snps,dw-apb-ictl.txt create mode 100644 drivers/irqchip/irq-dw-apb-ictl.c diff --git a/Documentation/devicetree/bindings/interrupt-controller/snps,dw-apb-ictl.txt b/Documentation/devicetree/bindings/interrupt-controller/snps,dw-apb-ictl.txt new file mode 100644 index 0000000..4929117 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/snps,dw-apb-ictl.txt @@ -0,0 +1,32 @@ +Synopsys DesignWare APB interrupt controller (dw_apb_ictl) + +Synopsys DesignWare provides interrupt controller IP for APB known as +dw_apb_ictl. The IP is used as secondary interrupt controller in some SoCs with +APB bus, e.g. Marvell Armada 1500. + +Required properties: +- compatible: shall be "snps,dw-apb-ictl" +- reg: physical base address of the controller and length of memory mapped + region starting with ENABLE_LOW register +- interrupt-controller: identifies the node as an interrupt controller +- #interrupt-cells: number of cells to encode an interrupt-specifier, shall be 1 +- interrupts: interrupt reference to primary interrupt controller +- interrupt-parent: (optional) reference specific primary interrupt controller + +The interrupt sources map to the corresponding bits in the interrupt +registers, i.e. +- 0 maps to bit 0 of low interrupts, +- 1 maps to bit 1 of low interrupts, +- 32 maps to bit 0 of high interrupts, +- 33 maps to bit 1 of high interrupts, +- (optional) fast interrupts start at 64. + +Example: + aic: interrupt-controller@3000 { + compatible = "snps,dw-apb-ictl"; + reg = <0x3000 0xc00>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic>; + interrupts = ; + }; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3792a1a..940638d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -30,6 +30,10 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config DW_APB_ICTL + bool + select IRQ_DOMAIN + config IMGPDC_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index c60b901..6427323 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_ARCH_MMP) += irq-mmp.o obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o obj-$(CONFIG_ARCH_MXS) += irq-mxs.o obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o +obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c new file mode 100644 index 0000000..31e231e --- /dev/null +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -0,0 +1,150 @@ +/* + * Synopsys DW APB ICTL irqchip driver. + * + * Sebastian Hesselbarth + * + * based on GPL'ed 2.6 kernel sources + * (c) Marvell International Ltd. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "irqchip.h" + +#define APB_INT_ENABLE_L 0x00 +#define APB_INT_ENABLE_H 0x04 +#define APB_INT_MASK_L 0x08 +#define APB_INT_MASK_H 0x0c +#define APB_INT_FINALSTATUS_L 0x30 +#define APB_INT_FINALSTATUS_H 0x34 + +static void dw_apb_ictl_handler(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct irq_chip_generic *gc = irq_get_handler_data(irq); + struct irq_domain *d = gc->private; + u32 stat; + int n; + + chained_irq_enter(chip, desc); + + for (n = 0; n < gc->num_ct; n++) { + stat = readl_relaxed(gc->reg_base + + APB_INT_FINALSTATUS_L + 4 * n); + while (stat) { + u32 hwirq = ffs(stat) - 1; + generic_handle_irq(irq_find_mapping(d, + gc->irq_base + hwirq + 32 * n)); + stat &= ~(1 << hwirq); + } + } + + chained_irq_exit(chip, desc); +} + +static int __init dw_apb_ictl_init(struct device_node *np, + struct device_node *parent) +{ + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + struct resource r; + struct irq_domain *domain; + struct irq_chip_generic *gc; + void __iomem *iobase; + int ret, nrirqs, irq; + u32 reg; + + /* Map the parent interrupt for the chained handler */ + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("%s: unable to parse irq\n", np->full_name); + return -EINVAL; + } + + ret = of_address_to_resource(np, 0, &r); + if (ret) { + pr_err("%s: unable to get resource\n", np->full_name); + return ret; + } + + if (!request_mem_region(r.start, resource_size(&r), np->full_name)) { + pr_err("%s: unable to request mem region\n", np->full_name); + return -ENOMEM; + } + + iobase = ioremap(r.start, resource_size(&r)); + if (!iobase) { + pr_err("%s: unable to map resource\n", np->full_name); + ret = -ENOMEM; + goto err_release; + } + + /* + * DW IP can be configured to allow 2-64 irqs. We can determine + * the number of irqs supported by writing into enable register + * and look for bits not set, as corresponding flip-flops will + * have been removed by sythesis tool. + */ + + /* mask and enable all interrupts */ + writel(~0, iobase + APB_INT_MASK_L); + writel(~0, iobase + APB_INT_MASK_H); + writel(~0, iobase + APB_INT_ENABLE_L); + writel(~0, iobase + APB_INT_ENABLE_H); + + reg = readl(iobase + APB_INT_ENABLE_H); + if (reg) + nrirqs = 32 + fls(reg); + else + nrirqs = fls(readl(iobase + APB_INT_ENABLE_L)); + + domain = irq_domain_add_linear(np, nrirqs, + &irq_generic_chip_ops, NULL); + if (!domain) { + pr_err("%s: unable to add irq domain\n", np->full_name); + ret = -ENOMEM; + goto err_unmap; + } + + ret = irq_alloc_domain_generic_chips(domain, 32, (nrirqs > 32) ? 2 : 1, + np->name, handle_level_irq, clr, 0, + IRQ_GC_INIT_MASK_CACHE); + if (ret) { + pr_err("%s: unable to alloc irq domain gc\n", np->full_name); + goto err_unmap; + } + + gc = irq_get_domain_generic_chip(domain, 0); + gc->private = domain; + gc->reg_base = iobase; + + gc->chip_types[0].regs.mask = APB_INT_MASK_L; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; + + if (nrirqs > 32) { + gc->chip_types[1].regs.mask = APB_INT_MASK_H; + gc->chip_types[1].chip.irq_mask = irq_gc_mask_set_bit; + gc->chip_types[1].chip.irq_unmask = irq_gc_mask_clr_bit; + } + + irq_set_handler_data(irq, gc); + irq_set_chained_handler(irq, dw_apb_ictl_handler); + + return 0; + +err_unmap: + iounmap(iobase); +err_release: + release_mem_region(r.start, resource_size(&r)); + return ret; +} +IRQCHIP_DECLARE(dw_apb_ictl, + "snps,dw-apb-ictl", dw_apb_ictl_init);