X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=blobdiff_plain;f=target%2Flinux%2Flayerscape%2Fpatches-4.14%2F816-pcie-support-layerscape.patch;h=f3f09989a7a446e4c5d621aafc12a4609977085e;hp=68d6a31a5ebb285ba049f6edd038e6a08261ecc3;hb=414b7c107aaa0e037b3f352e0650bbc429db41a7;hpb=0a827ebd2fa3c7dc210aff0a30d0dc1d536ed89c 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 index 68d6a31a5e..f3f09989a7 100644 --- a/target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch +++ b/target/linux/layerscape/patches-4.14/816-pcie-support-layerscape.patch @@ -1,63 +1,89 @@ -From c503e92983f2e18c1e18e21b15d5bedd6d0be8ac Mon Sep 17 00:00:00 2001 -From: Biwen Li -Date: Tue, 30 Oct 2018 18:26:56 +0800 -Subject: [PATCH 33/40] pcie: support layerscape +From c54a010fe105281259b996d318ed85efc4103fee Mon Sep 17 00:00:00 2001 +From: Yangbo Lu +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 +Signed-off-by: Bhumika Goyal +Signed-off-by: Biwen Li Signed-off-by: Bjorn Helgaas +Signed-off-by: Christoph Hellwig +Signed-off-by: Cyrille Pitchen +Signed-off-by: Dan Carpenter +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Gustavo Pimentel Signed-off-by: Hou Zhiqiang +Signed-off-by: Jia-Ju Bai +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Lorenzo Pieralisi Signed-off-by: Minghuan Lian +Signed-off-by: Niklas Cassel Signed-off-by: Po Liu +Signed-off-by: Rob Herring +Signed-off-by: Rolf Evers-Fischer +Signed-off-by: Subrahmanya Lingappa +Signed-off-by: Xiaowei Bao Signed-off-by: Zhang Ying-22455 -Signed-off-by: Biwen Li +Signed-off-by: Yangbo Lu --- - .../bindings/pci/layerscape-pci.txt | 14 +- - arch/arm/kernel/bios32.c | 43 ++++++ - arch/arm64/kernel/pci.c | 43 ++++++ - drivers/misc/pci_endpoint_test.c | 20 +-- - drivers/pci/dwc/Kconfig | 8 ++ - drivers/pci/dwc/pci-layerscape.c | 132 +++++++++++++++++- - drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- - drivers/pci/endpoint/pci-epf-core.c | 8 +- - drivers/pci/pcie/portdrv_core.c | 29 ++++ - drivers/pci/quirks.c | 15 ++ + arch/arm/kernel/bios32.c | 43 ++ + arch/arm64/kernel/pci.c | 43 ++ + drivers/misc/pci_endpoint_test.c | 332 ++++++++++--- + drivers/pci/Kconfig | 1 + + drivers/pci/dwc/Kconfig | 39 +- + drivers/pci/dwc/Makefile | 2 +- + drivers/pci/dwc/pci-dra7xx.c | 9 - + drivers/pci/dwc/pci-layerscape-ep.c | 146 ++++++ + drivers/pci/dwc/pci-layerscape.c | 12 + + drivers/pci/dwc/pcie-designware-ep.c | 338 ++++++++++++-- + drivers/pci/dwc/pcie-designware-host.c | 5 +- + drivers/pci/dwc/pcie-designware-plat.c | 159 ++++++- + drivers/pci/dwc/pcie-designware.c | 5 +- + drivers/pci/dwc/pcie-designware.h | 57 ++- + drivers/pci/endpoint/Kconfig | 1 + + drivers/pci/endpoint/Makefile | 1 + + drivers/pci/endpoint/functions/Kconfig | 1 + + drivers/pci/endpoint/functions/Makefile | 1 + + drivers/pci/endpoint/functions/pci-epf-test.c | 191 +++++--- + drivers/pci/endpoint/pci-ep-cfs.c | 95 +++- + drivers/pci/endpoint/pci-epc-core.c | 159 +++++-- + drivers/pci/endpoint/pci-epc-mem.c | 13 +- + drivers/pci/endpoint/pci-epf-core.c | 116 +++-- + drivers/pci/host/pci-host-common.c | 8 - + drivers/pci/host/pcie-xilinx-nwl.c | 9 - + drivers/pci/host/pcie-xilinx.c | 7 - + drivers/pci/mobiveil/Kconfig | 50 ++ + drivers/pci/mobiveil/Makefile | 7 + + drivers/pci/mobiveil/pci-layerscape-gen4-ep.c | 178 +++++++ + drivers/pci/mobiveil/pci-layerscape-gen4.c | 292 ++++++++++++ + drivers/pci/mobiveil/pcie-mobiveil-ep.c | 512 +++++++++++++++++++++ + drivers/pci/mobiveil/pcie-mobiveil-host.c | 640 ++++++++++++++++++++++++++ + drivers/pci/mobiveil/pcie-mobiveil-plat.c | 54 +++ + drivers/pci/mobiveil/pcie-mobiveil.c | 334 ++++++++++++++ + drivers/pci/mobiveil/pcie-mobiveil.h | 296 ++++++++++++ + drivers/pci/pcie/portdrv_core.c | 29 ++ + drivers/pci/quirks.c | 15 + + include/linux/pci-ep-cfs.h | 5 +- + include/linux/pci-epc.h | 73 +-- + include/linux/pci-epf.h | 12 +- include/linux/pci.h | 1 + - 11 files changed, 292 insertions(+), 23 deletions(-) + include/uapi/linux/pcitest.h | 3 + + tools/pci/pcitest.c | 51 +- + tools/pci/pcitest.sh | 15 + + 44 files changed, 3917 insertions(+), 443 deletions(-) + create mode 100644 drivers/pci/dwc/pci-layerscape-ep.c + create mode 100644 drivers/pci/mobiveil/Kconfig + create mode 100644 drivers/pci/mobiveil/Makefile + create mode 100644 drivers/pci/mobiveil/pci-layerscape-gen4-ep.c + create mode 100644 drivers/pci/mobiveil/pci-layerscape-gen4.c + create mode 100644 drivers/pci/mobiveil/pcie-mobiveil-ep.c + create mode 100644 drivers/pci/mobiveil/pcie-mobiveil-host.c + create mode 100644 drivers/pci/mobiveil/pcie-mobiveil-plat.c + create mode 100644 drivers/pci/mobiveil/pcie-mobiveil.c + create mode 100644 drivers/pci/mobiveil/pcie-mobiveil.h ---- a/Documentation/devicetree/bindings/pci/layerscape-pci.txt -+++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt -@@ -18,11 +18,16 @@ Required properties: - "fsl,ls2088a-pcie" - "fsl,ls1088a-pcie" - "fsl,ls1046a-pcie" -+ "fsl,ls1012a-pcie" - - reg: base addresses and lengths of the PCIe controller register blocks. - - interrupts: A list of interrupt outputs of the controller. Must contain an - entry for each entry in the interrupt-names property. --- interrupt-names: Must include the following entries: -- "intr": The interrupt that is asserted for controller interrupts -+- interrupt-names: It could include the following entries: -+ "aer": Asserted for aer interrupt when chip support the aer interrupt with -+ none MSI/MSI-X/INTx mode,but there is interrupt line for aer. -+ "pme": Asserted for pme interrupt when chip support the pme interrupt with -+ none MSI/MSI-X/INTx mode,but there is interrupt line for pme. -+ ...... - - fsl,pcie-scfg: Must include two entries. - The first entry must be a link to the SCFG device node - The second entry must be '0' or '1' based on physical PCIe controller index. -@@ -38,8 +43,9 @@ Example: - reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */ - 0x40 0x00000000 0x0 0x00002000>; /* configuration space */ - reg-names = "regs", "config"; -- interrupts = ; /* controller interrupt */ -- interrupt-names = "intr"; -+ interrupts = , /* aer interrupt */ -+ ; /* pme interrupt */ -+ interrupt-names = "aer", "pme"; - fsl,pcie-scfg = <&scfg 0>; - #address-cells = <3>; - #size-cells = <2>; --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -12,6 +12,8 @@ @@ -178,80 +204,551 @@ Signed-off-by: Biwen Li int raw_pci_read(unsigned int domain, unsigned int bus, --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c -@@ -97,6 +97,8 @@ struct pci_endpoint_test { - struct miscdevice miscdev; +@@ -35,38 +35,45 @@ + + #include + +-#define DRV_MODULE_NAME "pci-endpoint-test" ++#define DRV_MODULE_NAME "pci-endpoint-test" + +-#define PCI_ENDPOINT_TEST_MAGIC 0x0 ++#define IRQ_TYPE_UNDEFINED -1 ++#define IRQ_TYPE_LEGACY 0 ++#define IRQ_TYPE_MSI 1 ++#define IRQ_TYPE_MSIX 2 ++ ++#define PCI_ENDPOINT_TEST_MAGIC 0x0 ++ ++#define PCI_ENDPOINT_TEST_COMMAND 0x4 ++#define COMMAND_RAISE_LEGACY_IRQ BIT(0) ++#define COMMAND_RAISE_MSI_IRQ BIT(1) ++#define COMMAND_RAISE_MSIX_IRQ BIT(2) ++#define COMMAND_READ BIT(3) ++#define COMMAND_WRITE BIT(4) ++#define COMMAND_COPY BIT(5) ++ ++#define PCI_ENDPOINT_TEST_STATUS 0x8 ++#define STATUS_READ_SUCCESS BIT(0) ++#define STATUS_READ_FAIL BIT(1) ++#define STATUS_WRITE_SUCCESS BIT(2) ++#define STATUS_WRITE_FAIL BIT(3) ++#define STATUS_COPY_SUCCESS BIT(4) ++#define STATUS_COPY_FAIL BIT(5) ++#define STATUS_IRQ_RAISED BIT(6) ++#define STATUS_SRC_ADDR_INVALID BIT(7) ++#define STATUS_DST_ADDR_INVALID BIT(8) + +-#define PCI_ENDPOINT_TEST_COMMAND 0x4 +-#define COMMAND_RAISE_LEGACY_IRQ BIT(0) +-#define COMMAND_RAISE_MSI_IRQ BIT(1) +-#define MSI_NUMBER_SHIFT 2 +-/* 6 bits for MSI number */ +-#define COMMAND_READ BIT(8) +-#define COMMAND_WRITE BIT(9) +-#define COMMAND_COPY BIT(10) +- +-#define PCI_ENDPOINT_TEST_STATUS 0x8 +-#define STATUS_READ_SUCCESS BIT(0) +-#define STATUS_READ_FAIL BIT(1) +-#define STATUS_WRITE_SUCCESS BIT(2) +-#define STATUS_WRITE_FAIL BIT(3) +-#define STATUS_COPY_SUCCESS BIT(4) +-#define STATUS_COPY_FAIL BIT(5) +-#define STATUS_IRQ_RAISED BIT(6) +-#define STATUS_SRC_ADDR_INVALID BIT(7) +-#define STATUS_DST_ADDR_INVALID BIT(8) +- +-#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0xc ++#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c + #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 + + #define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14 + #define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18 + +-#define PCI_ENDPOINT_TEST_SIZE 0x1c +-#define PCI_ENDPOINT_TEST_CHECKSUM 0x20 ++#define PCI_ENDPOINT_TEST_SIZE 0x1c ++#define PCI_ENDPOINT_TEST_CHECKSUM 0x20 ++ ++#define PCI_ENDPOINT_TEST_IRQ_TYPE 0x24 ++#define PCI_ENDPOINT_TEST_IRQ_NUMBER 0x28 + + static DEFINE_IDA(pci_endpoint_test_ida); + +@@ -77,6 +84,10 @@ static bool no_msi; + module_param(no_msi, bool, 0444); + MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test"); + ++static int irq_type = IRQ_TYPE_MSI; ++module_param(irq_type, int, 0444); ++MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)"); ++ + enum pci_barno { + BAR_0, + BAR_1, +@@ -103,7 +114,7 @@ struct pci_endpoint_test { + struct pci_endpoint_test_data { enum pci_barno test_reg_bar; size_t alignment; -+ char name[20]; -+ int irq_num; +- bool no_msi; ++ int irq_type; }; - struct pci_endpoint_test_data { -@@ -454,9 +456,7 @@ static int pci_endpoint_test_probe(struc + static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, +@@ -147,6 +158,100 @@ static irqreturn_t pci_endpoint_test_irq + return IRQ_HANDLED; + } + ++static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test) ++{ ++ struct pci_dev *pdev = test->pdev; ++ ++ pci_free_irq_vectors(pdev); ++} ++ ++static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, ++ int type) ++{ ++ int irq = -1; ++ struct pci_dev *pdev = test->pdev; ++ struct device *dev = &pdev->dev; ++ bool res = true; ++ ++ switch (type) { ++ case IRQ_TYPE_LEGACY: ++ irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); ++ if (irq < 0) ++ dev_err(dev, "Failed to get Legacy interrupt\n"); ++ break; ++ case IRQ_TYPE_MSI: ++ irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); ++ if (irq < 0) ++ dev_err(dev, "Failed to get MSI interrupts\n"); ++ break; ++ case IRQ_TYPE_MSIX: ++ irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX); ++ if (irq < 0) ++ dev_err(dev, "Failed to get MSI-X interrupts\n"); ++ break; ++ default: ++ dev_err(dev, "Invalid IRQ type selected\n"); ++ } ++ ++ if (irq < 0) { ++ irq = 0; ++ res = false; ++ } ++ test->num_irqs = irq; ++ ++ return res; ++} ++ ++static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test) ++{ ++ int i; ++ struct pci_dev *pdev = test->pdev; ++ struct device *dev = &pdev->dev; ++ ++ for (i = 0; i < test->num_irqs; i++) ++ devm_free_irq(dev, pci_irq_vector(pdev, i), test); ++ ++ test->num_irqs = 0; ++} ++ ++static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test) ++{ ++ int i; ++ int err; ++ struct pci_dev *pdev = test->pdev; ++ struct device *dev = &pdev->dev; ++ ++ for (i = 0; i < test->num_irqs; i++) { ++ err = devm_request_irq(dev, pci_irq_vector(pdev, i), ++ pci_endpoint_test_irqhandler, ++ IRQF_SHARED, DRV_MODULE_NAME, test); ++ if (err) ++ goto fail; ++ } ++ ++ return true; ++ ++fail: ++ switch (irq_type) { ++ case IRQ_TYPE_LEGACY: ++ dev_err(dev, "Failed to request IRQ %d for Legacy\n", ++ pci_irq_vector(pdev, i)); ++ break; ++ case IRQ_TYPE_MSI: ++ dev_err(dev, "Failed to request IRQ %d for MSI %d\n", ++ pci_irq_vector(pdev, i), ++ i + 1); ++ break; ++ case IRQ_TYPE_MSIX: ++ dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n", ++ pci_irq_vector(pdev, i), ++ i + 1); ++ break; ++ } ++ ++ return false; ++} ++ + static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, + enum pci_barno barno) + { +@@ -179,6 +284,9 @@ static bool pci_endpoint_test_legacy_irq + { + u32 val; + ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, ++ IRQ_TYPE_LEGACY); ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + COMMAND_RAISE_LEGACY_IRQ); + val = wait_for_completion_timeout(&test->irq_raised, +@@ -190,20 +298,24 @@ static bool pci_endpoint_test_legacy_irq + } + + static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, +- u8 msi_num) ++ u16 msi_num, bool msix) + { + u32 val; + struct pci_dev *pdev = test->pdev; + ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, ++ msix == false ? IRQ_TYPE_MSI : ++ IRQ_TYPE_MSIX); ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, +- msi_num << MSI_NUMBER_SHIFT | +- COMMAND_RAISE_MSI_IRQ); ++ msix == false ? COMMAND_RAISE_MSI_IRQ : ++ COMMAND_RAISE_MSIX_IRQ); + val = wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000)); + if (!val) + return false; + +- if (test->last_irq - pdev->irq == msi_num - 1) ++ if (pci_irq_vector(pdev, msi_num - 1) == test->last_irq) + return true; + + return false; +@@ -230,10 +342,18 @@ static bool pci_endpoint_test_copy(struc + if (size > SIZE_MAX - alignment) + goto err; + ++ if (size > SIZE_MAX - alignment) ++ goto err; ++ ++ if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { ++ dev_err(dev, "Invalid IRQ type option\n"); ++ goto err; ++ } ++ + orig_src_addr = dma_alloc_coherent(dev, size + alignment, + &orig_src_phys_addr, GFP_KERNEL); + if (!orig_src_addr) { +- dev_err(dev, "failed to allocate source buffer\n"); ++ dev_err(dev, "Failed to allocate source buffer\n"); + ret = false; + goto err; + } +@@ -259,7 +379,7 @@ static bool pci_endpoint_test_copy(struc + orig_dst_addr = dma_alloc_coherent(dev, size + alignment, + &orig_dst_phys_addr, GFP_KERNEL); + if (!orig_dst_addr) { +- dev_err(dev, "failed to allocate destination address\n"); ++ dev_err(dev, "Failed to allocate destination address\n"); + ret = false; + goto err_orig_src_addr; + } +@@ -281,8 +401,10 @@ static bool pci_endpoint_test_copy(struc + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, + size); + ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, +- 1 << MSI_NUMBER_SHIFT | COMMAND_COPY); ++ COMMAND_COPY); + + wait_for_completion(&test->irq_raised); + +@@ -318,10 +440,18 @@ static bool pci_endpoint_test_write(stru + if (size > SIZE_MAX - alignment) + goto err; + ++ if (size > SIZE_MAX - alignment) ++ goto err; ++ ++ if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { ++ dev_err(dev, "Invalid IRQ type option\n"); ++ goto err; ++ } ++ + orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, + GFP_KERNEL); + if (!orig_addr) { +- dev_err(dev, "failed to allocate address\n"); ++ dev_err(dev, "Failed to allocate address\n"); + ret = false; + goto err; + } +@@ -348,8 +478,10 @@ static bool pci_endpoint_test_write(stru + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); + ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, +- 1 << MSI_NUMBER_SHIFT | COMMAND_READ); ++ COMMAND_READ); + + wait_for_completion(&test->irq_raised); + +@@ -379,10 +511,18 @@ static bool pci_endpoint_test_read(struc + if (size > SIZE_MAX - alignment) + goto err; + ++ if (size > SIZE_MAX - alignment) ++ goto err; ++ ++ if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { ++ dev_err(dev, "Invalid IRQ type option\n"); ++ goto err; ++ } ++ + orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, + GFP_KERNEL); + if (!orig_addr) { +- dev_err(dev, "failed to allocate destination address\n"); ++ dev_err(dev, "Failed to allocate destination address\n"); + ret = false; + goto err; + } +@@ -403,8 +543,10 @@ static bool pci_endpoint_test_read(struc + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); + ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); ++ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, +- 1 << MSI_NUMBER_SHIFT | COMMAND_WRITE); ++ COMMAND_WRITE); + + wait_for_completion(&test->irq_raised); + +@@ -417,6 +559,38 @@ err: + return ret; + } + ++static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test, ++ int req_irq_type) ++{ ++ struct pci_dev *pdev = test->pdev; ++ struct device *dev = &pdev->dev; ++ ++ if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) { ++ dev_err(dev, "Invalid IRQ type option\n"); ++ return false; ++ } ++ ++ if (irq_type == req_irq_type) ++ return true; ++ ++ pci_endpoint_test_release_irq(test); ++ pci_endpoint_test_free_irq_vectors(test); ++ ++ if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type)) ++ goto err; ++ ++ if (!pci_endpoint_test_request_irq(test)) ++ goto err; ++ ++ irq_type = req_irq_type; ++ return true; ++ ++err: ++ pci_endpoint_test_free_irq_vectors(test); ++ irq_type = IRQ_TYPE_UNDEFINED; ++ return false; ++} ++ + static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) + { +@@ -436,7 +610,8 @@ static long pci_endpoint_test_ioctl(stru + ret = pci_endpoint_test_legacy_irq(test); + break; + case PCITEST_MSI: +- ret = pci_endpoint_test_msi_irq(test, arg); ++ case PCITEST_MSIX: ++ ret = pci_endpoint_test_msi_irq(test, arg, cmd == PCITEST_MSIX); + break; + case PCITEST_WRITE: + ret = pci_endpoint_test_write(test, arg); +@@ -447,6 +622,12 @@ static long pci_endpoint_test_ioctl(stru + case PCITEST_COPY: + ret = pci_endpoint_test_copy(test, arg); + break; ++ case PCITEST_SET_IRQTYPE: ++ ret = pci_endpoint_test_set_irq(test, arg); ++ break; ++ case PCITEST_GET_IRQTYPE: ++ ret = irq_type; ++ break; + } + + ret: +@@ -462,9 +643,7 @@ static const struct file_operations pci_ + static int pci_endpoint_test_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { - int i; +- int i; int err; - int irq = 0; int id; -- char name[20]; + char name[20]; enum pci_barno bar; - void __iomem *base; - struct device *dev = &pdev->dev; -@@ -501,19 +501,19 @@ static int pci_endpoint_test_probe(struc +@@ -486,12 +665,15 @@ static int pci_endpoint_test_probe(struc + test->alignment = 0; + test->pdev = pdev; + ++ if (no_msi) ++ irq_type = IRQ_TYPE_LEGACY; ++ + data = (struct pci_endpoint_test_data *)ent->driver_data; + if (data) { + test_reg_bar = data->test_reg_bar; + test->test_reg_bar = test_reg_bar; + test->alignment = data->alignment; +- no_msi = data->no_msi; ++ irq_type = data->irq_type; + } + + init_completion(&test->irq_raised); +@@ -511,36 +693,21 @@ static int pci_endpoint_test_probe(struc + pci_set_master(pdev); - if (!no_msi) { +- if (!no_msi) { - irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); - if (irq < 0) -+ test->irq_num = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); -+ if (test->irq_num < 0) - dev_err(dev, "failed to get MSI interrupts\n"); - } +- dev_err(dev, "failed to get MSI interrupts\n"); +- test->num_irqs = irq; +- } ++ if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) ++ goto err_disable_irq; - err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler, -+ err = request_irq(pdev->irq, pci_endpoint_test_irqhandler, - IRQF_SHARED, DRV_MODULE_NAME, test); - if (err) { - dev_err(dev, "failed to request IRQ %d\n", pdev->irq); - goto err_disable_msi; +- IRQF_SHARED, DRV_MODULE_NAME, test); +- if (err) { +- dev_err(dev, "failed to request IRQ %d\n", pdev->irq); +- goto err_disable_msi; +- } +- +- for (i = 1; i < irq; i++) { +- err = devm_request_irq(dev, pdev->irq + i, +- pci_endpoint_test_irqhandler, +- IRQF_SHARED, DRV_MODULE_NAME, test); +- if (err) +- dev_err(dev, "failed to request IRQ %d for MSI %d\n", +- pdev->irq + i, i + 1); +- } ++ if (!pci_endpoint_test_request_irq(test)) ++ goto err_disable_irq; + + for (bar = BAR_0; bar <= BAR_5; bar++) { +- base = pci_ioremap_bar(pdev, bar); +- if (!base) { +- dev_err(dev, "failed to read BAR%d\n", bar); +- WARN_ON(bar == test_reg_bar); ++ if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { ++ base = pci_ioremap_bar(pdev, bar); ++ if (!base) { ++ dev_err(dev, "Failed to read BAR%d\n", bar); ++ WARN_ON(bar == test_reg_bar); ++ } ++ test->bar[bar] = base; + } +- test->bar[bar] = base; } -- for (i = 1; i < irq; i++) { -+ for (i = 1; i < test->irq_num; i++) { - err = devm_request_irq(dev, pdev->irq + i, - pci_endpoint_test_irqhandler, - IRQF_SHARED, DRV_MODULE_NAME, test); -@@ -548,10 +548,10 @@ static int pci_endpoint_test_probe(struc + test->base = test->bar[test_reg_bar]; +@@ -556,24 +723,31 @@ static int pci_endpoint_test_probe(struc + id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + err = id; +- dev_err(dev, "unable to get id\n"); ++ dev_err(dev, "Unable to get id\n"); goto err_iounmap; } -- snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id); -+ snprintf(test->name, sizeof(test->name), DRV_MODULE_NAME ".%d", id); + snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id); misc_device = &test->miscdev; misc_device->minor = MISC_DYNAMIC_MINOR; - misc_device->name = name; -+ misc_device->name = test->name; ++ misc_device->name = kstrdup(name, GFP_KERNEL); ++ if (!misc_device->name) { ++ err = -ENOMEM; ++ goto err_ida_remove; ++ } misc_device->fops = &pci_endpoint_test_fops, err = misc_register(misc_device); -@@ -584,6 +584,7 @@ err_disable_pdev: - static void pci_endpoint_test_remove(struct pci_dev *pdev) - { - int id; -+ int i; - enum pci_barno bar; - struct pci_endpoint_test *test = pci_get_drvdata(pdev); - struct miscdevice *misc_device = &test->miscdev; -@@ -599,6 +600,8 @@ static void pci_endpoint_test_remove(str - if (test->bar[bar]) + if (err) { +- dev_err(dev, "failed to register device\n"); +- goto err_ida_remove; ++ dev_err(dev, "Failed to register device\n"); ++ goto err_kfree_name; + } + + return 0; + ++err_kfree_name: ++ kfree(misc_device->name); ++ + err_ida_remove: + ida_simple_remove(&pci_endpoint_test_ida, id); + +@@ -583,11 +757,13 @@ err_iounmap: pci_iounmap(pdev, test->bar[bar]); } -+ for (i = 0; i < test->irq_num; i++) -+ free_irq(pdev->irq + i, test); + +- for (i = 0; i < irq; i++) +- devm_free_irq(dev, pdev->irq + i, test); ++ pci_endpoint_test_release_irq(test); + + err_disable_msi: pci_disable_msi(pdev); ++ ++err_disable_irq: ++ pci_endpoint_test_free_irq_vectors(test); + pci_release_regions(pdev); + + err_disable_pdev: +@@ -610,14 +786,15 @@ static void pci_endpoint_test_remove(str + return; + + misc_deregister(&test->miscdev); ++ kfree(misc_device->name); + ida_simple_remove(&pci_endpoint_test_ida, id); + for (bar = BAR_0; bar <= BAR_5; bar++) { + if (test->bar[bar]) + pci_iounmap(pdev, test->bar[bar]); + } +- for (i = 0; i < test->num_irqs; i++) +- devm_free_irq(&pdev->dev, pdev->irq + i, test); +- pci_disable_msi(pdev); ++ ++ pci_endpoint_test_release_irq(test); ++ pci_endpoint_test_free_irq_vectors(test); pci_release_regions(pdev); pci_disable_device(pdev); -@@ -607,6 +610,7 @@ static void pci_endpoint_test_remove(str + } +@@ -625,6 +802,7 @@ static void pci_endpoint_test_remove(str static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) }, { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) }, @@ -259,63 +756,254 @@ Signed-off-by: Biwen Li { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -142,6 +142,7 @@ config PCI_HYPERV + + source "drivers/pci/hotplug/Kconfig" + source "drivers/pci/dwc/Kconfig" ++source "drivers/pci/mobiveil/Kconfig" + source "drivers/pci/host/Kconfig" + source "drivers/pci/endpoint/Kconfig" + source "drivers/pci/switch/Kconfig" --- a/drivers/pci/dwc/Kconfig +++ b/drivers/pci/dwc/Kconfig -@@ -111,6 +111,14 @@ config PCI_LAYERSCAPE - help - Say Y here if you want PCIe controller support on Layerscape SoCs. +@@ -50,17 +50,36 @@ config PCI_DRA7XX_EP + endif + + config PCIE_DW_PLAT +- bool "Platform bus based DesignWare PCIe Controller" +- depends on PCI +- depends on PCI_MSI_IRQ_DOMAIN +- select PCIE_DW_HOST +- ---help--- +- This selects the DesignWare PCIe controller support. Select this if +- you have a PCIe controller on Platform bus. ++ bool + +- If you have a controller with this interface, say Y or M here. ++config PCIE_DW_PLAT_HOST ++ bool "Platform bus based DesignWare PCIe Controller - Host mode" ++ depends on PCI && PCI_MSI_IRQ_DOMAIN ++ select PCIE_DW_HOST ++ select PCIE_DW_PLAT ++ help ++ Enables support for the PCIe controller in the Designware IP to ++ work in host mode. There are two instances of PCIe controller in ++ Designware IP. ++ This controller can work either as EP or RC. In order to enable ++ host-specific features PCIE_DW_PLAT_HOST must be selected and in ++ order to enable device-specific features PCI_DW_PLAT_EP must be ++ selected. -+config PCI_LAYERSCAPE_EP -+ bool "PCI layerscape Endpoint Mode" +- If unsure, say N. ++config PCIE_DW_PLAT_EP ++ bool "Platform bus based DesignWare PCIe Controller - Endpoint mode" ++ depends on PCI && PCI_MSI_IRQ_DOMAIN + depends on PCI_ENDPOINT + select PCIE_DW_EP ++ select PCIE_DW_PLAT + help -+ Enables support for the PCIe controller in the layerscape SoC to work in -+ endpoint mode. ++ Enables support for the PCIe controller in the Designware IP to ++ work in endpoint mode. There are two instances of PCIe controller ++ in Designware IP. ++ This controller can work either as EP or RC. In order to enable ++ host-specific features PCIE_DW_PLAT_HOST must be selected and in ++ order to enable device-specific features PCI_DW_PLAT_EP must be ++ selected. + + config PCI_EXYNOS + bool "Samsung Exynos PCIe controller" +--- a/drivers/pci/dwc/Makefile ++++ b/drivers/pci/dwc/Makefile +@@ -10,7 +10,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o + obj-$(CONFIG_PCI_IMX6) += pci-imx6.o + obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o + obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o +-obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o ++obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o + obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o + obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o + obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o +--- a/drivers/pci/dwc/pci-dra7xx.c ++++ b/drivers/pci/dwc/pci-dra7xx.c +@@ -339,15 +339,6 @@ static irqreturn_t dra7xx_pcie_irq_handl + return IRQ_HANDLED; + } + +-static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +-{ +- u32 reg; +- +- reg = PCI_BASE_ADDRESS_0 + (4 * bar); +- dw_pcie_writel_dbi2(pci, reg, 0x0); +- dw_pcie_writel_dbi(pci, reg, 0x0); +-} +- + static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) + { + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +--- /dev/null ++++ b/drivers/pci/dwc/pci-layerscape-ep.c +@@ -0,0 +1,146 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PCIe controller EP driver for Freescale Layerscape SoCs ++ * ++ * Copyright (C) 2018 NXP Semiconductor. ++ * ++ * Author: Xiaowei Bao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pcie-designware.h" ++ ++#define PCIE_DBI2_OFFSET 0x1000 /* DBI2 base address*/ ++ ++struct ls_pcie_ep { ++ struct dw_pcie *pci; ++}; ++ ++#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev) ++ ++static int ls_pcie_establish_link(struct dw_pcie *pci) ++{ ++ return 0; ++} ++ ++static const struct dw_pcie_ops ls_pcie_ep_ops = { ++ .start_link = ls_pcie_establish_link, ++}; ++ ++static const struct of_device_id ls_pcie_ep_of_match[] = { ++ { .compatible = "fsl,ls-pcie-ep",}, ++ { }, ++}; ++ ++static void ls_pcie_ep_init(struct dw_pcie_ep *ep) ++{ ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ struct pci_epc *epc = ep->epc; ++ enum pci_barno bar; ++ ++ for (bar = BAR_0; bar <= BAR_5; bar++) ++ dw_pcie_ep_reset_bar(pci, bar); ++ ++ epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER; ++} ++ ++static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, ++ enum pci_epc_irq_type type, u16 interrupt_num) ++{ ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ ++ switch (type) { ++ case PCI_EPC_IRQ_LEGACY: ++ return dw_pcie_ep_raise_legacy_irq(ep, func_no); ++ case PCI_EPC_IRQ_MSI: ++ return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); ++ case PCI_EPC_IRQ_MSIX: ++ return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); ++ default: ++ dev_err(pci->dev, "UNKNOWN IRQ type\n"); ++ return -EINVAL; ++ } ++} ++ ++static struct dw_pcie_ep_ops pcie_ep_ops = { ++ .ep_init = ls_pcie_ep_init, ++ .raise_irq = ls_pcie_ep_raise_irq, ++}; ++ ++static int __init ls_add_pcie_ep(struct ls_pcie_ep *pcie, ++ struct platform_device *pdev) ++{ ++ struct dw_pcie *pci = pcie->pci; ++ struct device *dev = pci->dev; ++ struct dw_pcie_ep *ep; ++ struct resource *res; ++ int ret; ++ ++ ep = &pci->ep; ++ ep->ops = &pcie_ep_ops; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); ++ if (!res) ++ return -EINVAL; ++ ++ ep->phys_base = res->start; ++ ep->addr_size = resource_size(res); ++ ++ ret = dw_pcie_ep_init(ep); ++ if (ret) { ++ dev_err(dev, "failed to initialize endpoint\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int __init ls_pcie_ep_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct dw_pcie *pci; ++ struct ls_pcie_ep *pcie; ++ struct resource *dbi_base; ++ int ret; ++ ++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); ++ if (!pcie) ++ return -ENOMEM; ++ ++ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); ++ if (!pci) ++ return -ENOMEM; ++ ++ dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); ++ pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); ++ if (IS_ERR(pci->dbi_base)) ++ return PTR_ERR(pci->dbi_base); + - config PCI_HISI - depends on OF && ARM64 - bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers" ++ pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_OFFSET; ++ pci->dev = dev; ++ pci->ops = &ls_pcie_ep_ops; ++ pcie->pci = pci; ++ ++ platform_set_drvdata(pdev, pcie); ++ ++ ret = ls_add_pcie_ep(pcie, pdev); ++ ++ return ret; ++} ++ ++static struct platform_driver ls_pcie_ep_driver = { ++ .driver = { ++ .name = "layerscape-pcie-ep", ++ .of_match_table = ls_pcie_ep_of_match, ++ .suppress_bind_attrs = true, ++ }, ++}; ++builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe); --- a/drivers/pci/dwc/pci-layerscape.c +++ b/drivers/pci/dwc/pci-layerscape.c -@@ -33,8 +33,15 @@ +@@ -33,6 +33,8 @@ /* PEX Internal Configuration Registers */ #define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ +#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ +#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ -+#define PCIE_DBI2_BASE 0x1000 /* DBI2 base address*/ -+#define PCIE_MSI_MSG_DATA_OFF 0x5c /* MSI Data register address*/ -+#define PCIE_MSI_OB_SIZE 4096 -+#define PCIE_MSI_ADDR_OFFSET (1024 * 1024) #define PCIE_IATU_NUM 6 -+#define PCIE_EP_ADDR_SPACE_SIZE 0x100000000 - - struct ls_pcie_drvdata { - u32 lut_offset; -@@ -44,12 +51,20 @@ struct ls_pcie_drvdata { - const struct dw_pcie_ops *dw_pcie_ops; - }; - -+struct ls_pcie_ep { -+ dma_addr_t msi_phys_addr; -+ void __iomem *msi_virt_addr; -+ u64 msi_msg_addr; -+ u16 msi_msg_data; -+}; -+ - struct ls_pcie { - struct dw_pcie *pci; - void __iomem *lut; - struct regmap *scfg; - const struct ls_pcie_drvdata *drvdata; - int index; -+ struct ls_pcie_ep *pcie_ep; - }; - #define to_ls_pcie(x) dev_get_drvdata((x)->dev) -@@ -124,6 +139,14 @@ static int ls_pcie_link_up(struct dw_pci +@@ -124,6 +126,14 @@ static int ls_pcie_link_up(struct dw_pci return 1; } @@ -330,7 +1018,7 @@ Signed-off-by: Biwen Li static int ls_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); -@@ -135,6 +158,7 @@ static int ls_pcie_host_init(struct pcie +@@ -135,6 +145,7 @@ static int ls_pcie_host_init(struct pcie * dw_pcie_setup_rc() will reconfigure the outbound windows. */ ls_pcie_disable_outbound_atus(pcie); @@ -338,7 +1026,7 @@ Signed-off-by: Biwen Li dw_pcie_dbi_ro_wr_en(pci); ls_pcie_clear_multifunction(pcie); -@@ -253,6 +277,7 @@ static struct ls_pcie_drvdata ls2088_drv +@@ -253,6 +264,7 @@ static struct ls_pcie_drvdata ls2088_drv }; static const struct of_device_id ls_pcie_of_match[] = { @@ -346,167 +1034,4525 @@ Signed-off-by: Biwen Li { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata }, -@@ -263,6 +288,99 @@ static const struct of_device_id ls_pcie - { }, - }; +--- a/drivers/pci/dwc/pcie-designware-ep.c ++++ b/drivers/pci/dwc/pcie-designware-ep.c +@@ -1,20 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0 + /** + * Synopsys DesignWare PCIe Endpoint controller driver + * + * Copyright (C) 2017 Texas Instruments + * Author: Kishon Vijay Abraham I +- * +- * 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 . + */ + + #include +@@ -30,7 +19,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep + pci_epc_linkup(epc); + } + +-static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) ++static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, ++ int flags) + { + u32 reg; + +@@ -38,10 +28,52 @@ static void dw_pcie_ep_reset_bar(struct + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi2(pci, reg, 0x0); + dw_pcie_writel_dbi(pci, reg, 0x0); ++ if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { ++ dw_pcie_writel_dbi2(pci, reg + 4, 0x0); ++ dw_pcie_writel_dbi(pci, reg + 4, 0x0); ++ } + dw_pcie_dbi_ro_wr_dis(pci); + } -+static void ls_pcie_raise_msi_irq(struct ls_pcie_ep *pcie_ep) +-static int dw_pcie_ep_write_header(struct pci_epc *epc, ++void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ -+ iowrite32(pcie_ep->msi_msg_data, pcie_ep->msi_virt_addr); ++ __dw_pcie_ep_reset_bar(pci, bar, 0); +} + -+static int ls_pcie_raise_irq(struct dw_pcie_ep *ep, -+ enum pci_epc_irq_type type, u8 interrupt_num) ++static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, ++ u8 cap) +{ -+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); -+ struct ls_pcie *pcie = to_ls_pcie(pci); -+ struct ls_pcie_ep *pcie_ep = pcie->pcie_ep; -+ u32 free_win; -+ -+ /* get the msi message address and msi message data */ -+ pcie_ep->msi_msg_addr = ioread32(pci->dbi_base + MSI_MESSAGE_ADDR_L32) | -+ (((u64)ioread32(pci->dbi_base + MSI_MESSAGE_ADDR_U32)) << 32); -+ pcie_ep->msi_msg_data = ioread16(pci->dbi_base + PCIE_MSI_MSG_DATA_OFF); -+ -+ /* request and config the outband window for msi */ -+ free_win = find_first_zero_bit(&ep->ob_window_map, -+ sizeof(ep->ob_window_map)); -+ if (free_win >= ep->num_ob_windows) { -+ dev_err(pci->dev, "no free outbound window\n"); -+ return -ENOMEM; -+ } -+ -+ dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM, -+ pcie_ep->msi_phys_addr, -+ pcie_ep->msi_msg_addr, -+ PCIE_MSI_OB_SIZE); ++ u8 cap_id, next_cap_ptr; ++ u16 reg; + -+ set_bit(free_win, &ep->ob_window_map); ++ reg = dw_pcie_readw_dbi(pci, cap_ptr); ++ next_cap_ptr = (reg & 0xff00) >> 8; ++ cap_id = (reg & 0x00ff); + -+ /* generate the msi interrupt */ -+ ls_pcie_raise_msi_irq(pcie_ep); ++ if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX) ++ return 0; + -+ /* release the outband window of msi */ -+ dw_pcie_disable_atu(pci, free_win, DW_PCIE_REGION_OUTBOUND); -+ clear_bit(free_win, &ep->ob_window_map); ++ if (cap_id == cap) ++ return cap_ptr; + -+ return 0; ++ return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); +} + -+static struct dw_pcie_ep_ops pcie_ep_ops = { -+ .raise_irq = ls_pcie_raise_irq, -+}; -+ -+static int __init ls_add_pcie_ep(struct ls_pcie *pcie, -+ struct platform_device *pdev) ++static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap) +{ -+ struct dw_pcie *pci = pcie->pci; -+ struct device *dev = pci->dev; -+ struct dw_pcie_ep *ep; -+ struct ls_pcie_ep *pcie_ep; -+ struct resource *cfg_res; -+ int ret; ++ u8 next_cap_ptr; ++ u16 reg; + -+ ep = &pci->ep; -+ ep->ops = &pcie_ep_ops; ++ reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); ++ next_cap_ptr = (reg & 0x00ff); + -+ pcie_ep = devm_kzalloc(dev, sizeof(*pcie_ep), GFP_KERNEL); -+ if (!pcie_ep) -+ return -ENOMEM; ++ if (!next_cap_ptr) ++ return 0; + -+ pcie->pcie_ep = pcie_ep; ++ return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); ++} ++ ++static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, + struct pci_epf_header *hdr) + { + struct dw_pcie_ep *ep = epc_get_drvdata(epc); +@@ -114,24 +146,29 @@ static int dw_pcie_ep_outbound_atu(struc + return 0; + } + +-static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar) ++static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_bar *epf_bar) + { + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ enum pci_barno bar = epf_bar->barno; + u32 atu_index = ep->bar_to_atu[bar]; + +- dw_pcie_ep_reset_bar(pci, bar); ++ __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); + + dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); + clear_bit(atu_index, ep->ib_window_map); + } + +-static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar, +- dma_addr_t bar_phys, size_t size, int flags) ++static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_bar *epf_bar) + { + int ret; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ enum pci_barno bar = epf_bar->barno; ++ size_t size = epf_bar->size; ++ int flags = epf_bar->flags; + enum dw_pcie_as_type as_type; + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + +@@ -140,13 +177,20 @@ static int dw_pcie_ep_set_bar(struct pci + else + as_type = DW_PCIE_AS_IO; + +- ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type); ++ ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); + if (ret) + return ret; + + dw_pcie_dbi_ro_wr_en(pci); +- dw_pcie_writel_dbi2(pci, reg, size - 1); ++ ++ dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg, flags); ++ ++ if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { ++ dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); ++ dw_pcie_writel_dbi(pci, reg + 4, 0); ++ } ++ + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +@@ -167,7 +211,8 @@ static int dw_pcie_find_index(struct dw_ + return -EINVAL; + } + +-static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr) ++static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, ++ phys_addr_t addr) + { + int ret; + u32 atu_index; +@@ -182,8 +227,9 @@ static void dw_pcie_ep_unmap_addr(struct + clear_bit(atu_index, ep->ob_window_map); + } + +-static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr, +- u64 pci_addr, size_t size) ++static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, ++ phys_addr_t addr, ++ u64 pci_addr, size_t size) + { + int ret; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); +@@ -198,45 +244,93 @@ static int dw_pcie_ep_map_addr(struct pc + return 0; + } + +-static int dw_pcie_ep_get_msi(struct pci_epc *epc) ++static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) ++{ ++ struct dw_pcie_ep *ep = epc_get_drvdata(epc); ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ u32 val, reg; ++ ++ if (!ep->msi_cap) ++ return -EINVAL; ++ ++ reg = ep->msi_cap + PCI_MSI_FLAGS; ++ val = dw_pcie_readw_dbi(pci, reg); ++ if (!(val & PCI_MSI_FLAGS_ENABLE)) ++ return -EINVAL; ++ ++ val = (val & PCI_MSI_FLAGS_QSIZE) >> 4; ++ ++ return val; ++} ++ ++static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) ++{ ++ struct dw_pcie_ep *ep = epc_get_drvdata(epc); ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ u32 val, reg; ++ ++ if (!ep->msi_cap) ++ return -EINVAL; ++ ++ reg = ep->msi_cap + PCI_MSI_FLAGS; ++ val = dw_pcie_readw_dbi(pci, reg); ++ val &= ~PCI_MSI_FLAGS_QMASK; ++ val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK; ++ dw_pcie_dbi_ro_wr_en(pci); ++ dw_pcie_writew_dbi(pci, reg, val); ++ dw_pcie_dbi_ro_wr_dis(pci); ++ ++ return 0; ++} ++ ++static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no) + { +- int val; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ u32 val, reg; ++ ++ if (!ep->msix_cap) ++ return -EINVAL; + +- val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); +- if (!(val & MSI_CAP_MSI_EN_MASK)) ++ reg = ep->msix_cap + PCI_MSIX_FLAGS; ++ val = dw_pcie_readw_dbi(pci, reg); ++ if (!(val & PCI_MSIX_FLAGS_ENABLE)) + return -EINVAL; + +- val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; ++ val &= PCI_MSIX_FLAGS_QSIZE; ++ + return val; + } + +-static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int) ++static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts) + { +- int val; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ u32 val, reg; + +- val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); +- val &= ~MSI_CAP_MMC_MASK; +- val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; ++ if (!ep->msix_cap) ++ return -EINVAL; ++ ++ reg = ep->msix_cap + PCI_MSIX_FLAGS; ++ val = dw_pcie_readw_dbi(pci, reg); ++ val &= ~PCI_MSIX_FLAGS_QSIZE; ++ val |= interrupts; + dw_pcie_dbi_ro_wr_en(pci); +- dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); ++ dw_pcie_writew_dbi(pci, reg, val); + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; + } + +-static int dw_pcie_ep_raise_irq(struct pci_epc *epc, +- enum pci_epc_irq_type type, u8 interrupt_num) ++static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, ++ enum pci_epc_irq_type type, u16 interrupt_num) + { + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + + if (!ep->ops->raise_irq) + return -EINVAL; + +- return ep->ops->raise_irq(ep, type, interrupt_num); ++ return ep->ops->raise_irq(ep, func_no, type, interrupt_num); + } + + static void dw_pcie_ep_stop(struct pci_epc *epc) +@@ -269,15 +363,130 @@ static const struct pci_epc_ops epc_ops + .unmap_addr = dw_pcie_ep_unmap_addr, + .set_msi = dw_pcie_ep_set_msi, + .get_msi = dw_pcie_ep_get_msi, ++ .set_msix = dw_pcie_ep_set_msix, ++ .get_msix = dw_pcie_ep_get_msix, + .raise_irq = dw_pcie_ep_raise_irq, + .start = dw_pcie_ep_start, + .stop = dw_pcie_ep_stop, + }; + ++int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) ++{ ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ struct device *dev = pci->dev; ++ ++ dev_err(dev, "EP cannot trigger legacy IRQs\n"); ++ ++ return -EINVAL; ++} ++ ++int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, ++ u8 interrupt_num) ++{ ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ struct pci_epc *epc = ep->epc; ++ u16 msg_ctrl, msg_data; ++ u32 msg_addr_lower, msg_addr_upper, reg; ++ u64 msg_addr; ++ bool has_upper; ++ int ret; ++ ++ if (!ep->msi_cap) ++ return -EINVAL; ++ ++ /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ ++ reg = ep->msi_cap + PCI_MSI_FLAGS; ++ msg_ctrl = dw_pcie_readw_dbi(pci, reg); ++ has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); ++ reg = ep->msi_cap + PCI_MSI_ADDRESS_LO; ++ msg_addr_lower = dw_pcie_readl_dbi(pci, reg); ++ if (has_upper) { ++ reg = ep->msi_cap + PCI_MSI_ADDRESS_HI; ++ msg_addr_upper = dw_pcie_readl_dbi(pci, reg); ++ reg = ep->msi_cap + PCI_MSI_DATA_64; ++ msg_data = dw_pcie_readw_dbi(pci, reg); ++ } else { ++ msg_addr_upper = 0; ++ reg = ep->msi_cap + PCI_MSI_DATA_32; ++ msg_data = dw_pcie_readw_dbi(pci, reg); ++ } ++ msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; ++ ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, ++ epc->mem->page_size); ++ if (ret) ++ return ret; ++ ++ writel(msg_data | (interrupt_num - 1), ep->msi_mem); ++ ++ dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); ++ ++ return 0; ++} ++ ++int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, ++ u16 interrupt_num) ++{ ++ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); ++ struct pci_epc *epc = ep->epc; ++ u16 tbl_offset, bir; ++ u32 bar_addr_upper, bar_addr_lower; ++ u32 msg_addr_upper, msg_addr_lower; ++ u32 reg, msg_data, vec_ctrl; ++ u64 tbl_addr, msg_addr, reg_u64; ++ void __iomem *msix_tbl; ++ int ret; ++ ++ reg = ep->msix_cap + PCI_MSIX_TABLE; ++ tbl_offset = dw_pcie_readl_dbi(pci, reg); ++ bir = (tbl_offset & PCI_MSIX_TABLE_BIR); ++ tbl_offset &= PCI_MSIX_TABLE_OFFSET; ++ ++ reg = PCI_BASE_ADDRESS_0 + (4 * bir); ++ bar_addr_upper = 0; ++ bar_addr_lower = dw_pcie_readl_dbi(pci, reg); ++ reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK); ++ if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64) ++ bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4); ++ ++ tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower; ++ tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE)); ++ tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK; ++ ++ msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr, ++ PCI_MSIX_ENTRY_SIZE); ++ if (!msix_tbl) ++ return -EINVAL; ++ ++ msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR); ++ msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR); ++ msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; ++ msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA); ++ vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL); ++ ++ iounmap(msix_tbl); ++ ++ if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) ++ return -EPERM; ++ ++ ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, ++ epc->mem->page_size); ++ if (ret) ++ return ret; ++ ++ writel(msg_data, ep->msi_mem); ++ ++ dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); ++ ++ return 0; ++} ++ + void dw_pcie_ep_exit(struct dw_pcie_ep *ep) + { + struct pci_epc *epc = ep->epc; + ++ pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, ++ epc->mem->page_size); ++ + pci_epc_mem_exit(epc); + } + +@@ -291,7 +500,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *e + struct device_node *np = dev->of_node; + + if (!pci->dbi_base || !pci->dbi_base2) { +- dev_err(dev, "dbi_base/deb_base2 is not populated\n"); ++ dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); + return -EINVAL; + } + +@@ -333,15 +542,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *e + return -ENOMEM; + ep->outbound_addr = addr; + +- if (ep->ops->ep_init) +- ep->ops->ep_init(ep); +- + epc = devm_pci_epc_create(dev, &epc_ops); + if (IS_ERR(epc)) { + dev_err(dev, "failed to create epc device\n"); + return PTR_ERR(epc); + } + ++ ep->epc = epc; ++ epc_set_drvdata(epc, ep); ++ ++ if (ep->ops->ep_init) ++ ep->ops->ep_init(ep); ++ + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); + if (ret < 0) + epc->max_functions = 1; +@@ -353,8 +565,16 @@ int dw_pcie_ep_init(struct dw_pcie_ep *e + return ret; + } + +- ep->epc = epc; +- epc_set_drvdata(epc, ep); ++ ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, ++ epc->mem->page_size); ++ if (!ep->msi_mem) { ++ dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n"); ++ return -ENOMEM; ++ } ++ ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI); ++ ++ ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX); ++ + dw_pcie_setup(pci); + + return 0; +--- a/drivers/pci/dwc/pcie-designware-host.c ++++ b/drivers/pci/dwc/pcie-designware-host.c +@@ -1,3 +1,4 @@ ++// SPDX-License-Identifier: GPL-2.0 + /* + * Synopsys DesignWare PCIe host controller driver + * +@@ -5,10 +6,6 @@ + * http://www.samsung.com + * + * Author: Jingoo Han +- * +- * 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 +--- 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 +- * +- * 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 + #include +@@ -15,19 +12,29 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include + #include ++#include + + #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 +- * +- * 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 +--- 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 +- * +- * 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 +- * +- * 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 . + */ + + #include +@@ -29,13 +18,16 @@ + #include + #include + ++#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 +- * +- * 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 . + */ + + #include ++#include + #include + + #include + #include + #include + ++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 +- * +- * 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 . + */ + + #include +@@ -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 +- * +- * 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 . + */ + + #include +--- 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 +- * +- * 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 . + */ + + #include +@@ -26,6 +15,8 @@ + #include + #include + ++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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#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 ++ * Refactor: Zhiqiang Hou ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ * Refactor: Zhiqiang Hou ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); +--- /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 ++ * Refactor: Zhiqiang Hou ++ */ + -+ cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); -+ if (cfg_res) { -+ ep->phys_base = cfg_res->start; -+ ep->addr_size = PCIE_EP_ADDR_SPACE_SIZE; -+ } else { -+ dev_err(dev, "missing *config* space\n"); -+ return -ENODEV; ++#include ++#include ++#include ++#include ++#include ++ ++#include "pcie-mobiveil.h" ++ ++/* ++ * mobiveil_pcie_sel_page - routine to access paged register ++ * ++ * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged, ++ * for this scheme to work extracted higher 6 bits of the offset will be ++ * written to pg_sel field of PAB_CTRL register and rest of the lower 10 ++ * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register. ++ */ ++static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx) ++{ ++ u32 val; ++ ++ val = readl(pcie->csr_axi_slave_base + PAB_CTRL); ++ val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT); ++ val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT; ++ ++ writel(val, pcie->csr_axi_slave_base + PAB_CTRL); ++} ++ ++static void *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie, u32 off) ++{ ++ if (off < PAGED_ADDR_BNDRY) { ++ /* For directly accessed registers, clear the pg_sel field */ ++ mobiveil_pcie_sel_page(pcie, 0); ++ return pcie->csr_axi_slave_base + off; + } + -+ pcie_ep->msi_phys_addr = ep->phys_base + PCIE_MSI_ADDR_OFFSET; ++ mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off)); ++ return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off); ++} + -+ pcie_ep->msi_virt_addr = ioremap(pcie_ep->msi_phys_addr, -+ PCIE_MSI_OB_SIZE); -+ if (!pcie_ep->msi_virt_addr) { -+ dev_err(dev, "failed to map MSI outbound region\n"); -+ return -ENOMEM; ++static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val) ++{ ++ if ((uintptr_t)addr & (size - 1)) { ++ *val = 0; ++ return PCIBIOS_BAD_REGISTER_NUMBER; + } + -+ ret = dw_pcie_ep_init(ep); -+ if (ret) { -+ dev_err(dev, "failed to initialize endpoint\n"); -+ return ret; ++ switch (size) { ++ case 4: ++ *val = readl(addr); ++ break; ++ case 2: ++ *val = readw(addr); ++ break; ++ case 1: ++ *val = readb(addr); ++ break; ++ default: ++ *val = 0; ++ return PCIBIOS_BAD_REGISTER_NUMBER; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val) ++{ ++ if ((uintptr_t)addr & (size - 1)) ++ return PCIBIOS_BAD_REGISTER_NUMBER; ++ ++ switch (size) { ++ case 4: ++ writel(val, addr); ++ break; ++ case 2: ++ writew(val, addr); ++ break; ++ case 1: ++ writeb(val, addr); ++ break; ++ default: ++ return PCIBIOS_BAD_REGISTER_NUMBER; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++u32 csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) ++{ ++ void *addr; ++ u32 val; ++ int ret; ++ ++ addr = mobiveil_pcie_comp_addr(pcie, off); ++ ++ ret = mobiveil_pcie_read(addr, size, &val); ++ if (ret) ++ dev_err(&pcie->pdev->dev, "read CSR address failed\n"); ++ ++ return val; ++} ++ ++void csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, size_t size) ++{ ++ void *addr; ++ int ret; ++ ++ addr = mobiveil_pcie_comp_addr(pcie, off); ++ ++ ret = mobiveil_pcie_write(addr, size, val); ++ if (ret) ++ dev_err(&pcie->pdev->dev, "write CSR address failed\n"); ++} ++ ++bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) ++{ ++ if (pcie->ops->link_up) ++ return pcie->ops->link_up(pcie); ++ ++ return (csr_readl(pcie, LTSSM_STATUS) & ++ LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; ++} ++ ++void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr, ++ u64 pci_addr, u32 type, u64 size) ++{ ++ u32 value; ++ u64 size64 = ~(size - 1); ++ ++ if (win_num >= pcie->ppio_wins) { ++ dev_err(&pcie->pdev->dev, ++ "ERROR: max inbound windows reached !\n"); ++ return; ++ } ++ ++ value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); ++ value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | ++ WIN_SIZE_MASK << WIN_SIZE_SHIFT); ++ value |= (type << AMAP_CTRL_TYPE_SHIFT) | (1 << AMAP_CTRL_EN_SHIFT) | ++ (lower_32_bits(size64) & WIN_SIZE_MASK << WIN_SIZE_SHIFT); ++ csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); ++ ++ csr_writel(pcie, upper_32_bits(size64), ++ PAB_EXT_PEX_AMAP_SIZEN(win_num)); ++ ++ csr_writel(pcie, lower_32_bits(cpu_addr), ++ PAB_PEX_AMAP_AXI_WIN(win_num)); ++ csr_writel(pcie, upper_32_bits(cpu_addr), ++ PAB_EXT_PEX_AMAP_AXI_WIN(win_num)); ++ ++ csr_writel(pcie, lower_32_bits(pci_addr), ++ PAB_PEX_AMAP_PEX_WIN_L(win_num)); ++ csr_writel(pcie, upper_32_bits(pci_addr), ++ PAB_PEX_AMAP_PEX_WIN_H(win_num)); ++ ++ pcie->ib_wins_configured++; ++} ++ ++/* ++ * routine to program the outbound windows ++ */ ++void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr, ++ u64 pci_addr, u32 type, u64 size) ++{ ++ ++ u32 value; ++ u64 size64 = ~(size - 1); ++ ++ if (win_num >= pcie->apio_wins) { ++ dev_err(&pcie->pdev->dev, ++ "ERROR: max outbound windows reached !\n"); ++ return; + } + ++ /* ++ * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit ++ * to 4 KB in PAB_AXI_AMAP_CTRL register ++ */ ++ value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); ++ value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | ++ WIN_SIZE_MASK << WIN_SIZE_SHIFT); ++ value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | ++ (lower_32_bits(size64) & WIN_SIZE_MASK << WIN_SIZE_SHIFT); ++ csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); ++ ++ csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); ++ ++ /* ++ * program AXI window base with appropriate value in ++ * PAB_AXI_AMAP_AXI_WIN0 register ++ */ ++ csr_writel(pcie, lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), ++ PAB_AXI_AMAP_AXI_WIN(win_num)); ++ csr_writel(pcie, upper_32_bits(cpu_addr), ++ PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); ++ ++ csr_writel(pcie, lower_32_bits(pci_addr), ++ PAB_AXI_AMAP_PEX_WIN_L(win_num)); ++ csr_writel(pcie, upper_32_bits(pci_addr), ++ PAB_AXI_AMAP_PEX_WIN_H(win_num)); ++ ++ pcie->ob_wins_configured++; ++} ++ ++int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type, ++ u64 phys, u64 bus_addr, u8 func, u64 size) ++{ ++ u32 val; ++ u32 size_h, size_l; ++ ++ if (size & (size - 1)) ++ size = 1 << (1 + ilog2(size)); ++ ++ size_h = upper_32_bits(~(size - 1)); ++ size_l = lower_32_bits(~(size - 1)); ++ ++ val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); ++ val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | ++ WIN_SIZE_MASK << WIN_SIZE_SHIFT); ++ val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | ++ (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT)); ++ csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num)); ++ ++ csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num)); ++ csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num)); ++ csr_writel(pcie, upper_32_bits(phys), ++ PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); ++ csr_writel(pcie, lower_32_bits(bus_addr), ++ PAB_AXI_AMAP_PEX_WIN_L(win_num)); ++ csr_writel(pcie, upper_32_bits(bus_addr), ++ PAB_AXI_AMAP_PEX_WIN_H(win_num)); ++ csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num)); ++ + return 0; +} + - static int __init ls_add_pcie_port(struct ls_pcie *pcie) - { - struct dw_pcie *pci = pcie->pci; -@@ -309,18 +427,18 @@ static int __init ls_pcie_probe(struct p - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - -- pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset; -+ pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_BASE; - -- if (!ls_pcie_is_bridge(pcie)) -- return -ENODEV; -+ pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset; - - platform_set_drvdata(pdev, pcie); - -- ret = ls_add_pcie_port(pcie); -- if (ret < 0) -- return ret; -+ if (!ls_pcie_is_bridge(pcie)) -+ ret = ls_add_pcie_ep(pcie, pdev); -+ else -+ ret = ls_add_pcie_port(pcie); - -- return 0; -+ return ret; - } - - static struct platform_driver ls_pcie_driver = { ---- a/drivers/pci/endpoint/functions/pci-epf-test.c -+++ b/drivers/pci/endpoint/functions/pci-epf-test.c -@@ -471,7 +471,7 @@ static int pci_epf_test_probe(struct pci - const struct pci_epf_device_id *match; - struct pci_epf_test_data *data; - enum pci_barno test_reg_bar = BAR_0; -- bool linkup_notifier = true; -+ bool linkup_notifier = false; - - match = pci_epf_match_device(pci_epf_test_ids, epf); - data = (struct pci_epf_test_data *)match->driver_data; ---- a/drivers/pci/endpoint/pci-epf-core.c -+++ b/drivers/pci/endpoint/pci-epf-core.c -@@ -104,8 +104,8 @@ void pci_epf_free_space(struct pci_epf * - if (!addr) - return; - -- dma_free_coherent(dev, epf->bar[bar].size, addr, -- epf->bar[bar].phys_addr); -+ free_pages((unsigned long)addr, -+ get_order(epf->bar[bar].size)); - - epf->bar[bar].phys_addr = 0; - epf->bar[bar].size = 0; -@@ -129,7 +129,9 @@ void *pci_epf_alloc_space(struct pci_epf - size = 128; - size = roundup_pow_of_two(size); - -- space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); -+ space = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, -+ get_order(size)); -+ phys_addr = virt_to_phys(space); - if (!space) { - dev_err(dev, "failed to allocate mem space\n"); - return NULL; ++void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no, ++ int bar, u64 phys) ++{ ++ csr_writel(pcie, upper_32_bits(phys), ++ PAB_EXT_PEX_BAR_AMAP(func_no, bar)); ++ csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN, ++ PAB_PEX_BAR_AMAP(func_no, bar)); ++} ++ ++void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie, ++ u8 func_no, u8 bar) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar)); ++ val &= ~(1 << 0); ++ csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar)); ++} ++ ++int mobiveil_bringup_link(struct mobiveil_pcie *pcie) ++{ ++ int retries; ++ ++ /* check if the link is up or not */ ++ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { ++ if (mobiveil_pcie_link_up(pcie)) ++ return 0; ++ ++ usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); ++ } ++ ++ dev_info(&pcie->pdev->dev, "link never came up\n"); ++ ++ return -ETIMEDOUT; ++} ++ ++void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int win_num) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); ++ val &= ~(1 << AMAP_CTRL_EN_SHIFT); ++ csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num)); ++} ++ ++void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); ++ val &= ~(1 << WIN_ENABLE_SHIFT); ++ csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num)); ++} ++ ++void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_CTRL); ++ val |= 1 << AMBA_PIO_ENABLE_SHIFT; ++ val |= 1 << PEX_PIO_ENABLE_SHIFT; ++ csr_writel(pcie, val, PAB_CTRL); ++} ++ ++void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_AXI_PIO_CTRL); ++ val |= APIO_EN_MASK; ++ csr_writel(pcie, val, PAB_AXI_PIO_CTRL); ++} ++ ++void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_PEX_PIO_CTRL); ++ val |= 1 << PIO_ENABLE_SHIFT; ++ csr_writel(pcie, val, PAB_PEX_PIO_CTRL); ++} ++ ++void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie) ++{ ++ u32 val; ++ ++ val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); ++ val |= 1 << 0; ++ csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB); ++} +--- /dev/null ++++ b/drivers/pci/mobiveil/pcie-mobiveil.h +@@ -0,0 +1,296 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * PCIe host controller driver for Mobiveil PCIe Host controller ++ * ++ * Copyright (c) 2018 Mobiveil Inc. ++ * Author: Subrahmanya Lingappa ++ * Refactor: Zhiqiang Hou ++ */ ++ ++#ifndef _PCIE_MOBIVEIL_H ++#define _PCIE_MOBIVEIL_H ++ ++#include ++#include ++#include ++#include "../pci.h" ++ ++#include ++#include ++ ++#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 @@ -558,9 +5604,9 @@ Signed-off-by: Biwen Li * them, we have to fall back to INTx or other interrupts, e.g., a --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c -@@ -3376,6 +3376,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_A - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset); +@@ -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 @@ -572,9 +5618,9 @@ Signed-off-by: Biwen Li static void quirk_no_pm_reset(struct pci_dev *dev) { /* -@@ -4857,3 +4864,11 @@ static void quirk_no_ats(struct pci_dev - /* AMD Stoney platform GPU */ +@@ -4878,3 +4885,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 */ @@ -584,9 +5630,197 @@ Signed-off-by: Biwen Li + 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 +- * +- * 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 +- * +- * This program is free software: you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 of +- * the License as published by the Free Software Foundation. + */ + + #ifndef __LINUX_PCI_EPC_H +@@ -20,6 +17,7 @@ enum pci_epc_irq_type { + PCI_EPC_IRQ_UNKNOWN, + PCI_EPC_IRQ_LEGACY, + PCI_EPC_IRQ_MSI, ++ PCI_EPC_IRQ_MSIX, + }; + + /** +@@ -33,24 +31,32 @@ enum pci_epc_irq_type { + * capability register + * @get_msi: ops to get the number of MSI interrupts allocated by the RC from + * the MSI capability register +- * @raise_irq: ops to raise a legacy or MSI interrupt ++ * @set_msix: ops to set the requested number of MSI-X interrupts in the ++ * MSI-X capability register ++ * @get_msix: ops to get the number of MSI-X interrupts allocated by the RC ++ * from the MSI-X capability register ++ * @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt + * @start: ops to start the PCI link + * @stop: ops to stop the PCI link + * @owner: the module owner containing the ops + */ + struct pci_epc_ops { +- int (*write_header)(struct pci_epc *pci_epc, ++ int (*write_header)(struct pci_epc *epc, u8 func_no, + struct pci_epf_header *hdr); +- int (*set_bar)(struct pci_epc *epc, enum pci_barno bar, +- dma_addr_t bar_phys, size_t size, int flags); +- void (*clear_bar)(struct pci_epc *epc, enum pci_barno bar); +- int (*map_addr)(struct pci_epc *epc, phys_addr_t addr, +- u64 pci_addr, size_t size); +- void (*unmap_addr)(struct pci_epc *epc, phys_addr_t addr); +- int (*set_msi)(struct pci_epc *epc, u8 interrupts); +- int (*get_msi)(struct pci_epc *epc); +- int (*raise_irq)(struct pci_epc *pci_epc, +- enum pci_epc_irq_type type, u8 interrupt_num); ++ int (*set_bar)(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_bar *epf_bar); ++ void (*clear_bar)(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_bar *epf_bar); ++ int (*map_addr)(struct pci_epc *epc, u8 func_no, ++ phys_addr_t addr, u64 pci_addr, size_t size); ++ void (*unmap_addr)(struct pci_epc *epc, u8 func_no, ++ phys_addr_t addr); ++ int (*set_msi)(struct pci_epc *epc, u8 func_no, u8 interrupts); ++ int (*get_msi)(struct pci_epc *epc, u8 func_no); ++ int (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts); ++ int (*get_msix)(struct pci_epc *epc, u8 func_no); ++ int (*raise_irq)(struct pci_epc *epc, u8 func_no, ++ enum pci_epc_irq_type type, u16 interrupt_num); + int (*start)(struct pci_epc *epc); + void (*stop)(struct pci_epc *epc); + struct module *owner; +@@ -91,8 +97,17 @@ struct pci_epc { + struct config_group *group; + /* spinlock to protect against concurrent access of EP controller */ + spinlock_t lock; ++ unsigned int features; + }; + ++#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0) ++#define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3)) ++#define EPC_FEATURE_MSIX_AVAILABLE BIT(4) ++#define EPC_FEATURE_SET_BAR(features, bar) \ ++ (features |= (EPC_FEATURE_BAR_MASK & (bar << 1))) ++#define EPC_FEATURE_GET_BAR(features) \ ++ ((features & EPC_FEATURE_BAR_MASK) >> 1) ++ + #define to_pci_epc(device) container_of((device), struct pci_epc, dev) + + #define pci_epc_create(dev, ops) \ +@@ -124,17 +139,23 @@ void pci_epc_destroy(struct pci_epc *epc + int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf); + void pci_epc_linkup(struct pci_epc *epc); + void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); +-int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *hdr); +-int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar, +- dma_addr_t bar_phys, size_t size, int flags); +-void pci_epc_clear_bar(struct pci_epc *epc, int bar); +-int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr, ++int pci_epc_write_header(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_header *hdr); ++int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_bar *epf_bar); ++void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, ++ struct pci_epf_bar *epf_bar); ++int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, ++ phys_addr_t phys_addr, + u64 pci_addr, size_t size); +-void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr); +-int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts); +-int pci_epc_get_msi(struct pci_epc *epc); +-int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type, +- u8 interrupt_num); ++void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, ++ phys_addr_t phys_addr); ++int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts); ++int pci_epc_get_msi(struct pci_epc *epc, u8 func_no); ++int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts); ++int pci_epc_get_msix(struct pci_epc *epc, u8 func_no); ++int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, ++ enum pci_epc_irq_type type, u16 interrupt_num); + int pci_epc_start(struct pci_epc *epc); + void pci_epc_stop(struct pci_epc *epc); + struct pci_epc *pci_epc_get(const char *epc_name); +--- a/include/linux/pci-epf.h ++++ b/include/linux/pci-epf.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ + /** + * PCI Endpoint *Function* (EPF) header file + * + * Copyright (C) 2017 Texas Instruments + * Author: Kishon Vijay Abraham I +- * +- * 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 -@@ -1944,6 +1944,7 @@ void pcibios_release_device(struct pci_d +@@ -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); @@ -594,3 +5828,150 @@ Signed-off-by: Biwen Li #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 PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n" + "\t-b BAR test (bar number between 0..5)\n" + "\t-m MSI test (msi number between 1..32)\n" ++ "\t-x \tMSI-X test (msix number between 1..2048)\n" ++ "\t-i \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