diff mbox

[v3] irqchip: irq-mvebu-odmi: new driver for platform MSI on Marvell 7K/8K

Message ID 1455888883-5127-1-git-send-email-thomas.petazzoni@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Thomas Petazzoni Feb. 19, 2016, 1:34 p.m. UTC
This commits adds a new irqchip driver that handles the ODMI
controller found on Marvell 7K/8K processors. The ODMI controller
provide MSI interrupt functionality to on-board peripherals, much like
the GIC-v2m.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
Changes v2 -> v2:
 - Express NODMIS_SHIFT, NODMIS_PER_FRAME, NODMIS_MASK in term of each
   other. Suggested by Marc Zyngier.
 - Rework the global bitmask allocation to make sure we allocate a
   number of longs rather than a number of bytes, to avoid having the
   bitmap API (which operates on longs) access memory we haven't
   explicitly allocated. Reported by Marc Zyngier.

Changes v1 -> v2:
 - Better commit title, as suggested by Marc Zyngier.
 - Improve the DT binding documentation, as suggested by Marc Zingier:
   add a reference to the GIC documentation, be more specific about
   the marvell,spi-base values, and add the requirement of the
   interrupt-parent property.
 - As suggested by Marc Zyngier, use a single global bitmap to
   allocate all ODMIs, regardless of the frame they belong to. As part
   of this change, the hwirq used to identify the interrupt inside the
   ODMI irqdomain are 0-based (instead of being based on their
   corresponding SPI base value), which allows to significantly
   simplify the allocation/free logic.
---
 .../marvell,odmi-controller.txt                    |  41 ++++
 drivers/irqchip/Kconfig                            |   4 +
 drivers/irqchip/Makefile                           |   1 +
 drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
 4 files changed, 294 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
 create mode 100644 drivers/irqchip/irq-mvebu-odmi.c

Comments

Marc Zyngier Feb. 19, 2016, 2:15 p.m. UTC | #1
On 19/02/16 13:34, Thomas Petazzoni wrote:
> This commits adds a new irqchip driver that handles the ODMI
> controller found on Marvell 7K/8K processors. The ODMI controller
> provide MSI interrupt functionality to on-board peripherals, much like
> the GIC-v2m.
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
> Changes v2 -> v2:
>  - Express NODMIS_SHIFT, NODMIS_PER_FRAME, NODMIS_MASK in term of each
>    other. Suggested by Marc Zyngier.
>  - Rework the global bitmask allocation to make sure we allocate a
>    number of longs rather than a number of bytes, to avoid having the
>    bitmap API (which operates on longs) access memory we haven't
>    explicitly allocated. Reported by Marc Zyngier.
> 
> Changes v1 -> v2:
>  - Better commit title, as suggested by Marc Zyngier.
>  - Improve the DT binding documentation, as suggested by Marc Zingier:
>    add a reference to the GIC documentation, be more specific about
>    the marvell,spi-base values, and add the requirement of the
>    interrupt-parent property.
>  - As suggested by Marc Zyngier, use a single global bitmap to
>    allocate all ODMIs, regardless of the frame they belong to. As part
>    of this change, the hwirq used to identify the interrupt inside the
>    ODMI irqdomain are 0-based (instead of being based on their
>    corresponding SPI base value), which allows to significantly
>    simplify the allocation/free logic.
> ---
>  .../marvell,odmi-controller.txt                    |  41 ++++
>  drivers/irqchip/Kconfig                            |   4 +
>  drivers/irqchip/Makefile                           |   1 +
>  drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
>  4 files changed, 294 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
>  create mode 100644 drivers/irqchip/irq-mvebu-odmi.c

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

I'll queue that for 4.6.

Thanks,

	M.
Thomas Petazzoni Feb. 19, 2016, 2:17 p.m. UTC | #2
Marc,

On Fri, 19 Feb 2016 14:15:46 +0000, Marc Zyngier wrote:

> >  .../marvell,odmi-controller.txt                    |  41 ++++
> >  drivers/irqchip/Kconfig                            |   4 +
> >  drivers/irqchip/Makefile                           |   1 +
> >  drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
> >  4 files changed, 294 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
> >  create mode 100644 drivers/irqchip/irq-mvebu-odmi.c
> 
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
> 
> I'll queue that for 4.6.

Thanks a lot!

Thomas
Jason Cooper Feb. 19, 2016, 2:28 p.m. UTC | #3
On Fri, Feb 19, 2016 at 02:15:46PM +0000, Marc Zyngier wrote:
> On 19/02/16 13:34, Thomas Petazzoni wrote:
> > This commits adds a new irqchip driver that handles the ODMI
> > controller found on Marvell 7K/8K processors. The ODMI controller
> > provide MSI interrupt functionality to on-board peripherals, much like
> > the GIC-v2m.
> > 
> > Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> > ---
> > Changes v2 -> v2:
> >  - Express NODMIS_SHIFT, NODMIS_PER_FRAME, NODMIS_MASK in term of each
> >    other. Suggested by Marc Zyngier.
> >  - Rework the global bitmask allocation to make sure we allocate a
> >    number of longs rather than a number of bytes, to avoid having the
> >    bitmap API (which operates on longs) access memory we haven't
> >    explicitly allocated. Reported by Marc Zyngier.
> > 
> > Changes v1 -> v2:
> >  - Better commit title, as suggested by Marc Zyngier.
> >  - Improve the DT binding documentation, as suggested by Marc Zingier:
> >    add a reference to the GIC documentation, be more specific about
> >    the marvell,spi-base values, and add the requirement of the
> >    interrupt-parent property.
> >  - As suggested by Marc Zyngier, use a single global bitmap to
> >    allocate all ODMIs, regardless of the frame they belong to. As part
> >    of this change, the hwirq used to identify the interrupt inside the
> >    ODMI irqdomain are 0-based (instead of being based on their
> >    corresponding SPI base value), which allows to significantly
> >    simplify the allocation/free logic.
> > ---
> >  .../marvell,odmi-controller.txt                    |  41 ++++
> >  drivers/irqchip/Kconfig                            |   4 +
> >  drivers/irqchip/Makefile                           |   1 +
> >  drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
> >  4 files changed, 294 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
> >  create mode 100644 drivers/irqchip/irq-mvebu-odmi.c
> 
> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
> 
> I'll queue that for 4.6.

I had it on my list to queue up, but you beat me to it.  :-)

thx,

Jason.
Marc Zyngier Feb. 19, 2016, 2:47 p.m. UTC | #4
On 19/02/16 14:28, Jason Cooper wrote:
> On Fri, Feb 19, 2016 at 02:15:46PM +0000, Marc Zyngier wrote:
>> On 19/02/16 13:34, Thomas Petazzoni wrote:
>>> This commits adds a new irqchip driver that handles the ODMI
>>> controller found on Marvell 7K/8K processors. The ODMI controller
>>> provide MSI interrupt functionality to on-board peripherals, much like
>>> the GIC-v2m.
>>>
>>> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>> ---
>>> Changes v2 -> v2:
>>>  - Express NODMIS_SHIFT, NODMIS_PER_FRAME, NODMIS_MASK in term of each
>>>    other. Suggested by Marc Zyngier.
>>>  - Rework the global bitmask allocation to make sure we allocate a
>>>    number of longs rather than a number of bytes, to avoid having the
>>>    bitmap API (which operates on longs) access memory we haven't
>>>    explicitly allocated. Reported by Marc Zyngier.
>>>
>>> Changes v1 -> v2:
>>>  - Better commit title, as suggested by Marc Zyngier.
>>>  - Improve the DT binding documentation, as suggested by Marc Zingier:
>>>    add a reference to the GIC documentation, be more specific about
>>>    the marvell,spi-base values, and add the requirement of the
>>>    interrupt-parent property.
>>>  - As suggested by Marc Zyngier, use a single global bitmap to
>>>    allocate all ODMIs, regardless of the frame they belong to. As part
>>>    of this change, the hwirq used to identify the interrupt inside the
>>>    ODMI irqdomain are 0-based (instead of being based on their
>>>    corresponding SPI base value), which allows to significantly
>>>    simplify the allocation/free logic.
>>> ---
>>>  .../marvell,odmi-controller.txt                    |  41 ++++
>>>  drivers/irqchip/Kconfig                            |   4 +
>>>  drivers/irqchip/Makefile                           |   1 +
>>>  drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
>>>  4 files changed, 294 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
>>>  create mode 100644 drivers/irqchip/irq-mvebu-odmi.c
>>
>> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
>>
>> I'll queue that for 4.6.
> 
> I had it on my list to queue up, but you beat me to it.  :-)

Ah, sorry! The reason I was eager to queue it is because I have some
related changes in the GIC/GICv2m department, simplifying a few things
there:

http://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/gic-4.6&id=9b29f74f6b4b56aa4bb5cc91474aa2451de5c628

I'll post this on the list later today.

Thanks,

	M.
Jason Cooper Feb. 19, 2016, 2:50 p.m. UTC | #5
On Fri, Feb 19, 2016 at 02:47:29PM +0000, Marc Zyngier wrote:
> On 19/02/16 14:28, Jason Cooper wrote:
> > On Fri, Feb 19, 2016 at 02:15:46PM +0000, Marc Zyngier wrote:
> >> On 19/02/16 13:34, Thomas Petazzoni wrote:
> >>> This commits adds a new irqchip driver that handles the ODMI
> >>> controller found on Marvell 7K/8K processors. The ODMI controller
> >>> provide MSI interrupt functionality to on-board peripherals, much like
> >>> the GIC-v2m.
> >>>
> >>> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> >>> ---
> >>> Changes v2 -> v2:
> >>>  - Express NODMIS_SHIFT, NODMIS_PER_FRAME, NODMIS_MASK in term of each
> >>>    other. Suggested by Marc Zyngier.
> >>>  - Rework the global bitmask allocation to make sure we allocate a
> >>>    number of longs rather than a number of bytes, to avoid having the
> >>>    bitmap API (which operates on longs) access memory we haven't
> >>>    explicitly allocated. Reported by Marc Zyngier.
> >>>
> >>> Changes v1 -> v2:
> >>>  - Better commit title, as suggested by Marc Zyngier.
> >>>  - Improve the DT binding documentation, as suggested by Marc Zingier:
> >>>    add a reference to the GIC documentation, be more specific about
> >>>    the marvell,spi-base values, and add the requirement of the
> >>>    interrupt-parent property.
> >>>  - As suggested by Marc Zyngier, use a single global bitmap to
> >>>    allocate all ODMIs, regardless of the frame they belong to. As part
> >>>    of this change, the hwirq used to identify the interrupt inside the
> >>>    ODMI irqdomain are 0-based (instead of being based on their
> >>>    corresponding SPI base value), which allows to significantly
> >>>    simplify the allocation/free logic.
> >>> ---
> >>>  .../marvell,odmi-controller.txt                    |  41 ++++
> >>>  drivers/irqchip/Kconfig                            |   4 +
> >>>  drivers/irqchip/Makefile                           |   1 +
> >>>  drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
> >>>  4 files changed, 294 insertions(+)
> >>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
> >>>  create mode 100644 drivers/irqchip/irq-mvebu-odmi.c
> >>
> >> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
> >>
> >> I'll queue that for 4.6.
> > 
> > I had it on my list to queue up, but you beat me to it.  :-)
> 
> Ah, sorry! The reason I was eager to queue it is because I have some
> related changes in the GIC/GICv2m department, simplifying a few things
> there:
> 
> http://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/gic-4.6&id=9b29f74f6b4b56aa4bb5cc91474aa2451de5c628
> 
> I'll post this on the list later today.

ah, no problem.  Whichever is easiest for you.  Just let me know.

thx,

Jason.
Jason Cooper Feb. 19, 2016, 3:57 p.m. UTC | #6
On Fri, Feb 19, 2016 at 02:34:43PM +0100, Thomas Petazzoni wrote:
> This commits adds a new irqchip driver that handles the ODMI
> controller found on Marvell 7K/8K processors. The ODMI controller
> provide MSI interrupt functionality to on-board peripherals, much like
> the GIC-v2m.
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
> Changes v2 -> v2:
>  - Express NODMIS_SHIFT, NODMIS_PER_FRAME, NODMIS_MASK in term of each
>    other. Suggested by Marc Zyngier.
>  - Rework the global bitmask allocation to make sure we allocate a
>    number of longs rather than a number of bytes, to avoid having the
>    bitmap API (which operates on longs) access memory we haven't
>    explicitly allocated. Reported by Marc Zyngier.
> 
> Changes v1 -> v2:
>  - Better commit title, as suggested by Marc Zyngier.
>  - Improve the DT binding documentation, as suggested by Marc Zingier:
>    add a reference to the GIC documentation, be more specific about
>    the marvell,spi-base values, and add the requirement of the
>    interrupt-parent property.
>  - As suggested by Marc Zyngier, use a single global bitmap to
>    allocate all ODMIs, regardless of the frame they belong to. As part
>    of this change, the hwirq used to identify the interrupt inside the
>    ODMI irqdomain are 0-based (instead of being based on their
>    corresponding SPI base value), which allows to significantly
>    simplify the allocation/free logic.
> ---
>  .../marvell,odmi-controller.txt                    |  41 ++++
>  drivers/irqchip/Kconfig                            |   4 +
>  drivers/irqchip/Makefile                           |   1 +
>  drivers/irqchip/irq-mvebu-odmi.c                   | 248 +++++++++++++++++++++
>  4 files changed, 294 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
>  create mode 100644 drivers/irqchip/irq-mvebu-odmi.c

Applied to irqchip/mvebu with Marc's Reviewed-by.

thx,

Jason.
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
new file mode 100644
index 0000000..252d5c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
@@ -0,0 +1,41 @@ 
+
+* Marvell ODMI for MSI support
+
+Some Marvell SoCs have an On-Die Message Interrupt (ODMI) controller
+which can be used by on-board peripheral for MSI interrupts.
+
+Required properties:
+
+- compatible           : The value here should contain "marvell,odmi-controller".
+
+- interrupt,controller : Identifies the node as an interrupt controller.
+
+- msi-controller       : Identifies the node as an MSI controller.
+
+- marvell,odmi-frames  : Number of ODMI frames available. Each frame
+                         provides a number of events.
+
+- reg                  : List of register definitions, one for each
+                         ODMI frame.
+
+- marvell,spi-base     : List of GIC base SPI interrupts, one for each
+                         ODMI frame. Those SPI interrupts are 0-based,
+                         i.e marvell,spi-base = <128> will use SPI #96.
+                         See Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
+                         for details about the GIC Device Tree binding.
+
+- interrupt-parent     : Reference to the parent interrupt controller.
+
+Example:
+
+	odmi: odmi@300000 {
+		compatible = "marvell,odmi-controller";
+		interrupt-controller;
+		msi-controller;
+		marvell,odmi-frames = <4>;
+		reg = <0x300000 0x4000>,
+		      <0x304000 0x4000>,
+		      <0x308000 0x4000>,
+		      <0x30C000 0x4000>;
+		marvell,spi-base = <128>, <136>, <144>, <152>;
+	};
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 715923d..18bed3c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -217,3 +217,7 @@  config IRQ_MXS
 	def_bool y if MACH_ASM9260 || ARCH_MXS
 	select IRQ_DOMAIN
 	select STMP_DEVICE
+
+config MVEBU_ODMI
+	bool
+	select GENERIC_MSI_IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 18caacb..29c388f 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -59,3 +59,4 @@  obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
 obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
 obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
 obj-$(CONFIG_PIC32_EVIC)		+= irq-pic32-evic.o
+obj-$(CONFIG_MVEBU_ODMI)		+= irq-mvebu-odmi.o
diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c
new file mode 100644
index 0000000..0ae98b2
--- /dev/null
+++ b/drivers/irqchip/irq-mvebu-odmi.c
@@ -0,0 +1,248 @@ 
+/*
+ * Copyright (C) 2016 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "GIC-ODMI: " fmt
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#define GICP_ODMIN_SET			0x40
+#define   GICP_ODMI_INT_NUM_SHIFT	12
+#define GICP_ODMIN_GM_EP_R0		0x110
+#define GICP_ODMIN_GM_EP_R1		0x114
+#define GICP_ODMIN_GM_EA_R0		0x108
+#define GICP_ODMIN_GM_EA_R1		0x118
+
+/*
+ * We don't support the group events, so we simply have 8 interrupts
+ * per frame.
+ */
+#define NODMIS_SHIFT		3
+#define NODMIS_PER_FRAME	(1 << NODMIS_SHIFT)
+#define NODMIS_MASK		(NODMIS_PER_FRAME - 1)
+
+struct odmi_data {
+	struct resource res;
+	void __iomem *base;
+	unsigned int spi_base;
+};
+
+static struct odmi_data *odmis;
+static unsigned long *odmis_bm;
+static unsigned int odmis_count;
+
+/* Protects odmis_bm */
+static DEFINE_SPINLOCK(odmis_bm_lock);
+
+static int odmi_set_affinity(struct irq_data *d,
+			     const struct cpumask *mask, bool force)
+{
+	int ret;
+
+	ret = irq_chip_set_affinity_parent(d, mask, force);
+	if (ret == IRQ_SET_MASK_OK)
+		ret = IRQ_SET_MASK_OK_DONE;
+
+	return ret;
+}
+
+static void odmi_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
+{
+	struct odmi_data *odmi;
+	phys_addr_t addr;
+	unsigned int odmin;
+
+	if (WARN_ON(d->hwirq >= odmis_count * NODMIS_PER_FRAME))
+		return;
+
+	odmi = &odmis[d->hwirq >> NODMIS_SHIFT];
+	odmin = d->hwirq & NODMIS_MASK;
+
+	addr = odmi->res.start + GICP_ODMIN_SET;
+
+	msg->address_hi = upper_32_bits(addr);
+	msg->address_lo = lower_32_bits(addr);
+	msg->data = odmin << GICP_ODMI_INT_NUM_SHIFT;
+}
+
+static struct irq_chip odmi_irq_chip = {
+	.name			= "ODMI",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_affinity	= odmi_set_affinity,
+	.irq_compose_msi_msg	= odmi_compose_msi_msg,
+};
+
+static int odmi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				 unsigned int nr_irqs, void *args)
+{
+	struct odmi_data *odmi = NULL;
+	struct irq_fwspec fwspec;
+	struct irq_data *d;
+	unsigned int hwirq, odmin;
+	int ret;
+
+	spin_lock(&odmis_bm_lock);
+	hwirq = find_first_zero_bit(odmis_bm, NODMIS_PER_FRAME * odmis_count);
+	if (hwirq >= NODMIS_PER_FRAME * odmis_count) {
+		spin_unlock(&odmis_bm_lock);
+		return -ENOSPC;
+	}
+
+	__set_bit(hwirq, odmis_bm);
+	spin_unlock(&odmis_bm_lock);
+
+	odmi = &odmis[hwirq >> NODMIS_SHIFT];
+	odmin = hwirq & NODMIS_MASK;
+
+	fwspec.fwnode = domain->parent->fwnode;
+	fwspec.param_count = 3;
+	fwspec.param[0] = GIC_SPI;
+	fwspec.param[1] = odmi->spi_base - 32 + odmin;
+	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+	if (ret) {
+		pr_err("Cannot allocate parent IRQ\n");
+		spin_lock(&odmis_bm_lock);
+		__clear_bit(odmin, odmis_bm);
+		spin_unlock(&odmis_bm_lock);
+		return ret;
+	}
+
+	/* Configure the interrupt line to be edge */
+	d = irq_domain_get_irq_data(domain->parent, virq);
+	d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
+
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+				      &odmi_irq_chip, NULL);
+
+	return 0;
+}
+
+static void odmi_irq_domain_free(struct irq_domain *domain,
+				 unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+
+	if (d->hwirq >= odmis_count * NODMIS_PER_FRAME) {
+		pr_err("Failed to teardown msi. Invalid hwirq %lu\n", d->hwirq);
+		return;
+	}
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+
+	/* Actually free the MSI */
+	spin_lock(&odmis_bm_lock);
+	__clear_bit(d->hwirq, odmis_bm);
+	spin_unlock(&odmis_bm_lock);
+}
+
+static const struct irq_domain_ops odmi_domain_ops = {
+	.alloc	= odmi_irq_domain_alloc,
+	.free	= odmi_irq_domain_free,
+};
+
+static struct irq_chip odmi_msi_irq_chip = {
+	.name	= "ODMI",
+};
+
+static struct msi_domain_ops odmi_msi_ops = {
+};
+
+static struct msi_domain_info odmi_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+	.ops	= &odmi_msi_ops,
+	.chip	= &odmi_msi_irq_chip,
+};
+
+static int __init mvebu_odmi_init(struct device_node *node,
+				  struct device_node *parent)
+{
+	struct irq_domain *inner_domain, *plat_domain;
+	int ret, i;
+
+	if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
+		return -EINVAL;
+
+	odmis = kcalloc(odmis_count, sizeof(struct odmi_data), GFP_KERNEL);
+	if (!odmis)
+		return -ENOMEM;
+
+	odmis_bm = kcalloc(BITS_TO_LONGS(odmis_count * NODMIS_PER_FRAME),
+			   sizeof(long), GFP_KERNEL);
+	if (!odmis_bm) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	for (i = 0; i < odmis_count; i++) {
+		struct odmi_data *odmi = &odmis[i];
+
+		ret = of_address_to_resource(node, i, &odmi->res);
+		if (ret)
+			goto err_unmap;
+
+		odmi->base = of_io_request_and_map(node, i, "odmi");
+		if (IS_ERR(odmi->base)) {
+			ret = PTR_ERR(odmi->base);
+			goto err_unmap;
+		}
+
+		if (of_property_read_u32_index(node, "marvell,spi-base",
+					       i, &odmi->spi_base)) {
+			ret = -EINVAL;
+			goto err_unmap;
+		}
+	}
+
+	inner_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+						odmis_count * NODMIS_PER_FRAME,
+						&odmi_domain_ops, NULL);
+	if (!inner_domain) {
+		ret = -ENOMEM;
+		goto err_unmap;
+	}
+
+	inner_domain->parent = irq_find_host(parent);
+
+	plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
+						     &odmi_msi_domain_info,
+						     inner_domain);
+	if (!plat_domain) {
+		ret = -ENOMEM;
+		goto err_remove_inner;
+	}
+
+	return 0;
+
+err_remove_inner:
+	irq_domain_remove(inner_domain);
+err_unmap:
+	for (i = 0; i < odmis_count; i++) {
+		struct odmi_data *odmi = &odmis[i];
+
+		if (odmi->base && !IS_ERR(odmi->base))
+			iounmap(odmis[i].base);
+	}
+	kfree(odmis_bm);
+err_alloc:
+	kfree(odmis);
+	return ret;
+}
+
+IRQCHIP_DECLARE(mvebu_odmi, "marvell,odmi-controller", mvebu_odmi_init);