diff mbox

[V2] PCI: exynos: add support for MSI

Message ID 003d01ce9fc6$9c0f1c20$d42d5460$%han@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jingoo Han Aug. 23, 2013, 6:04 a.m. UTC
This patch adds support for Message Signaled Interrupt in the
Exynos PCIe diver using Synopsys designware PCIe core IP.

Signed-off-by: Siva Reddy Kallam <siva.kallam@samsung.com>
Signed-off-by: Srikanth T Shivanand <ts.srikanth@samsung.com>
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Cc: Pratyush Anand <pratyush.anand@st.com>
Cc: Mohit KUMAR <Mohit.KUMAR@st.com>
---
Changes since v1:
- removed unnecessary exynos_pcie_clear_irq_level()
- updated the bindings documentation
- used new msi_chip infrastructure
- removed ARCH_SUPPORTS_MSI
- replaced #ifdef guards with IS_ENABLED(CONFIG_PCI_MSI)

 .../devicetree/bindings/pci/designware-pcie.txt    |    2 +
 arch/arm/boot/dts/exynos5440.dtsi                  |    2 +
 drivers/pci/host/pci-exynos.c                      |   47 ++++
 drivers/pci/host/pcie-designware.c                 |  225 ++++++++++++++++++++
 drivers/pci/host/pcie-designware.h                 |    4 +
 5 files changed, 280 insertions(+)

Comments

Pratyush ANAND Aug. 23, 2013, 8:35 a.m. UTC | #1
On Fri, Aug 23, 2013 at 02:04:20PM +0800, Jingoo Han wrote:
> This patch adds support for Message Signaled Interrupt in the
> Exynos PCIe diver using Synopsys designware PCIe core IP.
> 
> Signed-off-by: Siva Reddy Kallam <siva.kallam@samsung.com>
> Signed-off-by: Srikanth T Shivanand <ts.srikanth@samsung.com>
> Signed-off-by: Jingoo Han <jg1.han@samsung.com>
> Cc: Pratyush Anand <pratyush.anand@st.com>
> Cc: Mohit KUMAR <Mohit.KUMAR@st.com>
> ---
> Changes since v1:
> - removed unnecessary exynos_pcie_clear_irq_level()
> - updated the bindings documentation
> - used new msi_chip infrastructure
> - removed ARCH_SUPPORTS_MSI
> - replaced #ifdef guards with IS_ENABLED(CONFIG_PCI_MSI)
> 
>  .../devicetree/bindings/pci/designware-pcie.txt    |    2 +
>  arch/arm/boot/dts/exynos5440.dtsi                  |    2 +
>  drivers/pci/host/pci-exynos.c                      |   47 ++++
>  drivers/pci/host/pcie-designware.c                 |  225 ++++++++++++++++++++
>  drivers/pci/host/pcie-designware.h                 |    4 +
>  5 files changed, 280 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
> index eabcb4b..00bb935 100644

[...]

> diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
> index 77b0c25..a4fed11 100644
> --- a/drivers/pci/host/pcie-designware.c
> +++ b/drivers/pci/host/pcie-designware.c
> @@ -11,8 +11,10 @@
>   * published by the Free Software Foundation.
>   */
> 
> +#include <linux/irq.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> +#include <linux/msi.h>
>  #include <linux/of_address.h>
>  #include <linux/pci.h>
>  #include <linux/pci_regs.h>
> @@ -62,6 +64,12 @@
>  #define PCIE_ATU_FUNC(x)               (((x) & 0x7) << 16)
>  #define PCIE_ATU_UPPER_TARGET          0x91C
> 
> +#define MAX_MSI_IRQS                   32

DW MSI controller can support upto 256. However, 32 seems a practical
choice, as there might not be any system which may use more
than 32. But a comment like as follows can be put here:

/*
* Maximum number of MSI IRQs can be 256 per controller. But keep
* it 32 as of now. Probably we will never need more than 32. If needed,
* then increment it in multiple of 32.
*/

> +#define MAX_MSI_CTRLS                  8

Why to waste cpu cycles when MAX_MSI_IRQS is 32 only.
#define MAX_MSI_CTRLS   (MAX_MSI_IRQS / 32)

> +
> +static unsigned int msi_data;
> +static DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);

What if one has more than one RC.
There are SOCs which support 3 RCs.
So something like this:

#define MAX_PCIE_PORT_SUPPORTED 3
static DECLARE_BITMAP(msi_irq_in_use[MAX_PCIE_PORT_SUPPORTED],
                  NUM_MSI_IRQS);
static unsigned int *msi_data[MAX_PCIE_PORT_SUPPORTED];

Allocate msi_data using __get_free_pages(GFP_KERNEL, 0)) as Thierry
suggested.

> +
>  static struct hw_pci dw_pci;
> 
>  unsigned long global_io_offset;
> @@ -144,6 +152,205 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
>         return ret;
>  }
> 

[...]

>  int dw_pcie_link_up(struct pcie_port *pp)
>  {
>         if (pp->ops->link_up)
> @@ -225,6 +432,13 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
>                 return -EINVAL;
>         }
> 
> +       if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +               if (of_property_read_u32(np, "msi-base", &pp->msi_irq_start)) {
> +                       dev_err(pp->dev, "Failed to parse the number of lanes\n");
> +                       return -EINVAL;
> +               }
> +       }
> +

What if an implementor want to use irq_domain method for msi_irq_start
allocation? Is it fine to return error if msi-base is not passed from
dt?

Also, with the limited knowledge of dt I do not understand one thing, how
would dt understand that you have used 32 msi irqs (MAX_MSI_IRQS)?

Regards
Pratyush
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thierry Reding Aug. 23, 2013, 9:22 a.m. UTC | #2
On Fri, Aug 23, 2013 at 02:05:39PM +0530, Pratyush Anand wrote:
> On Fri, Aug 23, 2013 at 02:04:20PM +0800, Jingoo Han wrote:
[...]
> > +
> >  static struct hw_pci dw_pci;
> > 
> >  unsigned long global_io_offset;
> > @@ -144,6 +152,205 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
> >         return ret;
> >  }
> > 
> 
> [...]
> 
> >  int dw_pcie_link_up(struct pcie_port *pp)
> >  {
> >         if (pp->ops->link_up)
> > @@ -225,6 +432,13 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
> >                 return -EINVAL;
> >         }
> > 
> > +       if (IS_ENABLED(CONFIG_PCI_MSI)) {
> > +               if (of_property_read_u32(np, "msi-base", &pp->msi_irq_start)) {
> > +                       dev_err(pp->dev, "Failed to parse the number of lanes\n");
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> 
> What if an implementor want to use irq_domain method for msi_irq_start
> allocation? Is it fine to return error if msi-base is not passed from
> dt?

I agree. This should be using an IRQ domain to represent the MSI
controller. Both Tegra and Marvell drivers do that already and if Exynos
can follow that same path it will increase the chances of refactoring
common bits.

Also the error message doesn't quite match up with what the code is
doing. =)

Thierry
Arnd Bergmann Aug. 25, 2013, 8:28 p.m. UTC | #3
On Friday 23 August 2013, Thierry Reding wrote:
> > > +       if (IS_ENABLED(CONFIG_PCI_MSI)) {
> > > +               if (of_property_read_u32(np, "msi-base", &pp->msi_irq_start)) {
> > > +                       dev_err(pp->dev, "Failed to parse the number of lanes\n");
> > > +                       return -EINVAL;
> > > +               }
> > > +       }
> > > +
> > 
> > What if an implementor want to use irq_domain method for msi_irq_start
> > allocation? Is it fine to return error if msi-base is not passed from
> > dt?
> 
> I agree. This should be using an IRQ domain to represent the MSI
> controller. Both Tegra and Marvell drivers do that already and if Exynos
> can follow that same path it will increase the chances of refactoring
> common bits.
> 
> Also the error message doesn't quite match up with what the code is
> doing. =)

Agreed. Besides the encoding of "base" irq values in the device tree is always
wrong, since the numbers are not at all a hardware property but an implementation
detail of how Linux currently uses interrupt numbers. When using DT probing,
you *have* to use IRQ domains, and the rest of Exynos does that.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jingoo Han Aug. 28, 2013, 8:17 a.m. UTC | #4
On Friday, August 23, 2013 5:36 PM, Pratyush Anand wrote:
> On Fri, Aug 23, 2013 at 02:04:20PM +0800, Jingoo Han wrote:

[.....]

> > +#define MAX_MSI_IRQS                   32
> 
> DW MSI controller can support upto 256. However, 32 seems a practical
> choice, as there might not be any system which may use more
> than 32. But a comment like as follows can be put here:
> 
> /*
> * Maximum number of MSI IRQs can be 256 per controller. But keep
> * it 32 as of now. Probably we will never need more than 32. If needed,
> * then increment it in multiple of 32.
> */

OK.
I will add this comment.

> 
> > +#define MAX_MSI_CTRLS                  8
> 
> Why to waste cpu cycles when MAX_MSI_IRQS is 32 only.
> #define MAX_MSI_CTRLS   (MAX_MSI_IRQS / 32)

OK.
I will fix it as you guide.

> 
> > +
> > +static unsigned int msi_data;
> > +static DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
> 
> What if one has more than one RC.
> There are SOCs which support 3 RCs.
> So something like this:
> 
> #define MAX_PCIE_PORT_SUPPORTED 3
> static DECLARE_BITMAP(msi_irq_in_use[MAX_PCIE_PORT_SUPPORTED],
>                   NUM_MSI_IRQS);
> static unsigned int *msi_data[MAX_PCIE_PORT_SUPPORTED];

No, there is no need to do it.
Without this, we can use 3 RCs by adding a node of 3rd RC to dt file.

> 
> Allocate msi_data using __get_free_pages(GFP_KERNEL, 0)) as Thierry
> suggested.

OK.
I will use '__get_free_pages(GFP_KERNEL, 0))' as Thierry guide.

> >  int dw_pcie_link_up(struct pcie_port *pp)
> >  {
> >         if (pp->ops->link_up)
> > @@ -225,6 +432,13 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
> >                 return -EINVAL;
> >         }
> >
> > +       if (IS_ENABLED(CONFIG_PCI_MSI)) {
> > +               if (of_property_read_u32(np, "msi-base", &pp->msi_irq_start)) {
> > +                       dev_err(pp->dev, "Failed to parse the number of lanes\n");
> > +                       return -EINVAL;
> > +               }
> > +       }
> > +
> 
> What if an implementor want to use irq_domain method for msi_irq_start
> allocation? Is it fine to return error if msi-base is not passed from
> dt?

Sure, I will consider using irq_domain method.

>
> Also, with the limited knowledge of dt I do not understand one thing, how
> would dt understand that you have used 32 msi irqs (MAX_MSI_IRQS)?

Sorry, I cannot understand exactly. :-(
I will look into it.

I really appreciate your comments. :-)

Best regards,
Jingoo Han

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pratyush ANAND Aug. 29, 2013, 3:45 a.m. UTC | #5
On Wed, Aug 28, 2013 at 04:17:24PM +0800, Jingoo Han wrote:
> On Friday, August 23, 2013 5:36 PM, Pratyush Anand wrote:
> > On Fri, Aug 23, 2013 at 02:04:20PM +0800, Jingoo Han wrote:
> 

[...]

> > #define MAX_PCIE_PORT_SUPPORTED 3
> > static DECLARE_BITMAP(msi_irq_in_use[MAX_PCIE_PORT_SUPPORTED],
> >                   NUM_MSI_IRQS);
> > static unsigned int *msi_data[MAX_PCIE_PORT_SUPPORTED];
> 
> No, there is no need to do it.
> Without this, we can use 3 RCs by adding a node of 3rd RC to dt file.
> 

Yes, you can use RCs by adding nodes to dt file. But you will need one 
msi_irq_in_use and one msi_data per RC (ie per port in this driver), isn't it? 

Regards
Pratyush

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jingoo Han Aug. 29, 2013, 4:43 a.m. UTC | #6
On Thursday, August 29, 2013 12:45 PM, Pratyush Anand wrote:
> On Wed, Aug 28, 2013 at 04:17:24PM +0800, Jingoo Han wrote:
> > On Friday, August 23, 2013 5:36 PM, Pratyush Anand wrote:
> > > On Fri, Aug 23, 2013 at 02:04:20PM +0800, Jingoo Han wrote:
> >
> 
> [...]
> 
> > > #define MAX_PCIE_PORT_SUPPORTED 3
> > > static DECLARE_BITMAP(msi_irq_in_use[MAX_PCIE_PORT_SUPPORTED],
> > >                   NUM_MSI_IRQS);
> > > static unsigned int *msi_data[MAX_PCIE_PORT_SUPPORTED];
> >
> > No, there is no need to do it.
> > Without this, we can use 3 RCs by adding a node of 3rd RC to dt file.
> >
> 
> Yes, you can use RCs by adding nodes to dt file. But you will need one
> msi_irq_in_use and one msi_data per RC (ie per port in this driver), isn't it?

Aha, you're right.

A few minutes ago, I found the same value of msi_data is
used for 2 different RCs. It is my fault.

As you mentioned, one msi_data per RC should be used.
I will modify it in order to use one msi_data per RC.

Thank you for your kind description. :-)

Best regards,
Jingoo Han


--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
index eabcb4b..00bb935 100644
--- a/Documentation/devicetree/bindings/pci/designware-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt
@@ -43,6 +43,7 @@  SoC specific DT Entry:
 		interrupt-map-mask = <0 0 0 0>;
 		interrupt-map = <0x0 0 &gic 53>;
 		num-lanes = <4>;
+		msi-base = <200>;
 	};
 
 	pcie@2a0000 {
@@ -63,6 +64,7 @@  SoC specific DT Entry:
 		interrupt-map-mask = <0 0 0 0>;
 		interrupt-map = <0x0 0 &gic 56>;
 		num-lanes = <4>;
+		msi-base = <232>;
 	};
 
 Board specific DT Entry:
diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi
index 5d6cf49..17549b9 100644
--- a/arch/arm/boot/dts/exynos5440.dtsi
+++ b/arch/arm/boot/dts/exynos5440.dtsi
@@ -276,6 +276,7 @@ 
 		interrupt-map-mask = <0 0 0 0>;
 		interrupt-map = <0x0 0 &gic 53>;
 		num-lanes = <4>;
+		msi-base = <200>;
 	};
 
 	pcie@2a0000 {
@@ -296,5 +297,6 @@ 
 		interrupt-map-mask = <0 0 0 0>;
 		interrupt-map = <0x0 0 &gic 56>;
 		num-lanes = <4>;
+		msi-base = <232>;
 	};
 };
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 012ca8a..aaead2c 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -48,6 +48,7 @@  struct exynos_pcie {
 #define PCIE_IRQ_SPECIAL		0x008
 #define PCIE_IRQ_EN_PULSE		0x00c
 #define PCIE_IRQ_EN_LEVEL		0x010
+#define IRQ_MSI_ENABLE			(0x1 << 2)
 #define PCIE_IRQ_EN_SPECIAL		0x014
 #define PCIE_PWR_RESET			0x018
 #define PCIE_CORE_RESET			0x01c
@@ -320,9 +321,38 @@  static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
+{
+	struct pcie_port *pp = arg;
+
+	/* handle msi irq */
+	dw_handle_msi_irq(pp);
+
+	return IRQ_HANDLED;
+}
+
+static void exynos_pcie_msi_init(struct pcie_port *pp)
+{
+	u32 val;
+	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+	void __iomem *elbi_base = exynos_pcie->elbi_base;
+
+	dw_pcie_msi_init(pp);
+
+	/* enable MSI interrupt */
+	val = readl(elbi_base + PCIE_IRQ_EN_LEVEL);
+	val |= IRQ_MSI_ENABLE;
+	writel(val, elbi_base + PCIE_IRQ_EN_LEVEL);
+	return;
+}
+
 static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
 {
 	exynos_pcie_enable_irq_pulse(pp);
+
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		exynos_pcie_msi_init(pp);
+
 	return;
 }
 
@@ -408,6 +438,23 @@  static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev)
 		return ret;
 	}
 
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		pp->msi_irq = platform_get_irq(pdev, 0);
+
+		if (!pp->msi_irq) {
+			dev_err(&pdev->dev, "failed to get msi irq\n");
+			return -ENODEV;
+		}
+
+		ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+					exynos_pcie_msi_irq_handler,
+					IRQF_SHARED, "exynos-pcie", pp);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to request msi irq\n");
+			return ret;
+		}
+	}
+
 	pp->root_bus_nr = -1;
 	pp->ops = &exynos_pcie_host_ops;
 
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 77b0c25..a4fed11 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -11,8 +11,10 @@ 
  * published by the Free Software Foundation.
  */
 
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/msi.h>
 #include <linux/of_address.h>
 #include <linux/pci.h>
 #include <linux/pci_regs.h>
@@ -62,6 +64,12 @@ 
 #define PCIE_ATU_FUNC(x)		(((x) & 0x7) << 16)
 #define PCIE_ATU_UPPER_TARGET		0x91C
 
+#define MAX_MSI_IRQS			32
+#define MAX_MSI_CTRLS			8
+
+static unsigned int msi_data;
+static DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
+
 static struct hw_pci dw_pci;
 
 unsigned long global_io_offset;
@@ -144,6 +152,205 @@  int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
 	return ret;
 }
 
+static struct irq_chip dw_msi_irq_chip = {
+	.name = "PCI-MSI",
+	.irq_enable = unmask_msi_irq,
+	.irq_disable = mask_msi_irq,
+	.irq_mask = mask_msi_irq,
+	.irq_unmask = unmask_msi_irq,
+};
+
+/* MSI int handler */
+void dw_handle_msi_irq(struct pcie_port *pp)
+{
+	unsigned long val;
+	int i, pos;
+
+	for (i = 0; i < MAX_MSI_CTRLS; i++) {
+		dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
+				(u32 *)&val);
+		if (val) {
+			pos = 0;
+			while ((pos = find_next_bit(&val, 32, pos)) != 32) {
+				generic_handle_irq(pp->msi_irq_start
+					+ (i * 32) + pos);
+				pos++;
+			}
+		}
+		dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val);
+	}
+}
+
+void dw_pcie_msi_init(struct pcie_port *pp)
+{
+	/* program the msi_data */
+	dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
+			__virt_to_phys((u32)(&msi_data)));
+	dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+}
+
+static int find_valid_pos0(int msgvec, int pos, int *pos0)
+{
+	int flag = 1;
+
+	do {
+		pos = find_next_zero_bit(msi_irq_in_use,
+				MAX_MSI_IRQS, pos);
+		/*if you have reached to the end then get out from here.*/
+		if (pos == MAX_MSI_IRQS)
+			return -ENOSPC;
+		/*
+		 * Check if this position is at correct offset.nvec is always a
+		 * power of two. pos0 must be nvec bit alligned.
+		 */
+		if (pos % msgvec)
+			pos += msgvec - (pos % msgvec);
+		else
+			flag = 0;
+	} while (flag);
+
+	*pos0 = pos;
+	return 0;
+}
+
+static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
+{
+	int res, bit, irq, pos0, pos1, i;
+	u32 val;
+	struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata);
+
+	if (!pp) {
+		BUG();
+		return -EINVAL;
+	}
+
+	pos0 = find_first_zero_bit(msi_irq_in_use,
+			MAX_MSI_IRQS);
+	if (pos0 % no_irqs) {
+		if (find_valid_pos0(no_irqs, pos0, &pos0))
+			goto no_valid_irq;
+	}
+	if (no_irqs > 1) {
+		pos1 = find_next_bit(msi_irq_in_use,
+				MAX_MSI_IRQS, pos0);
+		/* there must be nvec number of consecutive free bits */
+		while ((pos1 - pos0) < no_irqs) {
+			if (find_valid_pos0(no_irqs, pos1, &pos0))
+				goto no_valid_irq;
+			pos1 = find_next_bit(msi_irq_in_use,
+					MAX_MSI_IRQS, pos0);
+		}
+	}
+
+	irq = (pp->msi_irq_start + pos0);
+
+	if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1))
+		goto no_valid_irq;
+
+	i = 0;
+	while (i < no_irqs) {
+		set_bit(pos0 + i, msi_irq_in_use);
+		irq_alloc_descs((irq + i), (irq + i), 1, 0);
+		irq_set_msi_desc(irq + i, desc);
+		irq_set_chip_and_handler(irq + i, &dw_msi_irq_chip,
+					handle_simple_irq);
+		set_irq_flags(irq + i, IRQF_VALID);
+		/*Enable corresponding interrupt in MSI interrupt controller */
+		res = ((pos0 + i) / 32) * 12;
+		bit = (pos0 + i) % 32;
+		dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+		val |= 1 << bit;
+		dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+		i++;
+	}
+
+	*pos = pos0;
+	return irq;
+
+no_valid_irq:
+	*pos = pos0;
+	return -ENOSPC;
+}
+
+static void clear_irq(unsigned int irq)
+{
+	int res, bit, val, pos;
+	struct irq_desc *desc;
+	struct msi_desc *msi;
+	struct pcie_port *pp;
+
+	/* get the port structure */
+	desc = irq_to_desc(irq);
+	msi = irq_desc_get_msi_desc(desc);
+	pp = sys_to_pcie(msi->dev->bus->sysdata);
+	if (!pp) {
+		BUG();
+		return;
+	}
+
+	pos = irq - pp->msi_irq_start;
+
+	irq_free_desc(irq);
+
+	clear_bit(pos, msi_irq_in_use);
+
+	/* Disable corresponding interrupt on MSI interrupt controller */
+	res = (pos / 32) * 12;
+	bit = pos % 32;
+	dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+	val &= ~(1 << bit);
+	dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+}
+
+static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+			struct msi_desc *desc)
+{
+	int irq, pos, msgvec;
+	u16 msg_ctr;
+	struct msi_msg msg;
+	struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
+
+	if (!pp) {
+		BUG();
+		return -EINVAL;
+	}
+
+	pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS,
+				&msg_ctr);
+	msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4;
+	if (msgvec == 0)
+		msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1;
+	if (msgvec > 5)
+		msgvec = 0;
+
+	irq = assign_irq((1 << msgvec), desc, &pos);
+	if (irq < 0)
+		return irq;
+
+	msg_ctr &= ~PCI_MSI_FLAGS_QSIZE;
+	msg_ctr |= msgvec << 4;
+	pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS,
+				msg_ctr);
+	desc->msi_attrib.multiple = msgvec;
+
+	msg.address_hi = 0x0;
+	msg.address_lo = __virt_to_phys((u32)(&msi_data));
+	msg.data = pos;
+	write_msi_msg(irq, &msg);
+
+	return 0;
+}
+
+static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
+{
+	clear_irq(irq);
+}
+
+static struct msi_chip dw_pcie_msi_chip = {
+	.setup_irq = dw_msi_setup_irq,
+	.teardown_irq = dw_msi_teardown_irq,
+};
+
 int dw_pcie_link_up(struct pcie_port *pp)
 {
 	if (pp->ops->link_up)
@@ -225,6 +432,13 @@  int __init dw_pcie_host_init(struct pcie_port *pp)
 		return -EINVAL;
 	}
 
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		if (of_property_read_u32(np, "msi-base", &pp->msi_irq_start)) {
+			dev_err(pp->dev, "Failed to parse the number of lanes\n");
+			return -EINVAL;
+		}
+	}
+
 	if (pp->ops->host_init)
 		pp->ops->host_init(pp);
 
@@ -509,10 +723,21 @@  int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 	return pp->irq;
 }
 
+static void dw_pcie_add_bus(struct pci_bus *bus)
+{
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		struct pcie_port *pp = sys_to_pcie(bus->sysdata);
+
+		dw_pcie_msi_chip.dev = pp->dev;
+		bus->msi = &dw_pcie_msi_chip;
+	}
+}
+
 static struct hw_pci dw_pci = {
 	.setup		= dw_pcie_setup,
 	.scan		= dw_pcie_scan_bus,
 	.map_irq	= dw_pcie_map_irq,
+	.add_bus	= dw_pcie_add_bus,
 };
 
 void dw_pcie_setup_rc(struct pcie_port *pp)
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index 133820f..3c01935 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -38,6 +38,8 @@  struct pcie_port {
 	int			irq;
 	u32			lanes;
 	struct pcie_host_ops	*ops;
+	int			msi_irq;
+	int			msi_irq_start;
 };
 
 struct pcie_host_ops {
@@ -57,6 +59,8 @@  int cfg_read(void __iomem *addr, int where, int size, u32 *val);
 int cfg_write(void __iomem *addr, int where, int size, u32 val);
 int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val);
 int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val);
+void dw_handle_msi_irq(struct pcie_port *pp);
+void dw_pcie_msi_init(struct pcie_port *pp);
 int dw_pcie_link_up(struct pcie_port *pp);
 void dw_pcie_setup_rc(struct pcie_port *pp);
 int dw_pcie_host_init(struct pcie_port *pp);