From ad4a5becc689c3f32bbbc2b37eff89efe19dc2f9 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 Dec 2017 14:01:44 +0100 Subject: PCI: designware-ep: Fix find_first_zero_bit() usage find_first_zero_bit()'s parameter 'size' is defined in bits, not in bytes. find_first_zero_bit() is called with size in bytes rather than bits, which thus defines a too low upper limit, causing dw_pcie_ep_inbound_atu() to assign iatu index #4 to both bar 4 and bar 5, which makes bar 5 overwrite the settings set by bar 4. Since the sizes of the bitmaps are known, dynamically allocate the bitmaps, and use the correct size when calling find_first_zero_bit(). Additionally, make sure that ep->num_ob_windows and ep->num_ib_windows, which are obtained from device tree, are smaller than the maximum number of iATUs (MAX_IATU_IN/MAX_IATU_OUT). Fixes: f8aed6ec624f ("PCI: dwc: designware: Add EP mode support") Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/dwc/pcie-designware-ep.c | 34 ++++++++++++++++++++++++++-------- drivers/pci/dwc/pcie-designware.h | 8 ++++++-- 2 files changed, 32 insertions(+), 10 deletions(-) (limited to 'drivers/pci/dwc') diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index d53d5f168363..d5eb143040e3 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -70,8 +70,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar, u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - free_win = find_first_zero_bit(&ep->ib_window_map, - sizeof(ep->ib_window_map)); + free_win = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows); if (free_win >= ep->num_ib_windows) { dev_err(pci->dev, "no free inbound window\n"); return -EINVAL; @@ -85,7 +84,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar, } ep->bar_to_atu[bar] = free_win; - set_bit(free_win, &ep->ib_window_map); + set_bit(free_win, ep->ib_window_map); return 0; } @@ -96,8 +95,7 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - free_win = find_first_zero_bit(&ep->ob_window_map, - sizeof(ep->ob_window_map)); + free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows); if (free_win >= ep->num_ob_windows) { dev_err(pci->dev, "no free outbound window\n"); return -EINVAL; @@ -106,7 +104,7 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM, phys_addr, pci_addr, size); - set_bit(free_win, &ep->ob_window_map); + set_bit(free_win, ep->ob_window_map); ep->outbound_addr[free_win] = phys_addr; return 0; @@ -121,7 +119,7 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar) dw_pcie_ep_reset_bar(pci, bar); dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); - clear_bit(atu_index, &ep->ib_window_map); + clear_bit(atu_index, ep->ib_window_map); } static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar, @@ -175,7 +173,7 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr) return; dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); - clear_bit(atu_index, &ep->ob_window_map); + clear_bit(atu_index, ep->ob_window_map); } static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr, @@ -298,12 +296,32 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) dev_err(dev, "unable to read *num-ib-windows* property\n"); return ret; } + if (ep->num_ib_windows > MAX_IATU_IN) { + dev_err(dev, "invalid *num-ib-windows*\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->ib_window_map = devm_kzalloc(dev, sizeof(long) * + BITS_TO_LONGS(ep->num_ib_windows), + GFP_KERNEL); + if (!ep->ib_window_map) + return -ENOMEM; + + ep->ob_window_map = devm_kzalloc(dev, sizeof(long) * + BITS_TO_LONGS(ep->num_ob_windows), + GFP_KERNEL); + if (!ep->ob_window_map) + return -ENOMEM; addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows, GFP_KERNEL); diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index e5d9d77b778e..e6fd0b024b21 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -113,6 +113,10 @@ #define MAX_MSI_IRQS 32 #define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) +/* 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; @@ -192,8 +196,8 @@ struct dw_pcie_ep { size_t page_size; u8 bar_to_atu[6]; phys_addr_t *outbound_addr; - unsigned long ib_window_map; - unsigned long ob_window_map; + unsigned long *ib_window_map; + unsigned long *ob_window_map; u32 num_ib_windows; u32 num_ob_windows; }; -- cgit v1.2.3 From a134a457ed985dca8cce7ac4ea66129ea70eba73 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 19 Dec 2017 15:25:41 +0530 Subject: PCI: designware-ep: Fix ->get_msi() to check MSI_EN bit ->get_msi() now checks MSI_EN bit in the MSI CAPABILITY register to find whether the host supports MSI instead of using the MSI ADDRESS in the MSI CAPABILITY register. This fixes the issue with the following sequence 'modprobe pci_endpoint_test' enables MSI 'rmmod pci_endpoint_test' disables MSI but MSI address (in EP's capability register) has a valid value 'modprobe pci_endpoint_test no_msi=1' - Since MSI address (in EP's capability register) has a valid value (set during the previous insertion of the module), EP thinks host supports MSI. Fixes: f8aed6ec624f ("PCI: dwc: designware: Add EP mode support") Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/dwc/pcie-designware-ep.c | 12 +++--------- drivers/pci/dwc/pcie-designware.h | 1 + 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/pci/dwc') diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index d5eb143040e3..0989946df32c 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -195,20 +195,14 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr, static int dw_pcie_ep_get_msi(struct pci_epc *epc) { int val; - u32 lower_addr; - u32 upper_addr; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - val = dw_pcie_readb_dbi(pci, MSI_MESSAGE_CONTROL); - val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; - - lower_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); - upper_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); - - if (!(lower_addr || upper_addr)) + val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); + if (!(val & MSI_CAP_MSI_EN_MASK)) return -EINVAL; + val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; return val; } diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index e6fd0b024b21..8700ba12c130 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -101,6 +101,7 @@ #define MSI_MESSAGE_CONTROL 0x52 #define MSI_CAP_MMC_SHIFT 1 #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 -- cgit v1.2.3