1de80f95cSTom Joseph // SPDX-License-Identifier: GPL-2.0 2de80f95cSTom Joseph // Copyright (c) 2017 Cadence 3de80f95cSTom Joseph // Cadence PCIe endpoint controller driver. 4de80f95cSTom Joseph // Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com> 5de80f95cSTom Joseph 6de80f95cSTom Joseph #include <linux/delay.h> 7de80f95cSTom Joseph #include <linux/kernel.h> 8de80f95cSTom Joseph #include <linux/of.h> 9de80f95cSTom Joseph #include <linux/pci-epc.h> 10de80f95cSTom Joseph #include <linux/platform_device.h> 11de80f95cSTom Joseph #include <linux/sizes.h> 12de80f95cSTom Joseph 13de80f95cSTom Joseph #include "pcie-cadence.h" 14de80f95cSTom Joseph 15de80f95cSTom Joseph #define CDNS_PCIE_EP_MIN_APERTURE 128 /* 128 bytes */ 16de80f95cSTom Joseph #define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1 17de80f95cSTom Joseph #define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3 18de80f95cSTom Joseph 19de80f95cSTom Joseph static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, 20de80f95cSTom Joseph struct pci_epf_header *hdr) 21de80f95cSTom Joseph { 22de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 23de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 24de80f95cSTom Joseph 25de80f95cSTom Joseph cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid); 26de80f95cSTom Joseph cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid); 27de80f95cSTom Joseph cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CLASS_PROG, hdr->progif_code); 28de80f95cSTom Joseph cdns_pcie_ep_fn_writew(pcie, fn, PCI_CLASS_DEVICE, 29de80f95cSTom Joseph hdr->subclass_code | hdr->baseclass_code << 8); 30de80f95cSTom Joseph cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CACHE_LINE_SIZE, 31de80f95cSTom Joseph hdr->cache_line_size); 32de80f95cSTom Joseph cdns_pcie_ep_fn_writew(pcie, fn, PCI_SUBSYSTEM_ID, hdr->subsys_id); 33de80f95cSTom Joseph cdns_pcie_ep_fn_writeb(pcie, fn, PCI_INTERRUPT_PIN, hdr->interrupt_pin); 34de80f95cSTom Joseph 35de80f95cSTom Joseph /* 36de80f95cSTom Joseph * Vendor ID can only be modified from function 0, all other functions 37de80f95cSTom Joseph * use the same vendor ID as function 0. 38de80f95cSTom Joseph */ 39de80f95cSTom Joseph if (fn == 0) { 40de80f95cSTom Joseph /* Update the vendor IDs. */ 41de80f95cSTom Joseph u32 id = CDNS_PCIE_LM_ID_VENDOR(hdr->vendorid) | 42de80f95cSTom Joseph CDNS_PCIE_LM_ID_SUBSYS(hdr->subsys_vendor_id); 43de80f95cSTom Joseph 44de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id); 45de80f95cSTom Joseph } 46de80f95cSTom Joseph 47de80f95cSTom Joseph return 0; 48de80f95cSTom Joseph } 49de80f95cSTom Joseph 50de80f95cSTom Joseph static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, 51de80f95cSTom Joseph struct pci_epf_bar *epf_bar) 52de80f95cSTom Joseph { 53de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 543ef5d16fSAlan Douglas struct cdns_pcie_epf *epf = &ep->epf[fn]; 55de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 56de80f95cSTom Joseph dma_addr_t bar_phys = epf_bar->phys_addr; 57de80f95cSTom Joseph enum pci_barno bar = epf_bar->barno; 58de80f95cSTom Joseph int flags = epf_bar->flags; 59de80f95cSTom Joseph u32 addr0, addr1, reg, cfg, b, aperture, ctrl; 60de80f95cSTom Joseph u64 sz; 61de80f95cSTom Joseph 62de80f95cSTom Joseph /* BAR size is 2^(aperture + 7) */ 63de80f95cSTom Joseph sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE); 64de80f95cSTom Joseph /* 65de80f95cSTom Joseph * roundup_pow_of_two() returns an unsigned long, which is not suited 66de80f95cSTom Joseph * for 64bit values. 67de80f95cSTom Joseph */ 68de80f95cSTom Joseph sz = 1ULL << fls64(sz - 1); 69de80f95cSTom Joseph aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */ 70de80f95cSTom Joseph 71de80f95cSTom Joseph if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { 72de80f95cSTom Joseph ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS; 73de80f95cSTom Joseph } else { 74de80f95cSTom Joseph bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); 75de80f95cSTom Joseph bool is_64bits = sz > SZ_2G; 76de80f95cSTom Joseph 77de80f95cSTom Joseph if (is_64bits && (bar & 1)) 78de80f95cSTom Joseph return -EINVAL; 79de80f95cSTom Joseph 80de80f95cSTom Joseph if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) 81de80f95cSTom Joseph epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; 82de80f95cSTom Joseph 83de80f95cSTom Joseph if (is_64bits && is_prefetch) 84de80f95cSTom Joseph ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; 85de80f95cSTom Joseph else if (is_prefetch) 86de80f95cSTom Joseph ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS; 87de80f95cSTom Joseph else if (is_64bits) 88de80f95cSTom Joseph ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS; 89de80f95cSTom Joseph else 90de80f95cSTom Joseph ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS; 91de80f95cSTom Joseph } 92de80f95cSTom Joseph 93de80f95cSTom Joseph addr0 = lower_32_bits(bar_phys); 94de80f95cSTom Joseph addr1 = upper_32_bits(bar_phys); 95de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 96de80f95cSTom Joseph addr0); 97de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 98de80f95cSTom Joseph addr1); 99de80f95cSTom Joseph 100de80f95cSTom Joseph if (bar < BAR_4) { 101de80f95cSTom Joseph reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); 102de80f95cSTom Joseph b = bar; 103de80f95cSTom Joseph } else { 104de80f95cSTom Joseph reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); 105de80f95cSTom Joseph b = bar - BAR_4; 106de80f95cSTom Joseph } 107de80f95cSTom Joseph 108de80f95cSTom Joseph cfg = cdns_pcie_readl(pcie, reg); 109de80f95cSTom Joseph cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | 110de80f95cSTom Joseph CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); 111de80f95cSTom Joseph cfg |= (CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) | 112de80f95cSTom Joseph CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); 113de80f95cSTom Joseph cdns_pcie_writel(pcie, reg, cfg); 114de80f95cSTom Joseph 1153ef5d16fSAlan Douglas epf->epf_bar[bar] = epf_bar; 1163ef5d16fSAlan Douglas 117de80f95cSTom Joseph return 0; 118de80f95cSTom Joseph } 119de80f95cSTom Joseph 120de80f95cSTom Joseph static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, 121de80f95cSTom Joseph struct pci_epf_bar *epf_bar) 122de80f95cSTom Joseph { 123de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 1243ef5d16fSAlan Douglas struct cdns_pcie_epf *epf = &ep->epf[fn]; 125de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 126de80f95cSTom Joseph enum pci_barno bar = epf_bar->barno; 127de80f95cSTom Joseph u32 reg, cfg, b, ctrl; 128de80f95cSTom Joseph 129de80f95cSTom Joseph if (bar < BAR_4) { 130de80f95cSTom Joseph reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); 131de80f95cSTom Joseph b = bar; 132de80f95cSTom Joseph } else { 133de80f95cSTom Joseph reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); 134de80f95cSTom Joseph b = bar - BAR_4; 135de80f95cSTom Joseph } 136de80f95cSTom Joseph 137de80f95cSTom Joseph ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; 138de80f95cSTom Joseph cfg = cdns_pcie_readl(pcie, reg); 139de80f95cSTom Joseph cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | 140de80f95cSTom Joseph CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); 141de80f95cSTom Joseph cfg |= CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl); 142de80f95cSTom Joseph cdns_pcie_writel(pcie, reg, cfg); 143de80f95cSTom Joseph 144de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0); 145de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0); 1463ef5d16fSAlan Douglas 1473ef5d16fSAlan Douglas epf->epf_bar[bar] = NULL; 148de80f95cSTom Joseph } 149de80f95cSTom Joseph 150de80f95cSTom Joseph static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, 151de80f95cSTom Joseph u64 pci_addr, size_t size) 152de80f95cSTom Joseph { 153de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 154de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 155de80f95cSTom Joseph u32 r; 156de80f95cSTom Joseph 157de80f95cSTom Joseph r = find_first_zero_bit(&ep->ob_region_map, 158de80f95cSTom Joseph sizeof(ep->ob_region_map) * BITS_PER_LONG); 159de80f95cSTom Joseph if (r >= ep->max_regions - 1) { 160de80f95cSTom Joseph dev_err(&epc->dev, "no free outbound region\n"); 161de80f95cSTom Joseph return -EINVAL; 162de80f95cSTom Joseph } 163de80f95cSTom Joseph 164ec64e279SRob Herring cdns_pcie_set_outbound_region(pcie, 0, fn, r, false, addr, pci_addr, size); 165de80f95cSTom Joseph 166de80f95cSTom Joseph set_bit(r, &ep->ob_region_map); 167de80f95cSTom Joseph ep->ob_addr[r] = addr; 168de80f95cSTom Joseph 169de80f95cSTom Joseph return 0; 170de80f95cSTom Joseph } 171de80f95cSTom Joseph 172de80f95cSTom Joseph static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, 173de80f95cSTom Joseph phys_addr_t addr) 174de80f95cSTom Joseph { 175de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 176de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 177de80f95cSTom Joseph u32 r; 178de80f95cSTom Joseph 179de80f95cSTom Joseph for (r = 0; r < ep->max_regions - 1; r++) 180de80f95cSTom Joseph if (ep->ob_addr[r] == addr) 181de80f95cSTom Joseph break; 182de80f95cSTom Joseph 183de80f95cSTom Joseph if (r == ep->max_regions - 1) 184de80f95cSTom Joseph return; 185de80f95cSTom Joseph 186de80f95cSTom Joseph cdns_pcie_reset_outbound_region(pcie, r); 187de80f95cSTom Joseph 188de80f95cSTom Joseph ep->ob_addr[r] = 0; 189de80f95cSTom Joseph clear_bit(r, &ep->ob_region_map); 190de80f95cSTom Joseph } 191de80f95cSTom Joseph 192de80f95cSTom Joseph static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 mmc) 193de80f95cSTom Joseph { 194de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 195de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 196de80f95cSTom Joseph u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; 197de80f95cSTom Joseph u16 flags; 198de80f95cSTom Joseph 199de80f95cSTom Joseph /* 200de80f95cSTom Joseph * Set the Multiple Message Capable bitfield into the Message Control 201de80f95cSTom Joseph * register. 202de80f95cSTom Joseph */ 203de80f95cSTom Joseph flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); 204de80f95cSTom Joseph flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1); 205de80f95cSTom Joseph flags |= PCI_MSI_FLAGS_64BIT; 206de80f95cSTom Joseph flags &= ~PCI_MSI_FLAGS_MASKBIT; 207de80f95cSTom Joseph cdns_pcie_ep_fn_writew(pcie, fn, cap + PCI_MSI_FLAGS, flags); 208de80f95cSTom Joseph 209de80f95cSTom Joseph return 0; 210de80f95cSTom Joseph } 211de80f95cSTom Joseph 212de80f95cSTom Joseph static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) 213de80f95cSTom Joseph { 214de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 215de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 216de80f95cSTom Joseph u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; 217de80f95cSTom Joseph u16 flags, mme; 218de80f95cSTom Joseph 219de80f95cSTom Joseph /* Validate that the MSI feature is actually enabled. */ 220de80f95cSTom Joseph flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); 221de80f95cSTom Joseph if (!(flags & PCI_MSI_FLAGS_ENABLE)) 222de80f95cSTom Joseph return -EINVAL; 223de80f95cSTom Joseph 224de80f95cSTom Joseph /* 225de80f95cSTom Joseph * Get the Multiple Message Enable bitfield from the Message Control 226de80f95cSTom Joseph * register. 227de80f95cSTom Joseph */ 228de80f95cSTom Joseph mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; 229de80f95cSTom Joseph 230de80f95cSTom Joseph return mme; 231de80f95cSTom Joseph } 232de80f95cSTom Joseph 2333ef5d16fSAlan Douglas static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no) 2343ef5d16fSAlan Douglas { 2353ef5d16fSAlan Douglas struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 2363ef5d16fSAlan Douglas struct cdns_pcie *pcie = &ep->pcie; 2373ef5d16fSAlan Douglas u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; 2383ef5d16fSAlan Douglas u32 val, reg; 2393ef5d16fSAlan Douglas 2403ef5d16fSAlan Douglas reg = cap + PCI_MSIX_FLAGS; 2413ef5d16fSAlan Douglas val = cdns_pcie_ep_fn_readw(pcie, func_no, reg); 2423ef5d16fSAlan Douglas if (!(val & PCI_MSIX_FLAGS_ENABLE)) 2433ef5d16fSAlan Douglas return -EINVAL; 2443ef5d16fSAlan Douglas 2453ef5d16fSAlan Douglas val &= PCI_MSIX_FLAGS_QSIZE; 2463ef5d16fSAlan Douglas 2473ef5d16fSAlan Douglas return val; 2483ef5d16fSAlan Douglas } 2493ef5d16fSAlan Douglas 2503ef5d16fSAlan Douglas static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u16 interrupts, 2513ef5d16fSAlan Douglas enum pci_barno bir, u32 offset) 2523ef5d16fSAlan Douglas { 2533ef5d16fSAlan Douglas struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 2543ef5d16fSAlan Douglas struct cdns_pcie *pcie = &ep->pcie; 2553ef5d16fSAlan Douglas u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; 2563ef5d16fSAlan Douglas u32 val, reg; 2573ef5d16fSAlan Douglas 2583ef5d16fSAlan Douglas reg = cap + PCI_MSIX_FLAGS; 2593ef5d16fSAlan Douglas val = cdns_pcie_ep_fn_readw(pcie, fn, reg); 2603ef5d16fSAlan Douglas val &= ~PCI_MSIX_FLAGS_QSIZE; 2613ef5d16fSAlan Douglas val |= interrupts; 2623ef5d16fSAlan Douglas cdns_pcie_ep_fn_writew(pcie, fn, reg, val); 2633ef5d16fSAlan Douglas 2643ef5d16fSAlan Douglas /* Set MSIX BAR and offset */ 2653ef5d16fSAlan Douglas reg = cap + PCI_MSIX_TABLE; 2663ef5d16fSAlan Douglas val = offset | bir; 2673ef5d16fSAlan Douglas cdns_pcie_ep_fn_writel(pcie, fn, reg, val); 2683ef5d16fSAlan Douglas 2693ef5d16fSAlan Douglas /* Set PBA BAR and offset. BAR must match MSIX BAR */ 2703ef5d16fSAlan Douglas reg = cap + PCI_MSIX_PBA; 2713ef5d16fSAlan Douglas val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir; 2723ef5d16fSAlan Douglas cdns_pcie_ep_fn_writel(pcie, fn, reg, val); 2733ef5d16fSAlan Douglas 2743ef5d16fSAlan Douglas return 0; 2753ef5d16fSAlan Douglas } 2763ef5d16fSAlan Douglas 277de80f95cSTom Joseph static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, 278de80f95cSTom Joseph u8 intx, bool is_asserted) 279de80f95cSTom Joseph { 280de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 281a8b661ebSKishon Vijay Abraham I unsigned long flags; 282de80f95cSTom Joseph u32 offset; 283de80f95cSTom Joseph u16 status; 284de80f95cSTom Joseph u8 msg_code; 285de80f95cSTom Joseph 286de80f95cSTom Joseph intx &= 3; 287de80f95cSTom Joseph 288de80f95cSTom Joseph /* Set the outbound region if needed. */ 289de80f95cSTom Joseph if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY || 290de80f95cSTom Joseph ep->irq_pci_fn != fn)) { 291de80f95cSTom Joseph /* First region was reserved for IRQ writes. */ 292ec64e279SRob Herring cdns_pcie_set_outbound_region_for_normal_msg(pcie, 0, fn, 0, 293de80f95cSTom Joseph ep->irq_phys_addr); 294de80f95cSTom Joseph ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY; 295de80f95cSTom Joseph ep->irq_pci_fn = fn; 296de80f95cSTom Joseph } 297de80f95cSTom Joseph 298de80f95cSTom Joseph if (is_asserted) { 299de80f95cSTom Joseph ep->irq_pending |= BIT(intx); 300de80f95cSTom Joseph msg_code = MSG_CODE_ASSERT_INTA + intx; 301de80f95cSTom Joseph } else { 302de80f95cSTom Joseph ep->irq_pending &= ~BIT(intx); 303de80f95cSTom Joseph msg_code = MSG_CODE_DEASSERT_INTA + intx; 304de80f95cSTom Joseph } 305de80f95cSTom Joseph 306a8b661ebSKishon Vijay Abraham I spin_lock_irqsave(&ep->lock, flags); 307de80f95cSTom Joseph status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS); 308de80f95cSTom Joseph if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) { 309de80f95cSTom Joseph status ^= PCI_STATUS_INTERRUPT; 310de80f95cSTom Joseph cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status); 311de80f95cSTom Joseph } 312a8b661ebSKishon Vijay Abraham I spin_unlock_irqrestore(&ep->lock, flags); 313de80f95cSTom Joseph 314de80f95cSTom Joseph offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) | 315de80f95cSTom Joseph CDNS_PCIE_NORMAL_MSG_CODE(msg_code) | 316de80f95cSTom Joseph CDNS_PCIE_MSG_NO_DATA; 317de80f95cSTom Joseph writel(0, ep->irq_cpu_addr + offset); 318de80f95cSTom Joseph } 319de80f95cSTom Joseph 320de80f95cSTom Joseph static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 intx) 321de80f95cSTom Joseph { 322de80f95cSTom Joseph u16 cmd; 323de80f95cSTom Joseph 324de80f95cSTom Joseph cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND); 325de80f95cSTom Joseph if (cmd & PCI_COMMAND_INTX_DISABLE) 326de80f95cSTom Joseph return -EINVAL; 327de80f95cSTom Joseph 328de80f95cSTom Joseph cdns_pcie_ep_assert_intx(ep, fn, intx, true); 329de80f95cSTom Joseph /* 330de80f95cSTom Joseph * The mdelay() value was taken from dra7xx_pcie_raise_legacy_irq() 331de80f95cSTom Joseph */ 332de80f95cSTom Joseph mdelay(1); 333de80f95cSTom Joseph cdns_pcie_ep_assert_intx(ep, fn, intx, false); 334de80f95cSTom Joseph return 0; 335de80f95cSTom Joseph } 336de80f95cSTom Joseph 337de80f95cSTom Joseph static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, 338de80f95cSTom Joseph u8 interrupt_num) 339de80f95cSTom Joseph { 340de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 341de80f95cSTom Joseph u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; 342de80f95cSTom Joseph u16 flags, mme, data, data_mask; 343de80f95cSTom Joseph u8 msi_count; 344de80f95cSTom Joseph u64 pci_addr, pci_addr_mask = 0xff; 345de80f95cSTom Joseph 346de80f95cSTom Joseph /* Check whether the MSI feature has been enabled by the PCI host. */ 347de80f95cSTom Joseph flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); 348de80f95cSTom Joseph if (!(flags & PCI_MSI_FLAGS_ENABLE)) 349de80f95cSTom Joseph return -EINVAL; 350de80f95cSTom Joseph 351de80f95cSTom Joseph /* Get the number of enabled MSIs */ 352de80f95cSTom Joseph mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; 353de80f95cSTom Joseph msi_count = 1 << mme; 354de80f95cSTom Joseph if (!interrupt_num || interrupt_num > msi_count) 355de80f95cSTom Joseph return -EINVAL; 356de80f95cSTom Joseph 357de80f95cSTom Joseph /* Compute the data value to be written. */ 358de80f95cSTom Joseph data_mask = msi_count - 1; 359de80f95cSTom Joseph data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64); 360de80f95cSTom Joseph data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask); 361de80f95cSTom Joseph 362de80f95cSTom Joseph /* Get the PCI address where to write the data into. */ 363de80f95cSTom Joseph pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI); 364de80f95cSTom Joseph pci_addr <<= 32; 365de80f95cSTom Joseph pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO); 366de80f95cSTom Joseph pci_addr &= GENMASK_ULL(63, 2); 367de80f95cSTom Joseph 368de80f95cSTom Joseph /* Set the outbound region if needed. */ 369de80f95cSTom Joseph if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || 370de80f95cSTom Joseph ep->irq_pci_fn != fn)) { 371de80f95cSTom Joseph /* First region was reserved for IRQ writes. */ 372ec64e279SRob Herring cdns_pcie_set_outbound_region(pcie, 0, fn, 0, 373de80f95cSTom Joseph false, 374de80f95cSTom Joseph ep->irq_phys_addr, 375de80f95cSTom Joseph pci_addr & ~pci_addr_mask, 376de80f95cSTom Joseph pci_addr_mask + 1); 377de80f95cSTom Joseph ep->irq_pci_addr = (pci_addr & ~pci_addr_mask); 378de80f95cSTom Joseph ep->irq_pci_fn = fn; 379de80f95cSTom Joseph } 380de80f95cSTom Joseph writel(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask)); 381de80f95cSTom Joseph 382de80f95cSTom Joseph return 0; 383de80f95cSTom Joseph } 384de80f95cSTom Joseph 385dbcc542fSKishon Vijay Abraham I static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn, 386dbcc542fSKishon Vijay Abraham I phys_addr_t addr, u8 interrupt_num, 387dbcc542fSKishon Vijay Abraham I u32 entry_size, u32 *msi_data, 388dbcc542fSKishon Vijay Abraham I u32 *msi_addr_offset) 389dbcc542fSKishon Vijay Abraham I { 390dbcc542fSKishon Vijay Abraham I struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 391dbcc542fSKishon Vijay Abraham I u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; 392dbcc542fSKishon Vijay Abraham I struct cdns_pcie *pcie = &ep->pcie; 393dbcc542fSKishon Vijay Abraham I u64 pci_addr, pci_addr_mask = 0xff; 394dbcc542fSKishon Vijay Abraham I u16 flags, mme, data, data_mask; 395dbcc542fSKishon Vijay Abraham I u8 msi_count; 396dbcc542fSKishon Vijay Abraham I int ret; 397dbcc542fSKishon Vijay Abraham I int i; 398dbcc542fSKishon Vijay Abraham I 399dbcc542fSKishon Vijay Abraham I /* Check whether the MSI feature has been enabled by the PCI host. */ 400dbcc542fSKishon Vijay Abraham I flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); 401dbcc542fSKishon Vijay Abraham I if (!(flags & PCI_MSI_FLAGS_ENABLE)) 402dbcc542fSKishon Vijay Abraham I return -EINVAL; 403dbcc542fSKishon Vijay Abraham I 404dbcc542fSKishon Vijay Abraham I /* Get the number of enabled MSIs */ 405dbcc542fSKishon Vijay Abraham I mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; 406dbcc542fSKishon Vijay Abraham I msi_count = 1 << mme; 407dbcc542fSKishon Vijay Abraham I if (!interrupt_num || interrupt_num > msi_count) 408dbcc542fSKishon Vijay Abraham I return -EINVAL; 409dbcc542fSKishon Vijay Abraham I 410dbcc542fSKishon Vijay Abraham I /* Compute the data value to be written. */ 411dbcc542fSKishon Vijay Abraham I data_mask = msi_count - 1; 412dbcc542fSKishon Vijay Abraham I data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64); 413dbcc542fSKishon Vijay Abraham I data = data & ~data_mask; 414dbcc542fSKishon Vijay Abraham I 415dbcc542fSKishon Vijay Abraham I /* Get the PCI address where to write the data into. */ 416dbcc542fSKishon Vijay Abraham I pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI); 417dbcc542fSKishon Vijay Abraham I pci_addr <<= 32; 418dbcc542fSKishon Vijay Abraham I pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO); 419dbcc542fSKishon Vijay Abraham I pci_addr &= GENMASK_ULL(63, 2); 420dbcc542fSKishon Vijay Abraham I 421dbcc542fSKishon Vijay Abraham I for (i = 0; i < interrupt_num; i++) { 422dbcc542fSKishon Vijay Abraham I ret = cdns_pcie_ep_map_addr(epc, fn, addr, 423dbcc542fSKishon Vijay Abraham I pci_addr & ~pci_addr_mask, 424dbcc542fSKishon Vijay Abraham I entry_size); 425dbcc542fSKishon Vijay Abraham I if (ret) 426dbcc542fSKishon Vijay Abraham I return ret; 427dbcc542fSKishon Vijay Abraham I addr = addr + entry_size; 428dbcc542fSKishon Vijay Abraham I } 429dbcc542fSKishon Vijay Abraham I 430dbcc542fSKishon Vijay Abraham I *msi_data = data; 431dbcc542fSKishon Vijay Abraham I *msi_addr_offset = pci_addr & pci_addr_mask; 432dbcc542fSKishon Vijay Abraham I 433dbcc542fSKishon Vijay Abraham I return 0; 434dbcc542fSKishon Vijay Abraham I } 435dbcc542fSKishon Vijay Abraham I 4363ef5d16fSAlan Douglas static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, 4373ef5d16fSAlan Douglas u16 interrupt_num) 4383ef5d16fSAlan Douglas { 4393ef5d16fSAlan Douglas u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; 4403ef5d16fSAlan Douglas u32 tbl_offset, msg_data, reg; 4413ef5d16fSAlan Douglas struct cdns_pcie *pcie = &ep->pcie; 4423ef5d16fSAlan Douglas struct pci_epf_msix_tbl *msix_tbl; 4433ef5d16fSAlan Douglas struct cdns_pcie_epf *epf; 4443ef5d16fSAlan Douglas u64 pci_addr_mask = 0xff; 4453ef5d16fSAlan Douglas u64 msg_addr; 4463ef5d16fSAlan Douglas u16 flags; 4473ef5d16fSAlan Douglas u8 bir; 4483ef5d16fSAlan Douglas 4493ef5d16fSAlan Douglas /* Check whether the MSI-X feature has been enabled by the PCI host. */ 4503ef5d16fSAlan Douglas flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS); 4513ef5d16fSAlan Douglas if (!(flags & PCI_MSIX_FLAGS_ENABLE)) 4523ef5d16fSAlan Douglas return -EINVAL; 4533ef5d16fSAlan Douglas 4543ef5d16fSAlan Douglas reg = cap + PCI_MSIX_TABLE; 4553ef5d16fSAlan Douglas tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg); 4563ef5d16fSAlan Douglas bir = tbl_offset & PCI_MSIX_TABLE_BIR; 4573ef5d16fSAlan Douglas tbl_offset &= PCI_MSIX_TABLE_OFFSET; 4583ef5d16fSAlan Douglas 4593ef5d16fSAlan Douglas epf = &ep->epf[fn]; 4603ef5d16fSAlan Douglas msix_tbl = epf->epf_bar[bir]->addr + tbl_offset; 4613ef5d16fSAlan Douglas msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr; 4623ef5d16fSAlan Douglas msg_data = msix_tbl[(interrupt_num - 1)].msg_data; 4633ef5d16fSAlan Douglas 4643ef5d16fSAlan Douglas /* Set the outbound region if needed. */ 4653ef5d16fSAlan Douglas if (ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) || 4663ef5d16fSAlan Douglas ep->irq_pci_fn != fn) { 4673ef5d16fSAlan Douglas /* First region was reserved for IRQ writes. */ 46849e427e6SBjorn Helgaas cdns_pcie_set_outbound_region(pcie, 0, fn, 0, 4693ef5d16fSAlan Douglas false, 4703ef5d16fSAlan Douglas ep->irq_phys_addr, 4713ef5d16fSAlan Douglas msg_addr & ~pci_addr_mask, 4723ef5d16fSAlan Douglas pci_addr_mask + 1); 4733ef5d16fSAlan Douglas ep->irq_pci_addr = (msg_addr & ~pci_addr_mask); 4743ef5d16fSAlan Douglas ep->irq_pci_fn = fn; 4753ef5d16fSAlan Douglas } 4763ef5d16fSAlan Douglas writel(msg_data, ep->irq_cpu_addr + (msg_addr & pci_addr_mask)); 4773ef5d16fSAlan Douglas 4783ef5d16fSAlan Douglas return 0; 4793ef5d16fSAlan Douglas } 4803ef5d16fSAlan Douglas 481de80f95cSTom Joseph static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, 482de80f95cSTom Joseph enum pci_epc_irq_type type, 483de80f95cSTom Joseph u16 interrupt_num) 484de80f95cSTom Joseph { 485de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 486de80f95cSTom Joseph 487de80f95cSTom Joseph switch (type) { 488de80f95cSTom Joseph case PCI_EPC_IRQ_LEGACY: 489de80f95cSTom Joseph return cdns_pcie_ep_send_legacy_irq(ep, fn, 0); 490de80f95cSTom Joseph 491de80f95cSTom Joseph case PCI_EPC_IRQ_MSI: 492de80f95cSTom Joseph return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num); 493de80f95cSTom Joseph 4943ef5d16fSAlan Douglas case PCI_EPC_IRQ_MSIX: 4953ef5d16fSAlan Douglas return cdns_pcie_ep_send_msix_irq(ep, fn, interrupt_num); 4963ef5d16fSAlan Douglas 497de80f95cSTom Joseph default: 498de80f95cSTom Joseph break; 499de80f95cSTom Joseph } 500de80f95cSTom Joseph 501de80f95cSTom Joseph return -EINVAL; 502de80f95cSTom Joseph } 503de80f95cSTom Joseph 504de80f95cSTom Joseph static int cdns_pcie_ep_start(struct pci_epc *epc) 505de80f95cSTom Joseph { 506de80f95cSTom Joseph struct cdns_pcie_ep *ep = epc_get_drvdata(epc); 507de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 50840d957e6SKishon Vijay Abraham I struct device *dev = pcie->dev; 50940d957e6SKishon Vijay Abraham I int ret; 510de80f95cSTom Joseph 511de80f95cSTom Joseph /* 512de80f95cSTom Joseph * BIT(0) is hardwired to 1, hence function 0 is always enabled 513de80f95cSTom Joseph * and can't be disabled anyway. 514de80f95cSTom Joseph */ 515*a62074a9SKishon Vijay Abraham I cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, epc->function_num_map); 516de80f95cSTom Joseph 51740d957e6SKishon Vijay Abraham I ret = cdns_pcie_start_link(pcie); 51840d957e6SKishon Vijay Abraham I if (ret) { 51940d957e6SKishon Vijay Abraham I dev_err(dev, "Failed to start link\n"); 52040d957e6SKishon Vijay Abraham I return ret; 52140d957e6SKishon Vijay Abraham I } 52240d957e6SKishon Vijay Abraham I 523de80f95cSTom Joseph return 0; 524de80f95cSTom Joseph } 525de80f95cSTom Joseph 526de80f95cSTom Joseph static const struct pci_epc_features cdns_pcie_epc_features = { 527de80f95cSTom Joseph .linkup_notifier = false, 528de80f95cSTom Joseph .msi_capable = true, 5293ef5d16fSAlan Douglas .msix_capable = true, 530dbcc542fSKishon Vijay Abraham I .align = 256, 531de80f95cSTom Joseph }; 532de80f95cSTom Joseph 533de80f95cSTom Joseph static const struct pci_epc_features* 534de80f95cSTom Joseph cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) 535de80f95cSTom Joseph { 536de80f95cSTom Joseph return &cdns_pcie_epc_features; 537de80f95cSTom Joseph } 538de80f95cSTom Joseph 539de80f95cSTom Joseph static const struct pci_epc_ops cdns_pcie_epc_ops = { 540de80f95cSTom Joseph .write_header = cdns_pcie_ep_write_header, 541de80f95cSTom Joseph .set_bar = cdns_pcie_ep_set_bar, 542de80f95cSTom Joseph .clear_bar = cdns_pcie_ep_clear_bar, 543de80f95cSTom Joseph .map_addr = cdns_pcie_ep_map_addr, 544de80f95cSTom Joseph .unmap_addr = cdns_pcie_ep_unmap_addr, 545de80f95cSTom Joseph .set_msi = cdns_pcie_ep_set_msi, 546de80f95cSTom Joseph .get_msi = cdns_pcie_ep_get_msi, 5473ef5d16fSAlan Douglas .set_msix = cdns_pcie_ep_set_msix, 5483ef5d16fSAlan Douglas .get_msix = cdns_pcie_ep_get_msix, 549de80f95cSTom Joseph .raise_irq = cdns_pcie_ep_raise_irq, 550dbcc542fSKishon Vijay Abraham I .map_msi_irq = cdns_pcie_ep_map_msi_irq, 551de80f95cSTom Joseph .start = cdns_pcie_ep_start, 552de80f95cSTom Joseph .get_features = cdns_pcie_ep_get_features, 553de80f95cSTom Joseph }; 554de80f95cSTom Joseph 555de80f95cSTom Joseph 556de80f95cSTom Joseph int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) 557de80f95cSTom Joseph { 558de80f95cSTom Joseph struct device *dev = ep->pcie.dev; 559de80f95cSTom Joseph struct platform_device *pdev = to_platform_device(dev); 560de80f95cSTom Joseph struct device_node *np = dev->of_node; 561de80f95cSTom Joseph struct cdns_pcie *pcie = &ep->pcie; 562de80f95cSTom Joseph struct resource *res; 563de80f95cSTom Joseph struct pci_epc *epc; 564de80f95cSTom Joseph int ret; 565de80f95cSTom Joseph 566de80f95cSTom Joseph pcie->is_rc = false; 567de80f95cSTom Joseph 568e2dcd20bSDejin Zheng pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg"); 569de80f95cSTom Joseph if (IS_ERR(pcie->reg_base)) { 570de80f95cSTom Joseph dev_err(dev, "missing \"reg\"\n"); 571de80f95cSTom Joseph return PTR_ERR(pcie->reg_base); 572de80f95cSTom Joseph } 573de80f95cSTom Joseph 574de80f95cSTom Joseph res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); 575de80f95cSTom Joseph if (!res) { 576de80f95cSTom Joseph dev_err(dev, "missing \"mem\"\n"); 577de80f95cSTom Joseph return -EINVAL; 578de80f95cSTom Joseph } 579de80f95cSTom Joseph pcie->mem_res = res; 580de80f95cSTom Joseph 581e87d17caSKishon Vijay Abraham I ep->max_regions = CDNS_PCIE_MAX_OB; 582e87d17caSKishon Vijay Abraham I of_property_read_u32(np, "cdns,max-outbound-regions", &ep->max_regions); 583e87d17caSKishon Vijay Abraham I 584de80f95cSTom Joseph ep->ob_addr = devm_kcalloc(dev, 585de80f95cSTom Joseph ep->max_regions, sizeof(*ep->ob_addr), 586de80f95cSTom Joseph GFP_KERNEL); 587de80f95cSTom Joseph if (!ep->ob_addr) 588de80f95cSTom Joseph return -ENOMEM; 589de80f95cSTom Joseph 590de80f95cSTom Joseph /* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */ 591de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0)); 592de80f95cSTom Joseph 593de80f95cSTom Joseph epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops); 594de80f95cSTom Joseph if (IS_ERR(epc)) { 595de80f95cSTom Joseph dev_err(dev, "failed to create epc device\n"); 59619abcd79SKishon Vijay Abraham I return PTR_ERR(epc); 597de80f95cSTom Joseph } 598de80f95cSTom Joseph 599de80f95cSTom Joseph epc_set_drvdata(epc, ep); 600de80f95cSTom Joseph 601de80f95cSTom Joseph if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0) 602de80f95cSTom Joseph epc->max_functions = 1; 603de80f95cSTom Joseph 6043ef5d16fSAlan Douglas ep->epf = devm_kcalloc(dev, epc->max_functions, sizeof(*ep->epf), 6053ef5d16fSAlan Douglas GFP_KERNEL); 6063ef5d16fSAlan Douglas if (!ep->epf) 6073ef5d16fSAlan Douglas return -ENOMEM; 6083ef5d16fSAlan Douglas 609de80f95cSTom Joseph ret = pci_epc_mem_init(epc, pcie->mem_res->start, 610975cf23eSLad Prabhakar resource_size(pcie->mem_res), PAGE_SIZE); 611de80f95cSTom Joseph if (ret < 0) { 612de80f95cSTom Joseph dev_err(dev, "failed to initialize the memory space\n"); 61319abcd79SKishon Vijay Abraham I return ret; 614de80f95cSTom Joseph } 615de80f95cSTom Joseph 616de80f95cSTom Joseph ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, 617de80f95cSTom Joseph SZ_128K); 618de80f95cSTom Joseph if (!ep->irq_cpu_addr) { 619de80f95cSTom Joseph dev_err(dev, "failed to reserve memory space for MSI\n"); 620de80f95cSTom Joseph ret = -ENOMEM; 621de80f95cSTom Joseph goto free_epc_mem; 622de80f95cSTom Joseph } 623de80f95cSTom Joseph ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE; 624de80f95cSTom Joseph /* Reserve region 0 for IRQs */ 625de80f95cSTom Joseph set_bit(0, &ep->ob_region_map); 626a8b661ebSKishon Vijay Abraham I spin_lock_init(&ep->lock); 627de80f95cSTom Joseph 628de80f95cSTom Joseph return 0; 629de80f95cSTom Joseph 630de80f95cSTom Joseph free_epc_mem: 631de80f95cSTom Joseph pci_epc_mem_exit(epc); 632de80f95cSTom Joseph 633de80f95cSTom Joseph return ret; 634de80f95cSTom Joseph } 635