18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCIe host controller driver for Xilinx Versal CPM DMA Bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 2019 - 2020, Xilinx, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/irq.h> 118c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 128c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 138c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 188c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 198c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pci-ecam.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "../pci.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Register definitions */ 278c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_IDR 0x00000E10 288c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_IMR 0x00000E14 298c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_PSCR 0x00000E1C 308c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_RPSC 0x00000E20 318c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_RPEFR 0x00000E2C 328c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_IDRN 0x00000E38 338c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_IDRN_MASK 0x00000E3C 348c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_MISC_IR_STATUS 0x00000340 358c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_MISC_IR_ENABLE 0x00000348 368c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_MISC_IR_LOCAL BIT(1) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Interrupt registers definitions */ 398c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_LINK_DOWN 0 408c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_HOT_RESET 3 418c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_CFG_PCIE_TIMEOUT 4 428c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_CFG_TIMEOUT 8 438c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_CORRECTABLE 9 448c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_NONFATAL 10 458c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_FATAL 11 468c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_CFG_ERR_POISON 12 478c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_PME_TO_ACK_RCVD 15 488c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_INTX 16 498c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_PM_PME_RCVD 17 508c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_UNSUPP 20 518c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_UNEXP 21 528c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_COMPL 22 538c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_ERRP 23 548c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_CMPABT 24 558c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_ILLBUR 25 568c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_MST_DECERR 26 578c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_MST_SLVERR 27 588c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_INTR_SLV_PCIE_TIMEOUT 28 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define IMR(x) BIT(XILINX_CPM_PCIE_INTR_ ##x) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_IMR_ALL_MASK \ 638c2ecf20Sopenharmony_ci ( \ 648c2ecf20Sopenharmony_ci IMR(LINK_DOWN) | \ 658c2ecf20Sopenharmony_ci IMR(HOT_RESET) | \ 668c2ecf20Sopenharmony_ci IMR(CFG_PCIE_TIMEOUT) | \ 678c2ecf20Sopenharmony_ci IMR(CFG_TIMEOUT) | \ 688c2ecf20Sopenharmony_ci IMR(CORRECTABLE) | \ 698c2ecf20Sopenharmony_ci IMR(NONFATAL) | \ 708c2ecf20Sopenharmony_ci IMR(FATAL) | \ 718c2ecf20Sopenharmony_ci IMR(CFG_ERR_POISON) | \ 728c2ecf20Sopenharmony_ci IMR(PME_TO_ACK_RCVD) | \ 738c2ecf20Sopenharmony_ci IMR(INTX) | \ 748c2ecf20Sopenharmony_ci IMR(PM_PME_RCVD) | \ 758c2ecf20Sopenharmony_ci IMR(SLV_UNSUPP) | \ 768c2ecf20Sopenharmony_ci IMR(SLV_UNEXP) | \ 778c2ecf20Sopenharmony_ci IMR(SLV_COMPL) | \ 788c2ecf20Sopenharmony_ci IMR(SLV_ERRP) | \ 798c2ecf20Sopenharmony_ci IMR(SLV_CMPABT) | \ 808c2ecf20Sopenharmony_ci IMR(SLV_ILLBUR) | \ 818c2ecf20Sopenharmony_ci IMR(MST_DECERR) | \ 828c2ecf20Sopenharmony_ci IMR(MST_SLVERR) | \ 838c2ecf20Sopenharmony_ci IMR(SLV_PCIE_TIMEOUT) \ 848c2ecf20Sopenharmony_ci ) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_IDR_ALL_MASK 0xFFFFFFFF 878c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_IDRN_MASK GENMASK(19, 16) 888c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_IDRN_SHIFT 16 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Root Port Error FIFO Read Register definitions */ 918c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_RPEFR_ERR_VALID BIT(18) 928c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_RPEFR_REQ_ID GENMASK(15, 0) 938c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_RPEFR_ALL_MASK 0xFFFFFFFF 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Root Port Status/control Register definitions */ 968c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_RPSC_BEN BIT(0) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Phy Status/Control Register definitions */ 998c2ecf20Sopenharmony_ci#define XILINX_CPM_PCIE_REG_PSCR_LNKUP BIT(11) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/** 1028c2ecf20Sopenharmony_ci * struct xilinx_cpm_pcie_port - PCIe port information 1038c2ecf20Sopenharmony_ci * @reg_base: Bridge Register Base 1048c2ecf20Sopenharmony_ci * @cpm_base: CPM System Level Control and Status Register(SLCR) Base 1058c2ecf20Sopenharmony_ci * @dev: Device pointer 1068c2ecf20Sopenharmony_ci * @intx_domain: Legacy IRQ domain pointer 1078c2ecf20Sopenharmony_ci * @cpm_domain: CPM IRQ domain pointer 1088c2ecf20Sopenharmony_ci * @cfg: Holds mappings of config space window 1098c2ecf20Sopenharmony_ci * @intx_irq: legacy interrupt number 1108c2ecf20Sopenharmony_ci * @irq: Error interrupt number 1118c2ecf20Sopenharmony_ci * @lock: lock protecting shared register access 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistruct xilinx_cpm_pcie_port { 1148c2ecf20Sopenharmony_ci void __iomem *reg_base; 1158c2ecf20Sopenharmony_ci void __iomem *cpm_base; 1168c2ecf20Sopenharmony_ci struct device *dev; 1178c2ecf20Sopenharmony_ci struct irq_domain *intx_domain; 1188c2ecf20Sopenharmony_ci struct irq_domain *cpm_domain; 1198c2ecf20Sopenharmony_ci struct pci_config_window *cfg; 1208c2ecf20Sopenharmony_ci int intx_irq; 1218c2ecf20Sopenharmony_ci int irq; 1228c2ecf20Sopenharmony_ci raw_spinlock_t lock; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic u32 pcie_read(struct xilinx_cpm_pcie_port *port, u32 reg) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return readl_relaxed(port->reg_base + reg); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void pcie_write(struct xilinx_cpm_pcie_port *port, 1318c2ecf20Sopenharmony_ci u32 val, u32 reg) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci writel_relaxed(val, port->reg_base + reg); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic bool cpm_pcie_link_up(struct xilinx_cpm_pcie_port *port) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci return (pcie_read(port, XILINX_CPM_PCIE_REG_PSCR) & 1398c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_REG_PSCR_LNKUP); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void cpm_pcie_clear_err_interrupts(struct xilinx_cpm_pcie_port *port) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci unsigned long val = pcie_read(port, XILINX_CPM_PCIE_REG_RPEFR); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (val & XILINX_CPM_PCIE_RPEFR_ERR_VALID) { 1478c2ecf20Sopenharmony_ci dev_dbg(port->dev, "Requester ID %lu\n", 1488c2ecf20Sopenharmony_ci val & XILINX_CPM_PCIE_RPEFR_REQ_ID); 1498c2ecf20Sopenharmony_ci pcie_write(port, XILINX_CPM_PCIE_RPEFR_ALL_MASK, 1508c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_REG_RPEFR); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void xilinx_cpm_mask_leg_irq(struct irq_data *data) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(data); 1578c2ecf20Sopenharmony_ci unsigned long flags; 1588c2ecf20Sopenharmony_ci u32 mask; 1598c2ecf20Sopenharmony_ci u32 val; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci mask = BIT(data->hwirq + XILINX_CPM_PCIE_IDRN_SHIFT); 1628c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&port->lock, flags); 1638c2ecf20Sopenharmony_ci val = pcie_read(port, XILINX_CPM_PCIE_REG_IDRN_MASK); 1648c2ecf20Sopenharmony_ci pcie_write(port, (val & (~mask)), XILINX_CPM_PCIE_REG_IDRN_MASK); 1658c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&port->lock, flags); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void xilinx_cpm_unmask_leg_irq(struct irq_data *data) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(data); 1718c2ecf20Sopenharmony_ci unsigned long flags; 1728c2ecf20Sopenharmony_ci u32 mask; 1738c2ecf20Sopenharmony_ci u32 val; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mask = BIT(data->hwirq + XILINX_CPM_PCIE_IDRN_SHIFT); 1768c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&port->lock, flags); 1778c2ecf20Sopenharmony_ci val = pcie_read(port, XILINX_CPM_PCIE_REG_IDRN_MASK); 1788c2ecf20Sopenharmony_ci pcie_write(port, (val | mask), XILINX_CPM_PCIE_REG_IDRN_MASK); 1798c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&port->lock, flags); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct irq_chip xilinx_cpm_leg_irq_chip = { 1838c2ecf20Sopenharmony_ci .name = "INTx", 1848c2ecf20Sopenharmony_ci .irq_mask = xilinx_cpm_mask_leg_irq, 1858c2ecf20Sopenharmony_ci .irq_unmask = xilinx_cpm_unmask_leg_irq, 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/** 1898c2ecf20Sopenharmony_ci * xilinx_cpm_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid 1908c2ecf20Sopenharmony_ci * @domain: IRQ domain 1918c2ecf20Sopenharmony_ci * @irq: Virtual IRQ number 1928c2ecf20Sopenharmony_ci * @hwirq: HW interrupt number 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Return: Always returns 0. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic int xilinx_cpm_pcie_intx_map(struct irq_domain *domain, 1978c2ecf20Sopenharmony_ci unsigned int irq, irq_hw_number_t hwirq) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &xilinx_cpm_leg_irq_chip, 2008c2ecf20Sopenharmony_ci handle_level_irq); 2018c2ecf20Sopenharmony_ci irq_set_chip_data(irq, domain->host_data); 2028c2ecf20Sopenharmony_ci irq_set_status_flags(irq, IRQ_LEVEL); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* INTx IRQ Domain operations */ 2088c2ecf20Sopenharmony_cistatic const struct irq_domain_ops intx_domain_ops = { 2098c2ecf20Sopenharmony_ci .map = xilinx_cpm_pcie_intx_map, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void xilinx_cpm_pcie_intx_flow(struct irq_desc *desc) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = irq_desc_get_handler_data(desc); 2158c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 2168c2ecf20Sopenharmony_ci unsigned long val; 2178c2ecf20Sopenharmony_ci int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci val = FIELD_GET(XILINX_CPM_PCIE_IDRN_MASK, 2228c2ecf20Sopenharmony_ci pcie_read(port, XILINX_CPM_PCIE_REG_IDRN)); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci for_each_set_bit(i, &val, PCI_NUM_INTX) 2258c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(port->intx_domain, i)); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void xilinx_cpm_mask_event_irq(struct irq_data *d) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(d); 2338c2ecf20Sopenharmony_ci u32 val; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci raw_spin_lock(&port->lock); 2368c2ecf20Sopenharmony_ci val = pcie_read(port, XILINX_CPM_PCIE_REG_IMR); 2378c2ecf20Sopenharmony_ci val &= ~BIT(d->hwirq); 2388c2ecf20Sopenharmony_ci pcie_write(port, val, XILINX_CPM_PCIE_REG_IMR); 2398c2ecf20Sopenharmony_ci raw_spin_unlock(&port->lock); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void xilinx_cpm_unmask_event_irq(struct irq_data *d) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(d); 2458c2ecf20Sopenharmony_ci u32 val; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci raw_spin_lock(&port->lock); 2488c2ecf20Sopenharmony_ci val = pcie_read(port, XILINX_CPM_PCIE_REG_IMR); 2498c2ecf20Sopenharmony_ci val |= BIT(d->hwirq); 2508c2ecf20Sopenharmony_ci pcie_write(port, val, XILINX_CPM_PCIE_REG_IMR); 2518c2ecf20Sopenharmony_ci raw_spin_unlock(&port->lock); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic struct irq_chip xilinx_cpm_event_irq_chip = { 2558c2ecf20Sopenharmony_ci .name = "RC-Event", 2568c2ecf20Sopenharmony_ci .irq_mask = xilinx_cpm_mask_event_irq, 2578c2ecf20Sopenharmony_ci .irq_unmask = xilinx_cpm_unmask_event_irq, 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int xilinx_cpm_pcie_event_map(struct irq_domain *domain, 2618c2ecf20Sopenharmony_ci unsigned int irq, irq_hw_number_t hwirq) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &xilinx_cpm_event_irq_chip, 2648c2ecf20Sopenharmony_ci handle_level_irq); 2658c2ecf20Sopenharmony_ci irq_set_chip_data(irq, domain->host_data); 2668c2ecf20Sopenharmony_ci irq_set_status_flags(irq, IRQ_LEVEL); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct irq_domain_ops event_domain_ops = { 2718c2ecf20Sopenharmony_ci .map = xilinx_cpm_pcie_event_map, 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void xilinx_cpm_pcie_event_flow(struct irq_desc *desc) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = irq_desc_get_handler_data(desc); 2778c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 2788c2ecf20Sopenharmony_ci unsigned long val; 2798c2ecf20Sopenharmony_ci int i; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 2828c2ecf20Sopenharmony_ci val = pcie_read(port, XILINX_CPM_PCIE_REG_IDR); 2838c2ecf20Sopenharmony_ci val &= pcie_read(port, XILINX_CPM_PCIE_REG_IMR); 2848c2ecf20Sopenharmony_ci for_each_set_bit(i, &val, 32) 2858c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(port->cpm_domain, i)); 2868c2ecf20Sopenharmony_ci pcie_write(port, val, XILINX_CPM_PCIE_REG_IDR); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * XILINX_CPM_PCIE_MISC_IR_STATUS register is mapped to 2908c2ecf20Sopenharmony_ci * CPM SLCR block. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci val = readl_relaxed(port->cpm_base + XILINX_CPM_PCIE_MISC_IR_STATUS); 2938c2ecf20Sopenharmony_ci if (val) 2948c2ecf20Sopenharmony_ci writel_relaxed(val, 2958c2ecf20Sopenharmony_ci port->cpm_base + XILINX_CPM_PCIE_MISC_IR_STATUS); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#define _IC(x, s) \ 3018c2ecf20Sopenharmony_ci [XILINX_CPM_PCIE_INTR_ ## x] = { __stringify(x), s } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct { 3048c2ecf20Sopenharmony_ci const char *sym; 3058c2ecf20Sopenharmony_ci const char *str; 3068c2ecf20Sopenharmony_ci} intr_cause[32] = { 3078c2ecf20Sopenharmony_ci _IC(LINK_DOWN, "Link Down"), 3088c2ecf20Sopenharmony_ci _IC(HOT_RESET, "Hot reset"), 3098c2ecf20Sopenharmony_ci _IC(CFG_TIMEOUT, "ECAM access timeout"), 3108c2ecf20Sopenharmony_ci _IC(CORRECTABLE, "Correctable error message"), 3118c2ecf20Sopenharmony_ci _IC(NONFATAL, "Non fatal error message"), 3128c2ecf20Sopenharmony_ci _IC(FATAL, "Fatal error message"), 3138c2ecf20Sopenharmony_ci _IC(SLV_UNSUPP, "Slave unsupported request"), 3148c2ecf20Sopenharmony_ci _IC(SLV_UNEXP, "Slave unexpected completion"), 3158c2ecf20Sopenharmony_ci _IC(SLV_COMPL, "Slave completion timeout"), 3168c2ecf20Sopenharmony_ci _IC(SLV_ERRP, "Slave Error Poison"), 3178c2ecf20Sopenharmony_ci _IC(SLV_CMPABT, "Slave Completer Abort"), 3188c2ecf20Sopenharmony_ci _IC(SLV_ILLBUR, "Slave Illegal Burst"), 3198c2ecf20Sopenharmony_ci _IC(MST_DECERR, "Master decode error"), 3208c2ecf20Sopenharmony_ci _IC(MST_SLVERR, "Master slave error"), 3218c2ecf20Sopenharmony_ci _IC(CFG_PCIE_TIMEOUT, "PCIe ECAM access timeout"), 3228c2ecf20Sopenharmony_ci _IC(CFG_ERR_POISON, "ECAM poisoned completion received"), 3238c2ecf20Sopenharmony_ci _IC(PME_TO_ACK_RCVD, "PME_TO_ACK message received"), 3248c2ecf20Sopenharmony_ci _IC(PM_PME_RCVD, "PM_PME message received"), 3258c2ecf20Sopenharmony_ci _IC(SLV_PCIE_TIMEOUT, "PCIe completion timeout received"), 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic irqreturn_t xilinx_cpm_pcie_intr_handler(int irq, void *dev_id) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port = dev_id; 3318c2ecf20Sopenharmony_ci struct device *dev = port->dev; 3328c2ecf20Sopenharmony_ci struct irq_data *d; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci d = irq_domain_get_irq_data(port->cpm_domain, irq); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci switch (d->hwirq) { 3378c2ecf20Sopenharmony_ci case XILINX_CPM_PCIE_INTR_CORRECTABLE: 3388c2ecf20Sopenharmony_ci case XILINX_CPM_PCIE_INTR_NONFATAL: 3398c2ecf20Sopenharmony_ci case XILINX_CPM_PCIE_INTR_FATAL: 3408c2ecf20Sopenharmony_ci cpm_pcie_clear_err_interrupts(port); 3418c2ecf20Sopenharmony_ci fallthrough; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci default: 3448c2ecf20Sopenharmony_ci if (intr_cause[d->hwirq].str) 3458c2ecf20Sopenharmony_ci dev_warn(dev, "%s\n", intr_cause[d->hwirq].str); 3468c2ecf20Sopenharmony_ci else 3478c2ecf20Sopenharmony_ci dev_warn(dev, "Unknown IRQ %ld\n", d->hwirq); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void xilinx_cpm_free_irq_domains(struct xilinx_cpm_pcie_port *port) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci if (port->intx_domain) { 3568c2ecf20Sopenharmony_ci irq_domain_remove(port->intx_domain); 3578c2ecf20Sopenharmony_ci port->intx_domain = NULL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (port->cpm_domain) { 3618c2ecf20Sopenharmony_ci irq_domain_remove(port->cpm_domain); 3628c2ecf20Sopenharmony_ci port->cpm_domain = NULL; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/** 3678c2ecf20Sopenharmony_ci * xilinx_cpm_pcie_init_irq_domain - Initialize IRQ domain 3688c2ecf20Sopenharmony_ci * @port: PCIe port information 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * Return: '0' on success and error value on failure 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_cistatic int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie_port *port) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct device *dev = port->dev; 3758c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 3768c2ecf20Sopenharmony_ci struct device_node *pcie_intc_node; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Setup INTx */ 3798c2ecf20Sopenharmony_ci pcie_intc_node = of_get_next_child(node, NULL); 3808c2ecf20Sopenharmony_ci if (!pcie_intc_node) { 3818c2ecf20Sopenharmony_ci dev_err(dev, "No PCIe Intc node found\n"); 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci port->cpm_domain = irq_domain_add_linear(pcie_intc_node, 32, 3868c2ecf20Sopenharmony_ci &event_domain_ops, 3878c2ecf20Sopenharmony_ci port); 3888c2ecf20Sopenharmony_ci if (!port->cpm_domain) 3898c2ecf20Sopenharmony_ci goto out; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci irq_domain_update_bus_token(port->cpm_domain, DOMAIN_BUS_NEXUS); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, 3948c2ecf20Sopenharmony_ci &intx_domain_ops, 3958c2ecf20Sopenharmony_ci port); 3968c2ecf20Sopenharmony_ci if (!port->intx_domain) 3978c2ecf20Sopenharmony_ci goto out; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci of_node_put(pcie_intc_node); 4028c2ecf20Sopenharmony_ci raw_spin_lock_init(&port->lock); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ciout: 4068c2ecf20Sopenharmony_ci xilinx_cpm_free_irq_domains(port); 4078c2ecf20Sopenharmony_ci of_node_put(pcie_intc_node); 4088c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate IRQ domains\n"); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return -ENOMEM; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie_port *port) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct device *dev = port->dev; 4168c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 4178c2ecf20Sopenharmony_ci int i, irq; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci port->irq = platform_get_irq(pdev, 0); 4208c2ecf20Sopenharmony_ci if (port->irq < 0) 4218c2ecf20Sopenharmony_ci return port->irq; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(intr_cause); i++) { 4248c2ecf20Sopenharmony_ci int err; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!intr_cause[i].str) 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci irq = irq_create_mapping(port->cpm_domain, i); 4308c2ecf20Sopenharmony_ci if (!irq) { 4318c2ecf20Sopenharmony_ci dev_err(dev, "Failed to map interrupt\n"); 4328c2ecf20Sopenharmony_ci return -ENXIO; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci err = devm_request_irq(dev, irq, xilinx_cpm_pcie_intr_handler, 4368c2ecf20Sopenharmony_ci 0, intr_cause[i].sym, port); 4378c2ecf20Sopenharmony_ci if (err) { 4388c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request IRQ %d\n", irq); 4398c2ecf20Sopenharmony_ci return err; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci port->intx_irq = irq_create_mapping(port->cpm_domain, 4448c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_INTR_INTX); 4458c2ecf20Sopenharmony_ci if (!port->intx_irq) { 4468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to map INTx interrupt\n"); 4478c2ecf20Sopenharmony_ci return -ENXIO; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Plug the INTx chained handler */ 4518c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(port->intx_irq, 4528c2ecf20Sopenharmony_ci xilinx_cpm_pcie_intx_flow, port); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Plug the main event chained handler */ 4558c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(port->irq, 4568c2ecf20Sopenharmony_ci xilinx_cpm_pcie_event_flow, port); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/** 4628c2ecf20Sopenharmony_ci * xilinx_cpm_pcie_init_port - Initialize hardware 4638c2ecf20Sopenharmony_ci * @port: PCIe port information 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_cistatic void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie_port *port) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci if (cpm_pcie_link_up(port)) 4688c2ecf20Sopenharmony_ci dev_info(port->dev, "PCIe Link is UP\n"); 4698c2ecf20Sopenharmony_ci else 4708c2ecf20Sopenharmony_ci dev_info(port->dev, "PCIe Link is DOWN\n"); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* Disable all interrupts */ 4738c2ecf20Sopenharmony_ci pcie_write(port, ~XILINX_CPM_PCIE_IDR_ALL_MASK, 4748c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_REG_IMR); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Clear pending interrupts */ 4778c2ecf20Sopenharmony_ci pcie_write(port, pcie_read(port, XILINX_CPM_PCIE_REG_IDR) & 4788c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_IMR_ALL_MASK, 4798c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_REG_IDR); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * XILINX_CPM_PCIE_MISC_IR_ENABLE register is mapped to 4838c2ecf20Sopenharmony_ci * CPM SLCR block. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci writel(XILINX_CPM_PCIE_MISC_IR_LOCAL, 4868c2ecf20Sopenharmony_ci port->cpm_base + XILINX_CPM_PCIE_MISC_IR_ENABLE); 4878c2ecf20Sopenharmony_ci /* Enable the Bridge enable bit */ 4888c2ecf20Sopenharmony_ci pcie_write(port, pcie_read(port, XILINX_CPM_PCIE_REG_RPSC) | 4898c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_REG_RPSC_BEN, 4908c2ecf20Sopenharmony_ci XILINX_CPM_PCIE_REG_RPSC); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/** 4948c2ecf20Sopenharmony_ci * xilinx_cpm_pcie_parse_dt - Parse Device tree 4958c2ecf20Sopenharmony_ci * @port: PCIe port information 4968c2ecf20Sopenharmony_ci * @bus_range: Bus resource 4978c2ecf20Sopenharmony_ci * 4988c2ecf20Sopenharmony_ci * Return: '0' on success and error value on failure 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_cistatic int xilinx_cpm_pcie_parse_dt(struct xilinx_cpm_pcie_port *port, 5018c2ecf20Sopenharmony_ci struct resource *bus_range) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct device *dev = port->dev; 5048c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5058c2ecf20Sopenharmony_ci struct resource *res; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci port->cpm_base = devm_platform_ioremap_resource_byname(pdev, 5088c2ecf20Sopenharmony_ci "cpm_slcr"); 5098c2ecf20Sopenharmony_ci if (IS_ERR(port->cpm_base)) 5108c2ecf20Sopenharmony_ci return PTR_ERR(port->cpm_base); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); 5138c2ecf20Sopenharmony_ci if (!res) 5148c2ecf20Sopenharmony_ci return -ENXIO; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci port->cfg = pci_ecam_create(dev, res, bus_range, 5178c2ecf20Sopenharmony_ci &pci_generic_ecam_ops); 5188c2ecf20Sopenharmony_ci if (IS_ERR(port->cfg)) 5198c2ecf20Sopenharmony_ci return PTR_ERR(port->cfg); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci port->reg_base = port->cfg->win; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void xilinx_cpm_free_interrupts(struct xilinx_cpm_pcie_port *port) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(port->intx_irq, NULL, NULL); 5298c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(port->irq, NULL, NULL); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci/** 5338c2ecf20Sopenharmony_ci * xilinx_cpm_pcie_probe - Probe function 5348c2ecf20Sopenharmony_ci * @pdev: Platform device pointer 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * Return: '0' on success and error value on failure 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cistatic int xilinx_cpm_pcie_probe(struct platform_device *pdev) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct xilinx_cpm_pcie_port *port; 5418c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5428c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge; 5438c2ecf20Sopenharmony_ci struct resource_entry *bus; 5448c2ecf20Sopenharmony_ci int err; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); 5478c2ecf20Sopenharmony_ci if (!bridge) 5488c2ecf20Sopenharmony_ci return -ENODEV; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci port = pci_host_bridge_priv(bridge); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci port->dev = dev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci err = xilinx_cpm_pcie_init_irq_domain(port); 5558c2ecf20Sopenharmony_ci if (err) 5568c2ecf20Sopenharmony_ci return err; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); 5598c2ecf20Sopenharmony_ci if (!bus) 5608c2ecf20Sopenharmony_ci return -ENODEV; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci err = xilinx_cpm_pcie_parse_dt(port, bus->res); 5638c2ecf20Sopenharmony_ci if (err) { 5648c2ecf20Sopenharmony_ci dev_err(dev, "Parsing DT failed\n"); 5658c2ecf20Sopenharmony_ci goto err_parse_dt; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci xilinx_cpm_pcie_init_port(port); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci err = xilinx_cpm_setup_irq(port); 5718c2ecf20Sopenharmony_ci if (err) { 5728c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set up interrupts\n"); 5738c2ecf20Sopenharmony_ci goto err_setup_irq; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci bridge->sysdata = port->cfg; 5778c2ecf20Sopenharmony_ci bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci err = pci_host_probe(bridge); 5808c2ecf20Sopenharmony_ci if (err < 0) 5818c2ecf20Sopenharmony_ci goto err_host_bridge; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cierr_host_bridge: 5868c2ecf20Sopenharmony_ci xilinx_cpm_free_interrupts(port); 5878c2ecf20Sopenharmony_cierr_setup_irq: 5888c2ecf20Sopenharmony_ci pci_ecam_free(port->cfg); 5898c2ecf20Sopenharmony_cierr_parse_dt: 5908c2ecf20Sopenharmony_ci xilinx_cpm_free_irq_domains(port); 5918c2ecf20Sopenharmony_ci return err; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic const struct of_device_id xilinx_cpm_pcie_of_match[] = { 5958c2ecf20Sopenharmony_ci { .compatible = "xlnx,versal-cpm-host-1.00", }, 5968c2ecf20Sopenharmony_ci {} 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic struct platform_driver xilinx_cpm_pcie_driver = { 6008c2ecf20Sopenharmony_ci .driver = { 6018c2ecf20Sopenharmony_ci .name = "xilinx-cpm-pcie", 6028c2ecf20Sopenharmony_ci .of_match_table = xilinx_cpm_pcie_of_match, 6038c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 6048c2ecf20Sopenharmony_ci }, 6058c2ecf20Sopenharmony_ci .probe = xilinx_cpm_pcie_probe, 6068c2ecf20Sopenharmony_ci}; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cibuiltin_platform_driver(xilinx_cpm_pcie_driver); 609