From patchwork Thu Jul 28 06:41:27 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 1014562 Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by demeter1.kernel.org (8.14.4/8.14.4) with ESMTP id p6S6oeS8017944 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 28 Jul 2011 06:51:01 GMT Received: from canuck.infradead.org ([134.117.69.58]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QmKQK-0001oB-9m; Thu, 28 Jul 2011 06:50:24 +0000 Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1QmKQI-0006jD-Lz; Thu, 28 Jul 2011 06:50:22 +0000 Received: from dakia2.marvell.com ([65.219.4.35]) by canuck.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QmKPI-0006XO-S9 for linux-arm-kernel@lists.infradead.org; Thu, 28 Jul 2011 06:49:24 +0000 X-ASG-Debug-ID: 1311835756-082cefc10001-tbGyMd Received: from maili.marvell.com (maili.marvell.com [10.68.76.51]) by dakia2.marvell.com with ESMTP id tG11CFkAtogslTXK; Wed, 27 Jul 2011 23:49:16 -0700 (PDT) X-Barracuda-Envelope-From: haojian.zhuang@marvell.com Received: from localhost (unknown [10.38.164.65]) by maili.marvell.com (Postfix) with ESMTP id 623418A002; Wed, 27 Jul 2011 23:49:16 -0700 (PDT) From: Haojian Zhuang To: haojian.zhuang@gmail.com, eric.y.miao@gmail.com, linux@arm.linux.org.uk, grant.likely@secretlab.ca, alan@linux.intel.com, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org X-ASG-Orig-Subj: [PATCH v2 1/7] ARM: mmp: parse irq from DT Subject: [PATCH v2 1/7] ARM: mmp: parse irq from DT Date: Thu, 28 Jul 2011 14:41:27 +0800 X-ASG-Orig-Subj: [PATCH v2 1/7] ARM: mmp: parse irq from DT Message-Id: <1311835293-18125-2-git-send-email-haojian.zhuang@marvell.com> X-Mailer: git-send-email 1.5.6.5 In-Reply-To: <1311835293-18125-1-git-send-email-haojian.zhuang@marvell.com> References: <1311835293-18125-1-git-send-email-haojian.zhuang@marvell.com> X-Barracuda-Connect: maili.marvell.com[10.68.76.51] X-Barracuda-Start-Time: 1311835756 X-Barracuda-URL: http://10.68.76.222:80/cgi-mod/mark.cgi X-Barracuda-Spam-Score: -1002.00 X-Barracuda-Spam-Status: No, SCORE=-1002.00 using global scores of TAG_LEVEL=1000.0 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=1000.0 X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110728_024921_260365_96833569 X-CRM114-Status: GOOD ( 29.32 ) X-Spam-Score: -1.2 (-) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-1.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -1.2 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain Cc: Haojian Zhuang X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Thu, 28 Jul 2011 06:51:01 +0000 (UTC) Parse irq sepcifier from DT and translate it to Linux irq number. Remove the definition of NR_IRQS in irqs.h. Since sparse irq is enabled, nr_irqs will be calculated automatically. Signed-off-by: Haojian Zhuang --- .../devicetree/bindings/arm/marvell/intc.txt | 114 ++++++++++ arch/arm/Kconfig | 1 + arch/arm/mach-mmp/Makefile | 2 + arch/arm/mach-mmp/common.h | 1 + arch/arm/mach-mmp/include/mach/irqs.h | 2 +- arch/arm/mach-mmp/intc.c | 224 ++++++++++++++++++++ 6 files changed, 343 insertions(+), 1 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/marvell/intc.txt create mode 100644 arch/arm/mach-mmp/intc.c diff --git a/Documentation/devicetree/bindings/arm/marvell/intc.txt b/Documentation/devicetree/bindings/arm/marvell/intc.txt new file mode 100644 index 0000000..80cef58 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/marvell/intc.txt @@ -0,0 +1,114 @@ +* Interrupt Controller Binding for ARCH-MMP + +This binding specifies what properties must be available in device tree representation of an ARCH-MMP compliant interrupt controller. + +Required properties: + + - compatible: Specifies the compatibility list of the interrupt + controller. The type shall be and the value shall be + "mrvl,pxa168-intc" or "mrvl,mmp2-mux-intc". + "mrvl,pxa168-intc" is the base interrupt controller. It must be + included. It's compatible for pxa910, mmp2. "mrvl,mmp2-mux-intc" + is the expanded interrupt controller, and it's optional. + + - reg: Specified the base physical address(s) and size(s) of the + interrupt controller's addressable register space. The type + should be . + + - interrupt-controller: The presence of this property identifies + the node as interrupt controller. No property value should be + defined. + + - #interrupt-cells: Specifies the number of cells needed to encode + an interrupt source. The type should be and the value should + be 1. + + - mrvl,intc-numbers: Specifies the number of interrupts is supported + in this interrupt controller. The type should be . + +Optional properties: + + - mrvl,intc-priority: Specifies the which path the interrupt is routed + and the priority of this interrupt. The property is used in + pxa168-intc. The value should be . + + - mrvl,status-offset: Specifies the offset of status register. The + property is used in mmp2-mux-intc. The type should be . + + - mrvl,mask-offset: Specifies the offset of mask register. The + property is used in mmp2-mux-intc. The type should be . + + - mrvl,mfp-edge: Specifies the address of mfp edge detection register. + The property is used while acking specified interrupt. The type + should be . The first cell indicates the address + of mfp edge detection register. The second cell indicates the + index of interrupt in current interrupt controller that should + handle mfp edge detection. + +* Examples + +Example 1: + + /* + * base INTC + */ + mmp_intc: interrupt-controller@d4282000 { + /* Compatible with pxa168-intc. */ + compatible = "mrvl,pxa168-intc"; + #address-cells = <1>; + #size-cells = <1>; + /* Offset address of 0xd4282000 and size of 0x400. */ + reg = <0xd4282000 0x400>; + + #interrupt-cells = <1>; + interrupt-controller; + + /* 64 interrupts are supported in this INTC. */ + mrvl,intc-numbers = <64>; + + /* priority bits in configuration register */ + mrvl,intc-priority = <0x20>; + }; + +Example 2: + + /* + * mux INTC that is internal wired to base INTC + */ + mux_intc4: interrupt-controller@d4282150 { + compatible = "mrvl,mmp2-mux-intc"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xd4282000 0x400>; + + #interrupt-cells = <1>; + interrupt-controller; + interrupt-parent = <&mmp_intc>; + + /* interrupt source '4' of parent INTC. */ + interrupts = <4>; + + /* 2 interrupts are supported in this INTC. */ + mrvl,intc-numbers = <2>; + + /* Status offset address of 0x150. */ + mrvl,status-offset = <0x150>; + + /* Mask offset address of 0x168. */ + mrvl,mask-offset = <0x168>; + + /* mfp register of 0xd401e2c4 & interrupt index of 1 */ + mrvl,mfp-edge = <0xd401e2c4 1>; + }; + +Example 3: + /* + * An interrupt generating device that is wired to an INTC. + */ + uart0: uart@d4030000 { + /* parent's '#interrupt-cells' property. */ + interrupts = <27>; + + /* The INTC that this device is wired to. */ + interrupt-parent = <&mmp_intc>; + }; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 17507b8..f18eb14 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -542,6 +542,7 @@ config ARCH_MMP select ARCH_REQUIRE_GPIOLIB select CLKDEV_LOOKUP select GENERIC_CLOCKEVENTS + select GENERIC_IRQ_CHIP select HAVE_SCHED_CLOCK select TICK_ONESHOT select PLAT_PXA diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile index 5c68382..e7862ea 100644 --- a/arch/arm/mach-mmp/Makefile +++ b/arch/arm/mach-mmp/Makefile @@ -4,6 +4,8 @@ obj-y += common.o clock.o devices.o time.o +obj-$(CONFIG_OF_IRQ) += intc.o + # SoC support obj-$(CONFIG_CPU_PXA168) += pxa168.o irq-pxa168.o obj-$(CONFIG_CPU_PXA910) += pxa910.o irq-pxa168.o diff --git a/arch/arm/mach-mmp/common.h b/arch/arm/mach-mmp/common.h index ec8d65d..1c563c2 100644 --- a/arch/arm/mach-mmp/common.h +++ b/arch/arm/mach-mmp/common.h @@ -6,3 +6,4 @@ extern void timer_init(int irq); extern void __init icu_init_irq(void); extern void __init mmp_map_io(void); +extern void __init mmp_init_intc(void); diff --git a/arch/arm/mach-mmp/include/mach/irqs.h b/arch/arm/mach-mmp/include/mach/irqs.h index a09d328..65ec176 100644 --- a/arch/arm/mach-mmp/include/mach/irqs.h +++ b/arch/arm/mach-mmp/include/mach/irqs.h @@ -224,6 +224,6 @@ #define IRQ_BOARD_START (IRQ_GPIO_START + IRQ_GPIO_NUM) -#define NR_IRQS (IRQ_BOARD_START) +#define NR_IRQS 0 #endif /* __ASM_MACH_IRQS_H */ diff --git a/arch/arm/mach-mmp/intc.c b/arch/arm/mach-mmp/intc.c new file mode 100644 index 0000000..2e44057 --- /dev/null +++ b/arch/arm/mach-mmp/intc.c @@ -0,0 +1,224 @@ +/* + * linux/arch/arm/mach-mmp/intc.c + * + * Generic IRQ handling + * + * Author: Haojian Zhuang + * Copyright: Marvell International Ltd. 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct mmp_intc_info { + unsigned int en_mask; + void __iomem *virt_base; + void __iomem *status; + void __iomem *mfp_edge; + unsigned int mfp_edge_index; /* index in irq domain */ +}; + +static void mux_irq_ack(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct irq_chip_type *ct; + struct mmp_intc_info *info; + unsigned int data, irq_offs; + + ct = gc->chip_types; + info = (struct mmp_intc_info *)ct->regs.ack; + irq_offs = d->irq - gc->irq_base; + /* clear MFP edge-detect */ + if (info->mfp_edge && (info->mfp_edge_index == irq_offs)) { + data = __raw_readl(info->mfp_edge); + __raw_writel(data | (1 << 6), info->mfp_edge); + __raw_writel(data, info->mfp_edge); + } +} + +static void mmp_irq_demux_handler(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip_generic *gc = irq_get_handler_data(irq); + struct irq_chip_type *ct; + struct mmp_intc_info *info; + unsigned long status, n; + + ct = gc->chip_types; + info = (struct mmp_intc_info *)ct->regs.ack; + while (1) { + status = __raw_readl(info->status) & ~gc->mask_cache; + if (status == 0) + break; + n = find_first_bit(&status, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + generic_handle_irq(gc->irq_base + n); + n = find_next_bit(&status, BITS_PER_LONG, n + 1); + } + } +} + +static void mux_init_intc(struct mmp_intc_info *mmp_info) +{ + struct device_node *np; + struct mmp_intc_info *mux_info; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + const __be32 *edge; + unsigned int addr = 0, offs = 0; + unsigned int status, mask, irq_base, nr, data; + int cascade; + + for_each_compatible_node(np, NULL, "mrvl,mmp2-mux-intc") { + if (of_get_property(np, "interrupt-controller", NULL) == NULL) + continue; + if (of_property_read_u32(np, "mrvl,intc-numbers", &nr) < 0) { + pr_warn("intc-numbers property is missed\n"); + continue; + } + if (of_property_read_u32(np, "mrvl,status-offset", &status) + < 0) { + pr_warn("intc-status property is missed\n"); + continue; + } + if (of_property_read_u32(np, "mrvl,mask-offset", &mask) < 0) { + pr_warn("intc-mask property is missed\n"); + continue; + } + + mux_info = kzalloc(sizeof(*mux_info), GFP_KERNEL); + if (mux_info == NULL) + goto out; + status += (unsigned int)mmp_info->virt_base; + mux_info->status = (void __iomem *)status; + + edge = of_get_property(np, "mrvl,mfp-edge", NULL); + if (edge) { + addr = be32_to_cpu(*edge) & PAGE_MASK; + offs = be32_to_cpu(*edge) - addr; + mux_info->mfp_edge = ioremap(addr, PAGE_SIZE) + offs; + mux_info->mfp_edge_index = be32_to_cpu(*++edge); + /* clear mfp edge detection for initialization */ + data = __raw_readl(mux_info->mfp_edge); + __raw_writel(data | (1 << 6), mux_info->mfp_edge); + __raw_writel(data, mux_info->mfp_edge); + } + + /* allocate new irq */ + cascade = irq_of_parse_and_map(np, 0); + irq_base = irq_alloc_descs(-1, 0, nr, 0); + irq_domain_add_simple(np, irq_base); + + gc = irq_alloc_generic_chip("mux-intc", 1, irq_base, + mmp_info->virt_base, handle_level_irq); + ct = gc->chip_types; + ct->regs.ack = (unsigned int)mux_info; + ct->regs.mask = mask; + ct->chip.irq_ack = mux_irq_ack; + ct->chip.irq_mask = irq_gc_mask_set_bit; + ct->chip.irq_unmask = irq_gc_mask_clr_bit; + irq_setup_generic_chip(gc, IRQ_MSK(nr), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); + + irq_set_handler_data(cascade, gc); + irq_set_chained_handler(cascade, mmp_irq_demux_handler); + } +out: + return; +} + +static void mmp_irq_unmask(struct irq_data *d) +{ + struct mmp_intc_info *info = irq_data_get_irq_chip_data(d); + + /* ICU_INT_CONF */ + __raw_writel(info->en_mask, info->virt_base + (d->irq << 2)); +} + +static void mmp_irq_mask(struct irq_data *d) +{ + struct mmp_intc_info *info = irq_data_get_irq_chip_data(d); + + __raw_writel(0, info->virt_base + (d->irq << 2)); +} + +static struct irq_chip mmp_irq_chip = { + .name = "mmp-intc", + .irq_unmask = mmp_irq_unmask, + .irq_mask = mmp_irq_mask, + .irq_ack = mmp_irq_mask, +}; + +void __init mmp_init_intc(void) +{ + struct mmp_intc_info *info; + struct device_node *np; + struct resource rs; + unsigned int cells, nr, enable, irq_base; + int i; + + np = of_find_compatible_node(NULL, NULL, "mrvl,pxa168-intc"); + + BUG_ON(!np); + + of_node_get(np); + if (of_get_property(np, "interrupt-controller", NULL) == NULL) + goto out; + if (of_property_read_u32(np, "#interrupt-cells", &cells) < 0) { + pr_warn("mmp-intc: interrupt-cells property is missed\n"); + goto out; + } + if (cells != 1) { + pr_warn("mmp-intc: interrupt-cells property is incorrect\n"); + goto out; + } + if (of_property_read_u32(np, "mrvl,intc-numbers", &nr) < 0) { + pr_warn("mmp-intc: mrvl,intc-numbers property is missed\n"); + goto out; + } + if (of_property_read_u32(np, "mrvl,intc-priority", &enable) < 0) { + pr_warn("mmp-intc: mrvl,intc-priority property is missed\n"); + goto out; + } + if (of_address_to_resource(np, 0, &rs) < 0) { + pr_warn("mmp-intc: invalid reg property\n"); + goto out; + } + + info = kzalloc(sizeof(struct mmp_intc_info), GFP_KERNEL); + if (info == NULL) + goto out; + info->en_mask = enable; + info->virt_base = ioremap(rs.start, PAGE_SIZE); + if (info->virt_base == NULL) { + pr_warn("mmp-intc: failed to remap reg base\n"); + goto out_mem; + } + + /* allocate new irq */ + irq_base = irq_alloc_descs(-1, 0, nr, 0); + irq_domain_add_simple(np, 0); + + for (i = irq_base; i < irq_base + nr; i++) { + irq_set_chip_data(i, info); + mmp_irq_mask(irq_get_irq_data(i)); + irq_set_chip_and_handler(i, &mmp_irq_chip, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + mux_init_intc(info); + of_node_put(np); + return; +out_mem: + kfree(info); +out: + of_node_put(np); + return; +}