Refreshed all patches. Compile-tested on: ipq40xx, apm821xx Runtime-tested on: ipq40xx Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
		
			
				
	
	
		
			5978 lines
		
	
	
		
			170 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			5978 lines
		
	
	
		
			170 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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: Yangbo Lu <yangbo.lu@nxp.com>
 | 
						|
---
 | 
						|
 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 +
 | 
						|
 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/arch/arm/kernel/bios32.c
 | 
						|
+++ b/arch/arm/kernel/bios32.c
 | 
						|
@@ -12,6 +12,8 @@
 | 
						|
 #include <linux/slab.h>
 | 
						|
 #include <linux/init.h>
 | 
						|
 #include <linux/io.h>
 | 
						|
+#include <linux/of_irq.h>
 | 
						|
+#include <linux/pcieport_if.h>
 | 
						|
 
 | 
						|
 #include <asm/mach-types.h>
 | 
						|
 #include <asm/mach/map.h>
 | 
						|
@@ -65,6 +67,47 @@ void pcibios_report_status(u_int status_
 | 
						|
 }
 | 
						|
 
 | 
						|
 /*
 | 
						|
+ * Check device tree if the service interrupts are there
 | 
						|
+ */
 | 
						|
+int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
 | 
						|
+{
 | 
						|
+	int ret, count = 0;
 | 
						|
+	struct device_node *np = NULL;
 | 
						|
+
 | 
						|
+	if (dev->bus->dev.of_node)
 | 
						|
+		np = dev->bus->dev.of_node;
 | 
						|
+
 | 
						|
+	if (np == NULL)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	if (!IS_ENABLED(CONFIG_OF_IRQ))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	/* If root port doesn't support MSI/MSI-X/INTx in RC mode,
 | 
						|
+	 * request irq for aer
 | 
						|
+	 */
 | 
						|
+	if (mask & PCIE_PORT_SERVICE_AER) {
 | 
						|
+		ret = of_irq_get_byname(np, "aer");
 | 
						|
+		if (ret > 0) {
 | 
						|
+			irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret;
 | 
						|
+			count++;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (mask & PCIE_PORT_SERVICE_PME) {
 | 
						|
+		ret = of_irq_get_byname(np, "pme");
 | 
						|
+		if (ret > 0) {
 | 
						|
+			irqs[PCIE_PORT_SERVICE_PME_SHIFT] = ret;
 | 
						|
+			count++;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* TODO: add more service interrupts if there it is in the device tree*/
 | 
						|
+
 | 
						|
+	return count;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
  * We don't use this to fix the device, but initialisation of it.
 | 
						|
  * It's not the correct use for this, but it works.
 | 
						|
  * Note that the arbiter/ISA bridge appears to be buggy, specifically in
 | 
						|
--- a/arch/arm64/kernel/pci.c
 | 
						|
+++ b/arch/arm64/kernel/pci.c
 | 
						|
@@ -17,6 +17,8 @@
 | 
						|
 #include <linux/mm.h>
 | 
						|
 #include <linux/of_pci.h>
 | 
						|
 #include <linux/of_platform.h>
 | 
						|
+#include <linux/of_irq.h>
 | 
						|
+#include <linux/pcieport_if.h>
 | 
						|
 #include <linux/pci.h>
 | 
						|
 #include <linux/pci-acpi.h>
 | 
						|
 #include <linux/pci-ecam.h>
 | 
						|
@@ -36,6 +38,47 @@ int pcibios_alloc_irq(struct pci_dev *de
 | 
						|
 #endif
 | 
						|
 
 | 
						|
 /*
 | 
						|
+ * Check device tree if the service interrupts are there
 | 
						|
+ */
 | 
						|
+int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
 | 
						|
+{
 | 
						|
+	int ret, count = 0;
 | 
						|
+	struct device_node *np = NULL;
 | 
						|
+
 | 
						|
+	if (dev->bus->dev.of_node)
 | 
						|
+		np = dev->bus->dev.of_node;
 | 
						|
+
 | 
						|
+	if (np == NULL)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	if (!IS_ENABLED(CONFIG_OF_IRQ))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	/* If root port doesn't support MSI/MSI-X/INTx in RC mode,
 | 
						|
+	 * request irq for aer
 | 
						|
+	 */
 | 
						|
+	if (mask & PCIE_PORT_SERVICE_AER) {
 | 
						|
+		ret = of_irq_get_byname(np, "aer");
 | 
						|
+		if (ret > 0) {
 | 
						|
+			irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret;
 | 
						|
+			count++;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (mask & PCIE_PORT_SERVICE_PME) {
 | 
						|
+		ret = of_irq_get_byname(np, "pme");
 | 
						|
+		if (ret > 0) {
 | 
						|
+			irqs[PCIE_PORT_SERVICE_PME_SHIFT] = ret;
 | 
						|
+			count++;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* TODO: add more service interrupts if there it is in the device tree*/
 | 
						|
+
 | 
						|
+	return count;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
  * raw_pci_read/write - Platform-specific PCI config space access.
 | 
						|
  */
 | 
						|
 int raw_pci_read(unsigned int domain, unsigned int bus,
 | 
						|
--- a/drivers/misc/pci_endpoint_test.c
 | 
						|
+++ b/drivers/misc/pci_endpoint_test.c
 | 
						|
@@ -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;
 | 
						|
-	bool no_msi;
 | 
						|
+	int irq_type;
 | 
						|
 };
 | 
						|
 
 | 
						|
 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 err;
 | 
						|
-	int irq = 0;
 | 
						|
 	int id;
 | 
						|
 	char name[20];
 | 
						|
 	enum pci_barno bar;
 | 
						|
@@ -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) {
 | 
						|
-		irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
 | 
						|
-		if (irq < 0)
 | 
						|
-			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,
 | 
						|
-			       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;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	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);
 | 
						|
 	misc_device = &test->miscdev;
 | 
						|
 	misc_device->minor = MISC_DYNAMIC_MINOR;
 | 
						|
-	misc_device->name = 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);
 | 
						|
 	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 < 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);
 | 
						|
 }
 | 
						|
@@ -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) },
 | 
						|
+	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID) },
 | 
						|
 	{ }
 | 
						|
 };
 | 
						|
 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
 | 
						|
@@ -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.
 | 
						|
 
 | 
						|
-	 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 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);
 | 
						|
+
 | 
						|
+	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,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_IATU_NUM		6
 | 
						|
 
 | 
						|
@@ -124,6 +126,14 @@ static int ls_pcie_link_up(struct dw_pci
 | 
						|
 	return 1;
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* Forward error response of outbound non-posted requests */
 | 
						|
+static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
 | 
						|
+{
 | 
						|
+	struct dw_pcie *pci = pcie->pci;
 | 
						|
+
 | 
						|
+	iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR);
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int ls_pcie_host_init(struct pcie_port *pp)
 | 
						|
 {
 | 
						|
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 | 
						|
@@ -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);
 | 
						|
+	ls_pcie_fix_error_response(pcie);
 | 
						|
 
 | 
						|
 	dw_pcie_dbi_ro_wr_en(pci);
 | 
						|
 	ls_pcie_clear_multifunction(pcie);
 | 
						|
@@ -253,6 +264,7 @@ static struct ls_pcie_drvdata ls2088_drv
 | 
						|
 };
 | 
						|
 
 | 
						|
 static const struct of_device_id ls_pcie_of_match[] = {
 | 
						|
+	{ .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata },
 | 
						|
 	{ .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
 | 
						|
 	{ .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
 | 
						|
 	{ .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
 | 
						|
--- 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 int dw_pcie_ep_write_header(struct pci_epc *epc,
 | 
						|
+void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
 | 
						|
+{
 | 
						|
+	__dw_pcie_ep_reset_bar(pci, bar, 0);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
 | 
						|
+			      u8 cap)
 | 
						|
+{
 | 
						|
+	u8 cap_id, next_cap_ptr;
 | 
						|
+	u16 reg;
 | 
						|
+
 | 
						|
+	reg = dw_pcie_readw_dbi(pci, cap_ptr);
 | 
						|
+	next_cap_ptr = (reg & 0xff00) >> 8;
 | 
						|
+	cap_id = (reg & 0x00ff);
 | 
						|
+
 | 
						|
+	if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	if (cap_id == cap)
 | 
						|
+		return cap_ptr;
 | 
						|
+
 | 
						|
+	return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
 | 
						|
+{
 | 
						|
+	u8 next_cap_ptr;
 | 
						|
+	u16 reg;
 | 
						|
+
 | 
						|
+	reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
 | 
						|
+	next_cap_ptr = (reg & 0x00ff);
 | 
						|
+
 | 
						|
+	if (!next_cap_ptr)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	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, ®s);
 | 
						|
 	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>
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off));
 | 
						|
+	return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off);
 | 
						|
+}
 | 
						|
+
 | 
						|
+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;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	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;
 | 
						|
+}
 | 
						|
+
 | 
						|
+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
 | 
						|
 }
 | 
						|
 
 | 
						|
 /**
 | 
						|
+ * pcibios_check_service_irqs - check irqs in the device tree
 | 
						|
+ * @dev: PCI Express port to handle
 | 
						|
+ * @irqs: Array of irqs to populate
 | 
						|
+ * @mask: Bitmask of port capabilities returned by get_port_device_capability()
 | 
						|
+ *
 | 
						|
+ * Return value: 0 means no service irqs in the device tree
 | 
						|
+ *
 | 
						|
+ */
 | 
						|
+int __weak pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
 | 
						|
+{
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
  * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode
 | 
						|
  * for given port
 | 
						|
  * @dev: PCI Express port to handle
 | 
						|
@@ -185,10 +199,25 @@ out_free_irqs:
 | 
						|
 static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
 | 
						|
 {
 | 
						|
 	int ret, i;
 | 
						|
+	int irq = -1;
 | 
						|
 
 | 
						|
 	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
 | 
						|
 		irqs[i] = -1;
 | 
						|
 
 | 
						|
+	/* Check if some platforms owns independent irq pins for AER/PME etc.
 | 
						|
+	 * Some platforms may own independent AER/PME interrupts and set
 | 
						|
+	 * them in the device tree file.
 | 
						|
+	 */
 | 
						|
+	ret = pcibios_check_service_irqs(dev, irqs, mask);
 | 
						|
+	if (ret) {
 | 
						|
+		if (dev->irq)
 | 
						|
+			irq = dev->irq;
 | 
						|
+		for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
 | 
						|
+			if (irqs[i] == -1 && i != PCIE_PORT_SERVICE_VC_SHIFT)
 | 
						|
+				irqs[i] = irq;
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
 	/*
 | 
						|
 	 * If we support PME or hotplug, but we can't use MSI/MSI-X for
 | 
						|
 	 * them, we have to fall back to INTx or other interrupts, e.g., a
 | 
						|
--- a/drivers/pci/quirks.c
 | 
						|
+++ b/drivers/pci/quirks.c
 | 
						|
@@ -3394,6 +3394,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_A
 | 
						|
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
 | 
						|
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset);
 | 
						|
 
 | 
						|
+/*
 | 
						|
+ * NXP (Freescale Vendor ID) LS1088 chips do not behave correctly after
 | 
						|
+ * bus reset. Link state of device does not comes UP and so config space
 | 
						|
+ * never accessible again.
 | 
						|
+ */
 | 
						|
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_FREESCALE, 0x80c0, quirk_no_bus_reset);
 | 
						|
+
 | 
						|
 static void quirk_no_pm_reset(struct pci_dev *dev)
 | 
						|
 {
 | 
						|
 	/*
 | 
						|
@@ -4884,3 +4891,11 @@ static void quirk_no_ats(struct pci_dev
 | 
						|
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x98e4, quirk_no_ats);
 | 
						|
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6900, quirk_no_ats);
 | 
						|
 #endif /* CONFIG_PCI_ATS */
 | 
						|
+
 | 
						|
+/* Freescale PCIe doesn't support MSI in RC mode */
 | 
						|
+static void quirk_fsl_no_msi(struct pci_dev *pdev)
 | 
						|
+{
 | 
						|
+	if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
 | 
						|
+		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
 | 
						|
 void pcibios_penalize_isa_irq(int irq, int active);
 | 
						|
 int pcibios_alloc_irq(struct pci_dev *dev);
 | 
						|
 void pcibios_free_irq(struct pci_dev *dev);
 | 
						|
+int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask);
 | 
						|
 
 | 
						|
 #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
 |