18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI-E support for CNS3xxx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2008 Cavium Networks 68c2ecf20Sopenharmony_ci * Richard Liu <richard.liu@caviumnetworks.com> 78c2ecf20Sopenharmony_ci * Copyright 2010 MontaVista Software, LLC. 88c2ecf20Sopenharmony_ci * Anton Vorontsov <avorontsov@mvista.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/bug.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/ioport.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 198c2ecf20Sopenharmony_ci#include <asm/mach/map.h> 208c2ecf20Sopenharmony_ci#include "cns3xxx.h" 218c2ecf20Sopenharmony_ci#include "core.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct cns3xxx_pcie { 248c2ecf20Sopenharmony_ci void __iomem *host_regs; /* PCI config registers for host bridge */ 258c2ecf20Sopenharmony_ci void __iomem *cfg0_regs; /* PCI Type 0 config registers */ 268c2ecf20Sopenharmony_ci void __iomem *cfg1_regs; /* PCI Type 1 config registers */ 278c2ecf20Sopenharmony_ci unsigned int irqs[2]; 288c2ecf20Sopenharmony_ci struct resource res_io; 298c2ecf20Sopenharmony_ci struct resource res_mem; 308c2ecf20Sopenharmony_ci int port; 318c2ecf20Sopenharmony_ci bool linked; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct cns3xxx_pcie *sysdata_to_cnspci(void *sysdata) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct pci_sys_data *root = sysdata; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return root->private_data; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct cns3xxx_pcie *pdev_to_cnspci(const struct pci_dev *dev) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci return sysdata_to_cnspci(dev->sysdata); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic struct cns3xxx_pcie *pbus_to_cnspci(struct pci_bus *bus) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci return sysdata_to_cnspci(bus->sysdata); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void __iomem *cns3xxx_pci_map_bus(struct pci_bus *bus, 528c2ecf20Sopenharmony_ci unsigned int devfn, int where) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct cns3xxx_pcie *cnspci = pbus_to_cnspci(bus); 558c2ecf20Sopenharmony_ci int busno = bus->number; 568c2ecf20Sopenharmony_ci int slot = PCI_SLOT(devfn); 578c2ecf20Sopenharmony_ci void __iomem *base; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* If there is no link, just show the CNS PCI bridge. */ 608c2ecf20Sopenharmony_ci if (!cnspci->linked && busno > 0) 618c2ecf20Sopenharmony_ci return NULL; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci * The CNS PCI bridge doesn't fit into the PCI hierarchy, though 658c2ecf20Sopenharmony_ci * we still want to access it. 668c2ecf20Sopenharmony_ci * We place the host bridge on bus 0, and the directly connected 678c2ecf20Sopenharmony_ci * device on bus 1, slot 0. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci if (busno == 0) { /* internal PCIe bus, host bridge device */ 708c2ecf20Sopenharmony_ci if (devfn == 0) /* device# and function# are ignored by hw */ 718c2ecf20Sopenharmony_ci base = cnspci->host_regs; 728c2ecf20Sopenharmony_ci else 738c2ecf20Sopenharmony_ci return NULL; /* no such device */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci } else if (busno == 1) { /* directly connected PCIe device */ 768c2ecf20Sopenharmony_ci if (slot == 0) /* device# is ignored by hw */ 778c2ecf20Sopenharmony_ci base = cnspci->cfg0_regs; 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci return NULL; /* no such device */ 808c2ecf20Sopenharmony_ci } else /* remote PCI bus */ 818c2ecf20Sopenharmony_ci base = cnspci->cfg1_regs + ((busno & 0xf) << 20); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return base + where + (devfn << 12); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn, 878c2ecf20Sopenharmony_ci int where, int size, u32 *val) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int ret; 908c2ecf20Sopenharmony_ci u32 mask = (0x1ull << (size * 8)) - 1; 918c2ecf20Sopenharmony_ci int shift = (where % 4) * 8; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = pci_generic_config_read(bus, devfn, where, size, val); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn && 968c2ecf20Sopenharmony_ci (where & 0xffc) == PCI_CLASS_REVISION) 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * RC's class is 0xb, but Linux PCI driver needs 0x604 998c2ecf20Sopenharmony_ci * for a PCIe bridge. So we must fixup the class code 1008c2ecf20Sopenharmony_ci * to 0x604 here. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci *val = ((((*val << shift) & 0xff) | (0x604 << 16)) >> shift) & mask; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int cns3xxx_pci_setup(int nr, struct pci_sys_data *sys) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct cns3xxx_pcie *cnspci = sysdata_to_cnspci(sys); 1108c2ecf20Sopenharmony_ci struct resource *res_io = &cnspci->res_io; 1118c2ecf20Sopenharmony_ci struct resource *res_mem = &cnspci->res_mem; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci BUG_ON(request_resource(&iomem_resource, res_io) || 1148c2ecf20Sopenharmony_ci request_resource(&iomem_resource, res_mem)); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci pci_add_resource_offset(&sys->resources, res_io, sys->io_offset); 1178c2ecf20Sopenharmony_ci pci_add_resource_offset(&sys->resources, res_mem, sys->mem_offset); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 1; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct pci_ops cns3xxx_pcie_ops = { 1238c2ecf20Sopenharmony_ci .map_bus = cns3xxx_pci_map_bus, 1248c2ecf20Sopenharmony_ci .read = cns3xxx_pci_read_config, 1258c2ecf20Sopenharmony_ci .write = pci_generic_config_write, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int cns3xxx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct cns3xxx_pcie *cnspci = pdev_to_cnspci(dev); 1318c2ecf20Sopenharmony_ci int irq = cnspci->irqs[!!dev->bus->number]; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci pr_info("PCIe map irq: %04d:%02x:%02x.%02x slot %d, pin %d, irq: %d\n", 1348c2ecf20Sopenharmony_ci pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), 1358c2ecf20Sopenharmony_ci PCI_FUNC(dev->devfn), slot, pin, irq); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return irq; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic struct cns3xxx_pcie cns3xxx_pcie[] = { 1418c2ecf20Sopenharmony_ci [0] = { 1428c2ecf20Sopenharmony_ci .host_regs = (void __iomem *)CNS3XXX_PCIE0_HOST_BASE_VIRT, 1438c2ecf20Sopenharmony_ci .cfg0_regs = (void __iomem *)CNS3XXX_PCIE0_CFG0_BASE_VIRT, 1448c2ecf20Sopenharmony_ci .cfg1_regs = (void __iomem *)CNS3XXX_PCIE0_CFG1_BASE_VIRT, 1458c2ecf20Sopenharmony_ci .res_io = { 1468c2ecf20Sopenharmony_ci .name = "PCIe0 I/O space", 1478c2ecf20Sopenharmony_ci .start = CNS3XXX_PCIE0_IO_BASE, 1488c2ecf20Sopenharmony_ci .end = CNS3XXX_PCIE0_CFG0_BASE - 1, /* 16 MiB */ 1498c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO, 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci .res_mem = { 1528c2ecf20Sopenharmony_ci .name = "PCIe0 non-prefetchable", 1538c2ecf20Sopenharmony_ci .start = CNS3XXX_PCIE0_MEM_BASE, 1548c2ecf20Sopenharmony_ci .end = CNS3XXX_PCIE0_HOST_BASE - 1, /* 176 MiB */ 1558c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 1568c2ecf20Sopenharmony_ci }, 1578c2ecf20Sopenharmony_ci .irqs = { IRQ_CNS3XXX_PCIE0_RC, IRQ_CNS3XXX_PCIE0_DEVICE, }, 1588c2ecf20Sopenharmony_ci .port = 0, 1598c2ecf20Sopenharmony_ci }, 1608c2ecf20Sopenharmony_ci [1] = { 1618c2ecf20Sopenharmony_ci .host_regs = (void __iomem *)CNS3XXX_PCIE1_HOST_BASE_VIRT, 1628c2ecf20Sopenharmony_ci .cfg0_regs = (void __iomem *)CNS3XXX_PCIE1_CFG0_BASE_VIRT, 1638c2ecf20Sopenharmony_ci .cfg1_regs = (void __iomem *)CNS3XXX_PCIE1_CFG1_BASE_VIRT, 1648c2ecf20Sopenharmony_ci .res_io = { 1658c2ecf20Sopenharmony_ci .name = "PCIe1 I/O space", 1668c2ecf20Sopenharmony_ci .start = CNS3XXX_PCIE1_IO_BASE, 1678c2ecf20Sopenharmony_ci .end = CNS3XXX_PCIE1_CFG0_BASE - 1, /* 16 MiB */ 1688c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO, 1698c2ecf20Sopenharmony_ci }, 1708c2ecf20Sopenharmony_ci .res_mem = { 1718c2ecf20Sopenharmony_ci .name = "PCIe1 non-prefetchable", 1728c2ecf20Sopenharmony_ci .start = CNS3XXX_PCIE1_MEM_BASE, 1738c2ecf20Sopenharmony_ci .end = CNS3XXX_PCIE1_HOST_BASE - 1, /* 176 MiB */ 1748c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci .irqs = { IRQ_CNS3XXX_PCIE1_RC, IRQ_CNS3XXX_PCIE1_DEVICE, }, 1778c2ecf20Sopenharmony_ci .port = 1, 1788c2ecf20Sopenharmony_ci }, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void __init cns3xxx_pcie_check_link(struct cns3xxx_pcie *cnspci) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int port = cnspci->port; 1848c2ecf20Sopenharmony_ci u32 reg; 1858c2ecf20Sopenharmony_ci unsigned long time; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci reg = __raw_readl(MISC_PCIE_CTRL(port)); 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * Enable Application Request to 1, it will exit L1 automatically, 1908c2ecf20Sopenharmony_ci * but when chip back, it will use another clock, still can use 0x1. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci reg |= 0x3; 1938c2ecf20Sopenharmony_ci __raw_writel(reg, MISC_PCIE_CTRL(port)); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci pr_info("PCIe: Port[%d] Enable PCIe LTSSM\n", port); 1968c2ecf20Sopenharmony_ci pr_info("PCIe: Port[%d] Check data link layer...", port); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci time = jiffies; 1998c2ecf20Sopenharmony_ci while (1) { 2008c2ecf20Sopenharmony_ci reg = __raw_readl(MISC_PCIE_PM_DEBUG(port)); 2018c2ecf20Sopenharmony_ci if (reg & 0x1) { 2028c2ecf20Sopenharmony_ci pr_info("Link up.\n"); 2038c2ecf20Sopenharmony_ci cnspci->linked = 1; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci } else if (time_after(jiffies, time + 50)) { 2068c2ecf20Sopenharmony_ci pr_info("Device not found.\n"); 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void cns3xxx_write_config(struct cns3xxx_pcie *cnspci, 2138c2ecf20Sopenharmony_ci int where, int size, u32 val) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci void __iomem *base = cnspci->host_regs + (where & 0xffc); 2168c2ecf20Sopenharmony_ci u32 v; 2178c2ecf20Sopenharmony_ci u32 mask = (0x1ull << (size * 8)) - 1; 2188c2ecf20Sopenharmony_ci int shift = (where % 4) * 8; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci v = readl_relaxed(base); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci v &= ~(mask << shift); 2238c2ecf20Sopenharmony_ci v |= (val & mask) << shift; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci writel_relaxed(v, base); 2268c2ecf20Sopenharmony_ci readl_relaxed(base); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci u16 mem_base = cnspci->res_mem.start >> 16; 2328c2ecf20Sopenharmony_ci u16 mem_limit = cnspci->res_mem.end >> 16; 2338c2ecf20Sopenharmony_ci u16 io_base = cnspci->res_io.start >> 16; 2348c2ecf20Sopenharmony_ci u16 io_limit = cnspci->res_io.end >> 16; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_PRIMARY_BUS, 1, 0); 2378c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_SECONDARY_BUS, 1, 1); 2388c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_SUBORDINATE_BUS, 1, 1); 2398c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_MEMORY_BASE, 2, mem_base); 2408c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_MEMORY_LIMIT, 2, mem_limit); 2418c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_IO_BASE_UPPER16, 2, io_base); 2428c2ecf20Sopenharmony_ci cns3xxx_write_config(cnspci, PCI_IO_LIMIT_UPPER16, 2, io_limit); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (!cnspci->linked) 2458c2ecf20Sopenharmony_ci return; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Set Device Max_Read_Request_Size to 128 byte */ 2488c2ecf20Sopenharmony_ci pcie_bus_config = PCIE_BUS_PEER2PEER; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Disable PCIe0 Interrupt Mask INTA to INTD */ 2518c2ecf20Sopenharmony_ci __raw_writel(~0x3FFF, MISC_PCIE_INT_MASK(cnspci->port)); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr, 2558c2ecf20Sopenharmony_ci struct pt_regs *regs) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci if (fsr & (1 << 10)) 2588c2ecf20Sopenharmony_ci regs->ARM_pc += 4; 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_civoid __init cns3xxx_pcie_init_late(void) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int i; 2658c2ecf20Sopenharmony_ci void *private_data; 2668c2ecf20Sopenharmony_ci struct hw_pci hw_pci = { 2678c2ecf20Sopenharmony_ci .nr_controllers = 1, 2688c2ecf20Sopenharmony_ci .ops = &cns3xxx_pcie_ops, 2698c2ecf20Sopenharmony_ci .setup = cns3xxx_pci_setup, 2708c2ecf20Sopenharmony_ci .map_irq = cns3xxx_pcie_map_irq, 2718c2ecf20Sopenharmony_ci .private_data = &private_data, 2728c2ecf20Sopenharmony_ci }; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci pcibios_min_io = 0; 2758c2ecf20Sopenharmony_ci pcibios_min_mem = 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci hook_fault_code(16 + 6, cns3xxx_pcie_abort_handler, SIGBUS, 0, 2788c2ecf20Sopenharmony_ci "imprecise external abort"); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cns3xxx_pcie); i++) { 2818c2ecf20Sopenharmony_ci cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_PCIE(i)); 2828c2ecf20Sopenharmony_ci cns3xxx_pwr_soft_rst(0x1 << PM_SOFT_RST_REG_OFFST_PCIE(i)); 2838c2ecf20Sopenharmony_ci cns3xxx_pcie_check_link(&cns3xxx_pcie[i]); 2848c2ecf20Sopenharmony_ci cns3xxx_pcie_hw_init(&cns3xxx_pcie[i]); 2858c2ecf20Sopenharmony_ci private_data = &cns3xxx_pcie[i]; 2868c2ecf20Sopenharmony_ci pci_common_init(&hw_pci); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci pci_assign_unassigned_resources(); 2908c2ecf20Sopenharmony_ci} 291