PCI: endpoint: Fix ->set_msix() to take BIR and offset as arguments
authorKishon Vijay Abraham I <kishon@ti.com>
Tue, 25 Feb 2020 08:17:01 +0000 (13:47 +0530)
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Thu, 2 Apr 2020 16:57:10 +0000 (17:57 +0100)
commit 8963106eabdc ("PCI: endpoint: Add MSI-X interfaces") while
adding support to raise MSI-X interrupts from endpoint didn't include
BAR Indicator register (BIR) configuration and MSI-X table offset as
arguments in pci_epc_set_msix(). This would result in endpoint
controller register using random BAR indicator register, the memory
for which might not be allocated by the endpoint function driver.
Add BAR indicator register and MSI-X table offset as arguments in
pci_epc_set_msix() and allocate space for MSI-X table and pending
bit array (PBA) in pci-epf-test endpoint function driver.

Fixes: 8963106eabdc ("PCI: endpoint: Add MSI-X interfaces")
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
drivers/pci/controller/dwc/pcie-designware-ep.c
drivers/pci/endpoint/functions/pci-epf-test.c
drivers/pci/endpoint/pci-epc-core.c
include/linux/pci-epc.h

index 8e9f31144b69b2adb84eeecfcf0b02d97a0ad256..b48c70e8c7c9a6820e9c2b77c6db7771567e865a 100644 (file)
@@ -278,7 +278,8 @@ static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
        return val;
 }
 
-static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
+static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts,
+                              enum pci_barno bir, u32 offset)
 {
        struct dw_pcie_ep *ep = epc_get_drvdata(epc);
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
@@ -287,12 +288,22 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
        if (!ep->msix_cap)
                return -EINVAL;
 
+       dw_pcie_dbi_ro_wr_en(pci);
+
        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, reg, val);
+
+       reg = ep->msix_cap + PCI_MSIX_TABLE;
+       val = offset | bir;
+       dw_pcie_writel_dbi(pci, reg, val);
+
+       reg = ep->msix_cap + PCI_MSIX_PBA;
+       val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
+       dw_pcie_writel_dbi(pci, reg, val);
+
        dw_pcie_dbi_ro_wr_dis(pci);
 
        return 0;
index 0a5019ce7540be910050b57ac73b757e0d12eab4..60330f3e3751690b81d81032aac9396facaa77cb 100644 (file)
@@ -50,6 +50,7 @@ struct pci_epf_test {
        void                    *reg[PCI_STD_NUM_BARS];
        struct pci_epf          *epf;
        enum pci_barno          test_reg_bar;
+       size_t                  msix_table_offset;
        struct delayed_work     cmd_handler;
        struct dma_chan         *dma_chan;
        struct completion       transfer_complete;
@@ -659,6 +660,7 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
 
 static int pci_epf_test_core_init(struct pci_epf *epf)
 {
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
        struct pci_epf_header *header = epf->header;
        const struct pci_epc_features *epc_features;
        struct pci_epc *epc = epf->epc;
@@ -692,7 +694,9 @@ static int pci_epf_test_core_init(struct pci_epf *epf)
        }
 
        if (msix_capable) {
-               ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
+               ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts,
+                                      epf_test->test_reg_bar,
+                                      epf_test->msix_table_offset);
                if (ret) {
                        dev_err(dev, "MSI-X configuration failed\n");
                        return ret;
@@ -734,6 +738,10 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
        struct pci_epf_test *epf_test = epf_get_drvdata(epf);
        struct device *dev = &epf->dev;
        struct pci_epf_bar *epf_bar;
+       size_t msix_table_size = 0;
+       size_t test_reg_bar_size;
+       size_t pba_size = 0;
+       bool msix_capable;
        void *base;
        int bar, add;
        enum pci_barno test_reg_bar = epf_test->test_reg_bar;
@@ -742,13 +750,25 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
 
        epc_features = epf_test->epc_features;
 
-       if (epc_features->bar_fixed_size[test_reg_bar])
+       test_reg_bar_size = ALIGN(sizeof(struct pci_epf_test_reg), 128);
+
+       msix_capable = epc_features->msix_capable;
+       if (msix_capable) {
+               msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
+               epf_test->msix_table_offset = test_reg_bar_size;
+               /* Align to QWORD or 8 Bytes */
+               pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8);
+       }
+       test_reg_size = test_reg_bar_size + msix_table_size + pba_size;
+
+       if (epc_features->bar_fixed_size[test_reg_bar]) {
+               if (test_reg_size > bar_size[test_reg_bar])
+                       return -ENOMEM;
                test_reg_size = bar_size[test_reg_bar];
-       else
-               test_reg_size = sizeof(struct pci_epf_test_reg);
+       }
 
-       base = pci_epf_alloc_space(epf, test_reg_size,
-                                  test_reg_bar, epc_features->align);
+       base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar,
+                                  epc_features->align);
        if (!base) {
                dev_err(dev, "Failed to allocated register space\n");
                return -ENOMEM;
index 0d22a377a0cf70a85b6f25bae82c1b355877c014..82ba0dc7f2f5f6d81d5259d4014fd1a894655d26 100644 (file)
@@ -297,10 +297,13 @@ EXPORT_SYMBOL_GPL(pci_epc_get_msix);
  * @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
+ * @bir: BAR where the MSI-X table resides
+ * @offset: Offset pointing to the start of MSI-X table
  *
  * 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 pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts,
+                    enum pci_barno bir, u32 offset)
 {
        int ret;
 
@@ -312,7 +315,7 @@ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
                return 0;
 
        mutex_lock(&epc->lock);
-       ret = epc->ops->set_msix(epc, func_no, interrupts - 1);
+       ret = epc->ops->set_msix(epc, func_no, interrupts - 1, bir, offset);
        mutex_unlock(&epc->lock);
 
        return ret;
index 0d7e91bad91e184862425ff1e37044bdd79f9f57..e0ed9d01f6e521c90672607453216bac80787d44 100644 (file)
@@ -53,7 +53,8 @@ struct pci_epc_ops {
                              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     (*set_msix)(struct pci_epc *epc, u8 func_no, u16 interrupts,
+                           enum pci_barno, u32 offset);
        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);
@@ -180,7 +181,8 @@ 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_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts,
+                    enum pci_barno, u32 offset);
 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);