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 "pcie-designware.h" 206e0832faSShawn Lin 216e0832faSShawn Lin static struct pci_ops dw_pcie_ops; 22c2b0c098SRob Herring static struct pci_ops dw_child_pcie_ops; 236e0832faSShawn Lin 246e0832faSShawn Lin static void dw_msi_ack_irq(struct irq_data *d) 256e0832faSShawn Lin { 266e0832faSShawn Lin irq_chip_ack_parent(d); 276e0832faSShawn Lin } 286e0832faSShawn Lin 296e0832faSShawn Lin static void dw_msi_mask_irq(struct irq_data *d) 306e0832faSShawn Lin { 316e0832faSShawn Lin pci_msi_mask_irq(d); 326e0832faSShawn Lin irq_chip_mask_parent(d); 336e0832faSShawn Lin } 346e0832faSShawn Lin 356e0832faSShawn Lin static void dw_msi_unmask_irq(struct irq_data *d) 366e0832faSShawn Lin { 376e0832faSShawn Lin pci_msi_unmask_irq(d); 386e0832faSShawn Lin irq_chip_unmask_parent(d); 396e0832faSShawn Lin } 406e0832faSShawn Lin 416e0832faSShawn Lin static struct irq_chip dw_pcie_msi_irq_chip = { 426e0832faSShawn Lin .name = "PCI-MSI", 436e0832faSShawn Lin .irq_ack = dw_msi_ack_irq, 446e0832faSShawn Lin .irq_mask = dw_msi_mask_irq, 456e0832faSShawn Lin .irq_unmask = dw_msi_unmask_irq, 466e0832faSShawn Lin }; 476e0832faSShawn Lin 486e0832faSShawn Lin static struct msi_domain_info dw_pcie_msi_domain_info = { 496e0832faSShawn Lin .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 506e0832faSShawn Lin MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), 516e0832faSShawn Lin .chip = &dw_pcie_msi_irq_chip, 526e0832faSShawn Lin }; 536e0832faSShawn Lin 546e0832faSShawn Lin /* MSI int handler */ 5560b3c27fSSerge Semin irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp) 566e0832faSShawn Lin { 57d21faba1SMarc Zyngier int i, pos; 581137e61dSNiklas Cassel unsigned long val; 591137e61dSNiklas Cassel u32 status, num_ctrls; 606e0832faSShawn Lin irqreturn_t ret = IRQ_NONE; 61f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 626e0832faSShawn Lin 636e0832faSShawn Lin num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 646e0832faSShawn Lin 656e0832faSShawn Lin for (i = 0; i < num_ctrls; i++) { 66f81c770dSRob Herring status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + 67f81c770dSRob Herring (i * MSI_REG_CTRL_BLOCK_SIZE)); 681137e61dSNiklas Cassel if (!status) 696e0832faSShawn Lin continue; 706e0832faSShawn Lin 716e0832faSShawn Lin ret = IRQ_HANDLED; 721137e61dSNiklas Cassel val = status; 736e0832faSShawn Lin pos = 0; 741137e61dSNiklas Cassel while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, 756e0832faSShawn Lin pos)) != MAX_MSI_IRQS_PER_CTRL) { 76d21faba1SMarc Zyngier generic_handle_domain_irq(pp->irq_domain, 776e0832faSShawn Lin (i * MAX_MSI_IRQS_PER_CTRL) + 786e0832faSShawn Lin pos); 796e0832faSShawn Lin pos++; 806e0832faSShawn Lin } 816e0832faSShawn Lin } 826e0832faSShawn Lin 836e0832faSShawn Lin return ret; 846e0832faSShawn Lin } 856e0832faSShawn Lin 866e0832faSShawn Lin /* Chained MSI interrupt service routine */ 876e0832faSShawn Lin static void dw_chained_msi_isr(struct irq_desc *desc) 886e0832faSShawn Lin { 896e0832faSShawn Lin struct irq_chip *chip = irq_desc_get_chip(desc); 9060b3c27fSSerge Semin struct dw_pcie_rp *pp; 916e0832faSShawn Lin 926e0832faSShawn Lin chained_irq_enter(chip, desc); 936e0832faSShawn Lin 946e0832faSShawn Lin pp = irq_desc_get_handler_data(desc); 956e0832faSShawn Lin dw_handle_msi_irq(pp); 966e0832faSShawn Lin 976e0832faSShawn Lin chained_irq_exit(chip, desc); 986e0832faSShawn Lin } 996e0832faSShawn Lin 10059ea68b3SGustavo Pimentel static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) 1016e0832faSShawn Lin { 10260b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 1036e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1046e0832faSShawn Lin u64 msi_target; 1056e0832faSShawn Lin 1066e0832faSShawn Lin msi_target = (u64)pp->msi_data; 1076e0832faSShawn Lin 1086e0832faSShawn Lin msg->address_lo = lower_32_bits(msi_target); 1096e0832faSShawn Lin msg->address_hi = upper_32_bits(msi_target); 1106e0832faSShawn Lin 11159ea68b3SGustavo Pimentel msg->data = d->hwirq; 1126e0832faSShawn Lin 1136e0832faSShawn Lin dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", 11459ea68b3SGustavo Pimentel (int)d->hwirq, msg->address_hi, msg->address_lo); 1156e0832faSShawn Lin } 1166e0832faSShawn Lin 117fd5288a3SGustavo Pimentel static int dw_pci_msi_set_affinity(struct irq_data *d, 1186e0832faSShawn Lin const struct cpumask *mask, bool force) 1196e0832faSShawn Lin { 1206e0832faSShawn Lin return -EINVAL; 1216e0832faSShawn Lin } 1226e0832faSShawn Lin 12340e9892eSGustavo Pimentel static void dw_pci_bottom_mask(struct irq_data *d) 1246e0832faSShawn Lin { 12560b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 126f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1276e0832faSShawn Lin unsigned int res, bit, ctrl; 1286e0832faSShawn Lin unsigned long flags; 1296e0832faSShawn Lin 1306e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 1316e0832faSShawn Lin 13240e9892eSGustavo Pimentel ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1336e0832faSShawn Lin res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 13440e9892eSGustavo Pimentel bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1356e0832faSShawn Lin 13665772257SGustavo Pimentel pp->irq_mask[ctrl] |= BIT(bit); 137f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); 1386e0832faSShawn Lin 1396e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 1406e0832faSShawn Lin } 1416e0832faSShawn Lin 14240e9892eSGustavo Pimentel static void dw_pci_bottom_unmask(struct irq_data *d) 1436e0832faSShawn Lin { 14460b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 145f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1466e0832faSShawn Lin unsigned int res, bit, ctrl; 1476e0832faSShawn Lin unsigned long flags; 1486e0832faSShawn Lin 1496e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 1506e0832faSShawn Lin 15140e9892eSGustavo Pimentel ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1526e0832faSShawn Lin res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 15340e9892eSGustavo Pimentel bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1546e0832faSShawn Lin 15565772257SGustavo Pimentel pp->irq_mask[ctrl] &= ~BIT(bit); 156f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); 1576e0832faSShawn Lin 1586e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 1596e0832faSShawn Lin } 1606e0832faSShawn Lin 1616e0832faSShawn Lin static void dw_pci_bottom_ack(struct irq_data *d) 1626e0832faSShawn Lin { 16360b3c27fSSerge Semin struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 164f81c770dSRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1653f7bb2ecSMarc Zyngier unsigned int res, bit, ctrl; 1666e0832faSShawn Lin 1673f7bb2ecSMarc Zyngier ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1683f7bb2ecSMarc Zyngier res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 1693f7bb2ecSMarc Zyngier bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1706e0832faSShawn Lin 171f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_STATUS + res, BIT(bit)); 1726e0832faSShawn Lin } 1736e0832faSShawn Lin 1746e0832faSShawn Lin static struct irq_chip dw_pci_msi_bottom_irq_chip = { 1756e0832faSShawn Lin .name = "DWPCI-MSI", 1766e0832faSShawn Lin .irq_ack = dw_pci_bottom_ack, 1776e0832faSShawn Lin .irq_compose_msi_msg = dw_pci_setup_msi_msg, 1786e0832faSShawn Lin .irq_set_affinity = dw_pci_msi_set_affinity, 1796e0832faSShawn Lin .irq_mask = dw_pci_bottom_mask, 1806e0832faSShawn Lin .irq_unmask = dw_pci_bottom_unmask, 1816e0832faSShawn Lin }; 1826e0832faSShawn Lin 1836e0832faSShawn Lin static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, 1846e0832faSShawn Lin unsigned int virq, unsigned int nr_irqs, 1856e0832faSShawn Lin void *args) 1866e0832faSShawn Lin { 18760b3c27fSSerge Semin struct dw_pcie_rp *pp = domain->host_data; 1886e0832faSShawn Lin unsigned long flags; 1896e0832faSShawn Lin u32 i; 1906e0832faSShawn Lin int bit; 1916e0832faSShawn Lin 1926e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 1936e0832faSShawn Lin 1946e0832faSShawn Lin bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, 1956e0832faSShawn Lin order_base_2(nr_irqs)); 1966e0832faSShawn Lin 1976e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 1986e0832faSShawn Lin 1996e0832faSShawn Lin if (bit < 0) 2006e0832faSShawn Lin return -ENOSPC; 2016e0832faSShawn Lin 2026e0832faSShawn Lin for (i = 0; i < nr_irqs; i++) 2036e0832faSShawn Lin irq_domain_set_info(domain, virq + i, bit + i, 2049f67437bSKishon Vijay Abraham I pp->msi_irq_chip, 2056e0832faSShawn Lin pp, handle_edge_irq, 2066e0832faSShawn Lin NULL, NULL); 2076e0832faSShawn Lin 2086e0832faSShawn Lin return 0; 2096e0832faSShawn Lin } 2106e0832faSShawn Lin 2116e0832faSShawn Lin static void dw_pcie_irq_domain_free(struct irq_domain *domain, 2126e0832faSShawn Lin unsigned int virq, unsigned int nr_irqs) 2136e0832faSShawn Lin { 2144cfae0f1SGustavo Pimentel struct irq_data *d = irq_domain_get_irq_data(domain, virq); 21560b3c27fSSerge Semin struct dw_pcie_rp *pp = domain->host_data; 2166e0832faSShawn Lin unsigned long flags; 2176e0832faSShawn Lin 2186e0832faSShawn Lin raw_spin_lock_irqsave(&pp->lock, flags); 2196e0832faSShawn Lin 2204cfae0f1SGustavo Pimentel bitmap_release_region(pp->msi_irq_in_use, d->hwirq, 2216e0832faSShawn Lin order_base_2(nr_irqs)); 2226e0832faSShawn Lin 2236e0832faSShawn Lin raw_spin_unlock_irqrestore(&pp->lock, flags); 2246e0832faSShawn Lin } 2256e0832faSShawn Lin 2266e0832faSShawn Lin static const struct irq_domain_ops dw_pcie_msi_domain_ops = { 2276e0832faSShawn Lin .alloc = dw_pcie_irq_domain_alloc, 2286e0832faSShawn Lin .free = dw_pcie_irq_domain_free, 2296e0832faSShawn Lin }; 2306e0832faSShawn Lin 23160b3c27fSSerge Semin int dw_pcie_allocate_domains(struct dw_pcie_rp *pp) 2326e0832faSShawn Lin { 2336e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 2346e0832faSShawn Lin struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); 2356e0832faSShawn Lin 2366e0832faSShawn Lin pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, 2376e0832faSShawn Lin &dw_pcie_msi_domain_ops, pp); 2386e0832faSShawn Lin if (!pp->irq_domain) { 2396e0832faSShawn Lin dev_err(pci->dev, "Failed to create IRQ domain\n"); 2406e0832faSShawn Lin return -ENOMEM; 2416e0832faSShawn Lin } 2426e0832faSShawn Lin 2430414b93eSMarc Zyngier irq_domain_update_bus_token(pp->irq_domain, DOMAIN_BUS_NEXUS); 2440414b93eSMarc Zyngier 2456e0832faSShawn Lin pp->msi_domain = pci_msi_create_irq_domain(fwnode, 2466e0832faSShawn Lin &dw_pcie_msi_domain_info, 2476e0832faSShawn Lin pp->irq_domain); 2486e0832faSShawn Lin if (!pp->msi_domain) { 2496e0832faSShawn Lin dev_err(pci->dev, "Failed to create MSI domain\n"); 2506e0832faSShawn Lin irq_domain_remove(pp->irq_domain); 2516e0832faSShawn Lin return -ENOMEM; 2526e0832faSShawn Lin } 2536e0832faSShawn Lin 2546e0832faSShawn Lin return 0; 2556e0832faSShawn Lin } 2566e0832faSShawn Lin 25760b3c27fSSerge Semin static void dw_pcie_free_msi(struct dw_pcie_rp *pp) 2586e0832faSShawn Lin { 259db388348SDmitry Baryshkov u32 ctrl; 260db388348SDmitry Baryshkov 261db388348SDmitry Baryshkov for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) { 262db388348SDmitry Baryshkov if (pp->msi_irq[ctrl] > 0) 263db388348SDmitry Baryshkov irq_set_chained_handler_and_data(pp->msi_irq[ctrl], 264db388348SDmitry Baryshkov NULL, NULL); 265db388348SDmitry Baryshkov } 2666e0832faSShawn Lin 2676e0832faSShawn Lin irq_domain_remove(pp->msi_domain); 2686e0832faSShawn Lin irq_domain_remove(pp->irq_domain); 2696e0832faSShawn Lin } 2706e0832faSShawn Lin 27160b3c27fSSerge Semin static void dw_pcie_msi_init(struct dw_pcie_rp *pp) 2726e0832faSShawn Lin { 2736e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 27407940c36SJisheng Zhang u64 msi_target = (u64)pp->msi_data; 2756e0832faSShawn Lin 27659fbab1aSRob Herring if (!pci_msi_enabled() || !pp->has_msi_ctrl) 277cf627713SRob Herring return; 278cf627713SRob Herring 2796e0832faSShawn Lin /* Program the msi_data */ 280f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target)); 281f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target)); 2826e0832faSShawn Lin } 2836e0832faSShawn Lin 284cd761378SDmitry Baryshkov static int dw_pcie_parse_split_msi_irq(struct dw_pcie_rp *pp) 285cd761378SDmitry Baryshkov { 286cd761378SDmitry Baryshkov struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 287cd761378SDmitry Baryshkov struct device *dev = pci->dev; 288cd761378SDmitry Baryshkov struct platform_device *pdev = to_platform_device(dev); 289cd761378SDmitry Baryshkov u32 ctrl, max_vectors; 290cd761378SDmitry Baryshkov int irq; 291cd761378SDmitry Baryshkov 292cd761378SDmitry Baryshkov /* Parse any "msiX" IRQs described in the devicetree */ 293cd761378SDmitry Baryshkov for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) { 294cd761378SDmitry Baryshkov char msi_name[] = "msiX"; 295cd761378SDmitry Baryshkov 296cd761378SDmitry Baryshkov msi_name[3] = '0' + ctrl; 297cd761378SDmitry Baryshkov irq = platform_get_irq_byname_optional(pdev, msi_name); 298cd761378SDmitry Baryshkov if (irq == -ENXIO) 299cd761378SDmitry Baryshkov break; 300cd761378SDmitry Baryshkov if (irq < 0) 301cd761378SDmitry Baryshkov return dev_err_probe(dev, irq, 302cd761378SDmitry Baryshkov "Failed to parse MSI IRQ '%s'\n", 303cd761378SDmitry Baryshkov msi_name); 304cd761378SDmitry Baryshkov 305cd761378SDmitry Baryshkov pp->msi_irq[ctrl] = irq; 306cd761378SDmitry Baryshkov } 307cd761378SDmitry Baryshkov 308cd761378SDmitry Baryshkov /* If no "msiX" IRQs, caller should fallback to "msi" IRQ */ 309cd761378SDmitry Baryshkov if (ctrl == 0) 310cd761378SDmitry Baryshkov return -ENXIO; 311cd761378SDmitry Baryshkov 312cd761378SDmitry Baryshkov max_vectors = ctrl * MAX_MSI_IRQS_PER_CTRL; 313cd761378SDmitry Baryshkov if (pp->num_vectors > max_vectors) { 314cd761378SDmitry Baryshkov dev_warn(dev, "Exceeding number of MSI vectors, limiting to %u\n", 315cd761378SDmitry Baryshkov max_vectors); 316cd761378SDmitry Baryshkov pp->num_vectors = max_vectors; 317cd761378SDmitry Baryshkov } 318cd761378SDmitry Baryshkov if (!pp->num_vectors) 319cd761378SDmitry Baryshkov pp->num_vectors = max_vectors; 320cd761378SDmitry Baryshkov 321cd761378SDmitry Baryshkov return 0; 322cd761378SDmitry Baryshkov } 323cd761378SDmitry Baryshkov 324226ec087SDmitry Baryshkov static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) 325226ec087SDmitry Baryshkov { 326226ec087SDmitry Baryshkov struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 327226ec087SDmitry Baryshkov struct device *dev = pci->dev; 328226ec087SDmitry Baryshkov struct platform_device *pdev = to_platform_device(dev); 329423511ecSWill McVicker u64 *msi_vaddr; 330226ec087SDmitry Baryshkov int ret; 331226ec087SDmitry Baryshkov u32 ctrl, num_ctrls; 332226ec087SDmitry Baryshkov 333cd761378SDmitry Baryshkov for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) 334226ec087SDmitry Baryshkov pp->irq_mask[ctrl] = ~0; 335226ec087SDmitry Baryshkov 336db388348SDmitry Baryshkov if (!pp->msi_irq[0]) { 337cd761378SDmitry Baryshkov ret = dw_pcie_parse_split_msi_irq(pp); 338cd761378SDmitry Baryshkov if (ret < 0 && ret != -ENXIO) 339cd761378SDmitry Baryshkov return ret; 340cd761378SDmitry Baryshkov } 341cd761378SDmitry Baryshkov 342cd761378SDmitry Baryshkov if (!pp->num_vectors) 343cd761378SDmitry Baryshkov pp->num_vectors = MSI_DEF_NUM_VECTORS; 344cd761378SDmitry Baryshkov num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 345cd761378SDmitry Baryshkov 346cd761378SDmitry Baryshkov if (!pp->msi_irq[0]) { 347db388348SDmitry Baryshkov pp->msi_irq[0] = platform_get_irq_byname_optional(pdev, "msi"); 348db388348SDmitry Baryshkov if (pp->msi_irq[0] < 0) { 349db388348SDmitry Baryshkov pp->msi_irq[0] = platform_get_irq(pdev, 0); 350db388348SDmitry Baryshkov if (pp->msi_irq[0] < 0) 351db388348SDmitry Baryshkov return pp->msi_irq[0]; 352226ec087SDmitry Baryshkov } 353226ec087SDmitry Baryshkov } 354226ec087SDmitry Baryshkov 355cd761378SDmitry Baryshkov dev_dbg(dev, "Using %d MSI vectors\n", pp->num_vectors); 356cd761378SDmitry Baryshkov 357226ec087SDmitry Baryshkov pp->msi_irq_chip = &dw_pci_msi_bottom_irq_chip; 358226ec087SDmitry Baryshkov 359226ec087SDmitry Baryshkov ret = dw_pcie_allocate_domains(pp); 360226ec087SDmitry Baryshkov if (ret) 361226ec087SDmitry Baryshkov return ret; 362226ec087SDmitry Baryshkov 363db388348SDmitry Baryshkov for (ctrl = 0; ctrl < num_ctrls; ctrl++) { 364db388348SDmitry Baryshkov if (pp->msi_irq[ctrl] > 0) 365db388348SDmitry Baryshkov irq_set_chained_handler_and_data(pp->msi_irq[ctrl], 366226ec087SDmitry Baryshkov dw_chained_msi_isr, pp); 367db388348SDmitry Baryshkov } 368226ec087SDmitry Baryshkov 369423511ecSWill McVicker ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 370226ec087SDmitry Baryshkov if (ret) 371226ec087SDmitry Baryshkov dev_warn(dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n"); 372226ec087SDmitry Baryshkov 373423511ecSWill McVicker msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data, 374423511ecSWill McVicker GFP_KERNEL); 375423511ecSWill McVicker if (!msi_vaddr) { 376423511ecSWill McVicker dev_err(dev, "Failed to alloc and map MSI data\n"); 377226ec087SDmitry Baryshkov dw_pcie_free_msi(pp); 378423511ecSWill McVicker return -ENOMEM; 379226ec087SDmitry Baryshkov } 380226ec087SDmitry Baryshkov 381226ec087SDmitry Baryshkov return 0; 382226ec087SDmitry Baryshkov } 383226ec087SDmitry Baryshkov 38460b3c27fSSerge Semin int dw_pcie_host_init(struct dw_pcie_rp *pp) 3856e0832faSShawn Lin { 3866e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 3876e0832faSShawn Lin struct device *dev = pci->dev; 3886e0832faSShawn Lin struct device_node *np = dev->of_node; 3896e0832faSShawn Lin struct platform_device *pdev = to_platform_device(dev); 3907fe71aa8SRob Herring struct resource_entry *win; 3916e0832faSShawn Lin struct pci_host_bridge *bridge; 392bd42f310SSerge Semin struct resource *res; 3936e0832faSShawn Lin int ret; 3946e0832faSShawn Lin 39560a4352fSSerge Semin raw_spin_lock_init(&pp->lock); 3966e0832faSShawn Lin 397*ef8c5887SSerge Semin ret = dw_pcie_get_resources(pci); 398*ef8c5887SSerge Semin if (ret) 399*ef8c5887SSerge Semin return ret; 400*ef8c5887SSerge Semin 401bd42f310SSerge Semin res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); 402bd42f310SSerge Semin if (res) { 403bd42f310SSerge Semin pp->cfg0_size = resource_size(res); 404bd42f310SSerge Semin pp->cfg0_base = res->start; 4052f5ab5afSRob Herring 406bd42f310SSerge Semin pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); 4072f5ab5afSRob Herring if (IS_ERR(pp->va_cfg0_base)) 4082f5ab5afSRob Herring return PTR_ERR(pp->va_cfg0_base); 4092f5ab5afSRob Herring } else { 4106e0832faSShawn Lin dev_err(dev, "Missing *config* reg space\n"); 4112f5ab5afSRob Herring return -ENODEV; 4126e0832faSShawn Lin } 4136e0832faSShawn Lin 414e6fdd3bfSJisheng Zhang bridge = devm_pci_alloc_host_bridge(dev, 0); 4156e0832faSShawn Lin if (!bridge) 4166e0832faSShawn Lin return -ENOMEM; 4176e0832faSShawn Lin 418444ddca5SRob Herring pp->bridge = bridge; 419444ddca5SRob Herring 4202f5ab5afSRob Herring /* Get the I/O range from DT */ 4212f5ab5afSRob Herring win = resource_list_first_type(&bridge->windows, IORESOURCE_IO); 4222f5ab5afSRob Herring if (win) { 4230f71c60fSRob Herring pp->io_size = resource_size(win->res); 4240f71c60fSRob Herring pp->io_bus_addr = win->res->start - win->offset; 4250f71c60fSRob Herring pp->io_base = pci_pio_to_address(win->res->start); 4266e0832faSShawn Lin } 4276e0832faSShawn Lin 4287e919677SBjorn Andersson /* Set default bus ops */ 4297e919677SBjorn Andersson bridge->ops = &dw_pcie_ops; 4307e919677SBjorn Andersson bridge->child_ops = &dw_child_pcie_ops; 4317e919677SBjorn Andersson 4327e919677SBjorn Andersson if (pp->ops->host_init) { 4337e919677SBjorn Andersson ret = pp->ops->host_init(pp); 4347e919677SBjorn Andersson if (ret) 4357e919677SBjorn Andersson return ret; 4367e919677SBjorn Andersson } 4377e919677SBjorn Andersson 4389e2b5de5SJisheng Zhang if (pci_msi_enabled()) { 439f78f0263SRob Herring pp->has_msi_ctrl = !(pp->ops->msi_host_init || 440f78f0263SRob Herring of_property_read_bool(np, "msi-parent") || 441f78f0263SRob Herring of_property_read_bool(np, "msi-map")); 442f78f0263SRob Herring 443cd761378SDmitry Baryshkov /* 444cd761378SDmitry Baryshkov * For the has_msi_ctrl case the default assignment is handled 445cd761378SDmitry Baryshkov * in the dw_pcie_msi_host_init(). 446cd761378SDmitry Baryshkov */ 447cd761378SDmitry Baryshkov if (!pp->has_msi_ctrl && !pp->num_vectors) { 4486e0832faSShawn Lin pp->num_vectors = MSI_DEF_NUM_VECTORS; 449331e9bceSRob Herring } else if (pp->num_vectors > MAX_MSI_IRQS) { 450331e9bceSRob Herring dev_err(dev, "Invalid number of vectors\n"); 451c6481d51SSerge Semin ret = -EINVAL; 452c6481d51SSerge Semin goto err_deinit_host; 4536e0832faSShawn Lin } 4546e0832faSShawn Lin 455f78f0263SRob Herring if (pp->ops->msi_host_init) { 456f78f0263SRob Herring ret = pp->ops->msi_host_init(pp); 457f78f0263SRob Herring if (ret < 0) 458c6481d51SSerge Semin goto err_deinit_host; 459f78f0263SRob Herring } else if (pp->has_msi_ctrl) { 460226ec087SDmitry Baryshkov ret = dw_pcie_msi_host_init(pp); 461226ec087SDmitry Baryshkov if (ret < 0) 462c6481d51SSerge Semin goto err_deinit_host; 463c6481d51SSerge Semin } 4645bcb1757SRob Herring } 4656e0832faSShawn Lin 46613e9d390SSerge Semin dw_pcie_version_detect(pci); 46713e9d390SSerge Semin 4688bcca265SHou Zhiqiang dw_pcie_iatu_detect(pci); 4696e0832faSShawn Lin 470ce06bf57SSerge Semin ret = dw_pcie_setup_rc(pp); 471ce06bf57SSerge Semin if (ret) 472ce06bf57SSerge Semin goto err_free_msi; 47359fbab1aSRob Herring 474a37beefbSSerge Semin if (!dw_pcie_link_up(pci)) { 475a37beefbSSerge Semin ret = dw_pcie_start_link(pci); 476886a9c13SRob Herring if (ret) 477886a9c13SRob Herring goto err_free_msi; 478886a9c13SRob Herring } 479886a9c13SRob Herring 480886a9c13SRob Herring /* Ignore errors, the link may come up later */ 481886a9c13SRob Herring dw_pcie_wait_for_link(pci); 482886a9c13SRob Herring 4836e0832faSShawn Lin bridge->sysdata = pp; 4846e0832faSShawn Lin 4851df79305SRob Herring ret = pci_host_probe(bridge); 486113fa857SSerge Semin if (ret) 487113fa857SSerge Semin goto err_stop_link; 488113fa857SSerge Semin 4896e0832faSShawn Lin return 0; 4906e0832faSShawn Lin 491113fa857SSerge Semin err_stop_link: 492a37beefbSSerge Semin dw_pcie_stop_link(pci); 493113fa857SSerge Semin 4949e2b5de5SJisheng Zhang err_free_msi: 495f78f0263SRob Herring if (pp->has_msi_ctrl) 4969e2b5de5SJisheng Zhang dw_pcie_free_msi(pp); 497c6481d51SSerge Semin 498c6481d51SSerge Semin err_deinit_host: 499c6481d51SSerge Semin if (pp->ops->host_deinit) 500c6481d51SSerge Semin pp->ops->host_deinit(pp); 501c6481d51SSerge Semin 5026e0832faSShawn Lin return ret; 5036e0832faSShawn Lin } 504ca98329dSVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_host_init); 5056e0832faSShawn Lin 50660b3c27fSSerge Semin void dw_pcie_host_deinit(struct dw_pcie_rp *pp) 5079d071cadSVidya Sagar { 508113fa857SSerge Semin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 509113fa857SSerge Semin 5105808d43eSRob Herring pci_stop_root_bus(pp->bridge->bus); 5115808d43eSRob Herring pci_remove_root_bus(pp->bridge->bus); 512113fa857SSerge Semin 513a37beefbSSerge Semin dw_pcie_stop_link(pci); 514113fa857SSerge Semin 515f78f0263SRob Herring if (pp->has_msi_ctrl) 5169d071cadSVidya Sagar dw_pcie_free_msi(pp); 517c6481d51SSerge Semin 518c6481d51SSerge Semin if (pp->ops->host_deinit) 519c6481d51SSerge Semin pp->ops->host_deinit(pp); 5209d071cadSVidya Sagar } 521ca98329dSVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); 5229d071cadSVidya Sagar 523c2b0c098SRob Herring static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, 524c2b0c098SRob Herring unsigned int devfn, int where) 5256e0832faSShawn Lin { 52660b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 5276e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 528ce06bf57SSerge Semin int type, ret; 529ce06bf57SSerge Semin u32 busdev; 5306e0832faSShawn Lin 53115b23906SHou Zhiqiang /* 53215b23906SHou Zhiqiang * Checking whether the link is up here is a last line of defense 53315b23906SHou Zhiqiang * against platforms that forward errors on the system bus as 53415b23906SHou Zhiqiang * SError upon PCI configuration transactions issued when the link 53515b23906SHou Zhiqiang * is down. This check is racy by definition and does not stop 53615b23906SHou Zhiqiang * the system from triggering an SError if the link goes down 53715b23906SHou Zhiqiang * after this check is performed. 53815b23906SHou Zhiqiang */ 53915b23906SHou Zhiqiang if (!dw_pcie_link_up(pci)) 54015b23906SHou Zhiqiang return NULL; 54115b23906SHou Zhiqiang 5426e0832faSShawn Lin busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | 5436e0832faSShawn Lin PCIE_ATU_FUNC(PCI_FUNC(devfn)); 5446e0832faSShawn Lin 5452ef6b06aSRob Herring if (pci_is_root_bus(bus->parent)) 5466e0832faSShawn Lin type = PCIE_ATU_TYPE_CFG0; 5472ef6b06aSRob Herring else 5486e0832faSShawn Lin type = PCIE_ATU_TYPE_CFG1; 5492ef6b06aSRob Herring 550ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, 551ce06bf57SSerge Semin pp->cfg0_size); 552ce06bf57SSerge Semin if (ret) 553ce06bf57SSerge Semin return NULL; 554689e349aSAndrey Smirnov 5552ef6b06aSRob Herring return pp->va_cfg0_base + where; 556c2b0c098SRob Herring } 557c2b0c098SRob Herring 558c2b0c098SRob Herring static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, 559c2b0c098SRob Herring int where, int size, u32 *val) 560c2b0c098SRob Herring { 56160b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 562c2b0c098SRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 563ce06bf57SSerge Semin int ret; 564c2b0c098SRob Herring 565c2b0c098SRob Herring ret = pci_generic_config_read(bus, devfn, where, size, val); 566ce06bf57SSerge Semin if (ret != PCIBIOS_SUCCESSFUL) 5676e0832faSShawn Lin return ret; 568ce06bf57SSerge Semin 569ce06bf57SSerge Semin if (pp->cfg0_io_shared) { 570ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, 571ce06bf57SSerge Semin pp->io_base, pp->io_bus_addr, 572ce06bf57SSerge Semin pp->io_size); 573ce06bf57SSerge Semin if (ret) 574ce06bf57SSerge Semin return PCIBIOS_SET_FAILED; 575ce06bf57SSerge Semin } 576ce06bf57SSerge Semin 577ce06bf57SSerge Semin return PCIBIOS_SUCCESSFUL; 5786e0832faSShawn Lin } 5796e0832faSShawn Lin 580c2b0c098SRob Herring static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, 5816e0832faSShawn Lin int where, int size, u32 val) 5826e0832faSShawn Lin { 58360b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 584c2b0c098SRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 585ce06bf57SSerge Semin int ret; 5866e0832faSShawn Lin 587c2b0c098SRob Herring ret = pci_generic_config_write(bus, devfn, where, size, val); 588ce06bf57SSerge Semin if (ret != PCIBIOS_SUCCESSFUL) 589c2b0c098SRob Herring return ret; 590ce06bf57SSerge Semin 591ce06bf57SSerge Semin if (pp->cfg0_io_shared) { 592ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, 593ce06bf57SSerge Semin pp->io_base, pp->io_bus_addr, 594ce06bf57SSerge Semin pp->io_size); 595ce06bf57SSerge Semin if (ret) 596ce06bf57SSerge Semin return PCIBIOS_SET_FAILED; 597ce06bf57SSerge Semin } 598ce06bf57SSerge Semin 599ce06bf57SSerge Semin return PCIBIOS_SUCCESSFUL; 6006e0832faSShawn Lin } 6016e0832faSShawn Lin 602c2b0c098SRob Herring static struct pci_ops dw_child_pcie_ops = { 603c2b0c098SRob Herring .map_bus = dw_pcie_other_conf_map_bus, 604c2b0c098SRob Herring .read = dw_pcie_rd_other_conf, 605c2b0c098SRob Herring .write = dw_pcie_wr_other_conf, 606c2b0c098SRob Herring }; 607c2b0c098SRob Herring 60827e7ed01SRob Herring void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) 60927e7ed01SRob Herring { 61060b3c27fSSerge Semin struct dw_pcie_rp *pp = bus->sysdata; 61127e7ed01SRob Herring struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 61227e7ed01SRob Herring 61327e7ed01SRob Herring if (PCI_SLOT(devfn) > 0) 61427e7ed01SRob Herring return NULL; 61527e7ed01SRob Herring 61627e7ed01SRob Herring return pci->dbi_base + where; 61727e7ed01SRob Herring } 61827e7ed01SRob Herring EXPORT_SYMBOL_GPL(dw_pcie_own_conf_map_bus); 61927e7ed01SRob Herring 6206e0832faSShawn Lin static struct pci_ops dw_pcie_ops = { 621c2b0c098SRob Herring .map_bus = dw_pcie_own_conf_map_bus, 622c2b0c098SRob Herring .read = pci_generic_config_read, 623c2b0c098SRob Herring .write = pci_generic_config_write, 6246e0832faSShawn Lin }; 6256e0832faSShawn Lin 626ce06bf57SSerge Semin static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) 6276e0832faSShawn Lin { 6286e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 629ce06bf57SSerge Semin struct resource_entry *entry; 630ce06bf57SSerge Semin int i, ret; 631ce06bf57SSerge Semin 632ce06bf57SSerge Semin /* Note the very first outbound ATU is used for CFG IOs */ 633ce06bf57SSerge Semin if (!pci->num_ob_windows) { 634ce06bf57SSerge Semin dev_err(pci->dev, "No outbound iATU found\n"); 635ce06bf57SSerge Semin return -EINVAL; 636ce06bf57SSerge Semin } 637ce06bf57SSerge Semin 638ce06bf57SSerge Semin /* 6398522e17dSSerge Semin * Ensure all out/inbound windows are disabled before proceeding with 6408522e17dSSerge Semin * the MEM/IO (dma-)ranges setups. 641ce06bf57SSerge Semin */ 642ce06bf57SSerge Semin for (i = 0; i < pci->num_ob_windows; i++) 643ce06bf57SSerge Semin dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i); 644ce06bf57SSerge Semin 6458522e17dSSerge Semin for (i = 0; i < pci->num_ib_windows; i++) 6468522e17dSSerge Semin dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i); 6478522e17dSSerge Semin 648ce06bf57SSerge Semin i = 0; 649ce06bf57SSerge Semin resource_list_for_each_entry(entry, &pp->bridge->windows) { 650ce06bf57SSerge Semin if (resource_type(entry->res) != IORESOURCE_MEM) 651ce06bf57SSerge Semin continue; 652ce06bf57SSerge Semin 653ce06bf57SSerge Semin if (pci->num_ob_windows <= ++i) 654ce06bf57SSerge Semin break; 655ce06bf57SSerge Semin 656ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_MEM, 657ce06bf57SSerge Semin entry->res->start, 658ce06bf57SSerge Semin entry->res->start - entry->offset, 659ce06bf57SSerge Semin resource_size(entry->res)); 660ce06bf57SSerge Semin if (ret) { 661ce06bf57SSerge Semin dev_err(pci->dev, "Failed to set MEM range %pr\n", 662ce06bf57SSerge Semin entry->res); 663ce06bf57SSerge Semin return ret; 664ce06bf57SSerge Semin } 665ce06bf57SSerge Semin } 666ce06bf57SSerge Semin 667ce06bf57SSerge Semin if (pp->io_size) { 668ce06bf57SSerge Semin if (pci->num_ob_windows > ++i) { 669ce06bf57SSerge Semin ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_IO, 670ce06bf57SSerge Semin pp->io_base, 671ce06bf57SSerge Semin pp->io_bus_addr, 672ce06bf57SSerge Semin pp->io_size); 673ce06bf57SSerge Semin if (ret) { 674ce06bf57SSerge Semin dev_err(pci->dev, "Failed to set IO range %pr\n", 675ce06bf57SSerge Semin entry->res); 676ce06bf57SSerge Semin return ret; 677ce06bf57SSerge Semin } 678ce06bf57SSerge Semin } else { 679ce06bf57SSerge Semin pp->cfg0_io_shared = true; 680ce06bf57SSerge Semin } 681ce06bf57SSerge Semin } 682ce06bf57SSerge Semin 683ce06bf57SSerge Semin if (pci->num_ob_windows <= i) 6848522e17dSSerge Semin dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n", 685ce06bf57SSerge Semin pci->num_ob_windows); 686ce06bf57SSerge Semin 6878522e17dSSerge Semin i = 0; 6888522e17dSSerge Semin resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { 6898522e17dSSerge Semin if (resource_type(entry->res) != IORESOURCE_MEM) 6908522e17dSSerge Semin continue; 6918522e17dSSerge Semin 6928522e17dSSerge Semin if (pci->num_ib_windows <= i) 6938522e17dSSerge Semin break; 6948522e17dSSerge Semin 6958522e17dSSerge Semin ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, 6968522e17dSSerge Semin entry->res->start, 6978522e17dSSerge Semin entry->res->start - entry->offset, 6988522e17dSSerge Semin resource_size(entry->res)); 6998522e17dSSerge Semin if (ret) { 7008522e17dSSerge Semin dev_err(pci->dev, "Failed to set DMA range %pr\n", 7018522e17dSSerge Semin entry->res); 7028522e17dSSerge Semin return ret; 7038522e17dSSerge Semin } 7048522e17dSSerge Semin } 7058522e17dSSerge Semin 7068522e17dSSerge Semin if (pci->num_ib_windows <= i) 7078522e17dSSerge Semin dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n", 7088522e17dSSerge Semin pci->num_ib_windows); 7098522e17dSSerge Semin 710ce06bf57SSerge Semin return 0; 711ce06bf57SSerge Semin } 712ce06bf57SSerge Semin 713ce06bf57SSerge Semin int dw_pcie_setup_rc(struct dw_pcie_rp *pp) 714ce06bf57SSerge Semin { 715ce06bf57SSerge Semin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 716ce06bf57SSerge Semin u32 val, ctrl, num_ctrls; 717ce06bf57SSerge Semin int ret; 7186e0832faSShawn Lin 7193924bc2fSVidya Sagar /* 7203924bc2fSVidya Sagar * Enable DBI read-only registers for writing/updating configuration. 7213924bc2fSVidya Sagar * Write permission gets disabled towards the end of this function. 7223924bc2fSVidya Sagar */ 7233924bc2fSVidya Sagar dw_pcie_dbi_ro_wr_en(pci); 7243924bc2fSVidya Sagar 7256e0832faSShawn Lin dw_pcie_setup(pci); 7266e0832faSShawn Lin 727f78f0263SRob Herring if (pp->has_msi_ctrl) { 7286e0832faSShawn Lin num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 7296e0832faSShawn Lin 7306e0832faSShawn Lin /* Initialize IRQ Status array */ 731830920e0SMarc Zyngier for (ctrl = 0; ctrl < num_ctrls; ctrl++) { 732f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + 7336e0832faSShawn Lin (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 734f81c770dSRob Herring pp->irq_mask[ctrl]); 735f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + 736830920e0SMarc Zyngier (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 737f81c770dSRob Herring ~0); 738830920e0SMarc Zyngier } 739fd8a44bdSKishon Vijay Abraham I } 7406e0832faSShawn Lin 741294353d9SJisheng Zhang dw_pcie_msi_init(pp); 742294353d9SJisheng Zhang 7436e0832faSShawn Lin /* Setup RC BARs */ 7446e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); 7456e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); 7466e0832faSShawn Lin 7476e0832faSShawn Lin /* Setup interrupt pins */ 7486e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); 7496e0832faSShawn Lin val &= 0xffff00ff; 7506e0832faSShawn Lin val |= 0x00000100; 7516e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); 7526e0832faSShawn Lin 7536e0832faSShawn Lin /* Setup bus numbers */ 7546e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); 7556e0832faSShawn Lin val &= 0xff000000; 7566e0832faSShawn Lin val |= 0x00ff0100; 7576e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); 7586e0832faSShawn Lin 7596e0832faSShawn Lin /* Setup command register */ 7606e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCI_COMMAND); 7616e0832faSShawn Lin val &= 0xffff0000; 7626e0832faSShawn Lin val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | 7636e0832faSShawn Lin PCI_COMMAND_MASTER | PCI_COMMAND_SERR; 7646e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCI_COMMAND, val); 7656e0832faSShawn Lin 7666e0832faSShawn Lin /* 767444ddca5SRob Herring * If the platform provides its own child bus config accesses, it means 768444ddca5SRob Herring * the platform uses its own address translation component rather than 769444ddca5SRob Herring * ATU, so we should not program the ATU here. 7706e0832faSShawn Lin */ 771c2b0c098SRob Herring if (pp->bridge->child_ops == &dw_child_pcie_ops) { 772ce06bf57SSerge Semin ret = dw_pcie_iatu_setup(pp); 773ce06bf57SSerge Semin if (ret) 774ce06bf57SSerge Semin return ret; 7756e0832faSShawn Lin } 7766e0832faSShawn Lin 777f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); 7786e0832faSShawn Lin 7796e0832faSShawn Lin /* Program correct class for RC */ 780f81c770dSRob Herring dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); 7816e0832faSShawn Lin 782f81c770dSRob Herring val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); 7836e0832faSShawn Lin val |= PORT_LOGIC_SPEED_CHANGE; 784f81c770dSRob Herring dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); 7853924bc2fSVidya Sagar 7863924bc2fSVidya Sagar dw_pcie_dbi_ro_wr_dis(pci); 787ce06bf57SSerge Semin 788ce06bf57SSerge Semin return 0; 7896e0832faSShawn Lin } 790ca98329dSVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); 791