diff options
Diffstat (limited to 'target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch | 5977 |
1 files changed, 0 insertions, 5977 deletions
diff --git a/target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch b/target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch deleted file mode 100644 index 4362863b7b..0000000000 --- a/target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch +++ /dev/null @@ -1,5977 +0,0 @@ -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[24]; - 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) - { - /* -@@ -4918,3 +4925,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; -@@ -94,8 +100,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) \ -@@ -127,17 +142,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 |