This patch adds an irqchip driver for the main interrupt controller found
on MOXA ART SoCs.
Applies to 3.10-rc1 and arm-soc/for-next (2013-06-15)
Signed-off-by: Jonas Jensen <[email protected]>
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-moxart.c | 163 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 164 insertions(+), 0 deletions(-)
create mode 100644 drivers/irqchip/irq-moxart.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5..956d129 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..8606089
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,163 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+#define IRQ_SOURCE(base_addr) (base_addr + 0x00)
+#define IRQ_MASK(base_addr) (base_addr + 0x04)
+#define IRQ_CLEAR(base_addr) (base_addr + 0x08)
+#define IRQ_TMODE(base_addr) (base_addr + 0x0C)
+#define IRQ_TLEVEL(base_addr) (base_addr + 0x10)
+#define IRQ_STATUS(base_addr) (base_addr + 0x14)
+#define FIQ_SOURCE(base_addr) (base_addr + 0x20)
+#define FIQ_MASK(base_addr) (base_addr + 0x24)
+#define FIQ_CLEAR(base_addr) (base_addr + 0x28)
+#define FIQ_TMODE(base_addr) (base_addr + 0x2C)
+#define FIQ_TLEVEL(base_addr) (base_addr + 0x30)
+#define FIQ_STATUS(base_addr) (base_addr + 0x34)
+
+static void __iomem *moxart_irq_base;
+static struct irq_domain *moxart_irq_domain;
+static unsigned int interrupt_mask;
+
+asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs);
+
+void moxart_irq_ack(struct irq_data *irqd)
+{
+ unsigned int irq = irqd_to_hwirq(irqd);
+
+ writel(1 << irq, IRQ_CLEAR(moxart_irq_base));
+}
+
+static void moxart_irq_mask(struct irq_data *irqd)
+{
+ unsigned int irq = irqd_to_hwirq(irqd);
+ unsigned int mask;
+
+ mask = readl(IRQ_MASK(moxart_irq_base));
+ mask &= ~(1 << irq);
+ writel(mask, IRQ_MASK(moxart_irq_base));
+}
+
+static void moxart_irq_unmask(struct irq_data *irqd)
+{
+ unsigned int irq = irqd_to_hwirq(irqd);
+ unsigned int mask;
+
+ mask = readl(IRQ_MASK(moxart_irq_base));
+ mask |= (1 << irq);
+ writel(mask, IRQ_MASK(moxart_irq_base));
+}
+
+static struct irq_chip moxart_irq_chip = {
+ .name = "moxart_irq",
+ .irq_ack = moxart_irq_ack,
+ .irq_mask = moxart_irq_mask,
+ .irq_unmask = moxart_irq_unmask,
+ .irq_set_wake = NULL,
+};
+
+static int moxart_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ if ((1 << hw) && interrupt_mask) {
+ irq_set_chip_and_handler(virq, &moxart_irq_chip,
+ handle_edge_irq);
+ pr_info("%s: irq_set_chip_and_handler edge virq=%d hw=%d\n",
+ __func__, virq, (unsigned int) hw);
+ } else {
+ irq_set_chip_and_handler(virq, &moxart_irq_chip,
+ handle_level_irq);
+ pr_info("%s: irq_set_chip_and_handler level virq=%d hw=%d\n",
+ __func__, virq, (unsigned int) hw);
+ }
+
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops moxart_irq_ops = {
+ .map = moxart_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int __init moxart_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ interrupt_mask = be32_to_cpup(of_get_property(node,
+ "interrupt-mask", NULL));
+ pr_debug("%s: interrupt-mask=%x\n", node->full_name, interrupt_mask);
+
+ moxart_irq_base = of_iomap(node, 0);
+ if (!moxart_irq_base)
+ panic("%s: unable to map IC registers\n", node->full_name);
+
+ moxart_irq_domain = irq_domain_add_linear(node,
+ 32, &moxart_irq_ops, NULL);
+
+ if (!moxart_irq_domain)
+ panic("%s: unable to create IRQ domain\n", node->full_name);
+
+ writel(0, IRQ_MASK(moxart_irq_base));
+ writel(0xffffffff, IRQ_CLEAR(moxart_irq_base));
+
+ writel(interrupt_mask, IRQ_TMODE(moxart_irq_base));
+ writel(interrupt_mask, IRQ_TLEVEL(moxart_irq_base));
+
+ set_handle_irq(moxart_handle_irq);
+
+ pr_info("%s: %s finished\n", node->full_name, __func__);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-interrupt-controller",
+ moxart_of_init);
+
+asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(moxart_irq_base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_find_mapping(moxart_irq_domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+
--
1.7.2.5
On Tue, Jun 18, 2013 at 11:59:44AM +0200, Jonas Jensen wrote:
> +void moxart_irq_ack(struct irq_data *irqd)
> +{
> + unsigned int irq = irqd_to_hwirq(irqd);
> +
> + writel(1 << irq, IRQ_CLEAR(moxart_irq_base));
> +}
> +
> +static void moxart_irq_mask(struct irq_data *irqd)
> +{
> + unsigned int irq = irqd_to_hwirq(irqd);
> + unsigned int mask;
> +
> + mask = readl(IRQ_MASK(moxart_irq_base));
> + mask &= ~(1 << irq);
> + writel(mask, IRQ_MASK(moxart_irq_base));
> +}
> +
> +static void moxart_irq_unmask(struct irq_data *irqd)
> +{
> + unsigned int irq = irqd_to_hwirq(irqd);
> + unsigned int mask;
> +
> + mask = readl(IRQ_MASK(moxart_irq_base));
> + mask |= (1 << irq);
> + writel(mask, IRQ_MASK(moxart_irq_base));
> +}
Is there a reason this isn't using the irq_chip_generic stuff which would
eliminate all the above?
On Tuesday 18 June 2013, Russell King - ARM Linux wrote:
> Is there a reason this isn't using the irq_chip_generic stuff which would
> eliminate all the above?
When I first reviewed the moxart code, the generic irqchip wasn't compatible
with the linear irq domain. However this has changed now and it should indeed
use that.
Arnd
Dear Jonas Jensen,
On Tue, 18 Jun 2013 11:59:44 +0200, Jonas Jensen wrote:
> +asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs);
This forward declaration could be removed if moxart_handle_irq() was
implemented before the initialization function of your driver.
Thomas
--
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
This patch adds an irqchip driver for the main interrupt controller found
on MOXA ART SoCs.
v2:
* use irq_chip_generic
* remove macro duplicates
Applies to next-20130619
Signed-off-by: Jonas Jensen <[email protected]>
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-moxart.c | 109 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+), 0 deletions(-)
create mode 100644 drivers/irqchip/irq-moxart.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5..956d129 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..25c0ff7
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,109 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+static void __iomem *moxart_irq_base;
+static struct irq_domain *moxart_irq_domain;
+static unsigned int interrupt_mask;
+
+asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(moxart_irq_base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_find_mapping(moxart_irq_domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+static __init void moxart_alloc_gc(void __iomem *base,
+ unsigned int irq_start, unsigned int num)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+ struct irq_chip_generic *gc;
+
+ ret = irq_alloc_domain_generic_chips(moxart_irq_domain, 32, 1,
+ "MOXARTINTC", handle_edge_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
+
+ if (ret)
+ pr_err("%s: could not alloc generic chip\n", __func__);
+
+ gc = irq_get_domain_generic_chip(moxart_irq_domain, 0);
+
+ gc->reg_base = base;
+ gc->chip_types[0].regs.mask = IRQ_MASK_REG;
+ gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+}
+
+static int __init moxart_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ interrupt_mask = be32_to_cpup(of_get_property(node,
+ "interrupt-mask", NULL));
+ pr_debug("%s: interrupt-mask=%x\n", node->full_name, interrupt_mask);
+
+ moxart_irq_base = of_iomap(node, 0);
+ if (!moxart_irq_base)
+ panic("%s: unable to map INTC CPU registers\n",
+ node->full_name);
+
+ moxart_irq_domain = irq_domain_add_linear(node,
+ 32, &irq_generic_chip_ops, moxart_irq_base);
+
+ moxart_alloc_gc(moxart_irq_base, 0, 32);
+
+ writel(0, moxart_irq_base + IRQ_MASK_REG);
+ writel(0xffffffff, moxart_irq_base + IRQ_CLEAR_REG);
+
+ writel(interrupt_mask, moxart_irq_base + IRQ_MODE_REG);
+ writel(interrupt_mask, moxart_irq_base + IRQ_LEVEL_REG);
+
+ set_handle_irq(moxart_handle_irq);
+
+ pr_info("%s: %s finished\n", node->full_name, __func__);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-interrupt-controller",
+ moxart_of_init);
--
1.7.2.5
On Thursday 20 June 2013, Jonas Jensen wrote:
> This patch adds an irqchip driver for the main interrupt controller found
> on MOXA ART SoCs.
>
> v2:
> * use irq_chip_generic
> * remove macro duplicates
>
> Applies to next-20130619
>
> Signed-off-by: Jonas Jensen <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>
On Thu, 20 Jun 2013 10:58:52 +0200, Jonas Jensen <[email protected]> wrote:
> This patch adds an irqchip driver for the main interrupt controller found
> on MOXA ART SoCs.
>
> v2:
> * use irq_chip_generic
> * remove macro duplicates
>
> Applies to next-20130619
>
> Signed-off-by: Jonas Jensen <[email protected]>
> ---
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-moxart.c | 109 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 110 insertions(+), 0 deletions(-)
> create mode 100644 drivers/irqchip/irq-moxart.c
>
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index cda4cb5..956d129 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
> obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
> obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
> obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
> +obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
> diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
> new file mode 100644
> index 0000000..25c0ff7
> --- /dev/null
> +++ b/drivers/irqchip/irq-moxart.c
> @@ -0,0 +1,109 @@
> +/*
> + * MOXA ART SoCs IRQ chip driver.
> + *
> + * Copyright (C) 2013 Jonas Jensen
> + *
> + * Jonas Jensen <[email protected]>
> + *
> + * 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 <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqdomain.h>
> +
> +#include <asm/exception.h>
> +
> +#include "irqchip.h"
> +
> +#define IRQ_SOURCE_REG 0
> +#define IRQ_MASK_REG 0x04
> +#define IRQ_CLEAR_REG 0x08
> +#define IRQ_MODE_REG 0x0c
> +#define IRQ_LEVEL_REG 0x10
> +#define IRQ_STATUS_REG 0x14
> +
> +#define FIQ_SOURCE_REG 0x20
> +#define FIQ_MASK_REG 0x24
> +#define FIQ_CLEAR_REG 0x28
> +#define FIQ_MODE_REG 0x2c
> +#define FIQ_LEVEL_REG 0x30
> +#define FIQ_STATUS_REG 0x34
> +
> +static void __iomem *moxart_irq_base;
> +static struct irq_domain *moxart_irq_domain;
> +static unsigned int interrupt_mask;
> +
> +asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs)
> +{
> + u32 irqstat;
> + int hwirq;
> +
> + irqstat = readl(moxart_irq_base + IRQ_STATUS_REG);
> +
> + while (irqstat) {
> + hwirq = ffs(irqstat) - 1;
> + handle_IRQ(irq_find_mapping(moxart_irq_domain, hwirq), regs);
> + irqstat &= ~(1 << hwirq);
> + }
> +}
> +
> +static __init void moxart_alloc_gc(void __iomem *base,
> + unsigned int irq_start, unsigned int num)
> +{
> + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
> + int ret;
> + struct irq_chip_generic *gc;
> +
> + ret = irq_alloc_domain_generic_chips(moxart_irq_domain, 32, 1,
> + "MOXARTINTC", handle_edge_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
> +
> + if (ret)
> + pr_err("%s: could not alloc generic chip\n", __func__);
If this failure point is hit, the code following it will segfault. Need
to bail at this point.
> +
> + gc = irq_get_domain_generic_chip(moxart_irq_domain, 0);
> +
> + gc->reg_base = base;
> + gc->chip_types[0].regs.mask = IRQ_MASK_REG;
> + gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
> + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
> + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
> + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
> +}
Why is this split into a separate function? There really isn't any need
to.
> +
> +static int __init moxart_of_init(struct device_node *node,
> + struct device_node *parent)
> +{
> + interrupt_mask = be32_to_cpup(of_get_property(node,
> + "interrupt-mask", NULL));
> + pr_debug("%s: interrupt-mask=%x\n", node->full_name, interrupt_mask);
> +
> + moxart_irq_base = of_iomap(node, 0);
> + if (!moxart_irq_base)
> + panic("%s: unable to map INTC CPU registers\n",
> + node->full_name);
Is in necessary to panic here? Often even if the interrupt controller
setup fails, it is valueable to print an error, exit gracefully and let
the kernel try to continue. There is a greater chance of getting some
form output useful for debug that way.
> +
> + moxart_irq_domain = irq_domain_add_linear(node,
> + 32, &irq_generic_chip_ops, moxart_irq_base);
> +
> + moxart_alloc_gc(moxart_irq_base, 0, 32);
> +
> + writel(0, moxart_irq_base + IRQ_MASK_REG);
> + writel(0xffffffff, moxart_irq_base + IRQ_CLEAR_REG);
> +
> + writel(interrupt_mask, moxart_irq_base + IRQ_MODE_REG);
> + writel(interrupt_mask, moxart_irq_base + IRQ_LEVEL_REG);
> +
> + set_handle_irq(moxart_handle_irq);
> +
> + pr_info("%s: %s finished\n", node->full_name, __func__);
> +
> + return 0;
> +}
> +IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-interrupt-controller",
> + moxart_of_init);
> --
> 1.7.2.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
email sent from notmuch.vim plugin
This patch adds an irqchip driver for the main interrupt controller found
on MOXA ART SoCs.
Signed-off-by: Jonas Jensen <[email protected]>
---
Notes:
Applies to next-20130619
Changes since v2:
1. bail if irq_alloc_domain_generic_chips fails
2. remove separate function "moxart_alloc_gc", move all code to main init
3. if of_iomap fails, instead of panic, print error and return -EINVAL
4. use of_property_read_u32 instead of be32_to_cpup(of_get_property(..))
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-moxart.c | 110 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+)
create mode 100644 drivers/irqchip/irq-moxart.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5..956d129 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..0815108
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,110 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+static void __iomem *moxart_irq_base;
+static struct irq_domain *moxart_irq_domain;
+static unsigned int interrupt_mask;
+
+asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(moxart_irq_base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_find_mapping(moxart_irq_domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+static int __init moxart_of_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+ struct irq_chip_generic *gc;
+
+ ret = of_property_read_u32(node, "interrupt-mask", &interrupt_mask);
+ if (ret)
+ pr_err("%s: can't read interrupt-mask DT property\n",
+ node->full_name);
+ pr_debug("%s: interrupt-mask=%x\n", node->full_name, interrupt_mask);
+
+ moxart_irq_base = of_iomap(node, 0);
+ if (!moxart_irq_base) {
+ pr_err("%s: unable to map INTC CPU registers\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ moxart_irq_domain = irq_domain_add_linear(node, 32,
+ &irq_generic_chip_ops, moxart_irq_base);
+
+ ret = irq_alloc_domain_generic_chips(moxart_irq_domain, 32, 1,
+ "MOXARTINTC", handle_edge_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n", __func__);
+ return -EINVAL;
+ }
+
+ gc = irq_get_domain_generic_chip(moxart_irq_domain, 0);
+
+ gc->reg_base = moxart_irq_base;
+ gc->chip_types[0].regs.mask = IRQ_MASK_REG;
+ gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ writel(0, moxart_irq_base + IRQ_MASK_REG);
+ writel(0xffffffff, moxart_irq_base + IRQ_CLEAR_REG);
+
+ writel(interrupt_mask, moxart_irq_base + IRQ_MODE_REG);
+ writel(interrupt_mask, moxart_irq_base + IRQ_LEVEL_REG);
+
+ set_handle_irq(moxart_handle_irq);
+
+ pr_info("%s: %s finished\n", node->full_name, __func__);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-interrupt-controller",
+ moxart_of_intc_init);
--
1.8.2.1
Hi Jonas,
On Thu, Jun 27, 2013 at 03:29:33PM +0200, Jonas Jensen wrote:
> This patch adds an irqchip driver for the main interrupt controller found
> on MOXA ART SoCs.
>
> Signed-off-by: Jonas Jensen <[email protected]>
> ---
>
> Notes:
> Applies to next-20130619
>
> Changes since v2:
>
> 1. bail if irq_alloc_domain_generic_chips fails
> 2. remove separate function "moxart_alloc_gc", move all code to main init
> 3. if of_iomap fails, instead of panic, print error and return -EINVAL
> 4. use of_property_read_u32 instead of be32_to_cpup(of_get_property(..))
>
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-moxart.c | 110 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 111 insertions(+)
> create mode 100644 drivers/irqchip/irq-moxart.c
>
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index cda4cb5..956d129 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
> obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
> obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
> obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
> +obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
> diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
> new file mode 100644
> index 0000000..0815108
> --- /dev/null
> +++ b/drivers/irqchip/irq-moxart.c
> @@ -0,0 +1,110 @@
> +/*
> + * MOXA ART SoCs IRQ chip driver.
> + *
> + * Copyright (C) 2013 Jonas Jensen
> + *
> + * Jonas Jensen <[email protected]>
> + *
> + * 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 <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/irqdomain.h>
> +
> +#include <asm/exception.h>
> +
> +#include "irqchip.h"
> +
> +#define IRQ_SOURCE_REG 0
> +#define IRQ_MASK_REG 0x04
> +#define IRQ_CLEAR_REG 0x08
> +#define IRQ_MODE_REG 0x0c
> +#define IRQ_LEVEL_REG 0x10
> +#define IRQ_STATUS_REG 0x14
> +
> +#define FIQ_SOURCE_REG 0x20
> +#define FIQ_MASK_REG 0x24
> +#define FIQ_CLEAR_REG 0x28
> +#define FIQ_MODE_REG 0x2c
> +#define FIQ_LEVEL_REG 0x30
> +#define FIQ_STATUS_REG 0x34
> +
> +static void __iomem *moxart_irq_base;
> +static struct irq_domain *moxart_irq_domain;
> +static unsigned int interrupt_mask;
I would pack this into a struct s.t. you have only one global variable
for the chip instance. More or less a matter of taste.
> +
> +asmlinkage void __exception_irq_entry moxart_handle_irq(struct pt_regs *regs)
can this be static?
> +{
> + u32 irqstat;
> + int hwirq;
> +
> + irqstat = readl(moxart_irq_base + IRQ_STATUS_REG);
> +
> + while (irqstat) {
> + hwirq = ffs(irqstat) - 1;
> + handle_IRQ(irq_find_mapping(moxart_irq_domain, hwirq), regs);
Call irq_linear_revmap instead of irq_find_mapping.
> + irqstat &= ~(1 << hwirq);
> + }
> +}
> +
> +static int __init moxart_of_intc_init(struct device_node *node,
> + struct device_node *parent)
wrong indention.
> +{
> + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
> + int ret;
> + struct irq_chip_generic *gc;
> +
> + ret = of_property_read_u32(node, "interrupt-mask", &interrupt_mask);
> + if (ret)
> + pr_err("%s: can't read interrupt-mask DT property\n",
> + node->full_name);
ditto
> + pr_debug("%s: interrupt-mask=%x\n", node->full_name, interrupt_mask);
> +
> + moxart_irq_base = of_iomap(node, 0);
> + if (!moxart_irq_base) {
> + pr_err("%s: unable to map INTC CPU registers\n",
> + node->full_name);
> + return -EINVAL;
> + }
> +
> + moxart_irq_domain = irq_domain_add_linear(node, 32,
> + &irq_generic_chip_ops, moxart_irq_base);
if (!moxart_irq_domain) {
/* error handling */
}
> +
> + ret = irq_alloc_domain_generic_chips(moxart_irq_domain, 32, 1,
> + "MOXARTINTC", handle_edge_irq,
> + clr, 0, IRQ_GC_INIT_MASK_CACHE);
> +
> + if (ret) {
> + pr_err("%s: could not allocate generic chip\n", __func__);
you should free moxart_irq_domain here.
> + return -EINVAL;
> + }
> +
> + gc = irq_get_domain_generic_chip(moxart_irq_domain, 0);
> +
> + gc->reg_base = moxart_irq_base;
> + gc->chip_types[0].regs.mask = IRQ_MASK_REG;
> + gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
> + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
> + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
> + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
> +
> + writel(0, moxart_irq_base + IRQ_MASK_REG);
> + writel(0xffffffff, moxart_irq_base + IRQ_CLEAR_REG);
> +
> + writel(interrupt_mask, moxart_irq_base + IRQ_MODE_REG);
> + writel(interrupt_mask, moxart_irq_base + IRQ_LEVEL_REG);
> +
> + set_handle_irq(moxart_handle_irq);
> +
> + pr_info("%s: %s finished\n", node->full_name, __func__);
"finished" looks like a debug message. IMHO either drop the message or
write something more useful.
> +
> + return 0;
> +}
> +IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-interrupt-controller",
> + moxart_of_intc_init);
> --
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
This patch adds an irqchip driver for the main interrupt controller found
on MOXA ART SoCs.
Signed-off-by: Jonas Jensen <[email protected]>
---
Notes:
Applies to next-20130619
Changes since v3:
1. pack global variables in struct
2. make handle_irq function static
3. call irq_linear_revmap instead of irq_find_mapping
4. fix indentation
5. add error handling after irq_domain_add_linear
6. free the domain if irq_alloc_domain_generic_chips fails
7. use node->full_name instead of __func__ in messages
8. remove debug messages
9. make messages more consistent
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-moxart.c | 118 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 119 insertions(+)
create mode 100644 drivers/irqchip/irq-moxart.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5..956d129 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..a84b001
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,118 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+
+struct moxart_irq_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+ unsigned int interrupt_mask;
+};
+
+static struct moxart_irq_data intc;
+
+static asmlinkage void __exception_irq_entry handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(intc.base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+static int __init moxart_of_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+ struct irq_chip_generic *gc;
+
+ intc.base = of_iomap(node, 0);
+ if (!intc.base) {
+ pr_err("%s: unable to map IC registers\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ intc.domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+ intc.base);
+ if (!intc.domain) {
+ pr_err("%s: unable to create IRQ domain\n", node->full_name);
+ return -EINVAL;
+ }
+
+ ret = irq_alloc_domain_generic_chips(intc.domain, 32, 1,
+ "MOXARTINTC", handle_edge_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n",
+ node->full_name);
+ irq_domain_remove(intc.domain);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "interrupt-mask",
+ &intc.interrupt_mask);
+ if (ret)
+ pr_err("%s: could not read interrupt-mask DT property\n",
+ node->full_name);
+
+ gc = irq_get_domain_generic_chip(intc.domain, 0);
+
+ gc->reg_base = intc.base;
+ gc->chip_types[0].regs.mask = IRQ_MASK_REG;
+ gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ writel(0, intc.base + IRQ_MASK_REG);
+ writel(0xffffffff, intc.base + IRQ_CLEAR_REG);
+
+ writel(intc.interrupt_mask, intc.base + IRQ_MODE_REG);
+ writel(intc.interrupt_mask, intc.base + IRQ_LEVEL_REG);
+
+ set_handle_irq(handle_irq);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-interrupt-controller",
+ moxart_of_intc_init);
--
1.8.2.1
This patch adds an irqchip driver for the main interrupt controller found
on MOXA ART SoCs.
Signed-off-by: Jonas Jensen <[email protected]>
---
Notes:
Applies to next-20130703
Changes since v4:
1. shorten DT compatible value to "moxart-ic"
2. use alphabetic ordering in Makefile
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-moxart.c | 118 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 119 insertions(+)
create mode 100644 drivers/irqchip/irq-moxart.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 2065ef6..e65c41a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..a9d9f36
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,118 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+
+struct moxart_irq_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+ unsigned int interrupt_mask;
+};
+
+static struct moxart_irq_data intc;
+
+static asmlinkage void __exception_irq_entry handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(intc.base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+static int __init moxart_of_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+ struct irq_chip_generic *gc;
+
+ intc.base = of_iomap(node, 0);
+ if (!intc.base) {
+ pr_err("%s: unable to map IC registers\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ intc.domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+ intc.base);
+ if (!intc.domain) {
+ pr_err("%s: unable to create IRQ domain\n", node->full_name);
+ return -EINVAL;
+ }
+
+ ret = irq_alloc_domain_generic_chips(intc.domain, 32, 1,
+ "MOXARTINTC", handle_edge_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n",
+ node->full_name);
+ irq_domain_remove(intc.domain);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "interrupt-mask",
+ &intc.interrupt_mask);
+ if (ret)
+ pr_err("%s: could not read interrupt-mask DT property\n",
+ node->full_name);
+
+ gc = irq_get_domain_generic_chip(intc.domain, 0);
+
+ gc->reg_base = intc.base;
+ gc->chip_types[0].regs.mask = IRQ_MASK_REG;
+ gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ writel(0, intc.base + IRQ_MASK_REG);
+ writel(0xffffffff, intc.base + IRQ_CLEAR_REG);
+
+ writel(intc.interrupt_mask, intc.base + IRQ_MODE_REG);
+ writel(intc.interrupt_mask, intc.base + IRQ_LEVEL_REG);
+
+ set_handle_irq(handle_irq);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-ic",
+ moxart_of_intc_init);
--
1.8.2.1
Commit-ID: 4de563ae821b1935b3c467a4606e5738b0b0df87
Gitweb: http://git.kernel.org/tip/4de563ae821b1935b3c467a4606e5738b0b0df87
Author: Jonas Jensen <[email protected]>
AuthorDate: Thu, 4 Jul 2013 14:38:51 +0200
Committer: Thomas Gleixner <[email protected]>
CommitDate: Fri, 5 Jul 2013 11:39:25 +0200
irqchip: Add support for MOXA ART SoCs
This patch adds an irqchip driver for the main interrupt controller
found on MOXA ART SoCs.
Signed-off-by: Jonas Jensen <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Thomas Gleixner <[email protected]>
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-moxart.c | 117 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 118 insertions(+)
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 2065ef6..e65c41a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..5552fc2
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,117 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+
+struct moxart_irq_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+ unsigned int interrupt_mask;
+};
+
+static struct moxart_irq_data intc;
+
+static asmlinkage void __exception_irq_entry handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(intc.base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+static int __init moxart_of_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+ struct irq_chip_generic *gc;
+
+ intc.base = of_iomap(node, 0);
+ if (!intc.base) {
+ pr_err("%s: unable to map IC registers\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ intc.domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+ intc.base);
+ if (!intc.domain) {
+ pr_err("%s: unable to create IRQ domain\n", node->full_name);
+ return -EINVAL;
+ }
+
+ ret = irq_alloc_domain_generic_chips(intc.domain, 32, 1,
+ "MOXARTINTC", handle_edge_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n",
+ node->full_name);
+ irq_domain_remove(intc.domain);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "interrupt-mask",
+ &intc.interrupt_mask);
+ if (ret)
+ pr_err("%s: could not read interrupt-mask DT property\n",
+ node->full_name);
+
+ gc = irq_get_domain_generic_chip(intc.domain, 0);
+
+ gc->reg_base = intc.base;
+ gc->chip_types[0].regs.mask = IRQ_MASK_REG;
+ gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ writel(0, intc.base + IRQ_MASK_REG);
+ writel(0xffffffff, intc.base + IRQ_CLEAR_REG);
+
+ writel(intc.interrupt_mask, intc.base + IRQ_MODE_REG);
+ writel(intc.interrupt_mask, intc.base + IRQ_LEVEL_REG);
+
+ set_handle_irq(handle_irq);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-ic", moxart_of_intc_init);