18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Synopsys DesignWare PCIe host controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * https://www.samsung.com 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Jingoo Han <jg1.han@samsung.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 128c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 138c2ecf20Sopenharmony_ci#include <linux/msi.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 168c2ecf20Sopenharmony_ci#include <linux/pci_regs.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "../../pci.h" 208c2ecf20Sopenharmony_ci#include "pcie-designware.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct pci_ops dw_pcie_ops; 238c2ecf20Sopenharmony_cistatic struct pci_ops dw_child_pcie_ops; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void dw_msi_ack_irq(struct irq_data *d) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci irq_chip_ack_parent(d); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void dw_msi_mask_irq(struct irq_data *d) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci pci_msi_mask_irq(d); 338c2ecf20Sopenharmony_ci irq_chip_mask_parent(d); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void dw_msi_unmask_irq(struct irq_data *d) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci pci_msi_unmask_irq(d); 398c2ecf20Sopenharmony_ci irq_chip_unmask_parent(d); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct irq_chip dw_pcie_msi_irq_chip = { 438c2ecf20Sopenharmony_ci .name = "PCI-MSI", 448c2ecf20Sopenharmony_ci .irq_ack = dw_msi_ack_irq, 458c2ecf20Sopenharmony_ci .irq_mask = dw_msi_mask_irq, 468c2ecf20Sopenharmony_ci .irq_unmask = dw_msi_unmask_irq, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct msi_domain_info dw_pcie_msi_domain_info = { 508c2ecf20Sopenharmony_ci .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 518c2ecf20Sopenharmony_ci MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), 528c2ecf20Sopenharmony_ci .chip = &dw_pcie_msi_irq_chip, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* MSI int handler */ 568c2ecf20Sopenharmony_ciirqreturn_t dw_handle_msi_irq(struct pcie_port *pp) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int i, pos, irq; 598c2ecf20Sopenharmony_ci unsigned long val; 608c2ecf20Sopenharmony_ci u32 status, num_ctrls; 618c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 628c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (i = 0; i < num_ctrls; i++) { 678c2ecf20Sopenharmony_ci status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + 688c2ecf20Sopenharmony_ci (i * MSI_REG_CTRL_BLOCK_SIZE)); 698c2ecf20Sopenharmony_ci if (!status) 708c2ecf20Sopenharmony_ci continue; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 738c2ecf20Sopenharmony_ci val = status; 748c2ecf20Sopenharmony_ci pos = 0; 758c2ecf20Sopenharmony_ci while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, 768c2ecf20Sopenharmony_ci pos)) != MAX_MSI_IRQS_PER_CTRL) { 778c2ecf20Sopenharmony_ci irq = irq_find_mapping(pp->irq_domain, 788c2ecf20Sopenharmony_ci (i * MAX_MSI_IRQS_PER_CTRL) + 798c2ecf20Sopenharmony_ci pos); 808c2ecf20Sopenharmony_ci generic_handle_irq(irq); 818c2ecf20Sopenharmony_ci pos++; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Chained MSI interrupt service routine */ 898c2ecf20Sopenharmony_cistatic void dw_chained_msi_isr(struct irq_desc *desc) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 928c2ecf20Sopenharmony_ci struct pcie_port *pp; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pp = irq_desc_get_handler_data(desc); 978c2ecf20Sopenharmony_ci dw_handle_msi_irq(pp); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct pcie_port *pp = irq_data_get_irq_chip_data(d); 1058c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1068c2ecf20Sopenharmony_ci u64 msi_target; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci msi_target = (u64)pp->msi_data; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci msg->address_lo = lower_32_bits(msi_target); 1118c2ecf20Sopenharmony_ci msg->address_hi = upper_32_bits(msi_target); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci msg->data = d->hwirq; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", 1168c2ecf20Sopenharmony_ci (int)d->hwirq, msg->address_hi, msg->address_lo); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int dw_pci_msi_set_affinity(struct irq_data *d, 1208c2ecf20Sopenharmony_ci const struct cpumask *mask, bool force) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return -EINVAL; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void dw_pci_bottom_mask(struct irq_data *d) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct pcie_port *pp = irq_data_get_irq_chip_data(d); 1288c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1298c2ecf20Sopenharmony_ci unsigned int res, bit, ctrl; 1308c2ecf20Sopenharmony_ci unsigned long flags; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pp->lock, flags); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1358c2ecf20Sopenharmony_ci res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 1368c2ecf20Sopenharmony_ci bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci pp->irq_mask[ctrl] |= BIT(bit); 1398c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pp->lock, flags); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void dw_pci_bottom_unmask(struct irq_data *d) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct pcie_port *pp = irq_data_get_irq_chip_data(d); 1478c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1488c2ecf20Sopenharmony_ci unsigned int res, bit, ctrl; 1498c2ecf20Sopenharmony_ci unsigned long flags; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pp->lock, flags); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1548c2ecf20Sopenharmony_ci res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 1558c2ecf20Sopenharmony_ci bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pp->irq_mask[ctrl] &= ~BIT(bit); 1588c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pp->lock, flags); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void dw_pci_bottom_ack(struct irq_data *d) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct pcie_port *pp = irq_data_get_irq_chip_data(d); 1668c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1678c2ecf20Sopenharmony_ci unsigned int res, bit, ctrl; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; 1708c2ecf20Sopenharmony_ci res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; 1718c2ecf20Sopenharmony_ci bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_STATUS + res, BIT(bit)); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic struct irq_chip dw_pci_msi_bottom_irq_chip = { 1778c2ecf20Sopenharmony_ci .name = "DWPCI-MSI", 1788c2ecf20Sopenharmony_ci .irq_ack = dw_pci_bottom_ack, 1798c2ecf20Sopenharmony_ci .irq_compose_msi_msg = dw_pci_setup_msi_msg, 1808c2ecf20Sopenharmony_ci .irq_set_affinity = dw_pci_msi_set_affinity, 1818c2ecf20Sopenharmony_ci .irq_mask = dw_pci_bottom_mask, 1828c2ecf20Sopenharmony_ci .irq_unmask = dw_pci_bottom_unmask, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int dw_pcie_irq_domain_alloc(struct irq_domain *domain, 1868c2ecf20Sopenharmony_ci unsigned int virq, unsigned int nr_irqs, 1878c2ecf20Sopenharmony_ci void *args) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct pcie_port *pp = domain->host_data; 1908c2ecf20Sopenharmony_ci unsigned long flags; 1918c2ecf20Sopenharmony_ci u32 i; 1928c2ecf20Sopenharmony_ci int bit; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pp->lock, flags); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, 1978c2ecf20Sopenharmony_ci order_base_2(nr_irqs)); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pp->lock, flags); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (bit < 0) 2028c2ecf20Sopenharmony_ci return -ENOSPC; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) 2058c2ecf20Sopenharmony_ci irq_domain_set_info(domain, virq + i, bit + i, 2068c2ecf20Sopenharmony_ci pp->msi_irq_chip, 2078c2ecf20Sopenharmony_ci pp, handle_edge_irq, 2088c2ecf20Sopenharmony_ci NULL, NULL); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void dw_pcie_irq_domain_free(struct irq_domain *domain, 2148c2ecf20Sopenharmony_ci unsigned int virq, unsigned int nr_irqs) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct irq_data *d = irq_domain_get_irq_data(domain, virq); 2178c2ecf20Sopenharmony_ci struct pcie_port *pp = domain->host_data; 2188c2ecf20Sopenharmony_ci unsigned long flags; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pp->lock, flags); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci bitmap_release_region(pp->msi_irq_in_use, d->hwirq, 2238c2ecf20Sopenharmony_ci order_base_2(nr_irqs)); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pp->lock, flags); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic const struct irq_domain_ops dw_pcie_msi_domain_ops = { 2298c2ecf20Sopenharmony_ci .alloc = dw_pcie_irq_domain_alloc, 2308c2ecf20Sopenharmony_ci .free = dw_pcie_irq_domain_free, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint dw_pcie_allocate_domains(struct pcie_port *pp) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 2368c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, 2398c2ecf20Sopenharmony_ci &dw_pcie_msi_domain_ops, pp); 2408c2ecf20Sopenharmony_ci if (!pp->irq_domain) { 2418c2ecf20Sopenharmony_ci dev_err(pci->dev, "Failed to create IRQ domain\n"); 2428c2ecf20Sopenharmony_ci return -ENOMEM; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci irq_domain_update_bus_token(pp->irq_domain, DOMAIN_BUS_NEXUS); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pp->msi_domain = pci_msi_create_irq_domain(fwnode, 2488c2ecf20Sopenharmony_ci &dw_pcie_msi_domain_info, 2498c2ecf20Sopenharmony_ci pp->irq_domain); 2508c2ecf20Sopenharmony_ci if (!pp->msi_domain) { 2518c2ecf20Sopenharmony_ci dev_err(pci->dev, "Failed to create MSI domain\n"); 2528c2ecf20Sopenharmony_ci irq_domain_remove(pp->irq_domain); 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_civoid dw_pcie_free_msi(struct pcie_port *pp) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci if (pp->msi_irq) { 2628c2ecf20Sopenharmony_ci irq_set_chained_handler(pp->msi_irq, NULL); 2638c2ecf20Sopenharmony_ci irq_set_handler_data(pp->msi_irq, NULL); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci irq_domain_remove(pp->msi_domain); 2678c2ecf20Sopenharmony_ci irq_domain_remove(pp->irq_domain); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (pp->msi_data) { 2708c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 2718c2ecf20Sopenharmony_ci struct device *dev = pci->dev; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci dma_unmap_single_attrs(dev, pp->msi_data, sizeof(pp->msi_msg), 2748c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_civoid dw_pcie_msi_init(struct pcie_port *pp) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 2818c2ecf20Sopenharmony_ci u64 msi_target = (u64)pp->msi_data; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCI_MSI)) 2848c2ecf20Sopenharmony_ci return; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Program the msi_data */ 2878c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target)); 2888c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target)); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_msi_init); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciint dw_pcie_host_init(struct pcie_port *pp) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 2958c2ecf20Sopenharmony_ci struct device *dev = pci->dev; 2968c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2978c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 2988c2ecf20Sopenharmony_ci struct resource_entry *win; 2998c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge; 3008c2ecf20Sopenharmony_ci struct resource *cfg_res; 3018c2ecf20Sopenharmony_ci int ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci raw_spin_lock_init(&pci->pp.lock); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); 3068c2ecf20Sopenharmony_ci if (cfg_res) { 3078c2ecf20Sopenharmony_ci pp->cfg0_size = resource_size(cfg_res); 3088c2ecf20Sopenharmony_ci pp->cfg0_base = cfg_res->start; 3098c2ecf20Sopenharmony_ci } else if (!pp->va_cfg0_base) { 3108c2ecf20Sopenharmony_ci dev_err(dev, "Missing *config* reg space\n"); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci bridge = devm_pci_alloc_host_bridge(dev, 0); 3148c2ecf20Sopenharmony_ci if (!bridge) 3158c2ecf20Sopenharmony_ci return -ENOMEM; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pp->bridge = bridge; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Get the I/O and memory ranges from DT */ 3208c2ecf20Sopenharmony_ci resource_list_for_each_entry(win, &bridge->windows) { 3218c2ecf20Sopenharmony_ci switch (resource_type(win->res)) { 3228c2ecf20Sopenharmony_ci case IORESOURCE_IO: 3238c2ecf20Sopenharmony_ci pp->io_size = resource_size(win->res); 3248c2ecf20Sopenharmony_ci pp->io_bus_addr = win->res->start - win->offset; 3258c2ecf20Sopenharmony_ci pp->io_base = pci_pio_to_address(win->res->start); 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case 0: 3288c2ecf20Sopenharmony_ci dev_err(dev, "Missing *config* reg space\n"); 3298c2ecf20Sopenharmony_ci pp->cfg0_size = resource_size(win->res); 3308c2ecf20Sopenharmony_ci pp->cfg0_base = win->res->start; 3318c2ecf20Sopenharmony_ci if (!pci->dbi_base) { 3328c2ecf20Sopenharmony_ci pci->dbi_base = devm_pci_remap_cfgspace(dev, 3338c2ecf20Sopenharmony_ci pp->cfg0_base, 3348c2ecf20Sopenharmony_ci pp->cfg0_size); 3358c2ecf20Sopenharmony_ci if (!pci->dbi_base) { 3368c2ecf20Sopenharmony_ci dev_err(dev, "Error with ioremap\n"); 3378c2ecf20Sopenharmony_ci return -ENOMEM; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!pp->va_cfg0_base) { 3458c2ecf20Sopenharmony_ci pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, 3468c2ecf20Sopenharmony_ci pp->cfg0_base, pp->cfg0_size); 3478c2ecf20Sopenharmony_ci if (!pp->va_cfg0_base) { 3488c2ecf20Sopenharmony_ci dev_err(dev, "Error with ioremap in function\n"); 3498c2ecf20Sopenharmony_ci return -ENOMEM; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); 3548c2ecf20Sopenharmony_ci if (ret) 3558c2ecf20Sopenharmony_ci pci->num_viewport = 2; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (pci->link_gen < 1) 3588c2ecf20Sopenharmony_ci pci->link_gen = of_pci_get_max_link_speed(np); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (pci_msi_enabled()) { 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * If a specific SoC driver needs to change the 3638c2ecf20Sopenharmony_ci * default number of vectors, it needs to implement 3648c2ecf20Sopenharmony_ci * the set_num_vectors callback. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if (!pp->ops->set_num_vectors) { 3678c2ecf20Sopenharmony_ci pp->num_vectors = MSI_DEF_NUM_VECTORS; 3688c2ecf20Sopenharmony_ci } else { 3698c2ecf20Sopenharmony_ci pp->ops->set_num_vectors(pp); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (pp->num_vectors > MAX_MSI_IRQS || 3728c2ecf20Sopenharmony_ci pp->num_vectors == 0) { 3738c2ecf20Sopenharmony_ci dev_err(dev, 3748c2ecf20Sopenharmony_ci "Invalid number of vectors\n"); 3758c2ecf20Sopenharmony_ci return -EINVAL; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!pp->ops->msi_host_init) { 3808c2ecf20Sopenharmony_ci pp->msi_irq_chip = &dw_pci_msi_bottom_irq_chip; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = dw_pcie_allocate_domains(pp); 3838c2ecf20Sopenharmony_ci if (ret) 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (pp->msi_irq) 3878c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(pp->msi_irq, 3888c2ecf20Sopenharmony_ci dw_chained_msi_isr, 3898c2ecf20Sopenharmony_ci pp); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci pp->msi_data = dma_map_single_attrs(pci->dev, &pp->msi_msg, 3928c2ecf20Sopenharmony_ci sizeof(pp->msi_msg), 3938c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, 3948c2ecf20Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 3958c2ecf20Sopenharmony_ci ret = dma_mapping_error(pci->dev, pp->msi_data); 3968c2ecf20Sopenharmony_ci if (ret) { 3978c2ecf20Sopenharmony_ci dev_err(pci->dev, "Failed to map MSI data\n"); 3988c2ecf20Sopenharmony_ci pp->msi_data = 0; 3998c2ecf20Sopenharmony_ci goto err_free_msi; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci } else { 4028c2ecf20Sopenharmony_ci ret = pp->ops->msi_host_init(pp); 4038c2ecf20Sopenharmony_ci if (ret < 0) 4048c2ecf20Sopenharmony_ci return ret; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Set default bus ops */ 4098c2ecf20Sopenharmony_ci bridge->ops = &dw_pcie_ops; 4108c2ecf20Sopenharmony_ci bridge->child_ops = &dw_child_pcie_ops; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (pp->ops->host_init) { 4138c2ecf20Sopenharmony_ci ret = pp->ops->host_init(pp); 4148c2ecf20Sopenharmony_ci if (ret) 4158c2ecf20Sopenharmony_ci goto err_free_msi; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci bridge->sysdata = pp; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci ret = pci_host_probe(bridge); 4218c2ecf20Sopenharmony_ci if (!ret) 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cierr_free_msi: 4258c2ecf20Sopenharmony_ci if (pci_msi_enabled() && !pp->ops->msi_host_init) 4268c2ecf20Sopenharmony_ci dw_pcie_free_msi(pp); 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_host_init); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_civoid dw_pcie_host_deinit(struct pcie_port *pp) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci pci_stop_root_bus(pp->bridge->bus); 4348c2ecf20Sopenharmony_ci pci_remove_root_bus(pp->bridge->bus); 4358c2ecf20Sopenharmony_ci if (pci_msi_enabled() && !pp->ops->msi_host_init) 4368c2ecf20Sopenharmony_ci dw_pcie_free_msi(pp); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_host_deinit); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, 4418c2ecf20Sopenharmony_ci unsigned int devfn, int where) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int type; 4448c2ecf20Sopenharmony_ci u32 busdev; 4458c2ecf20Sopenharmony_ci struct pcie_port *pp = bus->sysdata; 4468c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * Checking whether the link is up here is a last line of defense 4508c2ecf20Sopenharmony_ci * against platforms that forward errors on the system bus as 4518c2ecf20Sopenharmony_ci * SError upon PCI configuration transactions issued when the link 4528c2ecf20Sopenharmony_ci * is down. This check is racy by definition and does not stop 4538c2ecf20Sopenharmony_ci * the system from triggering an SError if the link goes down 4548c2ecf20Sopenharmony_ci * after this check is performed. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci if (!dw_pcie_link_up(pci)) 4578c2ecf20Sopenharmony_ci return NULL; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | 4608c2ecf20Sopenharmony_ci PCIE_ATU_FUNC(PCI_FUNC(devfn)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (pci_is_root_bus(bus->parent)) 4638c2ecf20Sopenharmony_ci type = PCIE_ATU_TYPE_CFG0; 4648c2ecf20Sopenharmony_ci else 4658c2ecf20Sopenharmony_ci type = PCIE_ATU_TYPE_CFG1; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, 4698c2ecf20Sopenharmony_ci type, pp->cfg0_base, 4708c2ecf20Sopenharmony_ci busdev, pp->cfg0_size); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return pp->va_cfg0_base + where; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, 4768c2ecf20Sopenharmony_ci int where, int size, u32 *val) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci int ret; 4798c2ecf20Sopenharmony_ci struct pcie_port *pp = bus->sysdata; 4808c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = pci_generic_config_read(bus, devfn, where, size, val); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!ret && pci->num_viewport <= 2) 4858c2ecf20Sopenharmony_ci dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, 4868c2ecf20Sopenharmony_ci PCIE_ATU_TYPE_IO, pp->io_base, 4878c2ecf20Sopenharmony_ci pp->io_bus_addr, pp->io_size); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return ret; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, 4938c2ecf20Sopenharmony_ci int where, int size, u32 val) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci int ret; 4968c2ecf20Sopenharmony_ci struct pcie_port *pp = bus->sysdata; 4978c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ret = pci_generic_config_write(bus, devfn, where, size, val); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!ret && pci->num_viewport <= 2) 5028c2ecf20Sopenharmony_ci dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, 5038c2ecf20Sopenharmony_ci PCIE_ATU_TYPE_IO, pp->io_base, 5048c2ecf20Sopenharmony_ci pp->io_bus_addr, pp->io_size); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic struct pci_ops dw_child_pcie_ops = { 5108c2ecf20Sopenharmony_ci .map_bus = dw_pcie_other_conf_map_bus, 5118c2ecf20Sopenharmony_ci .read = dw_pcie_rd_other_conf, 5128c2ecf20Sopenharmony_ci .write = dw_pcie_wr_other_conf, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_civoid __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct pcie_port *pp = bus->sysdata; 5188c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (PCI_SLOT(devfn) > 0) 5218c2ecf20Sopenharmony_ci return NULL; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return pci->dbi_base + where; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_own_conf_map_bus); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic struct pci_ops dw_pcie_ops = { 5288c2ecf20Sopenharmony_ci .map_bus = dw_pcie_own_conf_map_bus, 5298c2ecf20Sopenharmony_ci .read = pci_generic_config_read, 5308c2ecf20Sopenharmony_ci .write = pci_generic_config_write, 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_civoid dw_pcie_setup_rc(struct pcie_port *pp) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci u32 val, ctrl, num_ctrls; 5368c2ecf20Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* 5398c2ecf20Sopenharmony_ci * Enable DBI read-only registers for writing/updating configuration. 5408c2ecf20Sopenharmony_ci * Write permission gets disabled towards the end of this function. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci dw_pcie_dbi_ro_wr_en(pci); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci dw_pcie_setup(pci); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (pci_msi_enabled() && !pp->ops->msi_host_init) { 5478c2ecf20Sopenharmony_ci num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Initialize IRQ Status array */ 5508c2ecf20Sopenharmony_ci for (ctrl = 0; ctrl < num_ctrls; ctrl++) { 5518c2ecf20Sopenharmony_ci pp->irq_mask[ctrl] = ~0; 5528c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + 5538c2ecf20Sopenharmony_ci (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 5548c2ecf20Sopenharmony_ci pp->irq_mask[ctrl]); 5558c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + 5568c2ecf20Sopenharmony_ci (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 5578c2ecf20Sopenharmony_ci ~0); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Setup RC BARs */ 5628c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); 5638c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Setup interrupt pins */ 5668c2ecf20Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); 5678c2ecf20Sopenharmony_ci val &= 0xffff00ff; 5688c2ecf20Sopenharmony_ci val |= 0x00000100; 5698c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Setup bus numbers */ 5728c2ecf20Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); 5738c2ecf20Sopenharmony_ci val &= 0xff000000; 5748c2ecf20Sopenharmony_ci val |= 0x00ff0100; 5758c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Setup command register */ 5788c2ecf20Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCI_COMMAND); 5798c2ecf20Sopenharmony_ci val &= 0xffff0000; 5808c2ecf20Sopenharmony_ci val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | 5818c2ecf20Sopenharmony_ci PCI_COMMAND_MASTER | PCI_COMMAND_SERR; 5828c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_COMMAND, val); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* 5858c2ecf20Sopenharmony_ci * If the platform provides its own child bus config accesses, it means 5868c2ecf20Sopenharmony_ci * the platform uses its own address translation component rather than 5878c2ecf20Sopenharmony_ci * ATU, so we should not program the ATU here. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci if (pp->bridge->child_ops == &dw_child_pcie_ops) { 5908c2ecf20Sopenharmony_ci struct resource_entry *tmp, *entry = NULL; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Get last memory resource entry */ 5938c2ecf20Sopenharmony_ci resource_list_for_each_entry(tmp, &pp->bridge->windows) 5948c2ecf20Sopenharmony_ci if (resource_type(tmp->res) == IORESOURCE_MEM) 5958c2ecf20Sopenharmony_ci entry = tmp; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, 5988c2ecf20Sopenharmony_ci PCIE_ATU_TYPE_MEM, entry->res->start, 5998c2ecf20Sopenharmony_ci entry->res->start - entry->offset, 6008c2ecf20Sopenharmony_ci resource_size(entry->res)); 6018c2ecf20Sopenharmony_ci if (pci->num_viewport > 2) 6028c2ecf20Sopenharmony_ci dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, 6038c2ecf20Sopenharmony_ci PCIE_ATU_TYPE_IO, pp->io_base, 6048c2ecf20Sopenharmony_ci pp->io_bus_addr, pp->io_size); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Program correct class for RC */ 6108c2ecf20Sopenharmony_ci dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); 6138c2ecf20Sopenharmony_ci val |= PORT_LOGIC_SPEED_CHANGE; 6148c2ecf20Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci dw_pcie_dbi_ro_wr_dis(pci); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_setup_rc); 619