16e0832faSShawn Lin // SPDX-License-Identifier: GPL-2.0 26e0832faSShawn Lin /* 36e0832faSShawn Lin * Synopsys DesignWare PCIe host controller driver 46e0832faSShawn Lin * 56e0832faSShawn Lin * Copyright (C) 2013 Samsung Electronics Co., Ltd. 67ecd4a81SAlexander A. Klimov * https://www.samsung.com 76e0832faSShawn Lin * 86e0832faSShawn Lin * Author: Jingoo Han <jg1.han@samsung.com> 96e0832faSShawn Lin */ 106e0832faSShawn Lin 116e0832faSShawn Lin #include <linux/irqchip/chained_irq.h> 126e0832faSShawn Lin #include <linux/irqdomain.h> 13bbd8810dSKrzysztof Wilczynski #include <linux/msi.h> 146e0832faSShawn Lin #include <linux/of_address.h> 156e0832faSShawn Lin #include <linux/of_pci.h> 166e0832faSShawn Lin #include <linux/pci_regs.h> 176e0832faSShawn Lin #include <linux/platform_device.h> 186e0832faSShawn Lin 196e0832faSShawn Lin #include "../../pci.h" 206e0832faSShawn Lin #include "pcie-designware.h" 216e0832faSShawn Lin 226e0832faSShawn Lin static struct pci_ops dw_pcie_ops; 23c2b0c098SRob Herring static struct pci_ops dw_child_pcie_ops; 246e0832faSShawn Lin 256e0832faSShawn Lin static void dw_msi_ack_irq(struct irq_data *d) 266e0832faSShawn Lin { 276e0832faSShawn Lin irq_chip_ack_parent(d); 286e0832faSShawn Lin } 296e0832faSShawn Lin 306e0832faSShawn Lin static void dw_msi_mask_irq(struct irq_data *d) 316e0832faSShawn Lin { 326e0832faSShawn Lin pci_msi_mask_irq(d); 336e0832faSShawn Lin irq_chip_mask_parent(d); 346e0832faSShawn Lin } 356e0832faSShawn Lin 366e0832faSShawn Lin static void dw_msi_unmask_irq(struct irq_data *d) 376e0832faSShawn Lin { 386e0832faSShawn Lin pci_msi_unmask_irq(d); 396e0832faSShawn Lin irq_chip_unmask_parent(d); 406e0832faSShawn Lin } 416e0832faSShawn Lin 426e0832faSShawn Lin static struct irq_chip dw_pcie_msi_irq_chip = { 436e0832faSShawn Lin .name = "PCI-MSI", 446e0832faSShawn Lin .irq_ack = dw_msi_ack_irq, 456e0832faSShawn Lin .irq_mask = dw_msi_mask_irq, 466e0832faSShawn Lin .irq_unmask = dw_msi_unmask_irq, 476e0832faSShawn Lin }; 486e0832faSShawn Lin 496e0832faSShawn Lin static struct msi_domain_info dw_pcie_msi_domain_info = { 506e0832faSShawn Lin .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 516e0832faSShawn Lin MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), 526e0832faSShawn Lin .chip = &dw_pcie_msi_irq_chip, 536e0832faSShawn Lin }; 546e0832faSShawn Lin 556e0832faSShawn Lin /* MSI int handler */ 5660b3c27fSSerge Semin irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp) 576e0832faSShawn Lin { 58d21faba1SMarc Zyngier int i, pos; 591137e61dSNiklas Cassel unsigned long val; 601137e61dSNiklas Cassel u32 status, num_ctrls; 616e0832faSShawn Lin irqreturn_t ret = IRQ_NONE; 62f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 636e0832faSShawn Lin 646e0832faSShawn Lin num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 656e0832faSShawn Lin 666e0832faSShawn Lin for (i = 0; i < num_ctrls; i++) { 67f81c770dSRob Herring status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + 68f81c770dSRob Herring (i * MSI_REG_CTRL_BLOCK_SIZE)); 691137e61dSNiklas Cassel if (!status) 706e0832faSShawn Lin continue; 716e0832faSShawn Lin 726e0832faSShawn Lin ret = IRQ_HANDLED; 731137e61dSNiklas Cassel val = status; 746e0832faSShawn Lin pos = 0; 751137e61dSNiklas Cassel while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, 766e0832faSShawn Lin pos)) != MAX_MSI_IRQS_PER_CTRL) { 77d21faba1SMarc Zyngier generic_handle_domain_irq(pp->irq_domain, 786e0832faSShawn Lin (i * MAX_MSI_IRQS_PER_CTRL) + 796e0832faSShawn Lin pos); 806e0832faSShawn Lin pos++; 816e0832faSShawn Lin } 826e0832faSShawn Lin } 836e0832faSShawn Lin 846e0832faSShawn Lin return ret; 856e0832faSShawn Lin } 866e0832faSShawn Lin 876e0832faSShawn Lin /* Chained MSI interrupt service routine */ 886e0832faSShawn Lin static void dw_chained_msi_isr(struct irq_desc *desc) 896e0832faSShawn Lin { 906e0832faSShawn Lin struct irq_chip *chip = irq_desc_get_chip(desc); 9160b3c27fSSerge Semin struct dw_pcie_rp *pp; 926e0832faSShawn Lin 936e0832faSShawn Lin chained_irq_enter(chip, desc); 946e0832faSShawn Lin 956e0832faSShawn Lin pp = irq_desc_get_handler_data(desc); 966e0832faSShawn Lin dw_handle_msi_irq(pp); 976e0832faSShawn Lin 986e0832faSShawn Lin chained_irq_exit(chip, desc); 996e0832faSShawn Lin } 1006e0832faSShawn Lin 10159ea68b3SGustavo Pimentel static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) 1026e0832faSShawn Lin { 10360b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 1046e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1056e0832faSShawn Lin u64 msi_target; 1066e0832faSShawn Lin 1076e0832faSShawn Lin msi_target = (u64)pp->msi_data; 1086e0832faSShawn Lin 1096e0832faSShawn Lin msg->address_lo = lower_32_bits(msi_target); 1106e0832faSShawn Lin msg->address_hi = upper_32_bits(msi_target); 1116e0832faSShawn Lin 11259ea68b3SGustavo Pimentel msg->data = d->hwirq; 1136e0832faSShawn Lin 1146e0832faSShawn Lin dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", 11559ea68b3SGustavo Pimentel (int)d->hwirq, msg->address_hi, msg->address_lo); 1166e0832faSShawn Lin } 1176e0832faSShawn Lin 118fd5288a3SGustavo Pimentel static int dw_pci_msi_set_affinity(struct irq_data *d, 1196e0832faSShawn Lin const struct cpumask *mask, bool force) 1206e0832faSShawn Lin { 1216e0832faSShawn Lin return -EINVAL; 1226e0832faSShawn Lin } 1236e0832faSShawn Lin 12440e9892eSGustavo Pimentel static void dw_pci_bottom_mask(struct irq_data *d) 1256e0832faSShawn Lin { 12660b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 127f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1286e0832faSShawn Lin unsigned int res, bit, ctrl; 1296e0832faSShawn Lin unsigned long flags; 1306e0832faSShawn Lin 1316e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 1326e0832faSShawn Lin 13340e9892eSGustavo Pimentel ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1346e0832faSShawn Lin res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 13540e9892eSGustavo Pimentel bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1366e0832faSShawn Lin 13765772257SGustavo Pimentel pp->irq_mask[ctrl] |= BIT(bit); 138f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); 1396e0832faSShawn Lin 1406e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 1416e0832faSShawn Lin } 1426e0832faSShawn Lin 14340e9892eSGustavo Pimentel static void dw_pci_bottom_unmask(struct irq_data *d) 1446e0832faSShawn Lin { 14560b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 146f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1476e0832faSShawn Lin unsigned int res, bit, ctrl; 1486e0832faSShawn Lin unsigned long flags; 1496e0832faSShawn Lin 1506e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 1516e0832faSShawn Lin 15240e9892eSGustavo Pimentel ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1536e0832faSShawn Lin res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 15440e9892eSGustavo Pimentel bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1556e0832faSShawn Lin 15665772257SGustavo Pimentel pp->irq_mask[ctrl] &= ~BIT(bit); 157f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); 1586e0832faSShawn Lin 1596e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 1606e0832faSShawn Lin } 1616e0832faSShawn Lin 1626e0832faSShawn Lin static void dw_pci_bottom_ack(struct irq_data *d) 1636e0832faSShawn Lin { 16460b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 165f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1663f7bb2ecSMarc Zyngier unsigned int res, bit, ctrl; 1676e0832faSShawn Lin 1683f7bb2ecSMarc Zyngier ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1693f7bb2ecSMarc Zyngier res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 1703f7bb2ecSMarc Zyngier bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1716e0832faSShawn Lin 172f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_STATUS + res, BIT(bit)); 1736e0832faSShawn Lin } 1746e0832faSShawn Lin 1756e0832faSShawn Lin static struct irq_chip dw_pci_msi_bottom_irq_chip = { 1766e0832faSShawn Lin .name = "DWPCI-MSI", 1776e0832faSShawn Lin .irq_ack = dw_pci_bottom_ack, 1786e0832faSShawn Lin .irq_compose_msi_msg = dw_pci_setup_msi_msg, 1796e0832faSShawn Lin .irq_set_affinity = dw_pci_msi_set_affinity, 1806e0832faSShawn Lin .irq_mask = dw_pci_bottom_mask, 1816e0832faSShawn Lin .irq_unmask = dw_pci_bottom_unmask, 1826e0832faSShawn Lin }; 1836e0832faSShawn Lin 1846e0832faSShawn Lin static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, 1856e0832faSShawn Lin unsigned int virq, unsigned int nr_irqs, 1866e0832faSShawn Lin void *args) 1876e0832faSShawn Lin { 18860b3c27fSSerge Semin struct dw_pcie_rp *pp = domain->host_data; 1896e0832faSShawn Lin unsigned long flags; 1906e0832faSShawn Lin u32 i; 1916e0832faSShawn Lin int bit; 1926e0832faSShawn Lin 1936e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 1946e0832faSShawn Lin 1956e0832faSShawn Lin bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, 1966e0832faSShawn Lin order_base_2(nr_irqs)); 1976e0832faSShawn Lin 1986e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 1996e0832faSShawn Lin 2006e0832faSShawn Lin if (bit < 0) 2016e0832faSShawn Lin return -ENOSPC; 2026e0832faSShawn Lin 2036e0832faSShawn Lin for (i = 0; i < nr_irqs; i++) 2046e0832faSShawn Lin irq_domain_set_info(domain, virq + i, bit + i, 2059f67437bSKishon Vijay Abraham I pp->msi_irq_chip, 2066e0832faSShawn Lin pp, handle_edge_irq, 2076e0832faSShawn Lin NULL, NULL); 2086e0832faSShawn Lin 2096e0832faSShawn Lin return 0; 2106e0832faSShawn Lin } 2116e0832faSShawn Lin 2126e0832faSShawn Lin static void dw_pcie_irq_domain_free(struct irq_domain *domain, 2136e0832faSShawn Lin unsigned int virq, unsigned int nr_irqs) 2146e0832faSShawn Lin { 2154cfae0f1SGustavo Pimentel struct irq_data *d = irq_domain_get_irq_data(domain, virq); 21660b3c27fSSerge Semin struct dw_pcie_rp *pp = domain->host_data; 2176e0832faSShawn Lin unsigned long flags; 2186e0832faSShawn Lin 2196e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 2206e0832faSShawn Lin 2214cfae0f1SGustavo Pimentel bitmap_release_region(pp->msi_irq_in_use, d->hwirq, 2226e0832faSShawn Lin order_base_2(nr_irqs)); 2236e0832faSShawn Lin 2246e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 2256e0832faSShawn Lin } 2266e0832faSShawn Lin 2276e0832faSShawn Lin static const struct irq_domain_ops dw_pcie_msi_domain_ops = { 2286e0832faSShawn Lin .alloc = dw_pcie_irq_domain_alloc, 2296e0832faSShawn Lin .free = dw_pcie_irq_domain_free, 2306e0832faSShawn Lin }; 2316e0832faSShawn Lin 23260b3c27fSSerge Semin int dw_pcie_allocate_domains(struct dw_pcie_rp *pp) 2336e0832faSShawn Lin { 2346e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 2356e0832faSShawn Lin struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); 2366e0832faSShawn Lin 2376e0832faSShawn Lin pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, 2386e0832faSShawn Lin &dw_pcie_msi_domain_ops, pp); 2396e0832faSShawn Lin if (!pp->irq_domain) { 2406e0832faSShawn Lin dev_err(pci->dev, "Failed to create IRQ domain\n"); 2416e0832faSShawn Lin return -ENOMEM; 2426e0832faSShawn Lin } 2436e0832faSShawn Lin 2440414b93eSMarc Zyngier irq_domain_update_bus_token(pp->irq_domain, DOMAIN_BUS_NEXUS); 2450414b93eSMarc Zyngier 2466e0832faSShawn Lin pp->msi_domain = pci_msi_create_irq_domain(fwnode, 2476e0832faSShawn Lin &dw_pcie_msi_domain_info, 2486e0832faSShawn Lin pp->irq_domain); 2496e0832faSShawn Lin if (!pp->msi_domain) { 2506e0832faSShawn Lin dev_err(pci->dev, "Failed to create MSI domain\n"); 2516e0832faSShawn Lin irq_domain_remove(pp->irq_domain); 2526e0832faSShawn Lin return -ENOMEM; 2536e0832faSShawn Lin } 2546e0832faSShawn Lin 2556e0832faSShawn Lin return 0; 2566e0832faSShawn Lin } 2576e0832faSShawn Lin 25860b3c27fSSerge Semin static void dw_pcie_free_msi(struct dw_pcie_rp *pp) 2596e0832faSShawn Lin { 260*db388348SDmitry Baryshkov u32 ctrl; 261*db388348SDmitry Baryshkov 262*db388348SDmitry Baryshkov for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) { 263*db388348SDmitry Baryshkov if (pp->msi_irq[ctrl] > 0) 264*db388348SDmitry Baryshkov irq_set_chained_handler_and_data(pp->msi_irq[ctrl], 265*db388348SDmitry Baryshkov NULL, NULL); 266*db388348SDmitry Baryshkov } 2676e0832faSShawn Lin 2686e0832faSShawn Lin irq_domain_remove(pp->msi_domain); 2696e0832faSShawn Lin irq_domain_remove(pp->irq_domain); 270dc69a3d5SJisheng Zhang 27107940c36SJisheng Zhang if (pp->msi_data) { 27207940c36SJisheng Zhang struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 27307940c36SJisheng Zhang struct device *dev = pci->dev; 27407940c36SJisheng Zhang 27535797e67SWill McVicker dma_unmap_page(dev, pp->msi_data, PAGE_SIZE, DMA_FROM_DEVICE); 27635797e67SWill McVicker if (pp->msi_page) 27735797e67SWill McVicker __free_page(pp->msi_page); 27807940c36SJisheng Zhang } 2796e0832faSShawn Lin } 2806e0832faSShawn Lin 28160b3c27fSSerge Semin static void dw_pcie_msi_init(struct dw_pcie_rp *pp) 2826e0832faSShawn Lin { 2836e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 28407940c36SJisheng Zhang u64 msi_target = (u64)pp->msi_data; 2856e0832faSShawn Lin 28659fbab1aSRob Herring if (!pci_msi_enabled() || !pp->has_msi_ctrl) 287cf627713SRob Herring return; 288cf627713SRob Herring 2896e0832faSShawn Lin /* Program the msi_data */ 290f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target)); 291f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target)); 2926e0832faSShawn Lin } 2936e0832faSShawn Lin 294226ec087SDmitry Baryshkov static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) 295226ec087SDmitry Baryshkov { 296226ec087SDmitry Baryshkov struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 297226ec087SDmitry Baryshkov struct device *dev = pci->dev; 298226ec087SDmitry Baryshkov struct platform_device *pdev = to_platform_device(dev); 299226ec087SDmitry Baryshkov int ret; 300226ec087SDmitry Baryshkov u32 ctrl, num_ctrls; 301226ec087SDmitry Baryshkov 302226ec087SDmitry Baryshkov num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 303226ec087SDmitry Baryshkov for (ctrl = 0; ctrl < num_ctrls; ctrl++) 304226ec087SDmitry Baryshkov pp->irq_mask[ctrl] = ~0; 305226ec087SDmitry Baryshkov 306*db388348SDmitry Baryshkov if (!pp->msi_irq[0]) { 307*db388348SDmitry Baryshkov pp->msi_irq[0] = platform_get_irq_byname_optional(pdev, "msi"); 308*db388348SDmitry Baryshkov if (pp->msi_irq[0] < 0) { 309*db388348SDmitry Baryshkov pp->msi_irq[0] = platform_get_irq(pdev, 0); 310*db388348SDmitry Baryshkov if (pp->msi_irq[0] < 0) 311*db388348SDmitry Baryshkov return pp->msi_irq[0]; 312226ec087SDmitry Baryshkov } 313226ec087SDmitry Baryshkov } 314226ec087SDmitry Baryshkov 315226ec087SDmitry Baryshkov pp->msi_irq_chip = &dw_pci_msi_bottom_irq_chip; 316226ec087SDmitry Baryshkov 317226ec087SDmitry Baryshkov ret = dw_pcie_allocate_domains(pp); 318226ec087SDmitry Baryshkov if (ret) 319226ec087SDmitry Baryshkov return ret; 320226ec087SDmitry Baryshkov 321*db388348SDmitry Baryshkov for (ctrl = 0; ctrl < num_ctrls; ctrl++) { 322*db388348SDmitry Baryshkov if (pp->msi_irq[ctrl] > 0) 323*db388348SDmitry Baryshkov irq_set_chained_handler_and_data(pp->msi_irq[ctrl], 324226ec087SDmitry Baryshkov dw_chained_msi_isr, pp); 325*db388348SDmitry Baryshkov } 326226ec087SDmitry Baryshkov 327226ec087SDmitry Baryshkov ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 328226ec087SDmitry Baryshkov if (ret) 329226ec087SDmitry Baryshkov dev_warn(dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n"); 330226ec087SDmitry Baryshkov 331226ec087SDmitry Baryshkov pp->msi_page = alloc_page(GFP_DMA32); 332226ec087SDmitry Baryshkov pp->msi_data = dma_map_page(dev, pp->msi_page, 0, 333226ec087SDmitry Baryshkov PAGE_SIZE, DMA_FROM_DEVICE); 334226ec087SDmitry Baryshkov ret = dma_mapping_error(dev, pp->msi_data); 335226ec087SDmitry Baryshkov if (ret) { 336226ec087SDmitry Baryshkov dev_err(pci->dev, "Failed to map MSI data\n"); 337226ec087SDmitry Baryshkov __free_page(pp->msi_page); 338226ec087SDmitry Baryshkov pp->msi_page = NULL; 339226ec087SDmitry Baryshkov pp->msi_data = 0; 340226ec087SDmitry Baryshkov dw_pcie_free_msi(pp); 341226ec087SDmitry Baryshkov 342226ec087SDmitry Baryshkov return ret; 343226ec087SDmitry Baryshkov } 344226ec087SDmitry Baryshkov 345226ec087SDmitry Baryshkov return 0; 346226ec087SDmitry Baryshkov } 347226ec087SDmitry Baryshkov 34860b3c27fSSerge Semin int dw_pcie_host_init(struct dw_pcie_rp *pp) 3496e0832faSShawn Lin { 3506e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 3516e0832faSShawn Lin struct device *dev = pci->dev; 3526e0832faSShawn Lin struct device_node *np = dev->of_node; 3536e0832faSShawn Lin struct platform_device *pdev = to_platform_device(dev); 3547fe71aa8SRob Herring struct resource_entry *win; 3556e0832faSShawn Lin struct pci_host_bridge *bridge; 356bd42f310SSerge Semin struct resource *res; 3576e0832faSShawn Lin int ret; 3586e0832faSShawn Lin 35960a4352fSSerge Semin raw_spin_lock_init(&pp->lock); 3606e0832faSShawn Lin 361bd42f310SSerge Semin res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); 362bd42f310SSerge Semin if (res) { 363bd42f310SSerge Semin pp->cfg0_size = resource_size(res); 364bd42f310SSerge Semin pp->cfg0_base = res->start; 3652f5ab5afSRob Herring 366bd42f310SSerge Semin pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); 3672f5ab5afSRob Herring if (IS_ERR(pp->va_cfg0_base)) 3682f5ab5afSRob Herring return PTR_ERR(pp->va_cfg0_base); 3692f5ab5afSRob Herring } else { 3706e0832faSShawn Lin dev_err(dev, "Missing *config* reg space\n"); 3712f5ab5afSRob Herring return -ENODEV; 3726e0832faSShawn Lin } 3736e0832faSShawn Lin 374a0fd361dSRob Herring if (!pci->dbi_base) { 375bd42f310SSerge Semin res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); 376bd42f310SSerge Semin pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); 377a0fd361dSRob Herring if (IS_ERR(pci->dbi_base)) 378a0fd361dSRob Herring return PTR_ERR(pci->dbi_base); 379a0fd361dSRob Herring } 380a0fd361dSRob Herring 381e6fdd3bfSJisheng Zhang bridge = devm_pci_alloc_host_bridge(dev, 0); 3826e0832faSShawn Lin if (!bridge) 3836e0832faSShawn Lin return -ENOMEM; 3846e0832faSShawn Lin 385444ddca5SRob Herring pp->bridge = bridge; 386444ddca5SRob Herring 3872f5ab5afSRob Herring /* Get the I/O range from DT */ 3882f5ab5afSRob Herring win = resource_list_first_type(&bridge->windows, IORESOURCE_IO); 3892f5ab5afSRob Herring if (win) { 3900f71c60fSRob Herring pp->io_size = resource_size(win->res); 3910f71c60fSRob Herring pp->io_bus_addr = win->res->start - win->offset; 3920f71c60fSRob Herring pp->io_base = pci_pio_to_address(win->res->start); 3936e0832faSShawn Lin } 3946e0832faSShawn Lin 39539bc5006SRob Herring if (pci->link_gen < 1) 39639bc5006SRob Herring pci->link_gen = of_pci_get_max_link_speed(np); 39739bc5006SRob Herring 3987e919677SBjorn Andersson /* Set default bus ops */ 3997e919677SBjorn Andersson bridge->ops = &dw_pcie_ops; 4007e919677SBjorn Andersson bridge->child_ops = &dw_child_pcie_ops; 4017e919677SBjorn Andersson 4027e919677SBjorn Andersson if (pp->ops->host_init) { 4037e919677SBjorn Andersson ret = pp->ops->host_init(pp); 4047e919677SBjorn Andersson if (ret) 4057e919677SBjorn Andersson return ret; 4067e919677SBjorn Andersson } 4077e919677SBjorn Andersson 4089e2b5de5SJisheng Zhang if (pci_msi_enabled()) { 409f78f0263SRob Herring pp->has_msi_ctrl = !(pp->ops->msi_host_init || 410f78f0263SRob Herring of_property_read_bool(np, "msi-parent") || 411f78f0263SRob Herring of_property_read_bool(np, "msi-map")); 412f78f0263SRob Herring 413331e9bceSRob Herring if (!pp->num_vectors) { 4146e0832faSShawn Lin pp->num_vectors = MSI_DEF_NUM_VECTORS; 415331e9bceSRob Herring } else if (pp->num_vectors > MAX_MSI_IRQS) { 416331e9bceSRob Herring dev_err(dev, "Invalid number of vectors\n"); 417c6481d51SSerge Semin ret = -EINVAL; 418c6481d51SSerge Semin goto err_deinit_host; 4196e0832faSShawn Lin } 4206e0832faSShawn Lin 421f78f0263SRob Herring if (pp->ops->msi_host_init) { 422f78f0263SRob Herring ret = pp->ops->msi_host_init(pp); 423f78f0263SRob Herring if (ret < 0) 424c6481d51SSerge Semin goto err_deinit_host; 425f78f0263SRob Herring } else if (pp->has_msi_ctrl) { 426226ec087SDmitry Baryshkov ret = dw_pcie_msi_host_init(pp); 427226ec087SDmitry Baryshkov if (ret < 0) 428c6481d51SSerge Semin goto err_deinit_host; 429c6481d51SSerge Semin } 4305bcb1757SRob Herring } 4316e0832faSShawn Lin 43213e9d390SSerge Semin dw_pcie_version_detect(pci); 43313e9d390SSerge Semin 4348bcca265SHou Zhiqiang dw_pcie_iatu_detect(pci); 4356e0832faSShawn Lin 436ce06bf57SSerge Semin ret = dw_pcie_setup_rc(pp); 437ce06bf57SSerge Semin if (ret) 438ce06bf57SSerge Semin goto err_free_msi; 43959fbab1aSRob Herring 440a37beefbSSerge Semin if (!dw_pcie_link_up(pci)) { 441a37beefbSSerge Semin ret = dw_pcie_start_link(pci); 442886a9c13SRob Herring if (ret) 443886a9c13SRob Herring goto err_free_msi; 444886a9c13SRob Herring } 445886a9c13SRob Herring 446886a9c13SRob Herring /* Ignore errors, the link may come up later */ 447886a9c13SRob Herring dw_pcie_wait_for_link(pci); 448886a9c13SRob Herring 4496e0832faSShawn Lin bridge->sysdata = pp; 4506e0832faSShawn Lin 4511df79305SRob Herring ret = pci_host_probe(bridge); 452113fa857SSerge Semin if (ret) 453113fa857SSerge Semin goto err_stop_link; 454113fa857SSerge Semin 4556e0832faSShawn Lin return 0; 4566e0832faSShawn Lin 457113fa857SSerge Semin err_stop_link: 458a37beefbSSerge Semin dw_pcie_stop_link(pci); 459113fa857SSerge Semin 4609e2b5de5SJisheng Zhang err_free_msi: 461f78f0263SRob Herring if (pp->has_msi_ctrl) 4629e2b5de5SJisheng Zhang dw_pcie_free_msi(pp); 463c6481d51SSerge Semin 464c6481d51SSerge Semin err_deinit_host: 465c6481d51SSerge Semin if (pp->ops->host_deinit) 466c6481d51SSerge Semin pp->ops->host_deinit(pp); 467c6481d51SSerge Semin 4686e0832faSShawn Lin return ret; 4696e0832faSShawn Lin } 470ca98329dSVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_host_init); 4716e0832faSShawn Lin 47260b3c27fSSerge Semin void dw_pcie_host_deinit(struct dw_pcie_rp *pp) 4739d071cadSVidya Sagar { 474113fa857SSerge Semin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 475113fa857SSerge Semin 4765808d43eSRob Herring pci_stop_root_bus(pp->bridge->bus); 4775808d43eSRob Herring pci_remove_root_bus(pp->bridge->bus); 478113fa857SSerge Semin 479a37beefbSSerge Semin dw_pcie_stop_link(pci); 480113fa857SSerge Semin 481f78f0263SRob Herring if (pp->has_msi_ctrl) 4829d071cadSVidya Sagar dw_pcie_free_msi(pp); 483c6481d51SSerge Semin 484c6481d51SSerge Semin if (pp->ops->host_deinit) 485c6481d51SSerge Semin pp->ops->host_deinit(pp); 4869d071cadSVidya Sagar } 487ca98329dSVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); 4889d071cadSVidya Sagar 489c2b0c098SRob Herring static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, 490c2b0c098SRob Herring unsigned int devfn, int where) 4916e0832faSShawn Lin { 49260b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 4936e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 494ce06bf57SSerge Semin int type, ret; 495ce06bf57SSerge Semin u32 busdev; 4966e0832faSShawn Lin 49715b23906SHou Zhiqiang /* 49815b23906SHou Zhiqiang * Checking whether the link is up here is a last line of defense 49915b23906SHou Zhiqiang * against platforms that forward errors on the system bus as 50015b23906SHou Zhiqiang * SError upon PCI configuration transactions issued when the link 50115b23906SHou Zhiqiang * is down. This check is racy by definition and does not stop 50215b23906SHou Zhiqiang * the system from triggering an SError if the link goes down 50315b23906SHou Zhiqiang * after this check is performed. 50415b23906SHou Zhiqiang */ 50515b23906SHou Zhiqiang if (!dw_pcie_link_up(pci)) 50615b23906SHou Zhiqiang return NULL; 50715b23906SHou Zhiqiang 5086e0832faSShawn Lin busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | 5096e0832faSShawn Lin PCIE_ATU_FUNC(PCI_FUNC(devfn)); 5106e0832faSShawn Lin 5112ef6b06aSRob Herring if (pci_is_root_bus(bus->parent)) 5126e0832faSShawn Lin type = PCIE_ATU_TYPE_CFG0; 5132ef6b06aSRob Herring else 5146e0832faSShawn Lin type = PCIE_ATU_TYPE_CFG1; 5152ef6b06aSRob Herring 516ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, 517ce06bf57SSerge Semin pp->cfg0_size); 518ce06bf57SSerge Semin if (ret) 519ce06bf57SSerge Semin return NULL; 520689e349aSAndrey Smirnov 5212ef6b06aSRob Herring return pp->va_cfg0_base + where; 522c2b0c098SRob Herring } 523c2b0c098SRob Herring 524c2b0c098SRob Herring static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, 525c2b0c098SRob Herring int where, int size, u32 *val) 526c2b0c098SRob Herring { 52760b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 528c2b0c098SRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 529ce06bf57SSerge Semin int ret; 530c2b0c098SRob Herring 531c2b0c098SRob Herring ret = pci_generic_config_read(bus, devfn, where, size, val); 532ce06bf57SSerge Semin if (ret != PCIBIOS_SUCCESSFUL) 5336e0832faSShawn Lin return ret; 534ce06bf57SSerge Semin 535ce06bf57SSerge Semin if (pp->cfg0_io_shared) { 536ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, 537ce06bf57SSerge Semin pp->io_base, pp->io_bus_addr, 538ce06bf57SSerge Semin pp->io_size); 539ce06bf57SSerge Semin if (ret) 540ce06bf57SSerge Semin return PCIBIOS_SET_FAILED; 541ce06bf57SSerge Semin } 542ce06bf57SSerge Semin 543ce06bf57SSerge Semin return PCIBIOS_SUCCESSFUL; 5446e0832faSShawn Lin } 5456e0832faSShawn Lin 546c2b0c098SRob Herring static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, 5476e0832faSShawn Lin int where, int size, u32 val) 5486e0832faSShawn Lin { 54960b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 550c2b0c098SRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 551ce06bf57SSerge Semin int ret; 5526e0832faSShawn Lin 553c2b0c098SRob Herring ret = pci_generic_config_write(bus, devfn, where, size, val); 554ce06bf57SSerge Semin if (ret != PCIBIOS_SUCCESSFUL) 555c2b0c098SRob Herring return ret; 556ce06bf57SSerge Semin 557ce06bf57SSerge Semin if (pp->cfg0_io_shared) { 558ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, 559ce06bf57SSerge Semin pp->io_base, pp->io_bus_addr, 560ce06bf57SSerge Semin pp->io_size); 561ce06bf57SSerge Semin if (ret) 562ce06bf57SSerge Semin return PCIBIOS_SET_FAILED; 563ce06bf57SSerge Semin } 564ce06bf57SSerge Semin 565ce06bf57SSerge Semin return PCIBIOS_SUCCESSFUL; 5666e0832faSShawn Lin } 5676e0832faSShawn Lin 568c2b0c098SRob Herring static struct pci_ops dw_child_pcie_ops = { 569c2b0c098SRob Herring .map_bus = dw_pcie_other_conf_map_bus, 570c2b0c098SRob Herring .read = dw_pcie_rd_other_conf, 571c2b0c098SRob Herring .write = dw_pcie_wr_other_conf, 572c2b0c098SRob Herring }; 573c2b0c098SRob Herring 57427e7ed01SRob Herring void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) 57527e7ed01SRob Herring { 57660b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 57727e7ed01SRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 57827e7ed01SRob Herring 57927e7ed01SRob Herring if (PCI_SLOT(devfn) > 0) 58027e7ed01SRob Herring return NULL; 58127e7ed01SRob Herring 58227e7ed01SRob Herring return pci->dbi_base + where; 58327e7ed01SRob Herring } 58427e7ed01SRob Herring EXPORT_SYMBOL_GPL(dw_pcie_own_conf_map_bus); 58527e7ed01SRob Herring 5866e0832faSShawn Lin static struct pci_ops dw_pcie_ops = { 587c2b0c098SRob Herring .map_bus = dw_pcie_own_conf_map_bus, 588c2b0c098SRob Herring .read = pci_generic_config_read, 589c2b0c098SRob Herring .write = pci_generic_config_write, 5906e0832faSShawn Lin }; 5916e0832faSShawn Lin 592ce06bf57SSerge Semin static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) 5936e0832faSShawn Lin { 5946e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 595ce06bf57SSerge Semin struct resource_entry *entry; 596ce06bf57SSerge Semin int i, ret; 597ce06bf57SSerge Semin 598ce06bf57SSerge Semin /* Note the very first outbound ATU is used for CFG IOs */ 599ce06bf57SSerge Semin if (!pci->num_ob_windows) { 600ce06bf57SSerge Semin dev_err(pci->dev, "No outbound iATU found\n"); 601ce06bf57SSerge Semin return -EINVAL; 602ce06bf57SSerge Semin } 603ce06bf57SSerge Semin 604ce06bf57SSerge Semin /* 605ce06bf57SSerge Semin * Ensure all outbound windows are disabled before proceeding with 606ce06bf57SSerge Semin * the MEM/IO ranges setups. 607ce06bf57SSerge Semin */ 608ce06bf57SSerge Semin for (i = 0; i < pci->num_ob_windows; i++) 609ce06bf57SSerge Semin dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i); 610ce06bf57SSerge Semin 611ce06bf57SSerge Semin i = 0; 612ce06bf57SSerge Semin resource_list_for_each_entry(entry, &pp->bridge->windows) { 613ce06bf57SSerge Semin if (resource_type(entry->res) != IORESOURCE_MEM) 614ce06bf57SSerge Semin continue; 615ce06bf57SSerge Semin 616ce06bf57SSerge Semin if (pci->num_ob_windows <= ++i) 617ce06bf57SSerge Semin break; 618ce06bf57SSerge Semin 619ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_MEM, 620ce06bf57SSerge Semin entry->res->start, 621ce06bf57SSerge Semin entry->res->start - entry->offset, 622ce06bf57SSerge Semin resource_size(entry->res)); 623ce06bf57SSerge Semin if (ret) { 624ce06bf57SSerge Semin dev_err(pci->dev, "Failed to set MEM range %pr\n", 625ce06bf57SSerge Semin entry->res); 626ce06bf57SSerge Semin return ret; 627ce06bf57SSerge Semin } 628ce06bf57SSerge Semin } 629ce06bf57SSerge Semin 630ce06bf57SSerge Semin if (pp->io_size) { 631ce06bf57SSerge Semin if (pci->num_ob_windows > ++i) { 632ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_IO, 633ce06bf57SSerge Semin pp->io_base, 634ce06bf57SSerge Semin pp->io_bus_addr, 635ce06bf57SSerge Semin pp->io_size); 636ce06bf57SSerge Semin if (ret) { 637ce06bf57SSerge Semin dev_err(pci->dev, "Failed to set IO range %pr\n", 638ce06bf57SSerge Semin entry->res); 639ce06bf57SSerge Semin return ret; 640ce06bf57SSerge Semin } 641ce06bf57SSerge Semin } else { 642ce06bf57SSerge Semin pp->cfg0_io_shared = true; 643ce06bf57SSerge Semin } 644ce06bf57SSerge Semin } 645ce06bf57SSerge Semin 646ce06bf57SSerge Semin if (pci->num_ob_windows <= i) 647ce06bf57SSerge Semin dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n", 648ce06bf57SSerge Semin pci->num_ob_windows); 649ce06bf57SSerge Semin 650ce06bf57SSerge Semin return 0; 651ce06bf57SSerge Semin } 652ce06bf57SSerge Semin 653ce06bf57SSerge Semin int dw_pcie_setup_rc(struct dw_pcie_rp *pp) 654ce06bf57SSerge Semin { 655ce06bf57SSerge Semin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 656ce06bf57SSerge Semin u32 val, ctrl, num_ctrls; 657ce06bf57SSerge Semin int ret; 6586e0832faSShawn Lin 6593924bc2fSVidya Sagar /* 6603924bc2fSVidya Sagar * Enable DBI read-only registers for writing/updating configuration. 6613924bc2fSVidya Sagar * Write permission gets disabled towards the end of this function. 6623924bc2fSVidya Sagar */ 6633924bc2fSVidya Sagar dw_pcie_dbi_ro_wr_en(pci); 6643924bc2fSVidya Sagar 6656e0832faSShawn Lin dw_pcie_setup(pci); 6666e0832faSShawn Lin 667f78f0263SRob Herring if (pp->has_msi_ctrl) { 6686e0832faSShawn Lin num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 6696e0832faSShawn Lin 6706e0832faSShawn Lin /* Initialize IRQ Status array */ 671830920e0SMarc Zyngier for (ctrl = 0; ctrl < num_ctrls; ctrl++) { 672f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + 6736e0832faSShawn Lin (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 674f81c770dSRob Herring pp->irq_mask[ctrl]); 675f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + 676830920e0SMarc Zyngier (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 677f81c770dSRob Herring ~0); 678830920e0SMarc Zyngier } 679fd8a44bdSKishon Vijay Abraham I } 6806e0832faSShawn Lin 681294353d9SJisheng Zhang dw_pcie_msi_init(pp); 682294353d9SJisheng Zhang 6836e0832faSShawn Lin /* Setup RC BARs */ 6846e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); 6856e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); 6866e0832faSShawn Lin 6876e0832faSShawn Lin /* Setup interrupt pins */ 6886e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); 6896e0832faSShawn Lin val &= 0xffff00ff; 6906e0832faSShawn Lin val |= 0x00000100; 6916e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); 6926e0832faSShawn Lin 6936e0832faSShawn Lin /* Setup bus numbers */ 6946e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); 6956e0832faSShawn Lin val &= 0xff000000; 6966e0832faSShawn Lin val |= 0x00ff0100; 6976e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); 6986e0832faSShawn Lin 6996e0832faSShawn Lin /* Setup command register */ 7006e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCI_COMMAND); 7016e0832faSShawn Lin val &= 0xffff0000; 7026e0832faSShawn Lin val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | 7036e0832faSShawn Lin PCI_COMMAND_MASTER | PCI_COMMAND_SERR; 7046e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_COMMAND, val); 7056e0832faSShawn Lin 7066e0832faSShawn Lin /* 707444ddca5SRob Herring * If the platform provides its own child bus config accesses, it means 708444ddca5SRob Herring * the platform uses its own address translation component rather than 709444ddca5SRob Herring * ATU, so we should not program the ATU here. 7106e0832faSShawn Lin */ 711c2b0c098SRob Herring if (pp->bridge->child_ops == &dw_child_pcie_ops) { 712ce06bf57SSerge Semin ret = dw_pcie_iatu_setup(pp); 713ce06bf57SSerge Semin if (ret) 714ce06bf57SSerge Semin return ret; 7156e0832faSShawn Lin } 7166e0832faSShawn Lin 717f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); 7186e0832faSShawn Lin 7196e0832faSShawn Lin /* Program correct class for RC */ 720f81c770dSRob Herring dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); 7216e0832faSShawn Lin 722f81c770dSRob Herring val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); 7236e0832faSShawn Lin val |= PORT_LOGIC_SPEED_CHANGE; 724f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); 7253924bc2fSVidya Sagar 7263924bc2fSVidya Sagar dw_pcie_dbi_ro_wr_dis(pci); 727ce06bf57SSerge Semin 728ce06bf57SSerge Semin return 0; 7296e0832faSShawn Lin } 730ca98329dSVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); 731