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