diff --git a/MAINTAINERS b/MAINTAINERS
index cf64b4b90222..afd6793d30d8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2145,6 +2145,7 @@ F:	arch/arm/boot/dts/infinity*.dtsi
 F:	arch/arm/boot/dts/mercury*.dtsi
 F:	arch/arm/boot/dts/mstar-v7.dtsi
 F:	arch/arm/mach-mstar/
+F:	drivers/irqchip/irq-msc313-intc.c
 
 ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT
 M:	Michael Petchkovsky <mkpetch@internode.on.net>
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 133f9c45744a..67f3ae3507b8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -111,3 +111,4 @@ obj-$(CONFIG_LOONGSON_HTPIC)		+= irq-loongson-htpic.o
 obj-$(CONFIG_LOONGSON_HTVEC)		+= irq-loongson-htvec.o
 obj-$(CONFIG_LOONGSON_PCH_PIC)		+= irq-loongson-pch-pic.o
 obj-$(CONFIG_LOONGSON_PCH_MSI)		+= irq-loongson-pch-msi.o
+obj-$(CONFIG_ARCH_MSTARV7)		+= irq-msc313-intc.o
diff --git a/drivers/irqchip/irq-msc313-intc.c b/drivers/irqchip/irq-msc313-intc.c
new file mode 100644
index 000000000000..5407c0da09e3
--- /dev/null
+++ b/drivers/irqchip/irq-msc313-intc.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Daniel Palmer
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#define REGOFF_MASK		0x0
+#define REGOFF_POLARITY		0x10
+#define REGOFF_STATUSCLEAR	0x20
+#define IRQSPERREG		16
+#define BITOFF(hwirq)		(hwirq % IRQSPERREG)
+#define REGOFF(hwirq)		((hwirq >> 4) * 4)
+
+struct msc313_intc {
+	struct irq_domain *domain;
+	void __iomem *base;
+	u8 gicoff;
+	struct irq_chip *irqchip;
+};
+
+static void msc313_intc_maskunmask(struct msc313_intc *intc, int hwirq, bool mask)
+{
+	int bitoff = BITOFF(hwirq);
+	int regoff = REGOFF(hwirq);
+	void __iomem *addr = intc->base + REGOFF_MASK + regoff;
+	u16 reg = readw_relaxed(addr);
+
+	if (mask)
+		reg |= 1 << bitoff;
+	else
+		reg &= ~(1 << bitoff);
+
+	writew_relaxed(reg, addr);
+}
+
+static void msc313_intc_mask_irq(struct irq_data *data)
+{
+	struct msc313_intc *intc = data->chip_data;
+
+	msc313_intc_maskunmask(intc, data->hwirq, true);
+	irq_chip_mask_parent(data);
+}
+
+static void msc313_intc_unmask_irq(struct irq_data *data)
+{
+	struct msc313_intc *intc = data->chip_data;
+
+	msc313_intc_maskunmask(intc, data->hwirq, false);
+	irq_chip_unmask_parent(data);
+}
+
+static int msc313_intc_set_type_irq(struct irq_data *data, unsigned int flow_type)
+{
+	struct msc313_intc *intc = data->chip_data;
+	int irq = data->hwirq;
+	int bitoff = BITOFF(irq);
+	int regoff = REGOFF(irq);
+	void __iomem *addr = intc->base + REGOFF_POLARITY + regoff;
+	u16 reg = readw_relaxed(addr);
+
+	if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_HIGH))
+		reg &= ~(1 << bitoff);
+	else
+		reg |= 1 << bitoff;
+
+	writew_relaxed(reg, addr);
+	return 0;
+}
+
+static void msc313_intc_irq_eoi(struct irq_data *data)
+{
+	struct msc313_intc *intc = data->chip_data;
+	int irq = data->hwirq;
+	int bitoff = BITOFF(irq);
+	int regoff = REGOFF(irq);
+	void __iomem *addr = intc->base + REGOFF_STATUSCLEAR + regoff;
+	u16 reg = readw_relaxed(addr);
+
+	reg |= 1 << bitoff;
+	writew_relaxed(reg, addr);
+	irq_chip_eoi_parent(data);
+}
+
+static struct irq_chip msc313_intc_irq_chip = {
+	.name			= "INTC-IRQ",
+	.irq_mask		= msc313_intc_mask_irq,
+	.irq_unmask		= msc313_intc_unmask_irq,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= msc313_intc_set_type_irq,
+	.flags			= IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static struct irq_chip msc313_intc_fiq_chip = {
+	.name			= "INTC-FIQ",
+	.irq_mask		= msc313_intc_mask_irq,
+	.irq_unmask		= msc313_intc_unmask_irq,
+	.irq_eoi		= msc313_intc_irq_eoi,
+	.irq_set_type		= msc313_intc_set_type_irq,
+	.flags			= IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int msc313_intc_domain_translate(struct irq_domain *d,
+				     struct irq_fwspec *fwspec,
+				     unsigned long *hwirq,
+				     unsigned int *type)
+{
+	if (!is_of_node(fwspec->fwnode) || fwspec->param_count != 2)
+		return -EINVAL;
+
+	*hwirq = fwspec->param[0];
+	*type = fwspec->param[1];
+
+	return 0;
+}
+
+static int msc313_intc_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				 unsigned int nr_irqs, void *data)
+{
+	struct irq_fwspec *fwspec = data;
+	struct irq_fwspec parent_fwspec;
+	struct msc313_intc *intc = domain->host_data;
+
+	if (fwspec->param_count != 2)
+		return -EINVAL;
+
+	irq_domain_set_hwirq_and_chip(domain, virq, fwspec->param[0], intc->irqchip, intc);
+
+	parent_fwspec.fwnode = domain->parent->fwnode;
+	parent_fwspec.param[0] = GIC_SPI;
+	parent_fwspec.param[1] = fwspec->param[0] + intc->gicoff;
+	parent_fwspec.param[2] = fwspec->param[1];
+	parent_fwspec.param_count = 3;
+
+	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+					    &parent_fwspec);
+}
+
+static const struct irq_domain_ops msc313_intc_domain_ops = {
+		.translate = msc313_intc_domain_translate,
+		.alloc = msc313_intc_domain_alloc,
+		.free = irq_domain_free_irqs_common,
+};
+
+static int  msc313_intc_of_init(struct device_node *node,
+				   struct device_node *parent,
+				   struct irq_chip *irqchip)
+{
+	struct irq_domain *domain_parent;
+	struct msc313_intc *intc;
+	int ret = 0;
+	u32 gicoffset, numirqs;
+
+	if (of_property_read_u32(node, "mstar,gic-offset", &gicoffset)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (of_property_read_u32(node, "mstar,nr-interrupts", &numirqs)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	domain_parent = irq_find_host(parent);
+	if (!domain_parent) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	intc = kzalloc(sizeof(*intc), GFP_KERNEL);
+	if (!intc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	intc->irqchip = irqchip;
+	intc->gicoff = gicoffset;
+
+	intc->base = of_iomap(node, 0);
+	if (IS_ERR(intc->base)) {
+		ret = PTR_ERR(intc->base);
+		goto free_intc;
+	}
+
+	intc->domain = irq_domain_add_hierarchy(domain_parent, 0, numirqs, node,
+			&msc313_intc_domain_ops, intc);
+	if (!intc->domain) {
+		ret = -ENOMEM;
+		goto unmap;
+	}
+
+	return 0;
+
+unmap:
+	iounmap(intc->base);
+free_intc:
+	kfree(intc);
+out:
+	return ret;
+}
+
+static int __init msc313_intc_irq_of_init(struct device_node *node,
+				   struct device_node *parent)
+{
+	return msc313_intc_of_init(node, parent, &msc313_intc_irq_chip);
+};
+
+static int __init msc313_intc_fiq_of_init(struct device_node *node,
+				   struct device_node *parent)
+{
+	return msc313_intc_of_init(node, parent, &msc313_intc_fiq_chip);
+};
+
+IRQCHIP_DECLARE(msc313_intc_irq, "mstar,msc313-intc-irq",
+		msc313_intc_irq_of_init);
+IRQCHIP_DECLARE(mstar_intc_fiq, "mstar,msc313-intc-fiq",
+		msc313_intc_fiq_of_init);
