PCI: dwc: Fix dw_pcie_ep_raise_msix_irq() to get correct MSI-X table address
authorKishon Vijay Abraham I <kishon@ti.com>
Tue, 25 Feb 2020 08:17:02 +0000 (13:47 +0530)
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Thu, 2 Apr 2020 16:57:10 +0000 (17:57 +0100)
commit beb4641a787d ("PCI: dwc: Add MSI-X callbacks handler"),
in order to raise MSI-X interrupt, obtained MSIX table address from
Base Address Register (BAR). However BAR only holds PCI address
programmed by the host whereas the MSI-X table should be in the local
memory.

Store the MSI-X table address (virtual address) as part of ->set_bar()
callback and use that to get the message address and message data
here.

Fixes: beb4641a787d ("PCI: dwc: Add MSI-X callbacks handler")
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/controller/dwc/pcie-designware.h
drivers/pci/endpoint/pci-epf-core.c
include/linux/pci-epf.h

index b48c70e8c7c9a6820e9c2b77c6db7771567e865a..1cdcbd102ce86830a424bd790bd653717b3eb578 100644 (file)
@@ -134,6 +134,7 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
 
        dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
        clear_bit(atu_index, ep->ib_window_map);
+       ep->epf_bar[bar] = NULL;
 }
 
 static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
@@ -167,6 +168,7 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
                dw_pcie_writel_dbi(pci, reg + 4, 0);
        }
 
+       ep->epf_bar[bar] = epf_bar;
        dw_pcie_dbi_ro_wr_dis(pci);
 
        return 0;
@@ -429,55 +431,41 @@ 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_epf_msix_tbl *msix_tbl;
        struct pci_epc *epc = ep->epc;
-       u16 tbl_offset, bir;
-       u32 bar_addr_upper, bar_addr_lower;
-       u32 msg_addr_upper, msg_addr_lower;
+       struct pci_epf_bar *epf_bar;
        u32 reg, msg_data, vec_ctrl;
-       u64 tbl_addr, msg_addr, reg_u64;
-       void __iomem *msix_tbl;
+       unsigned int aligned_offset;
+       u32 tbl_offset;
+       u64 msg_addr;
        int ret;
+       u8 bir;
 
        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);
+       epf_bar = ep->epf_bar[bir];
+       msix_tbl = epf_bar->addr;
+       msix_tbl = (struct pci_epf_msix_tbl *)((char *)msix_tbl + tbl_offset);
 
-       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(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);
+       msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
+       msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
+       vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl;
 
        if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
                dev_dbg(pci->dev, "MSI-X entry ctrl set\n");
                return -EPERM;
        }
 
-       ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
+       aligned_offset = msg_addr & (epc->mem->page_size - 1);
+       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);
+       writel(msg_data, ep->msi_mem + aligned_offset);
 
        dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
 
index aa98fbd50807d764475bf1a193664d41ac991406..d6e1f397e6b0fc6fc961dbdb4acfa802eab68bad 100644 (file)
@@ -233,6 +233,7 @@ struct dw_pcie_ep {
        phys_addr_t             msi_mem_phys;
        u8                      msi_cap;        /* MSI capability offset */
        u8                      msix_cap;       /* MSI-X capability offset */
+       struct pci_epf_bar      *epf_bar[PCI_STD_NUM_BARS];
 };
 
 struct dw_pcie_ops {
index 6e0648991b5ce5310c9e1e3c269b40f345646137..244e00f48c5cf007508d2dbef5a6a63c39327955 100644 (file)
@@ -87,6 +87,7 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
                          epf->bar[bar].phys_addr);
 
        epf->bar[bar].phys_addr = 0;
+       epf->bar[bar].addr = NULL;
        epf->bar[bar].size = 0;
        epf->bar[bar].barno = 0;
        epf->bar[bar].flags = 0;
@@ -123,6 +124,7 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
        }
 
        epf->bar[bar].phys_addr = phys_addr;
+       epf->bar[bar].addr = space;
        epf->bar[bar].size = size;
        epf->bar[bar].barno = bar;
        epf->bar[bar].flags |= upper_32_bits(size) ?
index 0c628e30c582c74a45b9cd464dfd245d8e3d00b1..6644ff3b07024508e83fef567c2975ae9aaf9a9a 100644 (file)
@@ -94,10 +94,12 @@ struct pci_epf_driver {
 /**
  * struct pci_epf_bar - represents the BAR of EPF device
  * @phys_addr: physical address that should be mapped to the BAR
+ * @addr: virtual address corresponding to the @phys_addr
  * @size: the size of the address space present in BAR
  */
 struct pci_epf_bar {
        dma_addr_t      phys_addr;
+       void            *addr;
        size_t          size;
        enum pci_barno  barno;
        int             flags;
@@ -134,6 +136,19 @@ struct pci_epf {
        struct mutex            lock;
 };
 
+/**
+ * struct pci_epf_msix_tbl - represents the MSIX table entry structure
+ * @msg_addr: Writes to this address will trigger MSIX interrupt in host
+ * @msg_data: Data that should be written to @msg_addr to trigger MSIX interrupt
+ * @vector_ctrl: Identifies if the function is prohibited from sending a message
+ * using this MSIX table entry
+ */
+struct pci_epf_msix_tbl {
+       u64 msg_addr;
+       u32 msg_data;
+       u32 vector_ctrl;
+};
+
 #define to_pci_epf(epf_dev) container_of((epf_dev), struct pci_epf, dev)
 
 #define pci_epf_register_driver(driver)    \