kernel: bump 4.14 to 4.14.156
[openwrt/openwrt.git] / target / linux / layerscape / patches-4.14 / 816-pcie-support-layerscape.patch
index d6e1e3afb4fd3dff4eff928af520013af26e5774..f3f09989a7a446e4c5d621aafc12a4609977085e 100644 (file)
@@ -1,63 +1,89 @@
-From c503e92983f2e18c1e18e21b15d5bedd6d0be8ac Mon Sep 17 00:00:00 2001
-From: Biwen Li <biwen.li@nxp.com>
-Date: Tue, 30 Oct 2018 18:26:56 +0800
-Subject: [PATCH 33/40] pcie: support layerscape
+From c54a010fe105281259b996d318ed85efc4103fee Mon Sep 17 00:00:00 2001
+From: Yangbo Lu <yangbo.lu@nxp.com>
+Date: Mon, 6 May 2019 15:18:05 +0800
+Subject: [PATCH] pcie: support layerscape
+
 This is an integrated patch of pcie for layerscape
 
 Signed-off-by: Bao Xiaowei <xiaowei.bao@nxp.com>
+Signed-off-by: Bhumika Goyal <bhumirks@gmail.com>
+Signed-off-by: Biwen Li <biwen.li@nxp.com>
 Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
 Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
+Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
+Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
 Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
+Signed-off-by: Niklas Cassel <niklas.cassel@axis.com>
 Signed-off-by: Po Liu <po.liu@nxp.com>
+Signed-off-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Rolf Evers-Fischer <rolf.evers.fischer@aptiv.com>
+Signed-off-by: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
+Signed-off-by: Xiaowei Bao <xiaowei.bao@nxp.com>
 Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
-Signed-off-by: Biwen Li <biwen.li@nxp.com>
+Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 ---
- .../bindings/pci/layerscape-pci.txt           |  14 +-
- arch/arm/kernel/bios32.c                      |  43 ++++++
- arch/arm64/kernel/pci.c                       |  43 ++++++
- drivers/misc/pci_endpoint_test.c              |  20 +--
- drivers/pci/dwc/Kconfig                       |   8 ++
- drivers/pci/dwc/pci-layerscape.c              | 132 +++++++++++++++++-
- drivers/pci/endpoint/functions/pci-epf-test.c |   2 +-
- drivers/pci/endpoint/pci-epf-core.c           |   8 +-
- drivers/pci/pcie/portdrv_core.c               |  29 ++++
- drivers/pci/quirks.c                          |  15 ++
+ arch/arm/kernel/bios32.c                      |  43 ++
+ arch/arm64/kernel/pci.c                       |  43 ++
+ drivers/misc/pci_endpoint_test.c              | 332 ++++++++++---
+ drivers/pci/Kconfig                           |   1 +
+ drivers/pci/dwc/Kconfig                       |  39 +-
+ drivers/pci/dwc/Makefile                      |   2 +-
+ drivers/pci/dwc/pci-dra7xx.c                  |   9 -
+ drivers/pci/dwc/pci-layerscape-ep.c           | 146 ++++++
+ drivers/pci/dwc/pci-layerscape.c              |  12 +
+ drivers/pci/dwc/pcie-designware-ep.c          | 338 ++++++++++++--
+ drivers/pci/dwc/pcie-designware-host.c        |   5 +-
+ drivers/pci/dwc/pcie-designware-plat.c        | 159 ++++++-
+ drivers/pci/dwc/pcie-designware.c             |   5 +-
+ drivers/pci/dwc/pcie-designware.h             |  57 ++-
+ drivers/pci/endpoint/Kconfig                  |   1 +
+ drivers/pci/endpoint/Makefile                 |   1 +
+ drivers/pci/endpoint/functions/Kconfig        |   1 +
+ drivers/pci/endpoint/functions/Makefile       |   1 +
+ drivers/pci/endpoint/functions/pci-epf-test.c | 191 +++++---
+ drivers/pci/endpoint/pci-ep-cfs.c             |  95 +++-
+ drivers/pci/endpoint/pci-epc-core.c           | 159 +++++--
+ drivers/pci/endpoint/pci-epc-mem.c            |  13 +-
+ drivers/pci/endpoint/pci-epf-core.c           | 116 +++--
+ drivers/pci/host/pci-host-common.c            |   8 -
+ drivers/pci/host/pcie-xilinx-nwl.c            |   9 -
+ drivers/pci/host/pcie-xilinx.c                |   7 -
+ drivers/pci/mobiveil/Kconfig                  |  50 ++
+ drivers/pci/mobiveil/Makefile                 |   7 +
+ drivers/pci/mobiveil/pci-layerscape-gen4-ep.c | 178 +++++++
+ drivers/pci/mobiveil/pci-layerscape-gen4.c    | 292 ++++++++++++
+ drivers/pci/mobiveil/pcie-mobiveil-ep.c       | 512 +++++++++++++++++++++
+ drivers/pci/mobiveil/pcie-mobiveil-host.c     | 640 ++++++++++++++++++++++++++
+ drivers/pci/mobiveil/pcie-mobiveil-plat.c     |  54 +++
+ drivers/pci/mobiveil/pcie-mobiveil.c          | 334 ++++++++++++++
+ drivers/pci/mobiveil/pcie-mobiveil.h          | 296 ++++++++++++
+ drivers/pci/pcie/portdrv_core.c               |  29 ++
+ drivers/pci/quirks.c                          |  15 +
+ include/linux/pci-ep-cfs.h                    |   5 +-
+ include/linux/pci-epc.h                       |  73 +--
+ include/linux/pci-epf.h                       |  12 +-
  include/linux/pci.h                           |   1 +
- 11 files changed, 292 insertions(+), 23 deletions(-)
+ include/uapi/linux/pcitest.h                  |   3 +
+ tools/pci/pcitest.c                           |  51 +-
+ tools/pci/pcitest.sh                          |  15 +
+ 44 files changed, 3917 insertions(+), 443 deletions(-)
+ create mode 100644 drivers/pci/dwc/pci-layerscape-ep.c
+ create mode 100644 drivers/pci/mobiveil/Kconfig
+ create mode 100644 drivers/pci/mobiveil/Makefile
+ create mode 100644 drivers/pci/mobiveil/pci-layerscape-gen4-ep.c
+ create mode 100644 drivers/pci/mobiveil/pci-layerscape-gen4.c
+ create mode 100644 drivers/pci/mobiveil/pcie-mobiveil-ep.c
+ create mode 100644 drivers/pci/mobiveil/pcie-mobiveil-host.c
+ create mode 100644 drivers/pci/mobiveil/pcie-mobiveil-plat.c
+ create mode 100644 drivers/pci/mobiveil/pcie-mobiveil.c
+ create mode 100644 drivers/pci/mobiveil/pcie-mobiveil.h
 
---- a/Documentation/devicetree/bindings/pci/layerscape-pci.txt
-+++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
-@@ -18,11 +18,16 @@ Required properties:
-         "fsl,ls2088a-pcie"
-         "fsl,ls1088a-pcie"
-         "fsl,ls1046a-pcie"
-+        "fsl,ls1012a-pcie"
- - reg: base addresses and lengths of the PCIe controller register blocks.
- - interrupts: A list of interrupt outputs of the controller. Must contain an
-   entry for each entry in the interrupt-names property.
--- interrupt-names: Must include the following entries:
--  "intr": The interrupt that is asserted for controller interrupts
-+- interrupt-names: It could include the following entries:
-+  "aer": Asserted for aer interrupt when chip support the aer interrupt with
-+               none MSI/MSI-X/INTx mode,but there is interrupt line for aer.
-+  "pme": Asserted for pme interrupt when chip support the pme interrupt with
-+               none MSI/MSI-X/INTx mode,but there is interrupt line for pme.
-+  ......
- - fsl,pcie-scfg: Must include two entries.
-   The first entry must be a link to the SCFG device node
-   The second entry must be '0' or '1' based on physical PCIe controller index.
-@@ -38,8 +43,9 @@ Example:
-               reg = <0x00 0x03400000 0x0 0x00010000   /* controller registers */
-                      0x40 0x00000000 0x0 0x00002000>; /* configuration space */
-               reg-names = "regs", "config";
--              interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>; /* controller interrupt */
--              interrupt-names = "intr";
-+              interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>, /* aer interrupt */
-+                      <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>; /* pme interrupt */
-+              interrupt-names = "aer", "pme";
-               fsl,pcie-scfg = <&scfg 0>;
-               #address-cells = <3>;
-               #size-cells = <2>;
 --- a/arch/arm/kernel/bios32.c
 +++ b/arch/arm/kernel/bios32.c
 @@ -12,6 +12,8 @@
@@ -178,80 +204,551 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
  int raw_pci_read(unsigned int domain, unsigned int bus,
 --- a/drivers/misc/pci_endpoint_test.c
 +++ b/drivers/misc/pci_endpoint_test.c
-@@ -97,6 +97,8 @@ struct pci_endpoint_test {
-       struct miscdevice miscdev;
+@@ -35,38 +35,45 @@
+ #include <uapi/linux/pcitest.h>
+-#define DRV_MODULE_NAME                       "pci-endpoint-test"
++#define DRV_MODULE_NAME                               "pci-endpoint-test"
+-#define PCI_ENDPOINT_TEST_MAGIC               0x0
++#define IRQ_TYPE_UNDEFINED                    -1
++#define IRQ_TYPE_LEGACY                               0
++#define IRQ_TYPE_MSI                          1
++#define IRQ_TYPE_MSIX                         2
++
++#define PCI_ENDPOINT_TEST_MAGIC                       0x0
++
++#define PCI_ENDPOINT_TEST_COMMAND             0x4
++#define COMMAND_RAISE_LEGACY_IRQ              BIT(0)
++#define COMMAND_RAISE_MSI_IRQ                 BIT(1)
++#define COMMAND_RAISE_MSIX_IRQ                        BIT(2)
++#define COMMAND_READ                          BIT(3)
++#define COMMAND_WRITE                         BIT(4)
++#define COMMAND_COPY                          BIT(5)
++
++#define PCI_ENDPOINT_TEST_STATUS              0x8
++#define STATUS_READ_SUCCESS                   BIT(0)
++#define STATUS_READ_FAIL                      BIT(1)
++#define STATUS_WRITE_SUCCESS                  BIT(2)
++#define STATUS_WRITE_FAIL                     BIT(3)
++#define STATUS_COPY_SUCCESS                   BIT(4)
++#define STATUS_COPY_FAIL                      BIT(5)
++#define STATUS_IRQ_RAISED                     BIT(6)
++#define STATUS_SRC_ADDR_INVALID                       BIT(7)
++#define STATUS_DST_ADDR_INVALID                       BIT(8)
+-#define PCI_ENDPOINT_TEST_COMMAND     0x4
+-#define COMMAND_RAISE_LEGACY_IRQ      BIT(0)
+-#define COMMAND_RAISE_MSI_IRQ         BIT(1)
+-#define MSI_NUMBER_SHIFT              2
+-/* 6 bits for MSI number */
+-#define COMMAND_READ                    BIT(8)
+-#define COMMAND_WRITE                   BIT(9)
+-#define COMMAND_COPY                    BIT(10)
+-
+-#define PCI_ENDPOINT_TEST_STATUS      0x8
+-#define STATUS_READ_SUCCESS             BIT(0)
+-#define STATUS_READ_FAIL                BIT(1)
+-#define STATUS_WRITE_SUCCESS            BIT(2)
+-#define STATUS_WRITE_FAIL               BIT(3)
+-#define STATUS_COPY_SUCCESS             BIT(4)
+-#define STATUS_COPY_FAIL                BIT(5)
+-#define STATUS_IRQ_RAISED               BIT(6)
+-#define STATUS_SRC_ADDR_INVALID         BIT(7)
+-#define STATUS_DST_ADDR_INVALID         BIT(8)
+-
+-#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR      0xc
++#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR      0x0c
+ #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR      0x10
+ #define PCI_ENDPOINT_TEST_LOWER_DST_ADDR      0x14
+ #define PCI_ENDPOINT_TEST_UPPER_DST_ADDR      0x18
+-#define PCI_ENDPOINT_TEST_SIZE                0x1c
+-#define PCI_ENDPOINT_TEST_CHECKSUM    0x20
++#define PCI_ENDPOINT_TEST_SIZE                        0x1c
++#define PCI_ENDPOINT_TEST_CHECKSUM            0x20
++
++#define PCI_ENDPOINT_TEST_IRQ_TYPE            0x24
++#define PCI_ENDPOINT_TEST_IRQ_NUMBER          0x28
+ static DEFINE_IDA(pci_endpoint_test_ida);
+@@ -77,6 +84,10 @@ static bool no_msi;
+ module_param(no_msi, bool, 0444);
+ MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test");
++static int irq_type = IRQ_TYPE_MSI;
++module_param(irq_type, int, 0444);
++MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)");
++
+ enum pci_barno {
+       BAR_0,
+       BAR_1,
+@@ -103,7 +114,7 @@ struct pci_endpoint_test {
+ struct pci_endpoint_test_data {
        enum pci_barno test_reg_bar;
        size_t alignment;
-+      char name[20];
-+      int irq_num;
+-      bool no_msi;
++      int irq_type;
  };
  
- struct pci_endpoint_test_data {
-@@ -454,9 +456,7 @@ static int pci_endpoint_test_probe(struc
+ static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
+@@ -147,6 +158,100 @@ static irqreturn_t pci_endpoint_test_irq
+       return IRQ_HANDLED;
+ }
++static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
++{
++      struct pci_dev *pdev = test->pdev;
++
++      pci_free_irq_vectors(pdev);
++}
++
++static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
++                                              int type)
++{
++      int irq = -1;
++      struct pci_dev *pdev = test->pdev;
++      struct device *dev = &pdev->dev;
++      bool res = true;
++
++      switch (type) {
++      case IRQ_TYPE_LEGACY:
++              irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY);
++              if (irq < 0)
++                      dev_err(dev, "Failed to get Legacy interrupt\n");
++              break;
++      case IRQ_TYPE_MSI:
++              irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
++              if (irq < 0)
++                      dev_err(dev, "Failed to get MSI interrupts\n");
++              break;
++      case IRQ_TYPE_MSIX:
++              irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX);
++              if (irq < 0)
++                      dev_err(dev, "Failed to get MSI-X interrupts\n");
++              break;
++      default:
++              dev_err(dev, "Invalid IRQ type selected\n");
++      }
++
++      if (irq < 0) {
++              irq = 0;
++              res = false;
++      }
++      test->num_irqs = irq;
++
++      return res;
++}
++
++static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
++{
++      int i;
++      struct pci_dev *pdev = test->pdev;
++      struct device *dev = &pdev->dev;
++
++      for (i = 0; i < test->num_irqs; i++)
++              devm_free_irq(dev, pci_irq_vector(pdev, i), test);
++
++      test->num_irqs = 0;
++}
++
++static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
++{
++      int i;
++      int err;
++      struct pci_dev *pdev = test->pdev;
++      struct device *dev = &pdev->dev;
++
++      for (i = 0; i < test->num_irqs; i++) {
++              err = devm_request_irq(dev, pci_irq_vector(pdev, i),
++                                     pci_endpoint_test_irqhandler,
++                                     IRQF_SHARED, DRV_MODULE_NAME, test);
++              if (err)
++                      goto fail;
++      }
++
++      return true;
++
++fail:
++      switch (irq_type) {
++      case IRQ_TYPE_LEGACY:
++              dev_err(dev, "Failed to request IRQ %d for Legacy\n",
++                      pci_irq_vector(pdev, i));
++              break;
++      case IRQ_TYPE_MSI:
++              dev_err(dev, "Failed to request IRQ %d for MSI %d\n",
++                      pci_irq_vector(pdev, i),
++                      i + 1);
++              break;
++      case IRQ_TYPE_MSIX:
++              dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n",
++                      pci_irq_vector(pdev, i),
++                      i + 1);
++              break;
++      }
++
++      return false;
++}
++
+ static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
+                                 enum pci_barno barno)
+ {
+@@ -179,6 +284,9 @@ static bool pci_endpoint_test_legacy_irq
+ {
+       u32 val;
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
++                               IRQ_TYPE_LEGACY);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                COMMAND_RAISE_LEGACY_IRQ);
+       val = wait_for_completion_timeout(&test->irq_raised,
+@@ -190,20 +298,24 @@ static bool pci_endpoint_test_legacy_irq
+ }
+ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
+-                                    u8 msi_num)
++                                     u16 msi_num, bool msix)
+ {
+       u32 val;
+       struct pci_dev *pdev = test->pdev;
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
++                               msix == false ? IRQ_TYPE_MSI :
++                               IRQ_TYPE_MSIX);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+-                               msi_num << MSI_NUMBER_SHIFT |
+-                               COMMAND_RAISE_MSI_IRQ);
++                               msix == false ? COMMAND_RAISE_MSI_IRQ :
++                               COMMAND_RAISE_MSIX_IRQ);
+       val = wait_for_completion_timeout(&test->irq_raised,
+                                         msecs_to_jiffies(1000));
+       if (!val)
+               return false;
+-      if (test->last_irq - pdev->irq == msi_num - 1)
++      if (pci_irq_vector(pdev, msi_num - 1) == test->last_irq)
+               return true;
+       return false;
+@@ -230,10 +342,18 @@ static bool pci_endpoint_test_copy(struc
+       if (size > SIZE_MAX - alignment)
+               goto err;
++      if (size > SIZE_MAX - alignment)
++              goto err;
++
++      if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
++              dev_err(dev, "Invalid IRQ type option\n");
++              goto err;
++      }
++
+       orig_src_addr = dma_alloc_coherent(dev, size + alignment,
+                                          &orig_src_phys_addr, GFP_KERNEL);
+       if (!orig_src_addr) {
+-              dev_err(dev, "failed to allocate source buffer\n");
++              dev_err(dev, "Failed to allocate source buffer\n");
+               ret = false;
+               goto err;
+       }
+@@ -259,7 +379,7 @@ static bool pci_endpoint_test_copy(struc
+       orig_dst_addr = dma_alloc_coherent(dev, size + alignment,
+                                          &orig_dst_phys_addr, GFP_KERNEL);
+       if (!orig_dst_addr) {
+-              dev_err(dev, "failed to allocate destination address\n");
++              dev_err(dev, "Failed to allocate destination address\n");
+               ret = false;
+               goto err_orig_src_addr;
+       }
+@@ -281,8 +401,10 @@ static bool pci_endpoint_test_copy(struc
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
+                                size);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+-                               1 << MSI_NUMBER_SHIFT | COMMAND_COPY);
++                               COMMAND_COPY);
+       wait_for_completion(&test->irq_raised);
+@@ -318,10 +440,18 @@ static bool pci_endpoint_test_write(stru
+       if (size > SIZE_MAX - alignment)
+               goto err;
++      if (size > SIZE_MAX - alignment)
++              goto err;
++
++      if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
++              dev_err(dev, "Invalid IRQ type option\n");
++              goto err;
++      }
++
+       orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
+                                      GFP_KERNEL);
+       if (!orig_addr) {
+-              dev_err(dev, "failed to allocate address\n");
++              dev_err(dev, "Failed to allocate address\n");
+               ret = false;
+               goto err;
+       }
+@@ -348,8 +478,10 @@ static bool pci_endpoint_test_write(stru
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+-                               1 << MSI_NUMBER_SHIFT | COMMAND_READ);
++                               COMMAND_READ);
+       wait_for_completion(&test->irq_raised);
+@@ -379,10 +511,18 @@ static bool pci_endpoint_test_read(struc
+       if (size > SIZE_MAX - alignment)
+               goto err;
++      if (size > SIZE_MAX - alignment)
++              goto err;
++
++      if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
++              dev_err(dev, "Invalid IRQ type option\n");
++              goto err;
++      }
++
+       orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
+                                      GFP_KERNEL);
+       if (!orig_addr) {
+-              dev_err(dev, "failed to allocate destination address\n");
++              dev_err(dev, "Failed to allocate destination address\n");
+               ret = false;
+               goto err;
+       }
+@@ -403,8 +543,10 @@ static bool pci_endpoint_test_read(struc
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
++      pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+-                               1 << MSI_NUMBER_SHIFT | COMMAND_WRITE);
++                               COMMAND_WRITE);
+       wait_for_completion(&test->irq_raised);
+@@ -417,6 +559,38 @@ err:
+       return ret;
+ }
++static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
++                                    int req_irq_type)
++{
++      struct pci_dev *pdev = test->pdev;
++      struct device *dev = &pdev->dev;
++
++      if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) {
++              dev_err(dev, "Invalid IRQ type option\n");
++              return false;
++      }
++
++      if (irq_type == req_irq_type)
++              return true;
++
++      pci_endpoint_test_release_irq(test);
++      pci_endpoint_test_free_irq_vectors(test);
++
++      if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type))
++              goto err;
++
++      if (!pci_endpoint_test_request_irq(test))
++              goto err;
++
++      irq_type = req_irq_type;
++      return true;
++
++err:
++      pci_endpoint_test_free_irq_vectors(test);
++      irq_type = IRQ_TYPE_UNDEFINED;
++      return false;
++}
++
+ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
+                                   unsigned long arg)
+ {
+@@ -436,7 +610,8 @@ static long pci_endpoint_test_ioctl(stru
+               ret = pci_endpoint_test_legacy_irq(test);
+               break;
+       case PCITEST_MSI:
+-              ret = pci_endpoint_test_msi_irq(test, arg);
++      case PCITEST_MSIX:
++              ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX);
+               break;
+       case PCITEST_WRITE:
+               ret = pci_endpoint_test_write(test, arg);
+@@ -447,6 +622,12 @@ static long pci_endpoint_test_ioctl(stru
+       case PCITEST_COPY:
+               ret = pci_endpoint_test_copy(test, arg);
+               break;
++      case PCITEST_SET_IRQTYPE:
++              ret = pci_endpoint_test_set_irq(test, arg);
++              break;
++      case PCITEST_GET_IRQTYPE:
++              ret = irq_type;
++              break;
+       }
+ ret:
+@@ -462,9 +643,7 @@ static const struct file_operations pci_
+ static int pci_endpoint_test_probe(struct pci_dev *pdev,
+                                  const struct pci_device_id *ent)
  {
-       int i;
+-      int i;
        int err;
 -      int irq = 0;
        int id;
--      char name[20];
+       char name[20];
        enum pci_barno bar;
-       void __iomem *base;
-       struct device *dev = &pdev->dev;
-@@ -501,19 +501,19 @@ static int pci_endpoint_test_probe(struc
+@@ -486,12 +665,15 @@ static int pci_endpoint_test_probe(struc
+       test->alignment = 0;
+       test->pdev = pdev;
++      if (no_msi)
++              irq_type = IRQ_TYPE_LEGACY;
++
+       data = (struct pci_endpoint_test_data *)ent->driver_data;
+       if (data) {
+               test_reg_bar = data->test_reg_bar;
+               test->test_reg_bar = test_reg_bar;
+               test->alignment = data->alignment;
+-              no_msi = data->no_msi;
++              irq_type = data->irq_type;
+       }
+       init_completion(&test->irq_raised);
+@@ -511,36 +693,21 @@ static int pci_endpoint_test_probe(struc
        pci_set_master(pdev);
  
-       if (!no_msi) {
+-      if (!no_msi) {
 -              irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
 -              if (irq < 0)
-+              test->irq_num = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
-+              if (test->irq_num < 0)
-                       dev_err(dev, "failed to get MSI interrupts\n");
-       }
+-                      dev_err(dev, "failed to get MSI interrupts\n");
+-              test->num_irqs = irq;
+-      }
++      if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type))
++              goto err_disable_irq;
  
 -      err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler,
-+      err = request_irq(pdev->irq, pci_endpoint_test_irqhandler,
-                              IRQF_SHARED, DRV_MODULE_NAME, test);
-       if (err) {
-               dev_err(dev, "failed to request IRQ %d\n", pdev->irq);
-               goto err_disable_msi;
+-                             IRQF_SHARED, DRV_MODULE_NAME, test);
+-      if (err) {
+-              dev_err(dev, "failed to request IRQ %d\n", pdev->irq);
+-              goto err_disable_msi;
+-      }
+-
+-      for (i = 1; i < irq; i++) {
+-              err = devm_request_irq(dev, pdev->irq + i,
+-                                     pci_endpoint_test_irqhandler,
+-                                     IRQF_SHARED, DRV_MODULE_NAME, test);
+-              if (err)
+-                      dev_err(dev, "failed to request IRQ %d for MSI %d\n",
+-                              pdev->irq + i, i + 1);
+-      }
++      if (!pci_endpoint_test_request_irq(test))
++              goto err_disable_irq;
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+-              base = pci_ioremap_bar(pdev, bar);
+-              if (!base) {
+-                      dev_err(dev, "failed to read BAR%d\n", bar);
+-                      WARN_ON(bar == test_reg_bar);
++              if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
++                      base = pci_ioremap_bar(pdev, bar);
++                      if (!base) {
++                              dev_err(dev, "Failed to read BAR%d\n", bar);
++                              WARN_ON(bar == test_reg_bar);
++                      }
++                      test->bar[bar] = base;
+               }
+-              test->bar[bar] = base;
        }
  
--      for (i = 1; i < irq; i++) {
-+      for (i = 1; i < test->irq_num; i++) {
-               err = devm_request_irq(dev, pdev->irq + i,
-                                      pci_endpoint_test_irqhandler,
-                                      IRQF_SHARED, DRV_MODULE_NAME, test);
-@@ -548,10 +548,10 @@ static int pci_endpoint_test_probe(struc
+       test->base = test->bar[test_reg_bar];
+@@ -556,24 +723,31 @@ static int pci_endpoint_test_probe(struc
+       id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               err = id;
+-              dev_err(dev, "unable to get id\n");
++              dev_err(dev, "Unable to get id\n");
                goto err_iounmap;
        }
  
--      snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
-+      snprintf(test->name, sizeof(test->name), DRV_MODULE_NAME ".%d", id);
+       snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
        misc_device = &test->miscdev;
        misc_device->minor = MISC_DYNAMIC_MINOR;
 -      misc_device->name = name;
-+      misc_device->name = test->name;
++      misc_device->name = kstrdup(name, GFP_KERNEL);
++      if (!misc_device->name) {
++              err = -ENOMEM;
++              goto err_ida_remove;
++      }
        misc_device->fops = &pci_endpoint_test_fops,
  
        err = misc_register(misc_device);
-@@ -584,6 +584,7 @@ err_disable_pdev:
- static void pci_endpoint_test_remove(struct pci_dev *pdev)
- {
-       int id;
-+      int i;
-       enum pci_barno bar;
-       struct pci_endpoint_test *test = pci_get_drvdata(pdev);
-       struct miscdevice *misc_device = &test->miscdev;
-@@ -599,6 +600,8 @@ static void pci_endpoint_test_remove(str
-               if (test->bar[bar])
+       if (err) {
+-              dev_err(dev, "failed to register device\n");
+-              goto err_ida_remove;
++              dev_err(dev, "Failed to register device\n");
++              goto err_kfree_name;
+       }
+       return 0;
++err_kfree_name:
++      kfree(misc_device->name);
++
+ err_ida_remove:
+       ida_simple_remove(&pci_endpoint_test_ida, id);
+@@ -583,11 +757,13 @@ err_iounmap:
                        pci_iounmap(pdev, test->bar[bar]);
        }
-+      for (i = 0; i < test->irq_num; i++)
-+              free_irq(pdev->irq + i, test);
+-      for (i = 0; i < irq; i++)
+-              devm_free_irq(dev, pdev->irq + i, test);
++      pci_endpoint_test_release_irq(test);
+ err_disable_msi:
        pci_disable_msi(pdev);
++
++err_disable_irq:
++      pci_endpoint_test_free_irq_vectors(test);
+       pci_release_regions(pdev);
+ err_disable_pdev:
+@@ -610,14 +786,15 @@ static void pci_endpoint_test_remove(str
+               return;
+       misc_deregister(&test->miscdev);
++      kfree(misc_device->name);
+       ida_simple_remove(&pci_endpoint_test_ida, id);
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               if (test->bar[bar])
+                       pci_iounmap(pdev, test->bar[bar]);
+       }
+-      for (i = 0; i < test->num_irqs; i++)
+-              devm_free_irq(&pdev->dev, pdev->irq + i, test);
+-      pci_disable_msi(pdev);
++
++      pci_endpoint_test_release_irq(test);
++      pci_endpoint_test_free_irq_vectors(test);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-@@ -607,6 +610,7 @@ static void pci_endpoint_test_remove(str
+ }
+@@ -625,6 +802,7 @@ static void pci_endpoint_test_remove(str
  static const struct pci_device_id pci_endpoint_test_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
        { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
@@ -259,63 +756,254 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
        { }
  };
  MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
+--- a/drivers/pci/Kconfig
++++ b/drivers/pci/Kconfig
+@@ -142,6 +142,7 @@ config PCI_HYPERV
+ source "drivers/pci/hotplug/Kconfig"
+ source "drivers/pci/dwc/Kconfig"
++source "drivers/pci/mobiveil/Kconfig"
+ source "drivers/pci/host/Kconfig"
+ source "drivers/pci/endpoint/Kconfig"
+ source "drivers/pci/switch/Kconfig"
 --- a/drivers/pci/dwc/Kconfig
 +++ b/drivers/pci/dwc/Kconfig
-@@ -111,6 +111,14 @@ config PCI_LAYERSCAPE
-       help
-         Say Y here if you want PCIe controller support on Layerscape SoCs.
+@@ -50,17 +50,36 @@ config PCI_DRA7XX_EP
+ endif
+ config PCIE_DW_PLAT
+-      bool "Platform bus based DesignWare PCIe Controller"
+-      depends on PCI
+-      depends on PCI_MSI_IRQ_DOMAIN
+-      select PCIE_DW_HOST
+-      ---help---
+-       This selects the DesignWare PCIe controller support. Select this if
+-       you have a PCIe controller on Platform bus.
++      bool
+-       If you have a controller with this interface, say Y or M here.
++config PCIE_DW_PLAT_HOST
++      bool "Platform bus based DesignWare PCIe Controller - Host mode"
++      depends on PCI && PCI_MSI_IRQ_DOMAIN
++      select PCIE_DW_HOST
++      select PCIE_DW_PLAT
++      help
++        Enables support for the PCIe controller in the Designware IP to
++        work in host mode. There are two instances of PCIe controller in
++        Designware IP.
++        This controller can work either as EP or RC. In order to enable
++        host-specific features PCIE_DW_PLAT_HOST must be selected and in
++        order to enable device-specific features PCI_DW_PLAT_EP must be
++        selected.
  
-+config PCI_LAYERSCAPE_EP
-+      bool "PCI layerscape Endpoint Mode"
+-       If unsure, say N.
++config PCIE_DW_PLAT_EP
++      bool "Platform bus based DesignWare PCIe Controller - Endpoint mode"
++      depends on PCI && PCI_MSI_IRQ_DOMAIN
 +      depends on PCI_ENDPOINT
 +      select PCIE_DW_EP
++      select PCIE_DW_PLAT
 +      help
-+       Enables support for the PCIe controller in the layerscape SoC to work in
-+       endpoint mode.
++        Enables support for the PCIe controller in the Designware IP to
++        work in endpoint mode. There are two instances of PCIe controller
++        in Designware IP.
++        This controller can work either as EP or RC. In order to enable
++        host-specific features PCIE_DW_PLAT_HOST must be selected and in
++        order to enable device-specific features PCI_DW_PLAT_EP must be
++        selected.
+ config PCI_EXYNOS
+       bool "Samsung Exynos PCIe controller"
+--- a/drivers/pci/dwc/Makefile
++++ b/drivers/pci/dwc/Makefile
+@@ -10,7 +10,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
+ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+-obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
++obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o
+ obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
+ obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
+ obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+--- a/drivers/pci/dwc/pci-dra7xx.c
++++ b/drivers/pci/dwc/pci-dra7xx.c
+@@ -339,15 +339,6 @@ static irqreturn_t dra7xx_pcie_irq_handl
+       return IRQ_HANDLED;
+ }
+-static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+-{
+-      u32 reg;
+-
+-      reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+-      dw_pcie_writel_dbi2(pci, reg, 0x0);
+-      dw_pcie_writel_dbi(pci, reg, 0x0);
+-}
+-
+ static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
+ {
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+--- /dev/null
++++ b/drivers/pci/dwc/pci-layerscape-ep.c
+@@ -0,0 +1,146 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCIe controller EP driver for Freescale Layerscape SoCs
++ *
++ * Copyright (C) 2018 NXP Semiconductor.
++ *
++ * Author: Xiaowei Bao <xiaowei.bao@nxp.com>
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/of_pci.h>
++#include <linux/of_platform.h>
++#include <linux/of_address.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/resource.h>
++
++#include "pcie-designware.h"
++
++#define PCIE_DBI2_OFFSET              0x1000  /* DBI2 base address*/
++
++struct ls_pcie_ep {
++      struct dw_pcie          *pci;
++};
++
++#define to_ls_pcie_ep(x)      dev_get_drvdata((x)->dev)
++
++static int ls_pcie_establish_link(struct dw_pcie *pci)
++{
++      return 0;
++}
++
++static const struct dw_pcie_ops ls_pcie_ep_ops = {
++      .start_link = ls_pcie_establish_link,
++};
++
++static const struct of_device_id ls_pcie_ep_of_match[] = {
++      { .compatible = "fsl,ls-pcie-ep",},
++      { },
++};
++
++static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      enum pci_barno bar;
++
++      for (bar = BAR_0; bar <= BAR_5; bar++)
++              dw_pcie_ep_reset_bar(pci, bar);
++
++      epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
++}
++
++static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
++                                enum pci_epc_irq_type type, u16 interrupt_num)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++
++      switch (type) {
++      case PCI_EPC_IRQ_LEGACY:
++              return dw_pcie_ep_raise_legacy_irq(ep, func_no);
++      case PCI_EPC_IRQ_MSI:
++              return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
++      case PCI_EPC_IRQ_MSIX:
++              return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
++      default:
++              dev_err(pci->dev, "UNKNOWN IRQ type\n");
++              return -EINVAL;
++      }
++}
++
++static struct dw_pcie_ep_ops pcie_ep_ops = {
++      .ep_init = ls_pcie_ep_init,
++      .raise_irq = ls_pcie_ep_raise_irq,
++};
++
++static int __init ls_add_pcie_ep(struct ls_pcie_ep *pcie,
++                                      struct platform_device *pdev)
++{
++      struct dw_pcie *pci = pcie->pci;
++      struct device *dev = pci->dev;
++      struct dw_pcie_ep *ep;
++      struct resource *res;
++      int ret;
++
++      ep = &pci->ep;
++      ep->ops = &pcie_ep_ops;
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
++      if (!res)
++              return -EINVAL;
++
++      ep->phys_base = res->start;
++      ep->addr_size = resource_size(res);
++
++      ret = dw_pcie_ep_init(ep);
++      if (ret) {
++              dev_err(dev, "failed to initialize endpoint\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int __init ls_pcie_ep_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct dw_pcie *pci;
++      struct ls_pcie_ep *pcie;
++      struct resource *dbi_base;
++      int ret;
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
++      if (!pci)
++              return -ENOMEM;
++
++      dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
++      pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
++      if (IS_ERR(pci->dbi_base))
++              return PTR_ERR(pci->dbi_base);
 +
- config PCI_HISI
-       depends on OF && ARM64
-       bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers"
++      pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_OFFSET;
++      pci->dev = dev;
++      pci->ops = &ls_pcie_ep_ops;
++      pcie->pci = pci;
++
++      platform_set_drvdata(pdev, pcie);
++
++      ret = ls_add_pcie_ep(pcie, pdev);
++
++      return ret;
++}
++
++static struct platform_driver ls_pcie_ep_driver = {
++      .driver = {
++              .name = "layerscape-pcie-ep",
++              .of_match_table = ls_pcie_ep_of_match,
++              .suppress_bind_attrs = true,
++      },
++};
++builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe);
 --- a/drivers/pci/dwc/pci-layerscape.c
 +++ b/drivers/pci/dwc/pci-layerscape.c
-@@ -33,8 +33,15 @@
+@@ -33,6 +33,8 @@
  
  /* PEX Internal Configuration Registers */
  #define PCIE_STRFMR1          0x71c /* Symbol Timer & Filter Mask Register1 */
 +#define PCIE_ABSERR           0x8d0 /* Bridge Slave Error Response Register */
 +#define PCIE_ABSERR_SETTING   0x9401 /* Forward error of non-posted request */
  
-+#define PCIE_DBI2_BASE                0x1000  /* DBI2 base address*/
-+#define PCIE_MSI_MSG_DATA_OFF 0x5c    /* MSI Data register address*/
-+#define PCIE_MSI_OB_SIZE      4096
-+#define PCIE_MSI_ADDR_OFFSET  (1024 * 1024)
  #define PCIE_IATU_NUM         6
-+#define PCIE_EP_ADDR_SPACE_SIZE 0x100000000
- struct ls_pcie_drvdata {
-       u32 lut_offset;
-@@ -44,12 +51,20 @@ struct ls_pcie_drvdata {
-       const struct dw_pcie_ops *dw_pcie_ops;
- };
-+struct ls_pcie_ep {
-+      dma_addr_t msi_phys_addr;
-+      void __iomem *msi_virt_addr;
-+      u64 msi_msg_addr;
-+      u16 msi_msg_data;
-+};
-+
- struct ls_pcie {
-       struct dw_pcie *pci;
-       void __iomem *lut;
-       struct regmap *scfg;
-       const struct ls_pcie_drvdata *drvdata;
-       int index;
-+      struct ls_pcie_ep *pcie_ep;
- };
  
- #define to_ls_pcie(x) dev_get_drvdata((x)->dev)
-@@ -124,6 +139,14 @@ static int ls_pcie_link_up(struct dw_pci
+@@ -124,6 +126,14 @@ static int ls_pcie_link_up(struct dw_pci
        return 1;
  }
  
@@ -330,7 +1018,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
  static int ls_pcie_host_init(struct pcie_port *pp)
  {
        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-@@ -135,6 +158,7 @@ static int ls_pcie_host_init(struct pcie
+@@ -135,6 +145,7 @@ static int ls_pcie_host_init(struct pcie
         * dw_pcie_setup_rc() will reconfigure the outbound windows.
         */
        ls_pcie_disable_outbound_atus(pcie);
@@ -338,7 +1026,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
  
        dw_pcie_dbi_ro_wr_en(pci);
        ls_pcie_clear_multifunction(pcie);
-@@ -253,6 +277,7 @@ static struct ls_pcie_drvdata ls2088_drv
+@@ -253,6 +264,7 @@ static struct ls_pcie_drvdata ls2088_drv
  };
  
  static const struct of_device_id ls_pcie_of_match[] = {
@@ -346,167 +1034,4525 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
        { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
        { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
        { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
-@@ -263,6 +288,99 @@ static const struct of_device_id ls_pcie
-       { },
- };
+--- a/drivers/pci/dwc/pcie-designware-ep.c
++++ b/drivers/pci/dwc/pcie-designware-ep.c
+@@ -1,20 +1,9 @@
++// SPDX-License-Identifier: GPL-2.0
+ /**
+  * Synopsys DesignWare PCIe Endpoint controller driver
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <linux/of.h>
+@@ -30,7 +19,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep
+       pci_epc_linkup(epc);
+ }
+-static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
++static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar,
++                                 int flags)
+ {
+       u32 reg;
+@@ -38,10 +28,52 @@ static void dw_pcie_ep_reset_bar(struct
+       dw_pcie_dbi_ro_wr_en(pci);
+       dw_pcie_writel_dbi2(pci, reg, 0x0);
+       dw_pcie_writel_dbi(pci, reg, 0x0);
++      if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
++              dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
++              dw_pcie_writel_dbi(pci, reg + 4, 0x0);
++      }
+       dw_pcie_dbi_ro_wr_dis(pci);
+ }
  
-+static void ls_pcie_raise_msi_irq(struct ls_pcie_ep *pcie_ep)
+-static int dw_pcie_ep_write_header(struct pci_epc *epc,
++void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
 +{
-+      iowrite32(pcie_ep->msi_msg_data, pcie_ep->msi_virt_addr);
++      __dw_pcie_ep_reset_bar(pci, bar, 0);
 +}
 +
-+static int ls_pcie_raise_irq(struct dw_pcie_ep *ep,
-+              enum pci_epc_irq_type type, u8 interrupt_num)
++static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
++                            u8 cap)
 +{
-+      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
-+      struct ls_pcie *pcie = to_ls_pcie(pci);
-+      struct ls_pcie_ep *pcie_ep = pcie->pcie_ep;
-+      u32 free_win;
-+
-+      /* get the msi message address and msi message data */
-+      pcie_ep->msi_msg_addr = ioread32(pci->dbi_base + MSI_MESSAGE_ADDR_L32) |
-+              (((u64)ioread32(pci->dbi_base + MSI_MESSAGE_ADDR_U32)) << 32);
-+      pcie_ep->msi_msg_data = ioread16(pci->dbi_base + PCIE_MSI_MSG_DATA_OFF);
-+
-+      /* request and config the outband window for msi */
-+      free_win = find_first_zero_bit(&ep->ob_window_map,
-+                                      sizeof(ep->ob_window_map));
-+      if (free_win >= ep->num_ob_windows) {
-+              dev_err(pci->dev, "no free outbound window\n");
-+              return -ENOMEM;
-+      }
-+
-+      dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM,
-+                                      pcie_ep->msi_phys_addr,
-+                                      pcie_ep->msi_msg_addr,
-+                                      PCIE_MSI_OB_SIZE);
++      u8 cap_id, next_cap_ptr;
++      u16 reg;
 +
-+      set_bit(free_win, &ep->ob_window_map);
++      reg = dw_pcie_readw_dbi(pci, cap_ptr);
++      next_cap_ptr = (reg & 0xff00) >> 8;
++      cap_id = (reg & 0x00ff);
 +
-+      /* generate the msi interrupt */
-+      ls_pcie_raise_msi_irq(pcie_ep);
++      if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
++              return 0;
 +
-+      /* release the outband window of msi */
-+      dw_pcie_disable_atu(pci, free_win, DW_PCIE_REGION_OUTBOUND);
-+      clear_bit(free_win, &ep->ob_window_map);
++      if (cap_id == cap)
++              return cap_ptr;
 +
-+      return 0;
++      return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
 +}
 +
-+static struct dw_pcie_ep_ops pcie_ep_ops = {
-+      .raise_irq = ls_pcie_raise_irq,
-+};
-+
-+static int __init ls_add_pcie_ep(struct ls_pcie *pcie,
-+                                      struct platform_device *pdev)
++static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
 +{
-+      struct dw_pcie *pci = pcie->pci;
-+      struct device *dev = pci->dev;
-+      struct dw_pcie_ep *ep;
-+      struct ls_pcie_ep *pcie_ep;
-+      struct resource *cfg_res;
-+      int ret;
++      u8 next_cap_ptr;
++      u16 reg;
 +
-+      ep = &pci->ep;
-+      ep->ops = &pcie_ep_ops;
++      reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
++      next_cap_ptr = (reg & 0x00ff);
 +
-+      pcie_ep = devm_kzalloc(dev, sizeof(*pcie_ep), GFP_KERNEL);
-+      if (!pcie_ep)
-+              return -ENOMEM;
++      if (!next_cap_ptr)
++              return 0;
 +
-+      pcie->pcie_ep = pcie_ep;
++      return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
++}
++
++static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
+                                  struct pci_epf_header *hdr)
+ {
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+@@ -114,24 +146,29 @@ static int dw_pcie_ep_outbound_atu(struc
+       return 0;
+ }
+-static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
++static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
++                              struct pci_epf_bar *epf_bar)
+ {
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      enum pci_barno bar = epf_bar->barno;
+       u32 atu_index = ep->bar_to_atu[bar];
+-      dw_pcie_ep_reset_bar(pci, bar);
++      __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags);
+       dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
+       clear_bit(atu_index, ep->ib_window_map);
+ }
+-static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
+-                            dma_addr_t bar_phys, size_t size, int flags)
++static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
++                              struct pci_epf_bar *epf_bar)
+ {
+       int ret;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      enum pci_barno bar = epf_bar->barno;
++      size_t size = epf_bar->size;
++      int flags = epf_bar->flags;
+       enum dw_pcie_as_type as_type;
+       u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+@@ -140,13 +177,20 @@ static int dw_pcie_ep_set_bar(struct pci
+       else
+               as_type = DW_PCIE_AS_IO;
+-      ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type);
++      ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type);
+       if (ret)
+               return ret;
+       dw_pcie_dbi_ro_wr_en(pci);
+-      dw_pcie_writel_dbi2(pci, reg, size - 1);
++
++      dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
+       dw_pcie_writel_dbi(pci, reg, flags);
++
++      if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
++              dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
++              dw_pcie_writel_dbi(pci, reg + 4, 0);
++      }
++
+       dw_pcie_dbi_ro_wr_dis(pci);
+       return 0;
+@@ -167,7 +211,8 @@ static int dw_pcie_find_index(struct dw_
+       return -EINVAL;
+ }
+-static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
++static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
++                                phys_addr_t addr)
+ {
+       int ret;
+       u32 atu_index;
+@@ -182,8 +227,9 @@ static void dw_pcie_ep_unmap_addr(struct
+       clear_bit(atu_index, ep->ob_window_map);
+ }
+-static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
+-                             u64 pci_addr, size_t size)
++static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
++                              phys_addr_t addr,
++                              u64 pci_addr, size_t size)
+ {
+       int ret;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+@@ -198,45 +244,93 @@ static int dw_pcie_ep_map_addr(struct pc
+       return 0;
+ }
+-static int dw_pcie_ep_get_msi(struct pci_epc *epc)
++static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
++{
++      struct dw_pcie_ep *ep = epc_get_drvdata(epc);
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msi_cap)
++              return -EINVAL;
++
++      reg = ep->msi_cap + PCI_MSI_FLAGS;
++      val = dw_pcie_readw_dbi(pci, reg);
++      if (!(val & PCI_MSI_FLAGS_ENABLE))
++              return -EINVAL;
++
++      val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
++
++      return val;
++}
++
++static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
++{
++      struct dw_pcie_ep *ep = epc_get_drvdata(epc);
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msi_cap)
++              return -EINVAL;
++
++      reg = ep->msi_cap + PCI_MSI_FLAGS;
++      val = dw_pcie_readw_dbi(pci, reg);
++      val &= ~PCI_MSI_FLAGS_QMASK;
++      val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
++      dw_pcie_dbi_ro_wr_en(pci);
++      dw_pcie_writew_dbi(pci, reg, val);
++      dw_pcie_dbi_ro_wr_dis(pci);
++
++      return 0;
++}
++
++static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
+ {
+-      int val;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msix_cap)
++              return -EINVAL;
+-      val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
+-      if (!(val & MSI_CAP_MSI_EN_MASK))
++      reg = ep->msix_cap + PCI_MSIX_FLAGS;
++      val = dw_pcie_readw_dbi(pci, reg);
++      if (!(val & PCI_MSIX_FLAGS_ENABLE))
+               return -EINVAL;
+-      val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
++      val &= PCI_MSIX_FLAGS_QSIZE;
++
+       return val;
+ }
+-static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
++static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
+ {
+-      int val;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      u32 val, reg;
+-      val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
+-      val &= ~MSI_CAP_MMC_MASK;
+-      val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK;
++      if (!ep->msix_cap)
++              return -EINVAL;
++
++      reg = ep->msix_cap + PCI_MSIX_FLAGS;
++      val = dw_pcie_readw_dbi(pci, reg);
++      val &= ~PCI_MSIX_FLAGS_QSIZE;
++      val |= interrupts;
+       dw_pcie_dbi_ro_wr_en(pci);
+-      dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val);
++      dw_pcie_writew_dbi(pci, reg, val);
+       dw_pcie_dbi_ro_wr_dis(pci);
+       return 0;
+ }
+-static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
+-                              enum pci_epc_irq_type type, u8 interrupt_num)
++static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
++                              enum pci_epc_irq_type type, u16 interrupt_num)
+ {
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       if (!ep->ops->raise_irq)
+               return -EINVAL;
+-      return ep->ops->raise_irq(ep, type, interrupt_num);
++      return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
+ }
+ static void dw_pcie_ep_stop(struct pci_epc *epc)
+@@ -269,15 +363,130 @@ static const struct pci_epc_ops epc_ops
+       .unmap_addr             = dw_pcie_ep_unmap_addr,
+       .set_msi                = dw_pcie_ep_set_msi,
+       .get_msi                = dw_pcie_ep_get_msi,
++      .set_msix               = dw_pcie_ep_set_msix,
++      .get_msix               = dw_pcie_ep_get_msix,
+       .raise_irq              = dw_pcie_ep_raise_irq,
+       .start                  = dw_pcie_ep_start,
+       .stop                   = dw_pcie_ep_stop,
+ };
++int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      struct device *dev = pci->dev;
++
++      dev_err(dev, "EP cannot trigger legacy IRQs\n");
++
++      return -EINVAL;
++}
++
++int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
++                           u8 interrupt_num)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      u16 msg_ctrl, msg_data;
++      u32 msg_addr_lower, msg_addr_upper, reg;
++      u64 msg_addr;
++      bool has_upper;
++      int ret;
++
++      if (!ep->msi_cap)
++              return -EINVAL;
++
++      /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
++      reg = ep->msi_cap + PCI_MSI_FLAGS;
++      msg_ctrl = dw_pcie_readw_dbi(pci, reg);
++      has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
++      reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
++      msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
++      if (has_upper) {
++              reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
++              msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
++              reg = ep->msi_cap + PCI_MSI_DATA_64;
++              msg_data = dw_pcie_readw_dbi(pci, reg);
++      } else {
++              msg_addr_upper = 0;
++              reg = ep->msi_cap + PCI_MSI_DATA_32;
++              msg_data = dw_pcie_readw_dbi(pci, reg);
++      }
++      msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
++      ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
++                                epc->mem->page_size);
++      if (ret)
++              return ret;
++
++      writel(msg_data | (interrupt_num - 1), ep->msi_mem);
++
++      dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
++
++      return 0;
++}
++
++int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
++                           u16 interrupt_num)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      u16 tbl_offset, bir;
++      u32 bar_addr_upper, bar_addr_lower;
++      u32 msg_addr_upper, msg_addr_lower;
++      u32 reg, msg_data, vec_ctrl;
++      u64 tbl_addr, msg_addr, reg_u64;
++      void __iomem *msix_tbl;
++      int ret;
++
++      reg = ep->msix_cap + PCI_MSIX_TABLE;
++      tbl_offset = dw_pcie_readl_dbi(pci, reg);
++      bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
++      tbl_offset &= PCI_MSIX_TABLE_OFFSET;
++
++      reg = PCI_BASE_ADDRESS_0 + (4 * bir);
++      bar_addr_upper = 0;
++      bar_addr_lower = dw_pcie_readl_dbi(pci, reg);
++      reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
++      if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64)
++              bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4);
++
++      tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower;
++      tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE));
++      tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK;
++
++      msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr,
++                                 PCI_MSIX_ENTRY_SIZE);
++      if (!msix_tbl)
++              return -EINVAL;
++
++      msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR);
++      msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR);
++      msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
++      msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA);
++      vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL);
++
++      iounmap(msix_tbl);
++
++      if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
++              return -EPERM;
++
++      ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
++                                epc->mem->page_size);
++      if (ret)
++              return ret;
++
++      writel(msg_data, ep->msi_mem);
++
++      dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
++
++      return 0;
++}
++
+ void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
+ {
+       struct pci_epc *epc = ep->epc;
++      pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
++                            epc->mem->page_size);
++
+       pci_epc_mem_exit(epc);
+ }
+@@ -291,7 +500,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *e
+       struct device_node *np = dev->of_node;
+       if (!pci->dbi_base || !pci->dbi_base2) {
+-              dev_err(dev, "dbi_base/deb_base2 is not populated\n");
++              dev_err(dev, "dbi_base/dbi_base2 is not populated\n");
+               return -EINVAL;
+       }
+@@ -333,15 +542,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *e
+               return -ENOMEM;
+       ep->outbound_addr = addr;
+-      if (ep->ops->ep_init)
+-              ep->ops->ep_init(ep);
+-
+       epc = devm_pci_epc_create(dev, &epc_ops);
+       if (IS_ERR(epc)) {
+               dev_err(dev, "failed to create epc device\n");
+               return PTR_ERR(epc);
+       }
++      ep->epc = epc;
++      epc_set_drvdata(epc, ep);
++
++      if (ep->ops->ep_init)
++              ep->ops->ep_init(ep);
++
+       ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
+       if (ret < 0)
+               epc->max_functions = 1;
+@@ -353,8 +565,16 @@ int dw_pcie_ep_init(struct dw_pcie_ep *e
+               return ret;
+       }
+-      ep->epc = epc;
+-      epc_set_drvdata(epc, ep);
++      ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
++                                           epc->mem->page_size);
++      if (!ep->msi_mem) {
++              dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
++              return -ENOMEM;
++      }
++      ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
++
++      ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
++
+       dw_pcie_setup(pci);
+       return 0;
+--- a/drivers/pci/dwc/pcie-designware-host.c
++++ b/drivers/pci/dwc/pcie-designware-host.c
+@@ -1,3 +1,4 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+  * Synopsys DesignWare PCIe host controller driver
+  *
+@@ -5,10 +6,6 @@
+  *            http://www.samsung.com
+  *
+  * Author: Jingoo Han <jg1.han@samsung.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 as
+- * published by the Free Software Foundation.
+  */
+ #include <linux/irqdomain.h>
+--- a/drivers/pci/dwc/pcie-designware-plat.c
++++ b/drivers/pci/dwc/pcie-designware-plat.c
+@@ -1,13 +1,10 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+  * PCIe RC driver for Synopsys DesignWare Core
+  *
+  * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+  *
+  * Authors: Joao Pinto <Joao.Pinto@synopsys.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 as
+- * published by the Free Software Foundation.
+  */
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+@@ -15,19 +12,29 @@
+ #include <linux/interrupt.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
++#include <linux/of_device.h>
+ #include <linux/of_gpio.h>
+ #include <linux/pci.h>
+ #include <linux/platform_device.h>
+ #include <linux/resource.h>
+ #include <linux/signal.h>
+ #include <linux/types.h>
++#include <linux/regmap.h>
+ #include "pcie-designware.h"
+ struct dw_plat_pcie {
+-      struct dw_pcie          *pci;
++      struct dw_pcie                  *pci;
++      struct regmap                   *regmap;
++      enum dw_pcie_device_mode        mode;
++};
++
++struct dw_plat_pcie_of_data {
++      enum dw_pcie_device_mode        mode;
+ };
++static const struct of_device_id dw_plat_pcie_of_match[];
++
+ static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
+ {
+       struct pcie_port *pp = arg;
+@@ -52,9 +59,58 @@ static const struct dw_pcie_host_ops dw_
+       .host_init = dw_plat_pcie_host_init,
+ };
+-static int dw_plat_add_pcie_port(struct pcie_port *pp,
++static int dw_plat_pcie_establish_link(struct dw_pcie *pci)
++{
++      return 0;
++}
++
++static const struct dw_pcie_ops dw_pcie_ops = {
++      .start_link = dw_plat_pcie_establish_link,
++};
++
++static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      enum pci_barno bar;
++
++      for (bar = BAR_0; bar <= BAR_5; bar++)
++              dw_pcie_ep_reset_bar(pci, bar);
++
++      epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
++      epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
++}
++
++static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
++                                   enum pci_epc_irq_type type,
++                                   u16 interrupt_num)
++{
++      struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
++
++      switch (type) {
++      case PCI_EPC_IRQ_LEGACY:
++              return dw_pcie_ep_raise_legacy_irq(ep, func_no);
++      case PCI_EPC_IRQ_MSI:
++              return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
++      case PCI_EPC_IRQ_MSIX:
++              return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
++      default:
++              dev_err(pci->dev, "UNKNOWN IRQ type\n");
++      }
++
++      return 0;
++}
++
++static struct dw_pcie_ep_ops pcie_ep_ops = {
++      .ep_init = dw_plat_pcie_ep_init,
++      .raise_irq = dw_plat_pcie_ep_raise_irq,
++};
++
++static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,
+                                struct platform_device *pdev)
+ {
++      struct dw_pcie *pci = dw_plat_pcie->pci;
++      struct pcie_port *pp = &pci->pp;
+       struct device *dev = &pdev->dev;
+       int ret;
+@@ -82,15 +138,44 @@ static int dw_plat_add_pcie_port(struct
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+-              dev_err(dev, "failed to initialize host\n");
++              dev_err(dev, "Failed to initialize host\n");
+               return ret;
+       }
+       return 0;
+ }
+-static const struct dw_pcie_ops dw_pcie_ops = {
+-};
++static int dw_plat_add_pcie_ep(struct dw_plat_pcie *dw_plat_pcie,
++                             struct platform_device *pdev)
++{
++      int ret;
++      struct dw_pcie_ep *ep;
++      struct resource *res;
++      struct device *dev = &pdev->dev;
++      struct dw_pcie *pci = dw_plat_pcie->pci;
++
++      ep = &pci->ep;
++      ep->ops = &pcie_ep_ops;
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2");
++      pci->dbi_base2 = devm_ioremap_resource(dev, res);
++      if (IS_ERR(pci->dbi_base2))
++              return PTR_ERR(pci->dbi_base2);
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
++      if (!res)
++              return -EINVAL;
++
++      ep->phys_base = res->start;
++      ep->addr_size = resource_size(res);
++
++      ret = dw_pcie_ep_init(ep);
++      if (ret) {
++              dev_err(dev, "Failed to initialize endpoint\n");
++              return ret;
++      }
++      return 0;
++}
+ static int dw_plat_pcie_probe(struct platform_device *pdev)
+ {
+@@ -99,6 +184,16 @@ static int dw_plat_pcie_probe(struct pla
+       struct dw_pcie *pci;
+       struct resource *res;  /* Resource from DT */
+       int ret;
++      const struct of_device_id *match;
++      const struct dw_plat_pcie_of_data *data;
++      enum dw_pcie_device_mode mode;
++
++      match = of_match_device(dw_plat_pcie_of_match, dev);
++      if (!match)
++              return -EINVAL;
++
++      data = (struct dw_plat_pcie_of_data *)match->data;
++      mode = (enum dw_pcie_device_mode)data->mode;
+       dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL);
+       if (!dw_plat_pcie)
+@@ -112,23 +207,59 @@ static int dw_plat_pcie_probe(struct pla
+       pci->ops = &dw_pcie_ops;
+       dw_plat_pcie->pci = pci;
++      dw_plat_pcie->mode = mode;
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
++      if (!res)
++              res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+-      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pci->dbi_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pci->dbi_base))
+               return PTR_ERR(pci->dbi_base);
+       platform_set_drvdata(pdev, dw_plat_pcie);
+-      ret = dw_plat_add_pcie_port(&pci->pp, pdev);
+-      if (ret < 0)
+-              return ret;
++      switch (dw_plat_pcie->mode) {
++      case DW_PCIE_RC_TYPE:
++              if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST))
++                      return -ENODEV;
++
++              ret = dw_plat_add_pcie_port(dw_plat_pcie, pdev);
++              if (ret < 0)
++                      return ret;
++              break;
++      case DW_PCIE_EP_TYPE:
++              if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_EP))
++                      return -ENODEV;
++
++              ret = dw_plat_add_pcie_ep(dw_plat_pcie, pdev);
++              if (ret < 0)
++                      return ret;
++              break;
++      default:
++              dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode);
++      }
+       return 0;
+ }
++static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = {
++      .mode = DW_PCIE_RC_TYPE,
++};
++
++static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = {
++      .mode = DW_PCIE_EP_TYPE,
++};
++
+ static const struct of_device_id dw_plat_pcie_of_match[] = {
+-      { .compatible = "snps,dw-pcie", },
++      {
++              .compatible = "snps,dw-pcie",
++              .data = &dw_plat_pcie_rc_of_data,
++      },
++      {
++              .compatible = "snps,dw-pcie-ep",
++              .data = &dw_plat_pcie_ep_of_data,
++      },
+       {},
+ };
+--- a/drivers/pci/dwc/pcie-designware.c
++++ b/drivers/pci/dwc/pcie-designware.c
+@@ -1,3 +1,4 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+  * Synopsys DesignWare PCIe host controller driver
+  *
+@@ -5,10 +6,6 @@
+  *            http://www.samsung.com
+  *
+  * Author: Jingoo Han <jg1.han@samsung.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 as
+- * published by the Free Software Foundation.
+  */
+ #include <linux/delay.h>
+--- a/drivers/pci/dwc/pcie-designware.h
++++ b/drivers/pci/dwc/pcie-designware.h
+@@ -1,3 +1,4 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+  * Synopsys DesignWare PCIe host controller driver
+  *
+@@ -5,10 +6,6 @@
+  *            http://www.samsung.com
+  *
+  * Author: Jingoo Han <jg1.han@samsung.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 as
+- * published by the Free Software Foundation.
+  */
+ #ifndef _PCIE_DESIGNWARE_H
+@@ -97,15 +94,6 @@
+ #define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region)                               \
+                       ((0x3 << 20) | ((region) << 9) | (0x1 << 8))
+-#define MSI_MESSAGE_CONTROL           0x52
+-#define MSI_CAP_MMC_SHIFT             1
+-#define MSI_CAP_MMC_MASK              (7 << MSI_CAP_MMC_SHIFT)
+-#define MSI_CAP_MME_SHIFT             4
+-#define MSI_CAP_MSI_EN_MASK           0x1
+-#define MSI_CAP_MME_MASK              (7 << MSI_CAP_MME_SHIFT)
+-#define MSI_MESSAGE_ADDR_L32          0x54
+-#define MSI_MESSAGE_ADDR_U32          0x58
+-
+ /*
+  * 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,
+@@ -118,6 +106,10 @@
+ #define MAX_IATU_IN                   256
+ #define MAX_IATU_OUT                  256
++/* Maximum number of inbound/outbound iATUs */
++#define MAX_IATU_IN                   256
++#define MAX_IATU_OUT                  256
++
+ struct pcie_port;
+ struct dw_pcie;
+ struct dw_pcie_ep;
+@@ -185,8 +177,8 @@ enum dw_pcie_as_type {
+ struct dw_pcie_ep_ops {
+       void    (*ep_init)(struct dw_pcie_ep *ep);
+-      int     (*raise_irq)(struct dw_pcie_ep *ep, enum pci_epc_irq_type type,
+-                           u8 interrupt_num);
++      int     (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
++                           enum pci_epc_irq_type type, u16 interrupt_num);
+ };
+ struct dw_pcie_ep {
+@@ -201,6 +193,10 @@ struct dw_pcie_ep {
+       unsigned long           *ob_window_map;
+       u32                     num_ib_windows;
+       u32                     num_ob_windows;
++      void __iomem            *msi_mem;
++      phys_addr_t             msi_mem_phys;
++      u8                      msi_cap;        /* MSI capability offset */
++      u8                      msix_cap;       /* MSI-X capability offset */
+ };
+ struct dw_pcie_ops {
+@@ -339,6 +335,12 @@ static inline int dw_pcie_host_init(stru
+ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
+ int dw_pcie_ep_init(struct dw_pcie_ep *ep);
+ void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
++int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no);
++int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
++                           u8 interrupt_num);
++int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
++                           u16 interrupt_num);
++void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
+ #else
+ static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
+ {
+@@ -352,5 +354,26 @@ static inline int dw_pcie_ep_init(struct
+ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
+ {
+ }
++
++static inline int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
++{
++      return 0;
++}
++
++static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
++                                         u8 interrupt_num)
++{
++      return 0;
++}
++
++static inline int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
++                                         u16 interrupt_num)
++{
++      return 0;
++}
++
++static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
++{
++}
+ #endif
+ #endif /* _PCIE_DESIGNWARE_H */
+--- a/drivers/pci/endpoint/Kconfig
++++ b/drivers/pci/endpoint/Kconfig
+@@ -1,3 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
+ #
+ # PCI Endpoint Support
+ #
+--- a/drivers/pci/endpoint/Makefile
++++ b/drivers/pci/endpoint/Makefile
+@@ -1,3 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
+ #
+ # Makefile for PCI Endpoint Support
+ #
+--- a/drivers/pci/endpoint/functions/Kconfig
++++ b/drivers/pci/endpoint/functions/Kconfig
+@@ -1,3 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
+ #
+ # PCI Endpoint Functions
+ #
+--- a/drivers/pci/endpoint/functions/Makefile
++++ b/drivers/pci/endpoint/functions/Makefile
+@@ -1,3 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
+ #
+ # Makefile for PCI Endpoint Functions
+ #
+--- a/drivers/pci/endpoint/functions/pci-epf-test.c
++++ b/drivers/pci/endpoint/functions/pci-epf-test.c
+@@ -1,20 +1,9 @@
++// SPDX-License-Identifier: GPL-2.0
+ /**
+  * Test driver to test endpoint functionality
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <linux/crc32.h>
+@@ -29,13 +18,16 @@
+ #include <linux/pci-epf.h>
+ #include <linux/pci_regs.h>
++#define IRQ_TYPE_LEGACY                       0
++#define IRQ_TYPE_MSI                  1
++#define IRQ_TYPE_MSIX                 2
++
+ #define COMMAND_RAISE_LEGACY_IRQ      BIT(0)
+ #define COMMAND_RAISE_MSI_IRQ         BIT(1)
+-#define MSI_NUMBER_SHIFT              2
+-#define MSI_NUMBER_MASK                       (0x3f << MSI_NUMBER_SHIFT)
+-#define COMMAND_READ                  BIT(8)
+-#define COMMAND_WRITE                 BIT(9)
+-#define COMMAND_COPY                  BIT(10)
++#define COMMAND_RAISE_MSIX_IRQ                BIT(2)
++#define COMMAND_READ                  BIT(3)
++#define COMMAND_WRITE                 BIT(4)
++#define COMMAND_COPY                  BIT(5)
+ #define STATUS_READ_SUCCESS           BIT(0)
+ #define STATUS_READ_FAIL              BIT(1)
+@@ -56,6 +48,7 @@ struct pci_epf_test {
+       struct pci_epf          *epf;
+       enum pci_barno          test_reg_bar;
+       bool                    linkup_notifier;
++      bool                    msix_available;
+       struct delayed_work     cmd_handler;
+ };
+@@ -67,6 +60,8 @@ struct pci_epf_test_reg {
+       u64     dst_addr;
+       u32     size;
+       u32     checksum;
++      u32     irq_type;
++      u32     irq_number;
+ } __packed;
+ static struct pci_epf_header test_header = {
+@@ -81,7 +76,7 @@ struct pci_epf_test_data {
+       bool            linkup_notifier;
+ };
+-static int bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
++static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
+ static int pci_epf_test_copy(struct pci_epf_test *epf_test)
+ {
+@@ -98,43 +93,45 @@ static int pci_epf_test_copy(struct pci_
+       src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size);
+       if (!src_addr) {
+-              dev_err(dev, "failed to allocate source address\n");
++              dev_err(dev, "Failed to allocate source address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err;
+       }
+-      ret = pci_epc_map_addr(epc, src_phys_addr, reg->src_addr, reg->size);
++      ret = pci_epc_map_addr(epc, epf->func_no, src_phys_addr, reg->src_addr,
++                             reg->size);
+       if (ret) {
+-              dev_err(dev, "failed to map source address\n");
++              dev_err(dev, "Failed to map source address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               goto err_src_addr;
+       }
+       dst_addr = pci_epc_mem_alloc_addr(epc, &dst_phys_addr, reg->size);
+       if (!dst_addr) {
+-              dev_err(dev, "failed to allocate destination address\n");
++              dev_err(dev, "Failed to allocate destination address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err_src_map_addr;
+       }
+-      ret = pci_epc_map_addr(epc, dst_phys_addr, reg->dst_addr, reg->size);
++      ret = pci_epc_map_addr(epc, epf->func_no, dst_phys_addr, reg->dst_addr,
++                             reg->size);
+       if (ret) {
+-              dev_err(dev, "failed to map destination address\n");
++              dev_err(dev, "Failed to map destination address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               goto err_dst_addr;
+       }
+       memcpy(dst_addr, src_addr, reg->size);
+-      pci_epc_unmap_addr(epc, dst_phys_addr);
++      pci_epc_unmap_addr(epc, epf->func_no, dst_phys_addr);
+ err_dst_addr:
+       pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size);
+ err_src_map_addr:
+-      pci_epc_unmap_addr(epc, src_phys_addr);
++      pci_epc_unmap_addr(epc, epf->func_no, src_phys_addr);
+ err_src_addr:
+       pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size);
+@@ -158,15 +155,16 @@ static int pci_epf_test_read(struct pci_
+       src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
+       if (!src_addr) {
+-              dev_err(dev, "failed to allocate address\n");
++              dev_err(dev, "Failed to allocate address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err;
+       }
+-      ret = pci_epc_map_addr(epc, phys_addr, reg->src_addr, reg->size);
++      ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->src_addr,
++                             reg->size);
+       if (ret) {
+-              dev_err(dev, "failed to map address\n");
++              dev_err(dev, "Failed to map address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               goto err_addr;
+       }
+@@ -186,7 +184,7 @@ static int pci_epf_test_read(struct pci_
+       kfree(buf);
+ err_map_addr:
+-      pci_epc_unmap_addr(epc, phys_addr);
++      pci_epc_unmap_addr(epc, epf->func_no, phys_addr);
+ err_addr:
+       pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size);
+@@ -209,15 +207,16 @@ static int pci_epf_test_write(struct pci
+       dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
+       if (!dst_addr) {
+-              dev_err(dev, "failed to allocate address\n");
++              dev_err(dev, "Failed to allocate address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err;
+       }
+-      ret = pci_epc_map_addr(epc, phys_addr, reg->dst_addr, reg->size);
++      ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->dst_addr,
++                             reg->size);
+       if (ret) {
+-              dev_err(dev, "failed to map address\n");
++              dev_err(dev, "Failed to map address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               goto err_addr;
+       }
+@@ -237,12 +236,12 @@ static int pci_epf_test_write(struct pci
+        * wait 1ms inorder for the write to complete. Without this delay L3
+        * error in observed in the host system.
+        */
+-      mdelay(1);
++      usleep_range(1000, 2000);
+       kfree(buf);
+ err_map_addr:
+-      pci_epc_unmap_addr(epc, phys_addr);
++      pci_epc_unmap_addr(epc, epf->func_no, phys_addr);
+ err_addr:
+       pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size);
+@@ -251,31 +250,42 @@ err:
+       return ret;
+ }
+-static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq)
++static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq_type,
++                                 u16 irq)
+ {
+-      u8 msi_count;
+       struct pci_epf *epf = epf_test->epf;
++      struct device *dev = &epf->dev;
+       struct pci_epc *epc = epf->epc;
+       enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+       struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
+       reg->status |= STATUS_IRQ_RAISED;
+-      msi_count = pci_epc_get_msi(epc);
+-      if (irq > msi_count || msi_count <= 0)
+-              pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0);
+-      else
+-              pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq);
++
++      switch (irq_type) {
++      case IRQ_TYPE_LEGACY:
++              pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0);
++              break;
++      case IRQ_TYPE_MSI:
++              pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
++              break;
++      case IRQ_TYPE_MSIX:
++              pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX, irq);
++              break;
++      default:
++              dev_err(dev, "Failed to raise IRQ, unknown type\n");
++              break;
++      }
+ }
+ static void pci_epf_test_cmd_handler(struct work_struct *work)
+ {
+       int ret;
+-      u8 irq;
+-      u8 msi_count;
++      int count;
+       u32 command;
+       struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
+                                                    cmd_handler.work);
+       struct pci_epf *epf = epf_test->epf;
++      struct device *dev = &epf->dev;
+       struct pci_epc *epc = epf->epc;
+       enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+       struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
+@@ -287,11 +297,14 @@ static void pci_epf_test_cmd_handler(str
+       reg->command = 0;
+       reg->status = 0;
+-      irq = (command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT;
++      if (reg->irq_type > IRQ_TYPE_MSIX) {
++              dev_err(dev, "Failed to detect IRQ type\n");
++              goto reset_handler;
++      }
+       if (command & COMMAND_RAISE_LEGACY_IRQ) {
+               reg->status = STATUS_IRQ_RAISED;
+-              pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0);
++              pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0);
+               goto reset_handler;
+       }
+@@ -301,7 +314,8 @@ static void pci_epf_test_cmd_handler(str
+                       reg->status |= STATUS_WRITE_FAIL;
+               else
+                       reg->status |= STATUS_WRITE_SUCCESS;
+-              pci_epf_test_raise_irq(epf_test, irq);
++              pci_epf_test_raise_irq(epf_test, reg->irq_type,
++                                     reg->irq_number);
+               goto reset_handler;
+       }
+@@ -311,7 +325,8 @@ static void pci_epf_test_cmd_handler(str
+                       reg->status |= STATUS_READ_SUCCESS;
+               else
+                       reg->status |= STATUS_READ_FAIL;
+-              pci_epf_test_raise_irq(epf_test, irq);
++              pci_epf_test_raise_irq(epf_test, reg->irq_type,
++                                     reg->irq_number);
+               goto reset_handler;
+       }
+@@ -321,16 +336,28 @@ static void pci_epf_test_cmd_handler(str
+                       reg->status |= STATUS_COPY_SUCCESS;
+               else
+                       reg->status |= STATUS_COPY_FAIL;
+-              pci_epf_test_raise_irq(epf_test, irq);
++              pci_epf_test_raise_irq(epf_test, reg->irq_type,
++                                     reg->irq_number);
+               goto reset_handler;
+       }
+       if (command & COMMAND_RAISE_MSI_IRQ) {
+-              msi_count = pci_epc_get_msi(epc);
+-              if (irq > msi_count || msi_count <= 0)
++              count = pci_epc_get_msi(epc, epf->func_no);
++              if (reg->irq_number > count || count <= 0)
++                      goto reset_handler;
++              reg->status = STATUS_IRQ_RAISED;
++              pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI,
++                                reg->irq_number);
++              goto reset_handler;
++      }
++
++      if (command & COMMAND_RAISE_MSIX_IRQ) {
++              count = pci_epc_get_msix(epc, epf->func_no);
++              if (reg->irq_number > count || count <= 0)
+                       goto reset_handler;
+               reg->status = STATUS_IRQ_RAISED;
+-              pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq);
++              pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSIX,
++                                reg->irq_number);
+               goto reset_handler;
+       }
+@@ -351,21 +378,23 @@ static void pci_epf_test_unbind(struct p
+ {
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+       struct pci_epc *epc = epf->epc;
++      struct pci_epf_bar *epf_bar;
+       int bar;
+       cancel_delayed_work(&epf_test->cmd_handler);
+       pci_epc_stop(epc);
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
++              epf_bar = &epf->bar[bar];
++
+               if (epf_test->reg[bar]) {
+                       pci_epf_free_space(epf, epf_test->reg[bar], bar);
+-                      pci_epc_clear_bar(epc, bar);
++                      pci_epc_clear_bar(epc, epf->func_no, epf_bar);
+               }
+       }
+ }
+ static int pci_epf_test_set_bar(struct pci_epf *epf)
+ {
+-      int flags;
+       int bar;
+       int ret;
+       struct pci_epf_bar *epf_bar;
+@@ -374,20 +403,27 @@ static int pci_epf_test_set_bar(struct p
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+       enum pci_barno test_reg_bar = epf_test->test_reg_bar;
+-      flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32;
+-      if (sizeof(dma_addr_t) == 0x8)
+-              flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+-
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               epf_bar = &epf->bar[bar];
+-              ret = pci_epc_set_bar(epc, bar, epf_bar->phys_addr,
+-                                    epf_bar->size, flags);
++
++              epf_bar->flags |= upper_32_bits(epf_bar->size) ?
++                      PCI_BASE_ADDRESS_MEM_TYPE_64 :
++                      PCI_BASE_ADDRESS_MEM_TYPE_32;
++
++              ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
+               if (ret) {
+                       pci_epf_free_space(epf, epf_test->reg[bar], bar);
+-                      dev_err(dev, "failed to set BAR%d\n", bar);
++                      dev_err(dev, "Failed to set BAR%d\n", bar);
+                       if (bar == test_reg_bar)
+                               return ret;
+               }
++              /*
++               * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64
++               * if the specific implementation required a 64-bit BAR,
++               * even if we only requested a 32-bit BAR.
++               */
++              if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
++                      bar++;
+       }
+       return 0;
+@@ -404,7 +440,7 @@ static int pci_epf_test_alloc_space(stru
+       base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg),
+                                  test_reg_bar);
+       if (!base) {
+-              dev_err(dev, "failed to allocated register space\n");
++              dev_err(dev, "Failed to allocated register space\n");
+               return -ENOMEM;
+       }
+       epf_test->reg[test_reg_bar] = base;
+@@ -414,7 +450,7 @@ static int pci_epf_test_alloc_space(stru
+                       continue;
+               base = pci_epf_alloc_space(epf, bar_size[bar], bar);
+               if (!base)
+-                      dev_err(dev, "failed to allocate space for BAR%d\n",
++                      dev_err(dev, "Failed to allocate space for BAR%d\n",
+                               bar);
+               epf_test->reg[bar] = base;
+       }
+@@ -433,9 +469,18 @@ static int pci_epf_test_bind(struct pci_
+       if (WARN_ON_ONCE(!epc))
+               return -EINVAL;
+-      ret = pci_epc_write_header(epc, header);
++      if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER)
++              epf_test->linkup_notifier = false;
++      else
++              epf_test->linkup_notifier = true;
++
++      epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
++
++      epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
++
++      ret = pci_epc_write_header(epc, epf->func_no, header);
+       if (ret) {
+-              dev_err(dev, "configuration header write failed\n");
++              dev_err(dev, "Configuration header write failed\n");
+               return ret;
+       }
+@@ -447,9 +492,19 @@ static int pci_epf_test_bind(struct pci_
+       if (ret)
+               return ret;
+-      ret = pci_epc_set_msi(epc, epf->msi_interrupts);
+-      if (ret)
++      ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
++      if (ret) {
++              dev_err(dev, "MSI configuration failed\n");
+               return ret;
++      }
++
++      if (epf_test->msix_available) {
++              ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
++              if (ret) {
++                      dev_err(dev, "MSI-X configuration failed\n");
++                      return ret;
++              }
++      }
+       if (!epf_test->linkup_notifier)
+               queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
+@@ -517,7 +572,7 @@ static int __init pci_epf_test_init(void
+                                            WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+       ret = pci_epf_register_driver(&test_driver);
+       if (ret) {
+-              pr_err("failed to register pci epf test driver --> %d\n", ret);
++              pr_err("Failed to register pci epf test driver --> %d\n", ret);
+               return ret;
+       }
+--- a/drivers/pci/endpoint/pci-ep-cfs.c
++++ b/drivers/pci/endpoint/pci-ep-cfs.c
+@@ -1,35 +1,28 @@
++// SPDX-License-Identifier: GPL-2.0
+ /**
+  * configfs to configure the PCI endpoint
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <linux/module.h>
++#include <linux/idr.h>
+ #include <linux/slab.h>
+ #include <linux/pci-epc.h>
+ #include <linux/pci-epf.h>
+ #include <linux/pci-ep-cfs.h>
++static DEFINE_IDR(functions_idr);
++static DEFINE_MUTEX(functions_mutex);
+ static struct config_group *functions_group;
+ static struct config_group *controllers_group;
+ struct pci_epf_group {
+       struct config_group group;
+       struct pci_epf *epf;
++      int index;
+ };
+ struct pci_epc_group {
+@@ -151,7 +144,7 @@ static struct configfs_item_operations p
+       .drop_link      = pci_epc_epf_unlink,
+ };
+-static struct config_item_type pci_epc_type = {
++static const struct config_item_type pci_epc_type = {
+       .ct_item_ops    = &pci_epc_item_ops,
+       .ct_attrs       = pci_epc_attrs,
+       .ct_owner       = THIS_MODULE,
+@@ -293,6 +286,28 @@ static ssize_t pci_epf_msi_interrupts_sh
+                      to_pci_epf_group(item)->epf->msi_interrupts);
+ }
++static ssize_t pci_epf_msix_interrupts_store(struct config_item *item,
++                                           const char *page, size_t len)
++{
++      u16 val;
++      int ret;
++
++      ret = kstrtou16(page, 0, &val);
++      if (ret)
++              return ret;
++
++      to_pci_epf_group(item)->epf->msix_interrupts = val;
++
++      return len;
++}
++
++static ssize_t pci_epf_msix_interrupts_show(struct config_item *item,
++                                          char *page)
++{
++      return sprintf(page, "%d\n",
++                     to_pci_epf_group(item)->epf->msix_interrupts);
++}
++
+ PCI_EPF_HEADER_R(vendorid)
+ PCI_EPF_HEADER_W_u16(vendorid)
+@@ -334,6 +349,7 @@ CONFIGFS_ATTR(pci_epf_, subsys_vendor_id
+ CONFIGFS_ATTR(pci_epf_, subsys_id);
+ CONFIGFS_ATTR(pci_epf_, interrupt_pin);
+ CONFIGFS_ATTR(pci_epf_, msi_interrupts);
++CONFIGFS_ATTR(pci_epf_, msix_interrupts);
+ static struct configfs_attribute *pci_epf_attrs[] = {
+       &pci_epf_attr_vendorid,
+@@ -347,6 +363,7 @@ static struct configfs_attribute *pci_ep
+       &pci_epf_attr_subsys_id,
+       &pci_epf_attr_interrupt_pin,
+       &pci_epf_attr_msi_interrupts,
++      &pci_epf_attr_msix_interrupts,
+       NULL,
+ };
+@@ -354,6 +371,9 @@ static void pci_epf_release(struct confi
+ {
+       struct pci_epf_group *epf_group = to_pci_epf_group(item);
++      mutex_lock(&functions_mutex);
++      idr_remove(&functions_idr, epf_group->index);
++      mutex_unlock(&functions_mutex);
+       pci_epf_destroy(epf_group->epf);
+       kfree(epf_group);
+ }
+@@ -362,7 +382,7 @@ static struct configfs_item_operations p
+       .release                = pci_epf_release,
+ };
+-static struct config_item_type pci_epf_type = {
++static const struct config_item_type pci_epf_type = {
+       .ct_item_ops    = &pci_epf_ops,
+       .ct_attrs       = pci_epf_attrs,
+       .ct_owner       = THIS_MODULE,
+@@ -373,22 +393,57 @@ static struct config_group *pci_epf_make
+ {
+       struct pci_epf_group *epf_group;
+       struct pci_epf *epf;
++      char *epf_name;
++      int index, err;
+       epf_group = kzalloc(sizeof(*epf_group), GFP_KERNEL);
+       if (!epf_group)
+               return ERR_PTR(-ENOMEM);
++      mutex_lock(&functions_mutex);
++      index = idr_alloc(&functions_idr, epf_group, 0, 0, GFP_KERNEL);
++      mutex_unlock(&functions_mutex);
++      if (index < 0) {
++              err = index;
++              goto free_group;
++      }
++
++      epf_group->index = index;
++
+       config_group_init_type_name(&epf_group->group, name, &pci_epf_type);
+-      epf = pci_epf_create(group->cg_item.ci_name);
++      epf_name = kasprintf(GFP_KERNEL, "%s.%d",
++                           group->cg_item.ci_name, epf_group->index);
++      if (!epf_name) {
++              err = -ENOMEM;
++              goto remove_idr;
++      }
++
++      epf = pci_epf_create(epf_name);
+       if (IS_ERR(epf)) {
+               pr_err("failed to create endpoint function device\n");
+-              return ERR_PTR(-EINVAL);
++              err = -EINVAL;
++              goto free_name;
+       }
+       epf_group->epf = epf;
++      kfree(epf_name);
++
+       return &epf_group->group;
++
++free_name:
++      kfree(epf_name);
++
++remove_idr:
++      mutex_lock(&functions_mutex);
++      idr_remove(&functions_idr, epf_group->index);
++      mutex_unlock(&functions_mutex);
++
++free_group:
++      kfree(epf_group);
++
++      return ERR_PTR(err);
+ }
+ static void pci_epf_drop(struct config_group *group, struct config_item *item)
+@@ -401,7 +456,7 @@ static struct configfs_group_operations
+       .drop_item      = &pci_epf_drop,
+ };
+-static struct config_item_type pci_epf_group_type = {
++static const struct config_item_type pci_epf_group_type = {
+       .ct_group_ops   = &pci_epf_group_ops,
+       .ct_owner       = THIS_MODULE,
+ };
+@@ -429,15 +484,15 @@ void pci_ep_cfs_remove_epf_group(struct
+ }
+ EXPORT_SYMBOL(pci_ep_cfs_remove_epf_group);
+-static struct config_item_type pci_functions_type = {
++static const struct config_item_type pci_functions_type = {
+       .ct_owner       = THIS_MODULE,
+ };
+-static struct config_item_type pci_controllers_type = {
++static const struct config_item_type pci_controllers_type = {
+       .ct_owner       = THIS_MODULE,
+ };
+-static struct config_item_type pci_ep_type = {
++static const struct config_item_type pci_ep_type = {
+       .ct_owner       = THIS_MODULE,
+ };
+--- a/drivers/pci/endpoint/pci-epc-core.c
++++ b/drivers/pci/endpoint/pci-epc-core.c
+@@ -1,20 +1,9 @@
++// SPDX-License-Identifier: GPL-2.0
+ /**
+  * PCI Endpoint *Controller* (EPC) library
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <linux/device.h>
+@@ -141,25 +130,26 @@ EXPORT_SYMBOL_GPL(pci_epc_start);
+ /**
+  * pci_epc_raise_irq() - interrupt the host system
+  * @epc: the EPC device which has to interrupt the host
+- * @type: specify the type of interrupt; legacy or MSI
+- * @interrupt_num: the MSI interrupt number
++ * @func_no: the endpoint function number in the EPC device
++ * @type: specify the type of interrupt; legacy, MSI or MSI-X
++ * @interrupt_num: the MSI or MSI-X interrupt number
+  *
+- * Invoke to raise an MSI or legacy interrupt
++ * Invoke to raise an legacy, MSI or MSI-X interrupt
+  */
+-int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
+-                    u8 interrupt_num)
++int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
++                    enum pci_epc_irq_type type, u16 interrupt_num)
+ {
+       int ret;
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+               return -EINVAL;
+       if (!epc->ops->raise_irq)
+               return 0;
+       spin_lock_irqsave(&epc->lock, flags);
+-      ret = epc->ops->raise_irq(epc, type, interrupt_num);
++      ret = epc->ops->raise_irq(epc, func_no, type, interrupt_num);
+       spin_unlock_irqrestore(&epc->lock, flags);
+       return ret;
+@@ -169,22 +159,23 @@ EXPORT_SYMBOL_GPL(pci_epc_raise_irq);
+ /**
+  * pci_epc_get_msi() - get the number of MSI interrupt numbers allocated
+  * @epc: the EPC device to which MSI interrupts was requested
++ * @func_no: the endpoint function number in the EPC device
+  *
+  * Invoke to get the number of MSI interrupts allocated by the RC
+  */
+-int pci_epc_get_msi(struct pci_epc *epc)
++int pci_epc_get_msi(struct pci_epc *epc, u8 func_no)
+ {
+       int interrupt;
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+               return 0;
+       if (!epc->ops->get_msi)
+               return 0;
+       spin_lock_irqsave(&epc->lock, flags);
+-      interrupt = epc->ops->get_msi(epc);
++      interrupt = epc->ops->get_msi(epc, func_no);
+       spin_unlock_irqrestore(&epc->lock, flags);
+       if (interrupt < 0)
+@@ -199,17 +190,19 @@ EXPORT_SYMBOL_GPL(pci_epc_get_msi);
+ /**
+  * pci_epc_set_msi() - set the number of MSI interrupt numbers required
+  * @epc: the EPC device on which MSI has to be configured
++ * @func_no: the endpoint function number in the EPC device
+  * @interrupts: number of MSI interrupts required by the EPF
+  *
+  * Invoke to set the required number of MSI interrupts.
+  */
+-int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts)
++int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
+ {
+       int ret;
+       u8 encode_int;
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
++          interrupts > 32)
+               return -EINVAL;
+       if (!epc->ops->set_msi)
+@@ -218,7 +211,7 @@ int pci_epc_set_msi(struct pci_epc *epc,
+       encode_int = order_base_2(interrupts);
+       spin_lock_irqsave(&epc->lock, flags);
+-      ret = epc->ops->set_msi(epc, encode_int);
++      ret = epc->ops->set_msi(epc, func_no, encode_int);
+       spin_unlock_irqrestore(&epc->lock, flags);
+       return ret;
+@@ -226,24 +219,83 @@ int pci_epc_set_msi(struct pci_epc *epc,
+ EXPORT_SYMBOL_GPL(pci_epc_set_msi);
+ /**
++ * pci_epc_get_msix() - get the number of MSI-X interrupt numbers allocated
++ * @epc: the EPC device to which MSI-X interrupts was requested
++ * @func_no: the endpoint function number in the EPC device
++ *
++ * Invoke to get the number of MSI-X interrupts allocated by the RC
++ */
++int pci_epc_get_msix(struct pci_epc *epc, u8 func_no)
++{
++      int interrupt;
++      unsigned long flags;
++
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
++              return 0;
++
++      if (!epc->ops->get_msix)
++              return 0;
++
++      spin_lock_irqsave(&epc->lock, flags);
++      interrupt = epc->ops->get_msix(epc, func_no);
++      spin_unlock_irqrestore(&epc->lock, flags);
++
++      if (interrupt < 0)
++              return 0;
++
++      return interrupt + 1;
++}
++EXPORT_SYMBOL_GPL(pci_epc_get_msix);
++
++/**
++ * pci_epc_set_msix() - set the number of MSI-X interrupt numbers required
++ * @epc: the EPC device on which MSI-X has to be configured
++ * @func_no: the endpoint function number in the EPC device
++ * @interrupts: number of MSI-X interrupts required by the EPF
++ *
++ * Invoke to set the required number of MSI-X interrupts.
++ */
++int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
++{
++      int ret;
++      unsigned long flags;
++
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
++          interrupts < 1 || interrupts > 2048)
++              return -EINVAL;
++
++      if (!epc->ops->set_msix)
++              return 0;
++
++      spin_lock_irqsave(&epc->lock, flags);
++      ret = epc->ops->set_msix(epc, func_no, interrupts - 1);
++      spin_unlock_irqrestore(&epc->lock, flags);
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(pci_epc_set_msix);
++
++/**
+  * pci_epc_unmap_addr() - unmap CPU address from PCI address
+  * @epc: the EPC device on which address is allocated
++ * @func_no: the endpoint function number in the EPC device
+  * @phys_addr: physical address of the local system
+  *
+  * Invoke to unmap the CPU address from PCI address.
+  */
+-void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr)
++void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no,
++                      phys_addr_t phys_addr)
+ {
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+               return;
+       if (!epc->ops->unmap_addr)
+               return;
+       spin_lock_irqsave(&epc->lock, flags);
+-      epc->ops->unmap_addr(epc, phys_addr);
++      epc->ops->unmap_addr(epc, func_no, phys_addr);
+       spin_unlock_irqrestore(&epc->lock, flags);
+ }
+ EXPORT_SYMBOL_GPL(pci_epc_unmap_addr);
+@@ -251,26 +303,27 @@ EXPORT_SYMBOL_GPL(pci_epc_unmap_addr);
+ /**
+  * pci_epc_map_addr() - map CPU address to PCI address
+  * @epc: the EPC device on which address is allocated
++ * @func_no: the endpoint function number in the EPC device
+  * @phys_addr: physical address of the local system
+  * @pci_addr: PCI address to which the physical address should be mapped
+  * @size: the size of the allocation
+  *
+  * Invoke to map CPU address with PCI address.
+  */
+-int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr,
+-                   u64 pci_addr, size_t size)
++int pci_epc_map_addr(struct pci_epc *epc, u8 func_no,
++                   phys_addr_t phys_addr, u64 pci_addr, size_t size)
+ {
+       int ret;
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+               return -EINVAL;
+       if (!epc->ops->map_addr)
+               return 0;
+       spin_lock_irqsave(&epc->lock, flags);
+-      ret = epc->ops->map_addr(epc, phys_addr, pci_addr, size);
++      ret = epc->ops->map_addr(epc, func_no, phys_addr, pci_addr, size);
+       spin_unlock_irqrestore(&epc->lock, flags);
+       return ret;
+@@ -280,22 +333,26 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr);
+ /**
+  * pci_epc_clear_bar() - reset the BAR
+  * @epc: the EPC device for which the BAR has to be cleared
+- * @bar: the BAR number that has to be reset
++ * @func_no: the endpoint function number in the EPC device
++ * @epf_bar: the struct epf_bar that contains the BAR information
+  *
+  * Invoke to reset the BAR of the endpoint device.
+  */
+-void pci_epc_clear_bar(struct pci_epc *epc, int bar)
++void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no,
++                     struct pci_epf_bar *epf_bar)
+ {
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
++          (epf_bar->barno == BAR_5 &&
++           epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
+               return;
+       if (!epc->ops->clear_bar)
+               return;
+       spin_lock_irqsave(&epc->lock, flags);
+-      epc->ops->clear_bar(epc, bar);
++      epc->ops->clear_bar(epc, func_no, epf_bar);
+       spin_unlock_irqrestore(&epc->lock, flags);
+ }
+ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
+@@ -303,26 +360,32 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
+ /**
+  * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space
+  * @epc: the EPC device on which BAR has to be configured
+- * @bar: the BAR number that has to be configured
+- * @size: the size of the addr space
+- * @flags: specify memory allocation/io allocation/32bit address/64 bit address
++ * @func_no: the endpoint function number in the EPC device
++ * @epf_bar: the struct epf_bar that contains the BAR information
+  *
+  * Invoke to configure the BAR of the endpoint device.
+  */
+-int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar,
+-                  dma_addr_t bar_phys, size_t size, int flags)
++int pci_epc_set_bar(struct pci_epc *epc, u8 func_no,
++                  struct pci_epf_bar *epf_bar)
+ {
+       int ret;
+       unsigned long irq_flags;
++      int flags = epf_bar->flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
++          (epf_bar->barno == BAR_5 &&
++           flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
++          (flags & PCI_BASE_ADDRESS_SPACE_IO &&
++           flags & PCI_BASE_ADDRESS_IO_MASK) ||
++          (upper_32_bits(epf_bar->size) &&
++           !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
+               return -EINVAL;
+       if (!epc->ops->set_bar)
+               return 0;
+       spin_lock_irqsave(&epc->lock, irq_flags);
+-      ret = epc->ops->set_bar(epc, bar, bar_phys, size, flags);
++      ret = epc->ops->set_bar(epc, func_no, epf_bar);
+       spin_unlock_irqrestore(&epc->lock, irq_flags);
+       return ret;
+@@ -332,6 +395,7 @@ EXPORT_SYMBOL_GPL(pci_epc_set_bar);
+ /**
+  * pci_epc_write_header() - write standard configuration header
+  * @epc: the EPC device to which the configuration header should be written
++ * @func_no: the endpoint function number in the EPC device
+  * @header: standard configuration header fields
+  *
+  * Invoke to write the configuration header to the endpoint controller. Every
+@@ -339,19 +403,20 @@ EXPORT_SYMBOL_GPL(pci_epc_set_bar);
+  * configuration header would be written. The callback function should write
+  * the header fields to this dedicated location.
+  */
+-int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *header)
++int pci_epc_write_header(struct pci_epc *epc, u8 func_no,
++                       struct pci_epf_header *header)
+ {
+       int ret;
+       unsigned long flags;
+-      if (IS_ERR(epc))
++      if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+               return -EINVAL;
+       if (!epc->ops->write_header)
+               return 0;
+       spin_lock_irqsave(&epc->lock, flags);
+-      ret = epc->ops->write_header(epc, header);
++      ret = epc->ops->write_header(epc, func_no, header);
+       spin_unlock_irqrestore(&epc->lock, flags);
+       return ret;
+--- a/drivers/pci/endpoint/pci-epc-mem.c
++++ b/drivers/pci/endpoint/pci-epc-mem.c
+@@ -1,20 +1,9 @@
++// SPDX-License-Identifier: GPL-2.0
+ /**
+  * PCI Endpoint *Controller* Address Space Management
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <linux/io.h>
+--- a/drivers/pci/endpoint/pci-epf-core.c
++++ b/drivers/pci/endpoint/pci-epf-core.c
+@@ -1,20 +1,9 @@
++// SPDX-License-Identifier: GPL-2.0
+ /**
+  * PCI Endpoint *Function* (EPF) library
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <linux/device.h>
+@@ -26,6 +15,8 @@
+ #include <linux/pci-epf.h>
+ #include <linux/pci-ep-cfs.h>
++static DEFINE_MUTEX(pci_epf_mutex);
++
+ static struct bus_type pci_epf_bus_type;
+ static const struct device_type pci_epf_type;
+@@ -109,6 +100,8 @@ void pci_epf_free_space(struct pci_epf *
+       epf->bar[bar].phys_addr = 0;
+       epf->bar[bar].size = 0;
++      epf->bar[bar].barno = 0;
++      epf->bar[bar].flags = 0;
+ }
+ EXPORT_SYMBOL_GPL(pci_epf_free_space);
+@@ -137,11 +130,27 @@ void *pci_epf_alloc_space(struct pci_epf
+       epf->bar[bar].phys_addr = phys_addr;
+       epf->bar[bar].size = size;
++      epf->bar[bar].barno = bar;
++      epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY;
+       return space;
+ }
+ EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
++static void pci_epf_remove_cfs(struct pci_epf_driver *driver)
++{
++      struct config_group *group, *tmp;
++
++      if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS))
++              return;
++
++      mutex_lock(&pci_epf_mutex);
++      list_for_each_entry_safe(group, tmp, &driver->epf_group, group_entry)
++              pci_ep_cfs_remove_epf_group(group);
++      list_del(&driver->epf_group);
++      mutex_unlock(&pci_epf_mutex);
++}
++
+ /**
+  * pci_epf_unregister_driver() - unregister the PCI EPF driver
+  * @driver: the PCI EPF driver that has to be unregistered
+@@ -150,11 +159,38 @@ EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
+  */
+ void pci_epf_unregister_driver(struct pci_epf_driver *driver)
+ {
+-      pci_ep_cfs_remove_epf_group(driver->group);
++      pci_epf_remove_cfs(driver);
+       driver_unregister(&driver->driver);
+ }
+ EXPORT_SYMBOL_GPL(pci_epf_unregister_driver);
++static int pci_epf_add_cfs(struct pci_epf_driver *driver)
++{
++      struct config_group *group;
++      const struct pci_epf_device_id *id;
++
++      if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS))
++              return 0;
++
++      INIT_LIST_HEAD(&driver->epf_group);
++
++      id = driver->id_table;
++      while (id->name[0]) {
++              group = pci_ep_cfs_add_epf_group(id->name);
++              if (IS_ERR(group)) {
++                      pci_epf_remove_cfs(driver);
++                      return PTR_ERR(group);
++              }
++
++              mutex_lock(&pci_epf_mutex);
++              list_add_tail(&group->group_entry, &driver->epf_group);
++              mutex_unlock(&pci_epf_mutex);
++              id++;
++      }
++
++      return 0;
++}
++
+ /**
+  * __pci_epf_register_driver() - register a new PCI EPF driver
+  * @driver: structure representing PCI EPF driver
+@@ -180,7 +216,7 @@ int __pci_epf_register_driver(struct pci
+       if (ret)
+               return ret;
+-      driver->group = pci_ep_cfs_add_epf_group(driver->driver.name);
++      pci_epf_add_cfs(driver);
+       return 0;
+ }
+@@ -211,29 +247,17 @@ struct pci_epf *pci_epf_create(const cha
+       int ret;
+       struct pci_epf *epf;
+       struct device *dev;
+-      char *func_name;
+-      char *buf;
++      int len;
+       epf = kzalloc(sizeof(*epf), GFP_KERNEL);
+-      if (!epf) {
+-              ret = -ENOMEM;
+-              goto err_ret;
+-      }
++      if (!epf)
++              return ERR_PTR(-ENOMEM);
+-      buf = kstrdup(name, GFP_KERNEL);
+-      if (!buf) {
+-              ret = -ENOMEM;
+-              goto free_epf;
+-      }
+-
+-      func_name = buf;
+-      buf = strchrnul(buf, '.');
+-      *buf = '\0';
+-
+-      epf->name = kstrdup(func_name, GFP_KERNEL);
++      len = strchrnul(name, '.') - name;
++      epf->name = kstrndup(name, len, GFP_KERNEL);
+       if (!epf->name) {
+-              ret = -ENOMEM;
+-              goto free_func_name;
++              kfree(epf);
++              return ERR_PTR(-ENOMEM);
+       }
+       dev = &epf->dev;
+@@ -242,28 +266,18 @@ struct pci_epf *pci_epf_create(const cha
+       dev->type = &pci_epf_type;
+       ret = dev_set_name(dev, "%s", name);
+-      if (ret)
+-              goto put_dev;
++      if (ret) {
++              put_device(dev);
++              return ERR_PTR(ret);
++      }
+       ret = device_add(dev);
+-      if (ret)
+-              goto put_dev;
++      if (ret) {
++              put_device(dev);
++              return ERR_PTR(ret);
++      }
+-      kfree(func_name);
+       return epf;
+-
+-put_dev:
+-      put_device(dev);
+-      kfree(epf->name);
+-
+-free_func_name:
+-      kfree(func_name);
+-
+-free_epf:
+-      kfree(epf);
+-
+-err_ret:
+-      return ERR_PTR(ret);
+ }
+ EXPORT_SYMBOL_GPL(pci_epf_create);
+--- a/drivers/pci/host/pci-host-common.c
++++ b/drivers/pci/host/pci-host-common.c
+@@ -113,9 +113,7 @@ err_out:
+ int pci_host_common_probe(struct platform_device *pdev,
+                         struct pci_ecam_ops *ops)
+ {
+-      const char *type;
+       struct device *dev = &pdev->dev;
+-      struct device_node *np = dev->of_node;
+       struct pci_bus *bus, *child;
+       struct pci_host_bridge *bridge;
+       struct pci_config_window *cfg;
+@@ -126,12 +124,6 @@ int pci_host_common_probe(struct platfor
+       if (!bridge)
+               return -ENOMEM;
+-      type = of_get_property(np, "device_type", NULL);
+-      if (!type || strcmp(type, "pci")) {
+-              dev_err(dev, "invalid \"device_type\" %s\n", type);
+-              return -EINVAL;
+-      }
+-
+       of_pci_check_probe_only();
+       /* Parse and map our Configuration Space windows */
+--- a/drivers/pci/host/pcie-xilinx-nwl.c
++++ b/drivers/pci/host/pcie-xilinx-nwl.c
+@@ -778,16 +778,7 @@ static int nwl_pcie_parse_dt(struct nwl_
+                            struct platform_device *pdev)
+ {
+       struct device *dev = pcie->dev;
+-      struct device_node *node = dev->of_node;
+       struct resource *res;
+-      const char *type;
+-
+-      /* Check for device type */
+-      type = of_get_property(node, "device_type", NULL);
+-      if (!type || strcmp(type, "pci")) {
+-              dev_err(dev, "invalid \"device_type\" %s\n", type);
+-              return -EINVAL;
+-      }
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg");
+       pcie->breg_base = devm_ioremap_resource(dev, res);
+--- a/drivers/pci/host/pcie-xilinx.c
++++ b/drivers/pci/host/pcie-xilinx.c
+@@ -584,15 +584,8 @@ static int xilinx_pcie_parse_dt(struct x
+       struct device *dev = port->dev;
+       struct device_node *node = dev->of_node;
+       struct resource regs;
+-      const char *type;
+       int err;
+-      type = of_get_property(node, "device_type", NULL);
+-      if (!type || strcmp(type, "pci")) {
+-              dev_err(dev, "invalid \"device_type\" %s\n", type);
+-              return -EINVAL;
+-      }
+-
+       err = of_address_to_resource(node, 0, &regs);
+       if (err) {
+               dev_err(dev, "missing \"reg\" property\n");
+--- /dev/null
++++ b/drivers/pci/mobiveil/Kconfig
+@@ -0,0 +1,50 @@
++# SPDX-License-Identifier: GPL-2.0
++
++menu "Mobiveil PCIe Core Support"
++      depends on PCI
++
++config PCIE_MOBIVEIL
++      bool
++
++config PCIE_MOBIVEIL_HOST
++        bool
++      depends on PCI_MSI_IRQ_DOMAIN
++        select PCIE_MOBIVEIL
++
++config PCIE_MOBIVEIL_EP
++      bool
++      depends on PCI_ENDPOINT
++      select PCIE_MOBIVEIL
++
++config PCIE_MOBIVEIL_PLAT
++      bool "Mobiveil AXI PCIe controller"
++      depends on ARCH_ZYNQMP || COMPILE_TEST
++      depends on OF
++      select PCIE_MOBIVEIL_HOST
++      help
++        Say Y here if you want to enable support for the Mobiveil AXI PCIe
++        Soft IP. It has up to 8 outbound and inbound windows
++        for address translation and it is a PCIe Gen4 IP.
++
++config PCI_LAYERSCAPE_GEN4
++      bool "Freescale Layerscpe PCIe Gen4 controller in RC mode"
++      depends on PCI
++      depends on OF && (ARM64 || ARCH_LAYERSCAPE)
++      depends on PCI_MSI_IRQ_DOMAIN
++      select PCIE_MOBIVEIL_HOST
++      help
++        Say Y here if you want PCIe Gen4 controller support on
++        Layerscape SoCs. And the PCIe controller work in RC mode
++        by setting the RCW[HOST_AGT_PEX] to 0.
++
++config PCI_LAYERSCAPE_GEN4_EP
++      bool "Freescale Layerscpe PCIe Gen4 controller in EP mode"
++      depends on PCI
++      depends on OF && (ARM64 || ARCH_LAYERSCAPE)
++      depends on PCI_ENDPOINT
++      select PCIE_MOBIVEIL_EP
++      help
++        Say Y here if you want PCIe Gen4 controller support on
++        Layerscape SoCs. And the PCIe controller work in EP mode
++        by setting the RCW[HOST_AGT_PEX] to 1.
++endmenu
+--- /dev/null
++++ b/drivers/pci/mobiveil/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
++obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
++obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
++obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
++obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o
++obj-$(CONFIG_PCI_LAYERSCAPE_GEN4_EP) += pci-layerscape-gen4-ep.o
+--- /dev/null
++++ b/drivers/pci/mobiveil/pci-layerscape-gen4-ep.c
+@@ -0,0 +1,178 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCIe controller EP driver for Freescale Layerscape SoCs
++ *
++ * Copyright (C) 2018 NXP Semiconductor.
++ *
++ * Author: Xiaowei Bao <xiaowei.bao@nxp.com>
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/of_pci.h>
++#include <linux/of_platform.h>
++#include <linux/of_address.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/resource.h>
++
++#include "pcie-mobiveil.h"
++
++struct ls_pcie_g4_ep {
++      struct mobiveil_pcie            *mv_pci;
++};
++
++#define to_ls_pcie_g4_ep(x)   dev_get_drvdata((x)->dev)
++
++static const struct of_device_id ls_pcie_g4_ep_of_match[] = {
++      { .compatible = "fsl,lx2160a-pcie-ep",},
++      { },
++};
++
++static void ls_pcie_g4_get_bar_num(struct mobiveil_pcie_ep *ep)
++{
++      struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
++      u32 type, reg;
++      u8 bar;
++
++      ep->bar_num = BAR_5 + 1;
++
++      for (bar = BAR_0; bar <= BAR_5; bar++) {
++              reg = PCI_BASE_ADDRESS_0 + (4 * bar);
++              type = csr_readl(mv_pci, reg) &
++                     PCI_BASE_ADDRESS_MEM_TYPE_MASK;
++              if (type & PCI_BASE_ADDRESS_MEM_TYPE_64)
++                      ep->bar_num--;
++      }
++}
++
++static void ls_pcie_g4_ep_init(struct mobiveil_pcie_ep *ep)
++{
++      struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      enum pci_barno bar;
++      int win_idx, val;
++
++      /*
++       * Errata: unsupported request error on inbound posted write
++       * transaction, PCIe controller reports advisory error instead
++       * of uncorrectable error message to RC.
++       * workaround: set the bit20(unsupported_request_Error_severity) with
++       * value 1 in uncorrectable_Error_Severity_Register, make the
++       * unsupported request error generate the fatal error.
++       */
++      val =  csr_readl(mv_pci, CFG_UNCORRECTABLE_ERROR_SEVERITY);
++      val |= 1 << UNSUPPORTED_REQUEST_ERROR_SHIFT;
++      csr_writel(mv_pci, val, CFG_UNCORRECTABLE_ERROR_SEVERITY);
++
++      ls_pcie_g4_get_bar_num(ep);
++
++      for (bar = BAR_0; bar < (ep->bar_num * ep->pf_num); bar++)
++              mobiveil_pcie_ep_reset_bar(mv_pci, bar);
++
++      for (win_idx = 0; win_idx < MAX_IATU_OUT; win_idx++)
++              mobiveil_pcie_disable_ob_win(mv_pci, win_idx);
++
++      epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
++      epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
++}
++
++static int ls_pcie_g4_ep_raise_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
++                                 enum pci_epc_irq_type type,
++                                 u16 interrupt_num)
++{
++      struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
++
++      switch (type) {
++      case PCI_EPC_IRQ_LEGACY:
++              return mobiveil_pcie_ep_raise_legacy_irq(ep, func_no);
++      case PCI_EPC_IRQ_MSI:
++              return mobiveil_pcie_ep_raise_msi_irq(ep, func_no,
++                                                    interrupt_num);
++      case PCI_EPC_IRQ_MSIX:
++              return mobiveil_pcie_ep_raise_msix_irq(ep, func_no,
++                                                     interrupt_num);
++      default:
++              dev_err(&mv_pci->pdev->dev, "UNKNOWN IRQ type\n");
++      }
++
++      return 0;
++}
++
++static struct mobiveil_pcie_ep_ops pcie_ep_ops = {
++      .ep_init = ls_pcie_g4_ep_init,
++      .raise_irq = ls_pcie_g4_ep_raise_irq,
++};
++
++static int __init ls_pcie_gen4_add_pcie_ep(struct ls_pcie_g4_ep *ls_pcie_g4_ep,
++                                         struct platform_device *pdev)
++{
++      struct mobiveil_pcie *mv_pci = ls_pcie_g4_ep->mv_pci;
++      struct device *dev = &pdev->dev;
++      struct mobiveil_pcie_ep *ep;
++      struct resource *res;
++      int ret;
++      struct device_node *np = dev->of_node;
++
++      ep = &mv_pci->ep;
++      ep->ops = &pcie_ep_ops;
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
++      if (!res)
++              return -EINVAL;
++
++      ep->phys_base = res->start;
++      ep->addr_size = resource_size(res);
++
++      ret = of_property_read_u32(np, "max-functions", &ep->pf_num);
++      if (ret < 0)
++              ep->pf_num = 1;
++
++      ret = mobiveil_pcie_ep_init(ep);
++      if (ret) {
++              dev_err(dev, "failed to initialize endpoint\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int __init ls_pcie_g4_ep_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct mobiveil_pcie *mv_pci;
++      struct ls_pcie_g4_ep *ls_pcie_g4_ep;
++      struct resource *res;
++      int ret;
++
++      ls_pcie_g4_ep = devm_kzalloc(dev, sizeof(*ls_pcie_g4_ep), GFP_KERNEL);
++      if (!ls_pcie_g4_ep)
++              return -ENOMEM;
++
++      mv_pci = devm_kzalloc(dev, sizeof(*mv_pci), GFP_KERNEL);
++      if (!mv_pci)
++              return -ENOMEM;
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
++      mv_pci->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
++      if (IS_ERR(mv_pci->csr_axi_slave_base))
++              return PTR_ERR(mv_pci->csr_axi_slave_base);
++
++      mv_pci->pdev = pdev;
++      ls_pcie_g4_ep->mv_pci = mv_pci;
++
++      platform_set_drvdata(pdev, ls_pcie_g4_ep);
++
++      ret = ls_pcie_gen4_add_pcie_ep(ls_pcie_g4_ep, pdev);
++
++      return ret;
++}
++
++static struct platform_driver ls_pcie_g4_ep_driver = {
++      .driver = {
++              .name = "layerscape-pcie-gen4-ep",
++              .of_match_table = ls_pcie_g4_ep_of_match,
++              .suppress_bind_attrs = true,
++      },
++};
++builtin_platform_driver_probe(ls_pcie_g4_ep_driver, ls_pcie_g4_ep_probe);
+--- /dev/null
++++ b/drivers/pci/mobiveil/pci-layerscape-gen4.c
+@@ -0,0 +1,292 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCIe host controller driver for NXP Layerscape SoCs
++ *
++ * Copyright 2018 NXP
++ *
++ * Author: Zhiqiang Hou <Zhiqiang.Hou@nxp.com>
++ */
++
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/of_pci.h>
++#include <linux/of_platform.h>
++#include <linux/of_irq.h>
++#include <linux/of_address.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/resource.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regmap.h>
++
++#include "pcie-mobiveil.h"
++
++/* LUT and PF control registers */
++#define PCIE_LUT_OFF                  (0x80000)
++#define PCIE_LUT_GCR                  (0x28)
++#define PCIE_LUT_GCR_RRE              (0)
++
++#define PCIE_PF_OFF                   (0xc0000)
++#define PCIE_PF_INT_STAT              (0x18)
++#define PF_INT_STAT_PABRST            (31)
++
++#define PCIE_PF_DBG                   (0x7fc)
++#define PF_DBG_LTSSM_MASK             (0x3f)
++#define PF_DBG_WE                     (31)
++#define PF_DBG_PABR                   (27)
++
++#define LS_PCIE_G4_LTSSM_L0           0x2d /* L0 state */
++
++#define to_ls_pcie_g4(x)              platform_get_drvdata((x)->pdev)
++
++struct ls_pcie_g4 {
++      struct mobiveil_pcie *pci;
++      struct delayed_work dwork;
++      int irq;
++};
++
++static inline u32 ls_pcie_g4_lut_readl(struct ls_pcie_g4 *pcie, u32 off)
++{
++      return ioread32(pcie->pci->csr_axi_slave_base + PCIE_LUT_OFF + off);
++}
++
++static inline void ls_pcie_g4_lut_writel(struct ls_pcie_g4 *pcie,
++                                       u32 off, u32 val)
++{
++      iowrite32(val, pcie->pci->csr_axi_slave_base + PCIE_LUT_OFF + off);
++}
++
++static inline u32 ls_pcie_g4_pf_readl(struct ls_pcie_g4 *pcie, u32 off)
++{
++      return ioread32(pcie->pci->csr_axi_slave_base + PCIE_PF_OFF + off);
++}
++
++static inline void ls_pcie_g4_pf_writel(struct ls_pcie_g4 *pcie,
++                                      u32 off, u32 val)
++{
++      iowrite32(val, pcie->pci->csr_axi_slave_base + PCIE_PF_OFF + off);
++}
++
++static bool ls_pcie_g4_is_bridge(struct ls_pcie_g4 *pcie)
++{
++      struct mobiveil_pcie *mv_pci = pcie->pci;
++      u32 header_type;
++
++      header_type = csr_readb(mv_pci, PCI_HEADER_TYPE);
++      header_type &= 0x7f;
++
++      return header_type == PCI_HEADER_TYPE_BRIDGE;
++}
++
++static int ls_pcie_g4_link_up(struct mobiveil_pcie *pci)
++{
++      struct ls_pcie_g4 *pcie = to_ls_pcie_g4(pci);
++      u32 state;
++
++      state = ls_pcie_g4_pf_readl(pcie, PCIE_PF_DBG);
++      state = state & PF_DBG_LTSSM_MASK;
++
++      if (state == LS_PCIE_G4_LTSSM_L0)
++              return 1;
++
++      return 0;
++}
++
++static void ls_pcie_g4_reinit_hw(struct ls_pcie_g4 *pcie)
++{
++      struct mobiveil_pcie *mv_pci = pcie->pci;
++      u32 val, act_stat;
++      int to = 100;
++
++      /* Poll for pab_csb_reset to set and PAB activity to clear */
++      do {
++              usleep_range(10, 15);
++              val = ls_pcie_g4_pf_readl(pcie, PCIE_PF_INT_STAT);
++              act_stat = csr_readl(mv_pci, PAB_ACTIVITY_STAT);
++      } while (((val & 1 << PF_INT_STAT_PABRST) == 0 || act_stat) && to--);
++      if (to < 0) {
++              dev_err(&mv_pci->pdev->dev, "poll PABRST&PABACT timeout\n");
++              return;
++      }
++
++      /* clear PEX_RESET bit in PEX_PF0_DBG register */
++      val = ls_pcie_g4_pf_readl(pcie, PCIE_PF_DBG);
++      val |= 1 << PF_DBG_WE;
++      ls_pcie_g4_pf_writel(pcie, PCIE_PF_DBG, val);
++
++      val = ls_pcie_g4_pf_readl(pcie, PCIE_PF_DBG);
++      val |= 1 << PF_DBG_PABR;
++      ls_pcie_g4_pf_writel(pcie, PCIE_PF_DBG, val);
++
++      val = ls_pcie_g4_pf_readl(pcie, PCIE_PF_DBG);
++      val &= ~(1 << PF_DBG_WE);
++      ls_pcie_g4_pf_writel(pcie, PCIE_PF_DBG, val);
++
++      mobiveil_host_init(mv_pci, true);
++
++      to = 100;
++      while (!ls_pcie_g4_link_up(mv_pci) && to--)
++              usleep_range(200, 250);
++      if (to < 0)
++              dev_err(&mv_pci->pdev->dev, "PCIe link trainning timeout\n");
++}
++
++static irqreturn_t ls_pcie_g4_handler(int irq, void *dev_id)
++{
++      struct ls_pcie_g4 *pcie = (struct ls_pcie_g4 *)dev_id;
++      struct mobiveil_pcie *mv_pci = pcie->pci;
++      u32 val;
++
++      val = csr_readl(mv_pci, PAB_INTP_AMBA_MISC_STAT);
++      if (!val)
++              return IRQ_NONE;
++
++      if (val & PAB_INTP_RESET)
++              schedule_delayed_work(&pcie->dwork, msecs_to_jiffies(1));
++
++      csr_writel(mv_pci, val, PAB_INTP_AMBA_MISC_STAT);
++
++      return IRQ_HANDLED;
++}
++
++static int ls_pcie_g4_interrupt_init(struct mobiveil_pcie *mv_pci)
++{
++      struct ls_pcie_g4 *pcie = to_ls_pcie_g4(mv_pci);
++      u32 val;
++      int ret;
++
++      pcie->irq = platform_get_irq_byname(mv_pci->pdev, "intr");
++      if (pcie->irq < 0) {
++              dev_err(&mv_pci->pdev->dev, "Can't get 'intr' irq.\n");
++              return pcie->irq;
++      }
++      ret = devm_request_irq(&mv_pci->pdev->dev, pcie->irq,
++                             ls_pcie_g4_handler, IRQF_SHARED,
++                             mv_pci->pdev->name, pcie);
++      if (ret) {
++              dev_err(&mv_pci->pdev->dev, "Can't register PCIe IRQ.\n");
++              return  ret;
++      }
++
++      /* Enable interrupts */
++      val = PAB_INTP_INTX_MASK | PAB_INTP_MSI | PAB_INTP_RESET |
++            PAB_INTP_PCIE_UE | PAB_INTP_IE_PMREDI | PAB_INTP_IE_EC;
++      csr_writel(mv_pci, val, PAB_INTP_AMBA_MISC_ENB);
++
++      return 0;
++}
++
++static void ls_pcie_g4_reset(struct work_struct *work)
++{
++      struct delayed_work *dwork = container_of(work, struct delayed_work,
++                                                work);
++      struct ls_pcie_g4 *pcie = container_of(dwork, struct ls_pcie_g4, dwork);
++      struct mobiveil_pcie *mv_pci = pcie->pci;
++      u16 ctrl;
++
++      ctrl = csr_readw(mv_pci, PCI_BRIDGE_CONTROL);
++      ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
++      csr_writew(mv_pci, ctrl, PCI_BRIDGE_CONTROL);
++      ls_pcie_g4_reinit_hw(pcie);
++}
++
++static int ls_pcie_g4_read_other_conf(struct pci_bus *bus, unsigned int devfn,
++                                 int where, int size, u32 *val)
++{
++      struct mobiveil_pcie *pci = bus->sysdata;
++      struct ls_pcie_g4 *pcie = to_ls_pcie_g4(pci);
++      int ret;
++
++      if (where == PCI_VENDOR_ID)
++              ls_pcie_g4_lut_writel(pcie, PCIE_LUT_GCR,
++                                    0 << PCIE_LUT_GCR_RRE);
++
++      ret = pci_generic_config_read(bus, devfn, where, size, val);
++
++      if (where == PCI_VENDOR_ID)
++              ls_pcie_g4_lut_writel(pcie, PCIE_LUT_GCR,
++                                    1 << PCIE_LUT_GCR_RRE);
++
++      return ret;
++}
++
++static struct mobiveil_rp_ops ls_pcie_g4_rp_ops = {
++      .interrupt_init = ls_pcie_g4_interrupt_init,
++      .read_other_conf = ls_pcie_g4_read_other_conf,
++};
++
++static const struct mobiveil_pab_ops ls_pcie_g4_pab_ops = {
++      .link_up = ls_pcie_g4_link_up,
++};
++
++static void workaround_tkt381274(struct ls_pcie_g4 *pcie)
++{
++      struct mobiveil_pcie *mv_pci = pcie->pci;
++      u32 val;
++
++      /* Set ACK latency timeout */
++      val = csr_readl(mv_pci, GPEX_ACK_REPLAY_TO);
++      val &= ~(ACK_LAT_TO_VAL_MASK << ACK_LAT_TO_VAL_SHIFT);
++      val |= (4 << ACK_LAT_TO_VAL_SHIFT);
++      csr_writel(mv_pci, val, GPEX_ACK_REPLAY_TO);
++}
++
++static int __init ls_pcie_g4_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct mobiveil_pcie *mv_pci;
++      struct ls_pcie_g4 *pcie;
++      struct device_node *np = dev->of_node;
++      int ret;
++
++      if (!of_parse_phandle(np, "msi-parent", 0)) {
++              dev_err(dev, "failed to find msi-parent\n");
++              return -EINVAL;
++      }
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      mv_pci = devm_kzalloc(dev, sizeof(*mv_pci), GFP_KERNEL);
++      if (!mv_pci)
++              return -ENOMEM;
++
++      mv_pci->pdev = pdev;
++      mv_pci->ops = &ls_pcie_g4_pab_ops;
++      mv_pci->rp.ops = &ls_pcie_g4_rp_ops;
++      pcie->pci = mv_pci;
++
++      platform_set_drvdata(pdev, pcie);
++
++      INIT_DELAYED_WORK(&pcie->dwork, ls_pcie_g4_reset);
++
++      ret = mobiveil_pcie_host_probe(mv_pci);
++      if (ret) {
++              dev_err(dev, "fail to probe!\n");
++              return  ret;
++      }
++
++      if (!ls_pcie_g4_is_bridge(pcie))
++              return -ENODEV;
++
++      workaround_tkt381274(pcie);
++
++      return 0;
++}
++
++static const struct of_device_id ls_pcie_g4_of_match[] = {
++      { .compatible = "fsl,lx2160a-pcie", },
++      { },
++};
++
++static struct platform_driver ls_pcie_g4_driver = {
++      .driver = {
++              .name = "layerscape-pcie-gen4",
++              .of_match_table = ls_pcie_g4_of_match,
++              .suppress_bind_attrs = true,
++      },
++};
++
++builtin_platform_driver_probe(ls_pcie_g4_driver, ls_pcie_g4_probe);
+--- /dev/null
++++ b/drivers/pci/mobiveil/pcie-mobiveil-ep.c
+@@ -0,0 +1,512 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Mobiveil PCIe Endpoint controller driver
++ *
++ * Copyright (C) 2018 NXP Semiconductor.
++ * Author: Xiaowei Bao <xiaowei.bao@nxp.com>
++ */
++
++#include <linux/of.h>
++#include <linux/pci-epc.h>
++#include <linux/pci-epf.h>
++#include <linux/platform_device.h>
++#include "pcie-mobiveil.h"
++
++void mobiveil_pcie_ep_linkup(struct mobiveil_pcie_ep *ep)
++{
++      struct pci_epc *epc = ep->epc;
++
++      pci_epc_linkup(epc);
++}
++
++static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
++                                       enum pci_barno bar)
++{
++      csr_writel(pcie, bar, GPEX_BAR_SELECT);
++      csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW);
++      csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW);
++}
++
++void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
++                              enum pci_barno bar)
++{
++      __mobiveil_pcie_ep_reset_bar(pcie, bar);
++}
++
++static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie,
++                                         u8 cap_ptr, u8 cap)
++{
++      u8 cap_id, next_cap_ptr;
++      u16 reg;
++
++      reg = csr_readw(pcie, cap_ptr);
++      next_cap_ptr = (reg & 0xff00) >> 8;
++      cap_id = (reg & 0x00ff);
++
++      if (cap_id == cap)
++              return cap_ptr;
++
++      if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
++              return 0;
++
++      return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
++}
++
++static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie *pcie,
++                                         u8 cap)
++{
++      u8 next_cap_ptr;
++      u16 reg;
++
++      reg = csr_readw(pcie, PCI_CAPABILITY_LIST);
++      next_cap_ptr = (reg & 0x00ff);
++
++      if (!next_cap_ptr)
++              return 0;
++
++      return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
++}
++
++static int mobiveil_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
++                                       struct pci_epf_header *hdr)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++
++      csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID);
++      csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID);
++      csr_writeb(pcie, hdr->revid, PCI_REVISION_ID);
++      csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG);
++      csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8,
++                 PCI_CLASS_DEVICE);
++      csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE);
++      csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID);
++      csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID);
++      csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN);
++
++      return 0;
++}
++
++static int mobiveil_pcie_ep_inbound_atu(struct mobiveil_pcie_ep *ep,
++                                      u8 func_no, enum pci_barno bar,
++                                      dma_addr_t cpu_addr)
++{
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++
++      program_ib_windows_ep(pcie, func_no, bar, cpu_addr);
++
++      return 0;
++}
++
++static int mobiveil_pcie_ep_outbound_atu(struct mobiveil_pcie_ep *ep,
++                                       phys_addr_t phys_addr,
++                                       u64 pci_addr, u8 func_no,
++                                       size_t size)
++{
++      int ret;
++      u32 free_win;
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++
++      free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
++      if (free_win >= ep->num_ob_windows) {
++              dev_err(&pcie->pdev->dev, "No free outbound window\n");
++              return -EINVAL;
++      }
++
++      ret = program_ob_windows_ep(pcie, free_win, MEM_WINDOW_TYPE,
++                                phys_addr, pci_addr, func_no, size);
++      if (ret < 0) {
++              dev_err(&pcie->pdev->dev, "Failed to program IB window\n");
++              return ret;
++      }
++
++      set_bit(free_win, ep->ob_window_map);
++      ep->outbound_addr[free_win] = phys_addr;
++
++      return 0;
++}
++
++static void mobiveil_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
++                                     struct pci_epf_bar *epf_bar)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      enum pci_barno bar = epf_bar->barno;
++
++      if (bar < ep->bar_num) {
++              __mobiveil_pcie_ep_reset_bar(pcie,
++                                           func_no * ep->bar_num + bar);
++
++              mobiveil_pcie_disable_ib_win_ep(pcie, func_no, bar);
++      }
++}
++
++static int mobiveil_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
++                                  struct pci_epf_bar *epf_bar)
++{
++      int ret;
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      enum pci_barno bar = epf_bar->barno;
++      size_t size = epf_bar->size;
++
++      if (bar < ep->bar_num) {
++              ret = mobiveil_pcie_ep_inbound_atu(ep, func_no, bar,
++                                                 epf_bar->phys_addr);
++              if (ret)
++                      return ret;
++
++              csr_writel(pcie, func_no * ep->bar_num + bar,
++                         GPEX_BAR_SELECT);
++              csr_writel(pcie, lower_32_bits(~(size - 1)),
++                         GPEX_BAR_SIZE_LDW);
++              csr_writel(pcie, upper_32_bits(~(size - 1)),
++                         GPEX_BAR_SIZE_UDW);
++      }
++
++      return 0;
++}
++
++static int mobiveil_pcie_find_index(struct mobiveil_pcie_ep *ep,
++                                  phys_addr_t addr,
++                                  u32 *atu_index)
++{
++      u32 index;
++
++      for (index = 0; index < ep->num_ob_windows; index++) {
++              if (ep->outbound_addr[index] != addr)
++                      continue;
++              *atu_index = index;
++              return 0;
++      }
++
++      return -EINVAL;
++}
++
++static void mobiveil_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
++                                      phys_addr_t addr)
++{
++      int ret;
++      u32 atu_index;
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++
++      ret = mobiveil_pcie_find_index(ep, addr, &atu_index);
++      if (ret < 0)
++              return;
++
++      mobiveil_pcie_disable_ob_win(pcie, atu_index);
++      clear_bit(atu_index, ep->ob_window_map);
++}
++
++static int mobiveil_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
++                                   phys_addr_t addr,
++                                   u64 pci_addr, size_t size)
++{
++      int ret;
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++
++      ret = mobiveil_pcie_ep_outbound_atu(ep, addr, pci_addr, func_no, size);
++      if (ret) {
++              dev_err(&pcie->pdev->dev, "Failed to enable address\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int mobiveil_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msi_cap)
++              return -EINVAL;
++
++      reg = ep->msi_cap + PCI_MSI_FLAGS;
++      val = csr_readw(pcie, reg);
++      if (!(val & PCI_MSI_FLAGS_ENABLE))
++              return -EINVAL;
++
++      val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
++
++      return val;
++}
++
++static int mobiveil_pcie_ep_set_msi(struct pci_epc *epc,
++                                  u8 func_no, u8 interrupts)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msi_cap)
++              return -EINVAL;
++
++      reg = ep->msi_cap + PCI_MSI_FLAGS;
++      val = csr_readw(pcie, reg);
++      val &= ~PCI_MSI_FLAGS_QMASK;
++      val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
++      csr_writew(pcie, val, reg);
++
++      return 0;
++}
++
++static int mobiveil_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msix_cap)
++              return -EINVAL;
++
++      reg = ep->msix_cap + PCI_MSIX_FLAGS;
++      val = csr_readw(pcie, reg);
++      if (!(val & PCI_MSIX_FLAGS_ENABLE))
++              return -EINVAL;
++
++      val &= PCI_MSIX_FLAGS_QSIZE;
++
++      return val;
++}
++
++static int mobiveil_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no,
++                                   u16 interrupts)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      u32 val, reg;
++
++      if (!ep->msix_cap)
++              return -EINVAL;
++
++      reg = ep->msix_cap + PCI_MSIX_FLAGS;
++      val = csr_readw(pcie, reg);
++      val &= ~PCI_MSIX_FLAGS_QSIZE;
++      val |= interrupts;
++      csr_writew(pcie, val, reg);
++
++      return 0;
++}
++
++static int mobiveil_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
++                                    enum pci_epc_irq_type type,
++                                    u16 interrupt_num)
++{
++      struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
++
++      if (!ep->ops->raise_irq)
++              return -EINVAL;
++
++      return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
++}
++
++static const struct pci_epc_ops epc_ops = {
++      .write_header           = mobiveil_pcie_ep_write_header,
++      .set_bar                = mobiveil_pcie_ep_set_bar,
++      .clear_bar              = mobiveil_pcie_ep_clear_bar,
++      .map_addr               = mobiveil_pcie_ep_map_addr,
++      .unmap_addr             = mobiveil_pcie_ep_unmap_addr,
++      .set_msi                = mobiveil_pcie_ep_set_msi,
++      .get_msi                = mobiveil_pcie_ep_get_msi,
++      .set_msix               = mobiveil_pcie_ep_set_msix,
++      .get_msix               = mobiveil_pcie_ep_get_msix,
++      .raise_irq              = mobiveil_pcie_ep_raise_irq,
++};
++
++int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no)
++{
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++
++      dev_err(&pcie->pdev->dev, "EP cannot trigger legacy IRQs\n");
++
++      return -EINVAL;
++}
++
++int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
++                                 u8 interrupt_num)
++{
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      u16 msg_ctrl, msg_data;
++      u32 msg_addr_lower, msg_addr_upper, reg;
++      u64 msg_addr;
++      u32 func_num;
++      bool has_upper;
++      int ret;
++
++      if (!ep->msi_cap)
++              return -EINVAL;
++
++      func_num = csr_readl(pcie, PAB_CTRL);
++      func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
++      func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
++      csr_writel(pcie, func_num, PAB_CTRL);
++
++      /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
++      reg = ep->msi_cap + PCI_MSI_FLAGS;
++      msg_ctrl = csr_readw(pcie, reg);
++      has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
++      reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
++      msg_addr_lower = csr_readl(pcie, reg);
++      if (has_upper) {
++              reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
++              msg_addr_upper = csr_readl(pcie, reg);
++              reg = ep->msi_cap + PCI_MSI_DATA_64;
++              msg_data = csr_readw(pcie, reg);
++      } else {
++              msg_addr_upper = 0;
++              reg = ep->msi_cap + PCI_MSI_DATA_32;
++              msg_data = csr_readw(pcie, reg);
++      }
++      msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
++
++      func_num = csr_readl(pcie, PAB_CTRL);
++      func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
++      csr_writel(pcie, func_num, PAB_CTRL);
++
++      ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
++                                      msg_addr, epc->mem->page_size);
++      if (ret)
++              return ret;
++
++      writel(msg_data | (interrupt_num - 1), ep->msi_mem);
++
++      mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
++
++      return 0;
++}
++
++int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
++                                  u16 interrupt_num)
++{
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      struct pci_epc *epc = ep->epc;
++      u32 msg_addr_upper, msg_addr_lower;
++      u32 msg_data;
++      u64 msg_addr;
++      u32 func_num;
++      int ret;
++
++      func_num = csr_readl(pcie, PAB_CTRL);
++      func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
++      func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
++      csr_writel(pcie, func_num, PAB_CTRL);
++
++      msg_addr_lower = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
++                                 PCI_MSIX_ENTRY_LOWER_ADDR +
++                                 (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
++      msg_addr_upper = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
++                                 PCI_MSIX_ENTRY_UPPER_ADDR +
++                                 (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
++      msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
++      msg_data = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
++                           PCI_MSIX_ENTRY_DATA +
++                           (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
++
++      func_num = csr_readl(pcie, PAB_CTRL);
++      func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
++      csr_writel(pcie, func_num, PAB_CTRL);
++
++      ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
++                                      msg_addr, epc->mem->page_size);
++      if (ret)
++              return ret;
++
++      writel(msg_data, ep->msi_mem);
++
++      mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
++
++      return 0;
++}
++
++void mobiveil_pcie_ep_exit(struct mobiveil_pcie_ep *ep)
++{
++      struct pci_epc *epc = ep->epc;
++
++      pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
++                            epc->mem->page_size);
++
++      pci_epc_mem_exit(epc);
++}
++
++int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep)
++{
++      int ret;
++      void *addr;
++      struct pci_epc *epc;
++      struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
++      struct device *dev = &pcie->pdev->dev;
++      struct device_node *np = dev->of_node;
++
++      if (!pcie->csr_axi_slave_base) {
++              dev_err(dev, "csr_base is not populated\n");
++              return -EINVAL;
++      }
++
++      ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
++      if (ret < 0) {
++              dev_err(dev, "Unable to read *num-ob-windows* property\n");
++              return ret;
++      }
++
++      if (ep->num_ob_windows > MAX_IATU_OUT) {
++              dev_err(dev, "Invalid *num-ob-windows*\n");
++              return -EINVAL;
++      }
++      ep->ob_window_map = devm_kcalloc(dev,
++                                       BITS_TO_LONGS(ep->num_ob_windows),
++                                       sizeof(long),
++                                       GFP_KERNEL);
++      if (!ep->ob_window_map)
++              return -ENOMEM;
++
++      addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
++                          GFP_KERNEL);
++      if (!addr)
++              return -ENOMEM;
++      ep->outbound_addr = addr;
++
++      mobiveil_pcie_enable_bridge_pio(pcie);
++      mobiveil_pcie_enable_engine_apio(pcie);
++      mobiveil_pcie_enable_engine_ppio(pcie);
++      mobiveil_pcie_enable_msi_ep(pcie);
++
++      epc = devm_pci_epc_create(dev, &epc_ops);
++      if (IS_ERR(epc)) {
++              dev_err(dev, "Failed to create epc device\n");
++              return PTR_ERR(epc);
++      }
++
++      ep->epc = epc;
++      epc_set_drvdata(epc, ep);
++
++      ep->msi_cap = mobiveil_pcie_ep_find_capability(pcie, PCI_CAP_ID_MSI);
++
++      ep->msix_cap = mobiveil_pcie_ep_find_capability(pcie,
++                                                      PCI_CAP_ID_MSIX);
++
++      if (ep->ops->ep_init)
++              ep->ops->ep_init(ep);
++
++      epc->max_functions = ep->pf_num;
++
++      ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
++                               ep->page_size);
++      if (ret < 0) {
++              dev_err(dev, "Failed to initialize address space\n");
++              return ret;
++      }
++
++      ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
++                                           epc->mem->page_size);
++      if (!ep->msi_mem) {
++              dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
++              return -ENOMEM;
++      }
++
++      return 0;
++}
+--- /dev/null
++++ b/drivers/pci/mobiveil/pcie-mobiveil-host.c
+@@ -0,0 +1,640 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCIe host controller driver for Mobiveil PCIe Host controller
++ *
++ * Copyright (c) 2018 Mobiveil Inc.
++ * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
++ * Refactor: Zhiqiang Hou <Zhiqiang.Hou@nxp.com>
++ */
++
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/msi.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_platform.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#include "pcie-mobiveil.h"
++
++static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
++{
++      struct mobiveil_pcie *pcie = bus->sysdata;
++
++      /* If there is no link, then there is no device */
++      if (bus->number > pcie->rp.root_bus_nr && !mobiveil_pcie_link_up(pcie))
++              return false;
++
++      /* Only one device down on each root port */
++      if ((bus->number == pcie->rp.root_bus_nr) && (devfn > 0))
++              return false;
++
++      /*
++       * Do not read more than one device on the bus directly
++       * attached to RC
++       */
++      if ((bus->primary == pcie->rp.root_bus_nr) && (PCI_SLOT(devfn) > 0))
++              return false;
++
++      return true;
++}
++
++/*
++ * mobiveil_pcie_map_bus - routine to get the configuration base of either
++ * root port or endpoint
++ */
++static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus,
++                                      unsigned int devfn, int where)
++{
++      struct mobiveil_pcie *pcie = bus->sysdata;
++      u32 value;
++
++      if (!mobiveil_pcie_valid_device(bus, devfn))
++              return NULL;
++
++      /* RC config access */
++      if (bus->number == pcie->rp.root_bus_nr)
++              return pcie->csr_axi_slave_base + where;
++
++      /*
++       * EP config access (in Config/APIO space)
++       * Program PEX Address base (31..16 bits) with appropriate value
++       * (BDF) in PAB_AXI_AMAP_PEX_WIN_L0 Register.
++       * Relies on pci_lock serialization
++       */
++      value = bus->number << PAB_BUS_SHIFT |
++              PCI_SLOT(devfn) << PAB_DEVICE_SHIFT |
++              PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT;
++
++      csr_writel(pcie, value, PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0));
++
++      return pcie->rp.config_axi_slave_base + where;
++}
++
++static int mobiveil_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
++                                   int where, int size, u32 *val)
++{
++      struct mobiveil_pcie *pcie = bus->sysdata;
++      struct root_port *rp = &pcie->rp;
++
++      if (bus->number > rp->root_bus_nr && rp->ops->read_other_conf)
++              return rp->ops->read_other_conf(bus, devfn, where, size, val);
++
++      return pci_generic_config_read(bus, devfn, where, size, val);
++}
++static struct pci_ops mobiveil_pcie_ops = {
++      .map_bus = mobiveil_pcie_map_bus,
++      .read = mobiveil_pcie_config_read,
++      .write = pci_generic_config_write,
++};
++
++static void mobiveil_pcie_isr(struct irq_desc *desc)
++{
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++      struct mobiveil_pcie *pcie = irq_desc_get_handler_data(desc);
++      struct device *dev = &pcie->pdev->dev;
++      struct mobiveil_msi *msi = &pcie->rp.msi;
++      u32 msi_data, msi_addr_lo, msi_addr_hi;
++      u32 intr_status, msi_status;
++      unsigned long shifted_status;
++      u32 bit, virq, val, mask;
++
++      /*
++       * The core provides a single interrupt for both INTx/MSI messages.
++       * So we'll read both INTx and MSI status
++       */
++
++      chained_irq_enter(chip, desc);
++
++      /* read INTx status */
++      val = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT);
++      mask = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
++      intr_status = val & mask;
++
++      /* Handle INTx */
++      if (intr_status & PAB_INTP_INTX_MASK) {
++              shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT);
++              shifted_status &= PAB_INTP_INTX_MASK;
++              shifted_status >>= PAB_INTX_START;
++              do {
++                      for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) {
++                              virq = irq_find_mapping(pcie->rp.intx_domain,
++                                                      bit + 1);
++                              if (virq)
++                                      generic_handle_irq(virq);
++                              else
++                                      dev_err_ratelimited(dev, "unexpected IRQ, INT%d\n",
++                                                          bit);
++
++                              /* clear interrupt handled */
++                              csr_writel(pcie, 1 << (PAB_INTX_START + bit),
++                                         PAB_INTP_AMBA_MISC_STAT);
++                      }
++
++                      shifted_status = csr_readl(pcie,
++                                                 PAB_INTP_AMBA_MISC_STAT);
++                      shifted_status &= PAB_INTP_INTX_MASK;
++                      shifted_status >>= PAB_INTX_START;
++              } while (shifted_status != 0);
++      }
++
++      /* read extra MSI status register */
++      msi_status = readl_relaxed(pcie->apb_csr_base + MSI_STATUS_OFFSET);
++
++      /* handle MSI interrupts */
++      while (msi_status & 1) {
++              msi_data = readl_relaxed(pcie->apb_csr_base + MSI_DATA_OFFSET);
++
++              /*
++               * MSI_STATUS_OFFSET register gets updated to zero
++               * once we pop not only the MSI data but also address
++               * from MSI hardware FIFO. So keeping these following
++               * two dummy reads.
++               */
++              msi_addr_lo = readl_relaxed(pcie->apb_csr_base +
++                                          MSI_ADDR_L_OFFSET);
++              msi_addr_hi = readl_relaxed(pcie->apb_csr_base +
++                                          MSI_ADDR_H_OFFSET);
++              dev_dbg(dev, "MSI registers, data: %08x, addr: %08x:%08x\n",
++                      msi_data, msi_addr_hi, msi_addr_lo);
++
++              virq = irq_find_mapping(msi->dev_domain, msi_data);
++              if (virq)
++                      generic_handle_irq(virq);
++
++              msi_status = readl_relaxed(pcie->apb_csr_base +
++                                         MSI_STATUS_OFFSET);
++      }
++
++      /* Clear the interrupt status */
++      csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT);
++      chained_irq_exit(chip, desc);
++}
++
++static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie)
++{
++      struct device *dev = &pcie->pdev->dev;
++      struct platform_device *pdev = pcie->pdev;
++      struct device_node *node = dev->of_node;
++      struct resource *res;
++
++      /* map config resource */
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++                                         "config_axi_slave");
++      pcie->rp.config_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
++      if (IS_ERR(pcie->rp.config_axi_slave_base))
++              return PTR_ERR(pcie->rp.config_axi_slave_base);
++      pcie->rp.ob_io_res = res;
++
++      /* map csr resource */
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++                                         "csr_axi_slave");
++      pcie->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
++      if (IS_ERR(pcie->csr_axi_slave_base))
++              return PTR_ERR(pcie->csr_axi_slave_base);
++      pcie->pcie_reg_base = res->start;
++
++      /* read the number of windows requested */
++      if (of_property_read_u32(node, "apio-wins", &pcie->apio_wins))
++              pcie->apio_wins = MAX_PIO_WINDOWS;
++
++      if (of_property_read_u32(node, "ppio-wins", &pcie->ppio_wins))
++              pcie->ppio_wins = MAX_PIO_WINDOWS;
++
++      return 0;
++}
++
++static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie)
++{
++      phys_addr_t msg_addr = pcie->pcie_reg_base;
++      struct mobiveil_msi *msi = &pcie->rp.msi;
++
++      msi->num_of_vectors = PCI_NUM_MSI;
++      msi->msi_pages_phys = (phys_addr_t)msg_addr;
++
++      writel_relaxed(lower_32_bits(msg_addr),
++                     pcie->apb_csr_base + MSI_BASE_LO_OFFSET);
++      writel_relaxed(upper_32_bits(msg_addr),
++                     pcie->apb_csr_base + MSI_BASE_HI_OFFSET);
++      writel_relaxed(4096, pcie->apb_csr_base + MSI_SIZE_OFFSET);
++      writel_relaxed(1, pcie->apb_csr_base + MSI_ENABLE_OFFSET);
++}
++
++int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit)
++{
++      u32 value, pab_ctrl, type;
++      struct resource_entry *win;
++      int i;
++
++      /* Disable all inbound/outbound windows */
++      for (i = 0; i < pcie->apio_wins; i++)
++              mobiveil_pcie_disable_ob_win(pcie, i);
++      for (i = 0; i < pcie->ppio_wins; i++)
++              mobiveil_pcie_disable_ib_win(pcie, i);
++
++      pcie->ib_wins_configured = 0;
++      pcie->ob_wins_configured = 0;
++
++      if (!reinit) {
++              /* setup bus numbers */
++              value = csr_readl(pcie, PCI_PRIMARY_BUS);
++              value &= 0xff000000;
++              value |= 0x00ff0100;
++              csr_writel(pcie, value, PCI_PRIMARY_BUS);
++      }
++
++      /*
++       * program Bus Master Enable Bit in Command Register in PAB Config
++       * Space
++       */
++      value = csr_readl(pcie, PCI_COMMAND);
++      value |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
++      csr_writel(pcie, value, PCI_COMMAND);
++
++      /*
++       * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL
++       * register
++       */
++      pab_ctrl = csr_readl(pcie, PAB_CTRL);
++      pab_ctrl |= (1 << AMBA_PIO_ENABLE_SHIFT) | (1 << PEX_PIO_ENABLE_SHIFT);
++      csr_writel(pcie, pab_ctrl, PAB_CTRL);
++
++      /*
++       * program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in
++       * PAB_AXI_PIO_CTRL Register
++       */
++      value = csr_readl(pcie, PAB_AXI_PIO_CTRL);
++      value |= APIO_EN_MASK;
++      csr_writel(pcie, value, PAB_AXI_PIO_CTRL);
++
++      /* Enable PCIe PIO master */
++      value = csr_readl(pcie, PAB_PEX_PIO_CTRL);
++      value |= 1 << PIO_ENABLE_SHIFT;
++      csr_writel(pcie, value, PAB_PEX_PIO_CTRL);
++
++      /*
++       * we'll program one outbound window for config reads and
++       * another default inbound window for all the upstream traffic
++       * rest of the outbound windows will be configured according to
++       * the "ranges" field defined in device tree
++       */
++
++      /* config outbound translation window */
++      program_ob_windows(pcie, WIN_NUM_0, pcie->rp.ob_io_res->start, 0,
++                         CFG_WINDOW_TYPE, resource_size(pcie->rp.ob_io_res));
++
++      /* memory inbound translation window */
++      program_ib_windows(pcie, WIN_NUM_0, 0, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE);
++
++      /* Get the I/O and memory ranges from DT */
++      resource_list_for_each_entry(win, pcie->resources) {
++              if (resource_type(win->res) == IORESOURCE_MEM) {
++                      type = MEM_WINDOW_TYPE;
++              } else if (resource_type(win->res) == IORESOURCE_IO) {
++                      type = IO_WINDOW_TYPE;
++              } else if (resource_type(win->res) == IORESOURCE_BUS) {
++                      pcie->rp.root_bus_nr = win->res->start;
++                      continue;
++              } else {
++                      continue;
++              }
++
++              /* configure outbound translation window */
++              program_ob_windows(pcie, pcie->ob_wins_configured,
++                                 win->res->start,
++                                 win->res->start - win->offset,
++                                 type, resource_size(win->res));
++      }
++
++      /* fixup for PCIe class register */
++      value = csr_readl(pcie, PAB_INTP_AXI_PIO_CLASS);
++      value &= 0xff;
++      value |= (PCI_CLASS_BRIDGE_PCI << 16);
++      csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS);
++
++      return 0;
++}
++
++static void mobiveil_mask_intx_irq(struct irq_data *data)
++{
++      struct irq_desc *desc = irq_to_desc(data->irq);
++      struct mobiveil_pcie *pcie;
++      unsigned long flags;
++      u32 mask, shifted_val;
++
++      pcie = irq_desc_get_chip_data(desc);
++      mask = 1 << ((data->hwirq + PAB_INTX_START) - 1);
++      raw_spin_lock_irqsave(&pcie->rp.intx_mask_lock, flags);
++      shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
++      shifted_val &= ~mask;
++      csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB);
++      raw_spin_unlock_irqrestore(&pcie->rp.intx_mask_lock, flags);
++}
++
++static void mobiveil_unmask_intx_irq(struct irq_data *data)
++{
++      struct irq_desc *desc = irq_to_desc(data->irq);
++      struct mobiveil_pcie *pcie;
++      unsigned long flags;
++      u32 shifted_val, mask;
++
++      pcie = irq_desc_get_chip_data(desc);
++      mask = 1 << ((data->hwirq + PAB_INTX_START) - 1);
++      raw_spin_lock_irqsave(&pcie->rp.intx_mask_lock, flags);
++      shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
++      shifted_val |= mask;
++      csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB);
++      raw_spin_unlock_irqrestore(&pcie->rp.intx_mask_lock, flags);
++}
++
++static struct irq_chip intx_irq_chip = {
++      .name = "mobiveil_pcie:intx",
++      .irq_enable = mobiveil_unmask_intx_irq,
++      .irq_disable = mobiveil_mask_intx_irq,
++      .irq_mask = mobiveil_mask_intx_irq,
++      .irq_unmask = mobiveil_unmask_intx_irq,
++};
++
++/* routine to setup the INTx related data */
++static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++                                irq_hw_number_t hwirq)
++{
++      irq_set_chip_and_handler(irq, &intx_irq_chip, handle_level_irq);
++      irq_set_chip_data(irq, domain->host_data);
++
++      return 0;
++}
++
++/* INTx domain operations structure */
++static const struct irq_domain_ops intx_domain_ops = {
++      .map = mobiveil_pcie_intx_map,
++};
++
++static struct irq_chip mobiveil_msi_irq_chip = {
++      .name = "Mobiveil PCIe MSI",
++      .irq_mask = pci_msi_mask_irq,
++      .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info mobiveil_msi_domain_info = {
++      .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++                 MSI_FLAG_PCI_MSIX),
++      .chip   = &mobiveil_msi_irq_chip,
++};
++
++static void mobiveil_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++      struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data);
++      phys_addr_t addr = pcie->pcie_reg_base + (data->hwirq * sizeof(int));
++
++      msg->address_lo = lower_32_bits(addr);
++      msg->address_hi = upper_32_bits(addr);
++      msg->data = data->hwirq;
++
++      dev_dbg(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
++              (int)data->hwirq, msg->address_hi, msg->address_lo);
++}
++
++static int mobiveil_msi_set_affinity(struct irq_data *irq_data,
++                                   const struct cpumask *mask, bool force)
++{
++      return -EINVAL;
++}
++
++static struct irq_chip mobiveil_msi_bottom_irq_chip = {
++      .name                   = "Mobiveil MSI",
++      .irq_compose_msi_msg    = mobiveil_compose_msi_msg,
++      .irq_set_affinity       = mobiveil_msi_set_affinity,
++};
++
++static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain,
++                                       unsigned int virq,
++                                       unsigned int nr_irqs, void *args)
++{
++      struct mobiveil_pcie *pcie = domain->host_data;
++      struct mobiveil_msi *msi = &pcie->rp.msi;
++      unsigned long bit;
++
++      WARN_ON(nr_irqs != 1);
++      mutex_lock(&msi->lock);
++
++      bit = find_first_zero_bit(msi->msi_irq_in_use, msi->num_of_vectors);
++      if (bit >= msi->num_of_vectors) {
++              mutex_unlock(&msi->lock);
++              return -ENOSPC;
++      }
++
++      set_bit(bit, msi->msi_irq_in_use);
++
++      mutex_unlock(&msi->lock);
++
++      irq_domain_set_info(domain, virq, bit, &mobiveil_msi_bottom_irq_chip,
++                          domain->host_data, handle_level_irq, NULL, NULL);
++      return 0;
++}
++
++static void mobiveil_irq_msi_domain_free(struct irq_domain *domain,
++                                       unsigned int virq,
++                                       unsigned int nr_irqs)
++{
++      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++      struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(d);
++      struct mobiveil_msi *msi = &pcie->rp.msi;
++
++      mutex_lock(&msi->lock);
++
++      if (!test_bit(d->hwirq, msi->msi_irq_in_use))
++              dev_err(&pcie->pdev->dev, "trying to free unused MSI#%lu\n",
++                      d->hwirq);
++      else
++              __clear_bit(d->hwirq, msi->msi_irq_in_use);
++
++      mutex_unlock(&msi->lock);
++}
++static const struct irq_domain_ops msi_domain_ops = {
++      .alloc  = mobiveil_irq_msi_domain_alloc,
++      .free   = mobiveil_irq_msi_domain_free,
++};
++
++static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie)
++{
++      struct device *dev = &pcie->pdev->dev;
++      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++      struct mobiveil_msi *msi = &pcie->rp.msi;
++
++      mutex_init(&msi->lock);
++      msi->dev_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
++                                              &msi_domain_ops, pcie);
++      if (!msi->dev_domain) {
++              dev_err(dev, "failed to create IRQ domain\n");
++              return -ENOMEM;
++      }
++
++      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++                                                  &mobiveil_msi_domain_info,
++                                                  msi->dev_domain);
++      if (!msi->msi_domain) {
++              dev_err(dev, "failed to create MSI domain\n");
++              irq_domain_remove(msi->dev_domain);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
++{
++      struct device *dev = &pcie->pdev->dev;
++      struct device_node *node = dev->of_node;
++      int ret;
++
++      /* setup INTx */
++      pcie->rp.intx_domain = irq_domain_add_linear(node, PCI_NUM_INTX,
++                                                   &intx_domain_ops, pcie);
++
++      if (!pcie->rp.intx_domain) {
++              dev_err(dev, "Failed to get a INTx IRQ domain\n");
++              return -ENOMEM;
++      }
++
++      raw_spin_lock_init(&pcie->rp.intx_mask_lock);
++
++      /* setup MSI */
++      ret = mobiveil_allocate_msi_domains(pcie);
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++static int mobiveil_pcie_interrupt_init(struct mobiveil_pcie *pcie)
++{
++      struct device *dev = &pcie->pdev->dev;
++      struct resource *res;
++      int ret;
++
++      if (pcie->rp.ops->interrupt_init)
++              return pcie->rp.ops->interrupt_init(pcie);
++
++      /* map MSI config resource */
++      res = platform_get_resource_byname(pcie->pdev, IORESOURCE_MEM,
++                                         "apb_csr");
++      pcie->apb_csr_base = devm_pci_remap_cfg_resource(dev, res);
++      if (IS_ERR(pcie->apb_csr_base))
++              return PTR_ERR(pcie->apb_csr_base);
++
++      /* setup MSI hardware registers */
++      mobiveil_pcie_enable_msi(pcie);
++
++      pcie->rp.irq = platform_get_irq(pcie->pdev, 0);
++      if (pcie->rp.irq <= 0) {
++              dev_err(dev, "failed to map IRQ: %d\n", pcie->rp.irq);
++              return -ENODEV;
++      }
++
++      /* initialize the IRQ domains */
++      ret = mobiveil_pcie_init_irq_domain(pcie);
++      if (ret) {
++              dev_err(dev, "Failed creating IRQ Domain\n");
++              return ret;
++      }
++
++      irq_set_chained_handler_and_data(pcie->rp.irq,
++                                       mobiveil_pcie_isr, pcie);
++
++      /* Enable interrupts */
++      csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK),
++                 PAB_INTP_AMBA_MISC_ENB);
++
++      return 0;
++}
++
++int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie)
++{
++      struct pci_bus *bus;
++      struct pci_bus *child;
++      struct pci_host_bridge *bridge;
++      struct device *dev = &pcie->pdev->dev;
++      struct device_node *np = dev->of_node;
++      resource_size_t iobase;
++      int ret;
++
++      ret = mobiveil_pcie_parse_dt(pcie);
++      if (ret) {
++              dev_err(dev, "Parsing DT failed, ret: %x\n", ret);
++              return ret;
++      }
++
++      /* allocate the PCIe port */
++      bridge = devm_pci_alloc_host_bridge(dev, 0);
++      if (!bridge)
++              return -ENOMEM;
++
++      /* parse the host bridge base addresses from the device tree file */
++      ret = of_pci_get_host_bridge_resources(np, 0, 0xff,
++                                             &bridge->windows, &iobase);
++      if (ret) {
++              dev_err(dev, "Getting bridge resources failed\n");
++              return ret;
++      }
++
++      pcie->resources = &bridge->windows;
++
++      /*
++       * configure all inbound and outbound windows and prepare the RC for
++       * config access
++       */
++      ret = mobiveil_host_init(pcie, false);
++      if (ret) {
++              dev_err(dev, "Failed to initialize host\n");
++              goto error;
++      }
++
++      ret = mobiveil_pcie_interrupt_init(pcie);
++      if (ret) {
++              dev_err(dev, "Interrupt init failed\n");
++              goto error;
++      }
++
++      ret = devm_request_pci_bus_resources(dev, pcie->resources);
++      if (ret)
++              goto error;
++
++      /* Initialize bridge */
++      bridge->dev.parent = dev;
++      bridge->sysdata = pcie;
++      bridge->busnr = pcie->rp.root_bus_nr;
++      bridge->ops = &mobiveil_pcie_ops;
++      bridge->map_irq = of_irq_parse_and_map_pci;
++      bridge->swizzle_irq = pci_common_swizzle;
++
++      ret = mobiveil_bringup_link(pcie);
++      if (ret) {
++              dev_info(dev, "link bring-up failed\n");
++      }
++
++      /* setup the kernel resources for the newly added PCIe root bus */
++      ret = pci_scan_root_bus_bridge(bridge);
++      if (ret)
++              goto error;
++
++      bus = bridge->bus;
++
++      pci_assign_unassigned_bus_resources(bus);
++      list_for_each_entry(child, &bus->children, node)
++              pcie_bus_configure_settings(child);
++      pci_bus_add_devices(bus);
++
++      return 0;
++error:
++      pci_free_resource_list(pcie->resources);
++      return ret;
++}
+--- /dev/null
++++ b/drivers/pci/mobiveil/pcie-mobiveil-plat.c
+@@ -0,0 +1,54 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCIe host controller driver for Mobiveil PCIe Host controller
++ *
++ * Copyright (c) 2018 Mobiveil Inc.
++ * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
++ * Refactor: Zhiqiang Hou <Zhiqiang.Hou@nxp.com>
++ */
++
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#include "pcie-mobiveil.h"
++
++static int mobiveil_pcie_probe(struct platform_device *pdev)
++{
++      struct mobiveil_pcie *pcie;
++      struct device *dev = &pdev->dev;
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      pcie->pdev = pdev;
++
++      return mobiveil_pcie_host_probe(pcie);
++}
++
++static const struct of_device_id mobiveil_pcie_of_match[] = {
++      {.compatible = "mbvl,gpex40-pcie",},
++      {},
++};
++
++MODULE_DEVICE_TABLE(of, mobiveil_pcie_of_match);
++
++static struct platform_driver mobiveil_pcie_driver = {
++      .probe = mobiveil_pcie_probe,
++      .driver = {
++              .name = "mobiveil-pcie",
++              .of_match_table = mobiveil_pcie_of_match,
++              .suppress_bind_attrs = true,
++      },
++};
++
++builtin_platform_driver(mobiveil_pcie_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("Mobiveil PCIe host controller driver");
++MODULE_AUTHOR("Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>");
+--- /dev/null
++++ b/drivers/pci/mobiveil/pcie-mobiveil.c
+@@ -0,0 +1,334 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCIe host controller driver for Mobiveil PCIe Host controller
++ *
++ * Copyright (c) 2018 Mobiveil Inc.
++ * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
++ * Refactor: Zhiqiang Hou <Zhiqiang.Hou@nxp.com>
++ */
 +
-+      cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
-+      if (cfg_res) {
-+              ep->phys_base = cfg_res->start;
-+              ep->addr_size = PCIE_EP_ADDR_SPACE_SIZE;
-+      } else {
-+              dev_err(dev, "missing *config* space\n");
-+              return -ENODEV;
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++
++#include "pcie-mobiveil.h"
++
++/*
++ * mobiveil_pcie_sel_page - routine to access paged register
++ *
++ * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged,
++ * for this scheme to work extracted higher 6 bits of the offset will be
++ * written to pg_sel field of PAB_CTRL register and rest of the lower 10
++ * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register.
++ */
++static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx)
++{
++      u32 val;
++
++      val = readl(pcie->csr_axi_slave_base + PAB_CTRL);
++      val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT);
++      val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT;
++
++      writel(val, pcie->csr_axi_slave_base + PAB_CTRL);
++}
++
++static void *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie, u32 off)
++{
++      if (off < PAGED_ADDR_BNDRY) {
++              /* For directly accessed registers, clear the pg_sel field */
++              mobiveil_pcie_sel_page(pcie, 0);
++              return pcie->csr_axi_slave_base + off;
 +      }
 +
-+      pcie_ep->msi_phys_addr = ep->phys_base + PCIE_MSI_ADDR_OFFSET;
++      mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off));
++      return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off);
++}
 +
-+      pcie_ep->msi_virt_addr = ioremap(pcie_ep->msi_phys_addr,
-+                                              PCIE_MSI_OB_SIZE);
-+      if (!pcie_ep->msi_virt_addr) {
-+              dev_err(dev, "failed to map MSI outbound region\n");
-+              return -ENOMEM;
++static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val)
++{
++      if ((uintptr_t)addr & (size - 1)) {
++              *val = 0;
++              return PCIBIOS_BAD_REGISTER_NUMBER;
 +      }
 +
-+      ret = dw_pcie_ep_init(ep);
-+      if (ret) {
-+              dev_err(dev, "failed to initialize endpoint\n");
-+              return ret;
++      switch (size) {
++      case 4:
++              *val = readl(addr);
++              break;
++      case 2:
++              *val = readw(addr);
++              break;
++      case 1:
++              *val = readb(addr);
++              break;
++      default:
++              *val = 0;
++              return PCIBIOS_BAD_REGISTER_NUMBER;
++      }
++
++      return PCIBIOS_SUCCESSFUL;
++}
++
++static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val)
++{
++      if ((uintptr_t)addr & (size - 1))
++              return PCIBIOS_BAD_REGISTER_NUMBER;
++
++      switch (size) {
++      case 4:
++              writel(val, addr);
++              break;
++      case 2:
++              writew(val, addr);
++              break;
++      case 1:
++              writeb(val, addr);
++              break;
++      default:
++              return PCIBIOS_BAD_REGISTER_NUMBER;
++      }
++
++      return PCIBIOS_SUCCESSFUL;
++}
++
++u32 csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size)
++{
++      void *addr;
++      u32 val;
++      int ret;
++
++      addr = mobiveil_pcie_comp_addr(pcie, off);
++
++      ret = mobiveil_pcie_read(addr, size, &val);
++      if (ret)
++              dev_err(&pcie->pdev->dev, "read CSR address failed\n");
++
++      return val;
++}
++
++void csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, size_t size)
++{
++      void *addr;
++      int ret;
++
++      addr = mobiveil_pcie_comp_addr(pcie, off);
++
++      ret = mobiveil_pcie_write(addr, size, val);
++      if (ret)
++              dev_err(&pcie->pdev->dev, "write CSR address failed\n");
++}
++
++bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie)
++{
++      if (pcie->ops->link_up)
++              return pcie->ops->link_up(pcie);
++
++      return (csr_readl(pcie, LTSSM_STATUS) &
++              LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0;
++}
++
++void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
++                      u64 pci_addr, u32 type, u64 size)
++{
++      u32 value;
++      u64 size64 = ~(size - 1);
++
++      if (win_num >= pcie->ppio_wins) {
++              dev_err(&pcie->pdev->dev,
++                      "ERROR: max inbound windows reached !\n");
++              return;
++      }
++
++      value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
++      value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT |
++               WIN_SIZE_MASK << WIN_SIZE_SHIFT);
++      value |= (type << AMAP_CTRL_TYPE_SHIFT) | (1 << AMAP_CTRL_EN_SHIFT) |
++               (lower_32_bits(size64) & WIN_SIZE_MASK << WIN_SIZE_SHIFT);
++      csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num));
++
++      csr_writel(pcie, upper_32_bits(size64),
++                 PAB_EXT_PEX_AMAP_SIZEN(win_num));
++
++      csr_writel(pcie, lower_32_bits(cpu_addr),
++                 PAB_PEX_AMAP_AXI_WIN(win_num));
++      csr_writel(pcie, upper_32_bits(cpu_addr),
++                 PAB_EXT_PEX_AMAP_AXI_WIN(win_num));
++
++      csr_writel(pcie, lower_32_bits(pci_addr),
++                 PAB_PEX_AMAP_PEX_WIN_L(win_num));
++      csr_writel(pcie, upper_32_bits(pci_addr),
++                 PAB_PEX_AMAP_PEX_WIN_H(win_num));
++
++      pcie->ib_wins_configured++;
++}
++
++/*
++ * routine to program the outbound windows
++ */
++void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
++                      u64 pci_addr, u32 type, u64 size)
++{
++
++      u32 value;
++      u64 size64 = ~(size - 1);
++
++      if (win_num >= pcie->apio_wins) {
++              dev_err(&pcie->pdev->dev,
++                      "ERROR: max outbound windows reached !\n");
++              return;
 +      }
 +
++      /*
++       * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit
++       * to 4 KB in PAB_AXI_AMAP_CTRL register
++       */
++      value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
++      value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
++               WIN_SIZE_MASK << WIN_SIZE_SHIFT);
++      value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
++               (lower_32_bits(size64) & WIN_SIZE_MASK << WIN_SIZE_SHIFT);
++      csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num));
++
++      csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num));
++
++      /*
++       * program AXI window base with appropriate value in
++       * PAB_AXI_AMAP_AXI_WIN0 register
++       */
++      csr_writel(pcie, lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK),
++                 PAB_AXI_AMAP_AXI_WIN(win_num));
++      csr_writel(pcie, upper_32_bits(cpu_addr),
++                 PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
++
++      csr_writel(pcie, lower_32_bits(pci_addr),
++                 PAB_AXI_AMAP_PEX_WIN_L(win_num));
++      csr_writel(pcie, upper_32_bits(pci_addr),
++                 PAB_AXI_AMAP_PEX_WIN_H(win_num));
++
++      pcie->ob_wins_configured++;
++}
++
++int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
++                        u64 phys, u64 bus_addr, u8 func, u64 size)
++{
++      u32 val;
++      u32 size_h, size_l;
++
++      if (size & (size - 1))
++              size = 1 << (1 + ilog2(size));
++
++      size_h = upper_32_bits(~(size - 1));
++      size_l = lower_32_bits(~(size - 1));
++
++      val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
++      val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
++               WIN_SIZE_MASK << WIN_SIZE_SHIFT);
++      val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
++               (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT));
++      csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
++
++      csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num));
++      csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num));
++      csr_writel(pcie, upper_32_bits(phys),
++                 PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
++      csr_writel(pcie, lower_32_bits(bus_addr),
++                 PAB_AXI_AMAP_PEX_WIN_L(win_num));
++      csr_writel(pcie, upper_32_bits(bus_addr),
++                 PAB_AXI_AMAP_PEX_WIN_H(win_num));
++      csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num));
++
 +      return 0;
 +}
 +
- static int __init ls_add_pcie_port(struct ls_pcie *pcie)
- {
-       struct dw_pcie *pci = pcie->pci;
-@@ -309,18 +427,18 @@ static int __init ls_pcie_probe(struct p
-       if (IS_ERR(pci->dbi_base))
-               return PTR_ERR(pci->dbi_base);
--      pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset;
-+      pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_BASE;
--      if (!ls_pcie_is_bridge(pcie))
--              return -ENODEV;
-+      pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset;
-       platform_set_drvdata(pdev, pcie);
--      ret = ls_add_pcie_port(pcie);
--      if (ret < 0)
--              return ret;
-+      if (!ls_pcie_is_bridge(pcie))
-+              ret = ls_add_pcie_ep(pcie, pdev);
-+      else
-+              ret = ls_add_pcie_port(pcie);
--      return 0;
-+      return ret;
- }
- static struct platform_driver ls_pcie_driver = {
---- a/drivers/pci/endpoint/functions/pci-epf-test.c
-+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
-@@ -471,7 +471,7 @@ static int pci_epf_test_probe(struct pci
-       const struct pci_epf_device_id *match;
-       struct pci_epf_test_data *data;
-       enum pci_barno test_reg_bar = BAR_0;
--      bool linkup_notifier = true;
-+      bool linkup_notifier = false;
-       match = pci_epf_match_device(pci_epf_test_ids, epf);
-       data = (struct pci_epf_test_data *)match->driver_data;
---- a/drivers/pci/endpoint/pci-epf-core.c
-+++ b/drivers/pci/endpoint/pci-epf-core.c
-@@ -104,8 +104,8 @@ void pci_epf_free_space(struct pci_epf *
-       if (!addr)
-               return;
--      dma_free_coherent(dev, epf->bar[bar].size, addr,
--                        epf->bar[bar].phys_addr);
-+      free_pages((unsigned long)addr,
-+              get_order(epf->bar[bar].size));
-       epf->bar[bar].phys_addr = 0;
-       epf->bar[bar].size = 0;
-@@ -129,7 +129,9 @@ void *pci_epf_alloc_space(struct pci_epf
-               size = 128;
-       size = roundup_pow_of_two(size);
--      space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
-+      space = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
-+                                              get_order(size));
-+      phys_addr = virt_to_phys(space);
-       if (!space) {
-               dev_err(dev, "failed to allocate mem space\n");
-               return NULL;
++void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
++                         int bar, u64 phys)
++{
++      csr_writel(pcie, upper_32_bits(phys),
++                 PAB_EXT_PEX_BAR_AMAP(func_no, bar));
++      csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN,
++                 PAB_PEX_BAR_AMAP(func_no, bar));
++}
++
++void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie,
++                                   u8 func_no, u8 bar)
++{
++      u32 val;
++
++      val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar));
++      val &= ~(1 << 0);
++      csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar));
++}
++
++int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
++{
++      int retries;
++
++      /* check if the link is up or not */
++      for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
++              if (mobiveil_pcie_link_up(pcie))
++                      return 0;
++
++              usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
++      }
++
++      dev_info(&pcie->pdev->dev, "link never came up\n");
++
++      return -ETIMEDOUT;
++}
++
++void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int win_num)
++{
++      u32 val;
++
++      val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
++      val &= ~(1 << AMAP_CTRL_EN_SHIFT);
++      csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num));
++}
++
++void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num)
++{
++      u32 val;
++
++      val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
++      val &= ~(1 << WIN_ENABLE_SHIFT);
++      csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
++}
++
++void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie)
++{
++      u32 val;
++
++      val = csr_readl(pcie, PAB_CTRL);
++      val |= 1 << AMBA_PIO_ENABLE_SHIFT;
++      val |= 1 << PEX_PIO_ENABLE_SHIFT;
++      csr_writel(pcie, val, PAB_CTRL);
++}
++
++void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie)
++{
++      u32 val;
++
++      val = csr_readl(pcie, PAB_AXI_PIO_CTRL);
++      val |= APIO_EN_MASK;
++      csr_writel(pcie, val, PAB_AXI_PIO_CTRL);
++}
++
++void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie)
++{
++      u32 val;
++
++      val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
++      val |= 1 << PIO_ENABLE_SHIFT;
++      csr_writel(pcie, val, PAB_PEX_PIO_CTRL);
++}
++
++void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie)
++{
++      u32 val;
++
++      val =  csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
++      val |= 1 << 0;
++      csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB);
++}
+--- /dev/null
++++ b/drivers/pci/mobiveil/pcie-mobiveil.h
+@@ -0,0 +1,296 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PCIe host controller driver for Mobiveil PCIe Host controller
++ *
++ * Copyright (c) 2018 Mobiveil Inc.
++ * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
++ * Refactor: Zhiqiang Hou <Zhiqiang.Hou@nxp.com>
++ */
++
++#ifndef _PCIE_MOBIVEIL_H
++#define _PCIE_MOBIVEIL_H
++
++#include <linux/pci.h>
++#include <linux/irq.h>
++#include <linux/msi.h>
++#include "../pci.h"
++
++#include <linux/pci-epc.h>
++#include <linux/pci-epf.h>
++
++#define MAX_IATU_OUT                  256
++/* register offsets and bit positions */
++
++/*
++ * translation tables are grouped into windows, each window registers are
++ * grouped into blocks of 4 or 16 registers each
++ */
++#define PAB_REG_BLOCK_SIZE            16
++#define PAB_EXT_REG_BLOCK_SIZE                4
++
++#define PAB_REG_ADDR(offset, win)     \
++      (offset + (win * PAB_REG_BLOCK_SIZE))
++#define PAB_EXT_REG_ADDR(offset, win) \
++      (offset + (win * PAB_EXT_REG_BLOCK_SIZE))
++
++#define LTSSM_STATUS                  0x0404
++#define  LTSSM_STATUS_L0_MASK         0x3f
++#define  LTSSM_STATUS_L0              0x2d
++
++#define PAB_CTRL                      0x0808
++#define  AMBA_PIO_ENABLE_SHIFT                0
++#define  PEX_PIO_ENABLE_SHIFT         1
++#define  PAGE_SEL_SHIFT                       13
++#define  PAGE_SEL_MASK                        0x3f
++#define  PAGE_LO_MASK                 0x3ff
++#define  PAGE_SEL_OFFSET_SHIFT                10
++#define  FUNC_SEL_SHIFT       19
++#define  FUNC_SEL_MASK                0x1ff
++#define  MSI_SW_CTRL_EN       (1 << 29)
++
++#define PAB_ACTIVITY_STAT             0x81c
++
++#define PAB_AXI_PIO_CTRL              0x0840
++#define  APIO_EN_MASK                 0xf
++
++#define PAB_PEX_PIO_CTRL              0x08c0
++#define  PIO_ENABLE_SHIFT             0
++
++#define PAB_INTP_AMBA_MISC_ENB                0x0b0c
++#define PAB_INTP_AMBA_MISC_STAT               0x0b1c
++#define  PAB_INTP_RESET                       (0x1 << 1)
++#define  PAB_INTP_MSI                 (0x1 << 3)
++#define  PAB_INTP_INTA                        (0x1 << 5)
++#define  PAB_INTP_INTB                        (0x1 << 6)
++#define  PAB_INTP_INTC                        (0x1 << 7)
++#define  PAB_INTP_INTD                        (0x1 << 8)
++#define  PAB_INTP_PCIE_UE             (0x1 << 9)
++#define  PAB_INTP_IE_PMREDI           (0x1 << 29)
++#define  PAB_INTP_IE_EC                       (0x1 << 30)
++#define  PAB_INTP_MSI_MASK            PAB_INTP_MSI
++#define  PAB_INTP_INTX_MASK           (PAB_INTP_INTA | PAB_INTP_INTB |\
++                                      PAB_INTP_INTC | PAB_INTP_INTD)
++
++#define PAB_AXI_AMAP_CTRL(win)                PAB_REG_ADDR(0x0ba0, win)
++#define  WIN_ENABLE_SHIFT             0
++#define  WIN_TYPE_SHIFT                       1
++#define  WIN_TYPE_MASK                        0x3
++#define  WIN_SIZE_SHIFT                       10
++#define  WIN_SIZE_MASK                        0x3fffff
++
++#define PAB_EXT_AXI_AMAP_SIZE(win)    PAB_EXT_REG_ADDR(0xbaf0, win)
++
++#define PAB_EXT_AXI_AMAP_AXI_WIN(win) PAB_EXT_REG_ADDR(0x80a0, win)
++#define PAB_AXI_AMAP_AXI_WIN(win)     PAB_REG_ADDR(0x0ba4, win)
++#define  AXI_WINDOW_ALIGN_MASK                3
++
++#define PAB_AXI_AMAP_PEX_WIN_L(win)   PAB_REG_ADDR(0x0ba8, win)
++#define  PAB_BUS_SHIFT                        24
++#define  PAB_DEVICE_SHIFT             19
++#define  PAB_FUNCTION_SHIFT           16
++
++#define PAB_AXI_AMAP_PEX_WIN_H(win)   PAB_REG_ADDR(0x0bac, win)
++#define PAB_INTP_AXI_PIO_CLASS                0x474
++
++#define GPEX_ACK_REPLAY_TO            0x438
++#define  ACK_LAT_TO_VAL_MASK          0x1fff
++#define  ACK_LAT_TO_VAL_SHIFT         0
++
++#define PAB_PEX_AMAP_CTRL(win)                PAB_REG_ADDR(0x4ba0, win)
++#define  AMAP_CTRL_EN_SHIFT           0
++#define  AMAP_CTRL_TYPE_SHIFT         1
++#define  AMAP_CTRL_TYPE_MASK          3
++
++#define PAB_EXT_PEX_AMAP_SIZEN(win)   PAB_EXT_REG_ADDR(0xbef0, win)
++#define PAB_EXT_PEX_AMAP_AXI_WIN(win) PAB_EXT_REG_ADDR(0xb4a0, win)
++#define PAB_PEX_AMAP_AXI_WIN(win)     PAB_REG_ADDR(0x4ba4, win)
++#define PAB_PEX_AMAP_PEX_WIN_L(win)   PAB_REG_ADDR(0x4ba8, win)
++#define PAB_PEX_AMAP_PEX_WIN_H(win)   PAB_REG_ADDR(0x4bac, win)
++
++/* PPIO WINs EP mode */
++#define PAB_PEX_BAR_AMAP(func, bar)           (0x1ba0 + 0x20 * func + 4 * bar)
++#define PAB_EXT_PEX_BAR_AMAP(func, bar)               (0x84a0 + 0x20 * func + 4 * bar)
++#define PEX_BAR_AMAP_EN                               (1 << 0)
++
++#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx)               (0x5ba0 + 0x04 * idx)
++#define PAB_MSIX_TABLE_PBA_ACCESS             0xD000
++
++#define GPEX_BAR_ENABLE                         0x4D4
++#define GPEX_BAR_SIZE_LDW                       0x4D8
++#define GPEX_BAR_SIZE_UDW                       0x4DC
++#define GPEX_BAR_SELECT                         0x4E0
++
++#define CFG_UNCORRECTABLE_ERROR_SEVERITY      0x10c
++#define UNSUPPORTED_REQUEST_ERROR_SHIFT               20
++#define CFG_UNCORRECTABLE_ERROR_MASK          0x108
++
++/* starting offset of INTX bits in status register */
++#define PAB_INTX_START                        5
++
++/* supported number of MSI interrupts */
++#define PCI_NUM_MSI                   16
++
++/* MSI registers */
++#define MSI_BASE_LO_OFFSET            0x04
++#define MSI_BASE_HI_OFFSET            0x08
++#define MSI_SIZE_OFFSET                       0x0c
++#define MSI_ENABLE_OFFSET             0x14
++#define MSI_STATUS_OFFSET             0x18
++#define MSI_DATA_OFFSET                       0x20
++#define MSI_ADDR_L_OFFSET             0x24
++#define MSI_ADDR_H_OFFSET             0x28
++
++/* outbound and inbound window definitions */
++#define WIN_NUM_0                     0
++#define WIN_NUM_1                     1
++#define CFG_WINDOW_TYPE                       0
++#define IO_WINDOW_TYPE                        1
++#define MEM_WINDOW_TYPE                       2
++#define IB_WIN_SIZE                   ((u64)256 * 1024 * 1024 * 1024)
++#define MAX_PIO_WINDOWS                       8
++
++/* Parameters for the waiting for link up routine */
++#define LINK_WAIT_MAX_RETRIES         10
++#define LINK_WAIT_MIN                 90000
++#define LINK_WAIT_MAX                 100000
++
++#define PAGED_ADDR_BNDRY              0xc00
++#define OFFSET_TO_PAGE_ADDR(off)      \
++      ((off & PAGE_LO_MASK) | PAGED_ADDR_BNDRY)
++#define OFFSET_TO_PAGE_IDX(off)               \
++      ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK)
++
++struct mobiveil_pcie;
++struct mobiveil_pcie_ep;
++
++struct mobiveil_msi {                 /* MSI information */
++      struct mutex lock;              /* protect bitmap variable */
++      struct irq_domain *msi_domain;
++      struct irq_domain *dev_domain;
++      phys_addr_t msi_pages_phys;
++      int num_of_vectors;
++      DECLARE_BITMAP(msi_irq_in_use, PCI_NUM_MSI);
++};
++
++struct mobiveil_rp_ops {
++      int (*interrupt_init)(struct mobiveil_pcie *pcie);
++      int (*read_other_conf)(struct pci_bus *bus, unsigned int devfn,
++                             int where, int size, u32 *val);
++};
++
++struct root_port {
++      u8 root_bus_nr;
++      void __iomem *config_axi_slave_base;    /* endpoint config base */
++      struct resource *ob_io_res;
++      struct mobiveil_rp_ops *ops;
++      int irq;
++      raw_spinlock_t intx_mask_lock;
++      struct irq_domain *intx_domain;
++      struct mobiveil_msi msi;
++};
++
++struct mobiveil_pab_ops {
++      int (*link_up)(struct mobiveil_pcie *pcie);
++};
++
++struct mobiveil_pcie_ep_ops {
++      void (*ep_init)(struct mobiveil_pcie_ep *ep);
++      int (*raise_irq)(struct mobiveil_pcie_ep *ep, u8 func_no,
++                       enum pci_epc_irq_type type, u16 interrupt_num);
++};
++
++struct mobiveil_pcie_ep {
++      struct pci_epc *epc;
++      struct mobiveil_pcie_ep_ops *ops;
++      phys_addr_t phys_base;
++      size_t addr_size;
++      size_t page_size;
++      phys_addr_t *outbound_addr;
++      unsigned long *ob_window_map;
++      u32 num_ob_windows;
++      void __iomem *msi_mem;
++      phys_addr_t msi_mem_phys;
++      u8 msi_cap;     /* MSI capability offset */
++      u8 msix_cap;    /* MSI-X capability offset */
++      u8 bar_num;
++      u32 pf_num;
++};
++
++struct mobiveil_pcie {
++      struct platform_device *pdev;
++      struct list_head *resources;
++      void __iomem *csr_axi_slave_base;       /* PAB registers base */
++      phys_addr_t pcie_reg_base;      /* Physical PCIe Controller Base */
++      void __iomem *apb_csr_base;     /* MSI register base */
++      u32 apio_wins;
++      u32 ppio_wins;
++      u32 ob_wins_configured;         /* configured outbound windows */
++      u32 ib_wins_configured;         /* configured inbound windows */
++      const struct mobiveil_pab_ops *ops;
++      struct root_port rp;
++      struct mobiveil_pcie_ep ep;
++};
++#define to_mobiveil_pcie_from_ep(endpoint)   \
++                          container_of((endpoint), struct mobiveil_pcie, ep)
++
++int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie);
++int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit);
++bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie);
++int mobiveil_bringup_link(struct mobiveil_pcie *pcie);
++void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
++                      u64 pci_addr, u32 type, u64 size);
++void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
++                      u64 pci_addr, u32 type, u64 size);
++void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pci, int win_num);
++void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pci, int win_num);
++u32 csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size);
++void csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, size_t size);
++
++static inline u32 csr_readl(struct mobiveil_pcie *pcie, u32 off)
++{
++      return csr_read(pcie, off, 0x4);
++}
++
++static inline u32 csr_readw(struct mobiveil_pcie *pcie, u32 off)
++{
++      return csr_read(pcie, off, 0x2);
++}
++
++static inline u32 csr_readb(struct mobiveil_pcie *pcie, u32 off)
++{
++      return csr_read(pcie, off, 0x1);
++}
++
++static inline void csr_writel(struct mobiveil_pcie *pcie, u32 val, u32 off)
++{
++      csr_write(pcie, val, off, 0x4);
++}
++
++static inline void csr_writew(struct mobiveil_pcie *pcie, u32 val, u32 off)
++{
++      csr_write(pcie, val, off, 0x2);
++}
++
++static inline void csr_writeb(struct mobiveil_pcie *pcie, u32 val, u32 off)
++{
++      csr_write(pcie, val, off, 0x1);
++}
++
++void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
++                         int bar, u64 phys);
++int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
++                        u64 phys, u64 bus_addr, u8 func, u64 size);
++void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci,
++                                   u8 func_no, u8 bar);
++int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep);
++int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no);
++int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
++                           u8 interrupt_num);
++int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
++                           u16 interrupt_num);
++void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pci, enum pci_barno bar);
++void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pci);
++void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pci);
++void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pci);
++void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pci);
++#endif /* _PCIE_MOBIVEIL_H */
 --- a/drivers/pci/pcie/portdrv_core.c
 +++ b/drivers/pci/pcie/portdrv_core.c
 @@ -45,6 +45,20 @@ static void release_pcie_device(struct d
@@ -584,6 +5630,194 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
 +              pdev->no_msi = 1;
 +}
 +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
+--- a/include/linux/pci-ep-cfs.h
++++ b/include/linux/pci-ep-cfs.h
+@@ -1,12 +1,9 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
+ /**
+  * PCI Endpoint ConfigFS header file
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+  */
+ #ifndef __LINUX_PCI_EP_CFS_H
+--- a/include/linux/pci-epc.h
++++ b/include/linux/pci-epc.h
+@@ -1,12 +1,9 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
+ /**
+  * PCI Endpoint *Controller* (EPC) header file
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+  */
+ #ifndef __LINUX_PCI_EPC_H
+@@ -20,6 +17,7 @@ enum pci_epc_irq_type {
+       PCI_EPC_IRQ_UNKNOWN,
+       PCI_EPC_IRQ_LEGACY,
+       PCI_EPC_IRQ_MSI,
++      PCI_EPC_IRQ_MSIX,
+ };
+ /**
+@@ -33,24 +31,32 @@ enum pci_epc_irq_type {
+  *         capability register
+  * @get_msi: ops to get the number of MSI interrupts allocated by the RC from
+  *         the MSI capability register
+- * @raise_irq: ops to raise a legacy or MSI interrupt
++ * @set_msix: ops to set the requested number of MSI-X interrupts in the
++ *         MSI-X capability register
++ * @get_msix: ops to get the number of MSI-X interrupts allocated by the RC
++ *         from the MSI-X capability register
++ * @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
+  * @start: ops to start the PCI link
+  * @stop: ops to stop the PCI link
+  * @owner: the module owner containing the ops
+  */
+ struct pci_epc_ops {
+-      int     (*write_header)(struct pci_epc *pci_epc,
++      int     (*write_header)(struct pci_epc *epc, u8 func_no,
+                               struct pci_epf_header *hdr);
+-      int     (*set_bar)(struct pci_epc *epc, enum pci_barno bar,
+-                         dma_addr_t bar_phys, size_t size, int flags);
+-      void    (*clear_bar)(struct pci_epc *epc, enum pci_barno bar);
+-      int     (*map_addr)(struct pci_epc *epc, phys_addr_t addr,
+-                          u64 pci_addr, size_t size);
+-      void    (*unmap_addr)(struct pci_epc *epc, phys_addr_t addr);
+-      int     (*set_msi)(struct pci_epc *epc, u8 interrupts);
+-      int     (*get_msi)(struct pci_epc *epc);
+-      int     (*raise_irq)(struct pci_epc *pci_epc,
+-                           enum pci_epc_irq_type type, u8 interrupt_num);
++      int     (*set_bar)(struct pci_epc *epc, u8 func_no,
++                         struct pci_epf_bar *epf_bar);
++      void    (*clear_bar)(struct pci_epc *epc, u8 func_no,
++                           struct pci_epf_bar *epf_bar);
++      int     (*map_addr)(struct pci_epc *epc, u8 func_no,
++                          phys_addr_t addr, u64 pci_addr, size_t size);
++      void    (*unmap_addr)(struct pci_epc *epc, u8 func_no,
++                            phys_addr_t addr);
++      int     (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts);
++      int     (*get_msi)(struct pci_epc *epc, u8 func_no);
++      int     (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts);
++      int     (*get_msix)(struct pci_epc *epc, u8 func_no);
++      int     (*raise_irq)(struct pci_epc *epc, u8 func_no,
++                           enum pci_epc_irq_type type, u16 interrupt_num);
+       int     (*start)(struct pci_epc *epc);
+       void    (*stop)(struct pci_epc *epc);
+       struct module *owner;
+@@ -91,8 +97,17 @@ struct pci_epc {
+       struct config_group             *group;
+       /* spinlock to protect against concurrent access of EP controller */
+       spinlock_t                      lock;
++      unsigned int                    features;
+ };
++#define EPC_FEATURE_NO_LINKUP_NOTIFIER                BIT(0)
++#define EPC_FEATURE_BAR_MASK                  (BIT(1) | BIT(2) | BIT(3))
++#define EPC_FEATURE_MSIX_AVAILABLE            BIT(4)
++#define EPC_FEATURE_SET_BAR(features, bar)    \
++              (features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
++#define EPC_FEATURE_GET_BAR(features)         \
++              ((features & EPC_FEATURE_BAR_MASK) >> 1)
++
+ #define to_pci_epc(device) container_of((device), struct pci_epc, dev)
+ #define pci_epc_create(dev, ops)    \
+@@ -124,17 +139,23 @@ void pci_epc_destroy(struct pci_epc *epc
+ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf);
+ void pci_epc_linkup(struct pci_epc *epc);
+ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf);
+-int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *hdr);
+-int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar,
+-                  dma_addr_t bar_phys, size_t size, int flags);
+-void pci_epc_clear_bar(struct pci_epc *epc, int bar);
+-int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr,
++int pci_epc_write_header(struct pci_epc *epc, u8 func_no,
++                       struct pci_epf_header *hdr);
++int pci_epc_set_bar(struct pci_epc *epc, u8 func_no,
++                  struct pci_epf_bar *epf_bar);
++void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no,
++                     struct pci_epf_bar *epf_bar);
++int pci_epc_map_addr(struct pci_epc *epc, u8 func_no,
++                   phys_addr_t phys_addr,
+                    u64 pci_addr, size_t size);
+-void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr);
+-int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts);
+-int pci_epc_get_msi(struct pci_epc *epc);
+-int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
+-                    u8 interrupt_num);
++void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no,
++                      phys_addr_t phys_addr);
++int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts);
++int pci_epc_get_msi(struct pci_epc *epc, u8 func_no);
++int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts);
++int pci_epc_get_msix(struct pci_epc *epc, u8 func_no);
++int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
++                    enum pci_epc_irq_type type, u16 interrupt_num);
+ int pci_epc_start(struct pci_epc *epc);
+ void pci_epc_stop(struct pci_epc *epc);
+ struct pci_epc *pci_epc_get(const char *epc_name);
+--- a/include/linux/pci-epf.h
++++ b/include/linux/pci-epf.h
+@@ -1,12 +1,9 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
+ /**
+  * PCI Endpoint *Function* (EPF) header file
+  *
+  * Copyright (C) 2017 Texas Instruments
+  * Author: Kishon Vijay Abraham I <kishon@ti.com>
+- *
+- * This program is free software: you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 of
+- * the License as published by the Free Software Foundation.
+  */
+ #ifndef __LINUX_PCI_EPF_H
+@@ -75,7 +72,7 @@ struct pci_epf_ops {
+  * @driver: PCI EPF driver
+  * @ops: set of function pointers for performing EPF operations
+  * @owner: the owner of the module that registers the PCI EPF driver
+- * @group: configfs group corresponding to the PCI EPF driver
++ * @epf_group: list of configfs group corresponding to the PCI EPF driver
+  * @id_table: identifies EPF devices for probing
+  */
+ struct pci_epf_driver {
+@@ -85,7 +82,7 @@ struct pci_epf_driver {
+       struct device_driver    driver;
+       struct pci_epf_ops      *ops;
+       struct module           *owner;
+-      struct config_group     *group;
++      struct list_head        epf_group;
+       const struct pci_epf_device_id  *id_table;
+ };
+@@ -100,6 +97,8 @@ struct pci_epf_driver {
+ struct pci_epf_bar {
+       dma_addr_t      phys_addr;
+       size_t          size;
++      enum pci_barno  barno;
++      int             flags;
+ };
+ /**
+@@ -120,6 +119,7 @@ struct pci_epf {
+       struct pci_epf_header   *header;
+       struct pci_epf_bar      bar[6];
+       u8                      msi_interrupts;
++      u16                     msix_interrupts;
+       u8                      func_no;
+       struct pci_epc          *epc;
 --- a/include/linux/pci.h
 +++ b/include/linux/pci.h
 @@ -1946,6 +1946,7 @@ void pcibios_release_device(struct pci_d
@@ -594,3 +5828,150 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
  
  #ifdef CONFIG_HIBERNATE_CALLBACKS
  extern struct dev_pm_ops pcibios_pm_ops;
+--- a/include/uapi/linux/pcitest.h
++++ b/include/uapi/linux/pcitest.h
+@@ -16,5 +16,8 @@
+ #define PCITEST_WRITE         _IOW('P', 0x4, unsigned long)
+ #define PCITEST_READ          _IOW('P', 0x5, unsigned long)
+ #define PCITEST_COPY          _IOW('P', 0x6, unsigned long)
++#define PCITEST_MSIX          _IOW('P', 0x7, int)
++#define PCITEST_SET_IRQTYPE   _IOW('P', 0x8, int)
++#define PCITEST_GET_IRQTYPE   _IO('P', 0x9)
+ #endif /* __UAPI_LINUX_PCITEST_H */
+--- a/tools/pci/pcitest.c
++++ b/tools/pci/pcitest.c
+@@ -30,12 +30,17 @@
+ #define BILLION 1E9
+ static char *result[] = { "NOT OKAY", "OKAY" };
++static char *irq[] = { "LEGACY", "MSI", "MSI-X" };
+ struct pci_test {
+       char            *device;
+       char            barnum;
+       bool            legacyirq;
+       unsigned int    msinum;
++      unsigned int    msixnum;
++      int             irqtype;
++      bool            set_irqtype;
++      bool            get_irqtype;
+       bool            read;
+       bool            write;
+       bool            copy;
+@@ -62,6 +67,24 @@ static int run_test(struct pci_test *tes
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
++      if (test->set_irqtype) {
++              ret = ioctl(fd, PCITEST_SET_IRQTYPE, test->irqtype);
++              fprintf(stdout, "SET IRQ TYPE TO %s:\t\t", irq[test->irqtype]);
++              if (ret < 0)
++                      fprintf(stdout, "FAILED\n");
++              else
++                      fprintf(stdout, "%s\n", result[ret]);
++      }
++
++      if (test->get_irqtype) {
++              ret = ioctl(fd, PCITEST_GET_IRQTYPE);
++              fprintf(stdout, "GET IRQ TYPE:\t\t");
++              if (ret < 0)
++                      fprintf(stdout, "FAILED\n");
++              else
++                      fprintf(stdout, "%s\n", irq[ret]);
++      }
++
+       if (test->legacyirq) {
+               ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
+               fprintf(stdout, "LEGACY IRQ:\t");
+@@ -80,6 +103,15 @@ static int run_test(struct pci_test *tes
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
++      if (test->msixnum > 0 && test->msixnum <= 2048) {
++              ret = ioctl(fd, PCITEST_MSIX, test->msixnum);
++              fprintf(stdout, "MSI-X%d:\t\t", test->msixnum);
++              if (ret < 0)
++                      fprintf(stdout, "TEST FAILED\n");
++              else
++                      fprintf(stdout, "%s\n", result[ret]);
++      }
++
+       if (test->write) {
+               ret = ioctl(fd, PCITEST_WRITE, test->size);
+               fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
+@@ -130,7 +162,7 @@ int main(int argc, char **argv)
+       /* set default endpoint device */
+       test->device = "/dev/pci-endpoint-test.0";
+-      while ((c = getopt(argc, argv, "D:b:m:lrwcs:")) != EOF)
++      while ((c = getopt(argc, argv, "D:b:m:x:i:Ilrwcs:")) != EOF)
+       switch (c) {
+       case 'D':
+               test->device = optarg;
+@@ -148,6 +180,20 @@ int main(int argc, char **argv)
+               if (test->msinum < 1 || test->msinum > 32)
+                       goto usage;
+               continue;
++      case 'x':
++              test->msixnum = atoi(optarg);
++              if (test->msixnum < 1 || test->msixnum > 2048)
++                      goto usage;
++              continue;
++      case 'i':
++              test->irqtype = atoi(optarg);
++              if (test->irqtype < 0 || test->irqtype > 2)
++                      goto usage;
++              test->set_irqtype = true;
++              continue;
++      case 'I':
++              test->get_irqtype = true;
++              continue;
+       case 'r':
+               test->read = true;
+               continue;
+@@ -170,6 +216,9 @@ usage:
+                       "\t-D <dev>             PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n"
+                       "\t-b <bar num>         BAR test (bar number between 0..5)\n"
+                       "\t-m <msi num>         MSI test (msi number between 1..32)\n"
++                      "\t-x <msix num>        \tMSI-X test (msix number between 1..2048)\n"
++                      "\t-i <irq type>        \tSet IRQ type (0 - Legacy, 1 - MSI, 2 - MSI-X)\n"
++                      "\t-I                   Get current IRQ type configured\n"
+                       "\t-l                   Legacy IRQ test\n"
+                       "\t-r                   Read buffer test\n"
+                       "\t-w                   Write buffer test\n"
+--- a/tools/pci/pcitest.sh
++++ b/tools/pci/pcitest.sh
+@@ -16,7 +16,10 @@ echo
+ echo "Interrupt tests"
+ echo
++pcitest -i 0
+ pcitest -l
++
++pcitest -i 1
+ msi=1
+ while [ $msi -lt 33 ]
+@@ -26,9 +29,21 @@ do
+ done
+ echo
++pcitest -i 2
++msix=1
++
++while [ $msix -lt 2049 ]
++do
++        pcitest -x $msix
++        msix=`expr $msix + 1`
++done
++echo
++
+ echo "Read Tests"
+ echo
++pcitest -i 1
++
+ pcitest -r -s 1
+ pcitest -r -s 1024
+ pcitest -r -s 1025