162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/mach-dove/pcie.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * PCIe functions for Marvell Dove 88AP510 SoC 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <video/vga.h> 1262306a36Sopenharmony_ci#include <asm/mach/pci.h> 1362306a36Sopenharmony_ci#include <asm/mach/arch.h> 1462306a36Sopenharmony_ci#include <asm/setup.h> 1562306a36Sopenharmony_ci#include <asm/delay.h> 1662306a36Sopenharmony_ci#include <plat/pcie.h> 1762306a36Sopenharmony_ci#include <plat/addr-map.h> 1862306a36Sopenharmony_ci#include "irqs.h" 1962306a36Sopenharmony_ci#include "bridge-regs.h" 2062306a36Sopenharmony_ci#include "common.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct pcie_port { 2362306a36Sopenharmony_ci u8 index; 2462306a36Sopenharmony_ci u8 root_bus_nr; 2562306a36Sopenharmony_ci void __iomem *base; 2662306a36Sopenharmony_ci spinlock_t conf_lock; 2762306a36Sopenharmony_ci char mem_space_name[16]; 2862306a36Sopenharmony_ci struct resource res; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct pcie_port pcie_port[2]; 3262306a36Sopenharmony_cistatic int num_pcie_ports; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int __init dove_pcie_setup(int nr, struct pci_sys_data *sys) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct pcie_port *pp; 3862306a36Sopenharmony_ci struct resource realio; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (nr >= num_pcie_ports) 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pp = &pcie_port[nr]; 4462306a36Sopenharmony_ci sys->private_data = pp; 4562306a36Sopenharmony_ci pp->root_bus_nr = sys->busnr; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * Generic PCIe unit setup. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci orion_pcie_set_local_bus_nr(pp->base, sys->busnr); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci orion_pcie_setup(pp->base); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci realio.start = sys->busnr * SZ_64K; 5562306a36Sopenharmony_ci realio.end = realio.start + SZ_64K - 1; 5662306a36Sopenharmony_ci pci_remap_iospace(&realio, pp->index == 0 ? DOVE_PCIE0_IO_PHYS_BASE : 5762306a36Sopenharmony_ci DOVE_PCIE1_IO_PHYS_BASE); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * IORESOURCE_MEM 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), 6362306a36Sopenharmony_ci "PCIe %d MEM", pp->index); 6462306a36Sopenharmony_ci pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; 6562306a36Sopenharmony_ci pp->res.name = pp->mem_space_name; 6662306a36Sopenharmony_ci if (pp->index == 0) { 6762306a36Sopenharmony_ci pp->res.start = DOVE_PCIE0_MEM_PHYS_BASE; 6862306a36Sopenharmony_ci pp->res.end = pp->res.start + DOVE_PCIE0_MEM_SIZE - 1; 6962306a36Sopenharmony_ci } else { 7062306a36Sopenharmony_ci pp->res.start = DOVE_PCIE1_MEM_PHYS_BASE; 7162306a36Sopenharmony_ci pp->res.end = pp->res.start + DOVE_PCIE1_MEM_SIZE - 1; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci pp->res.flags = IORESOURCE_MEM; 7462306a36Sopenharmony_ci if (request_resource(&iomem_resource, &pp->res)) 7562306a36Sopenharmony_ci panic("Request PCIe Memory resource failed\n"); 7662306a36Sopenharmony_ci pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 1; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int pcie_valid_config(struct pcie_port *pp, int bus, int dev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * Don't go out when trying to access nonexisting devices 8562306a36Sopenharmony_ci * on the local bus. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci if (bus == pp->root_bus_nr && dev > 1) 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return 1; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 9462306a36Sopenharmony_ci int size, u32 *val) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct pci_sys_data *sys = bus->sysdata; 9762306a36Sopenharmony_ci struct pcie_port *pp = sys->private_data; 9862306a36Sopenharmony_ci unsigned long flags; 9962306a36Sopenharmony_ci int ret; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { 10262306a36Sopenharmony_ci *val = 0xffffffff; 10362306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_lock_irqsave(&pp->conf_lock, flags); 10762306a36Sopenharmony_ci ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); 10862306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->conf_lock, flags); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return ret; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int pcie_wr_conf(struct pci_bus *bus, u32 devfn, 11462306a36Sopenharmony_ci int where, int size, u32 val) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct pci_sys_data *sys = bus->sysdata; 11762306a36Sopenharmony_ci struct pcie_port *pp = sys->private_data; 11862306a36Sopenharmony_ci unsigned long flags; 11962306a36Sopenharmony_ci int ret; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) 12262306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_lock_irqsave(&pp->conf_lock, flags); 12562306a36Sopenharmony_ci ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); 12662306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->conf_lock, flags); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct pci_ops pcie_ops = { 13262306a36Sopenharmony_ci .read = pcie_rd_conf, 13362306a36Sopenharmony_ci .write = pcie_wr_conf, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * The root complex has a hardwired class of PCI_CLASS_MEMORY_OTHER, when it 13862306a36Sopenharmony_ci * is operating as a root complex this needs to be switched to 13962306a36Sopenharmony_ci * PCI_CLASS_BRIDGE_HOST or Linux will errantly try to process the BAR's on 14062306a36Sopenharmony_ci * the device. Decoding setup is handled by the orion code. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistatic void rc_pci_fixup(struct pci_dev *dev) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (dev->bus->parent == NULL && dev->devfn == 0) { 14562306a36Sopenharmony_ci struct resource *r; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dev->class &= 0xff; 14862306a36Sopenharmony_ci dev->class |= PCI_CLASS_BRIDGE_HOST << 8; 14962306a36Sopenharmony_ci pci_dev_for_each_resource(dev, r) { 15062306a36Sopenharmony_ci r->start = 0; 15162306a36Sopenharmony_ci r->end = 0; 15262306a36Sopenharmony_ci r->flags = 0; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int __init 15962306a36Sopenharmony_cidove_pcie_scan_bus(int nr, struct pci_host_bridge *bridge) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct pci_sys_data *sys = pci_host_bridge_priv(bridge); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (nr >= num_pcie_ports) { 16462306a36Sopenharmony_ci BUG(); 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci list_splice_init(&sys->resources, &bridge->windows); 16962306a36Sopenharmony_ci bridge->dev.parent = NULL; 17062306a36Sopenharmony_ci bridge->sysdata = sys; 17162306a36Sopenharmony_ci bridge->busnr = sys->busnr; 17262306a36Sopenharmony_ci bridge->ops = &pcie_ops; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int __init dove_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct pci_sys_data *sys = dev->sysdata; 18062306a36Sopenharmony_ci struct pcie_port *pp = sys->private_data; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return pp->index ? IRQ_DOVE_PCIE1 : IRQ_DOVE_PCIE0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic struct hw_pci dove_pci __initdata = { 18662306a36Sopenharmony_ci .nr_controllers = 2, 18762306a36Sopenharmony_ci .setup = dove_pcie_setup, 18862306a36Sopenharmony_ci .scan = dove_pcie_scan_bus, 18962306a36Sopenharmony_ci .map_irq = dove_pcie_map_irq, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void __init add_pcie_port(int index, void __iomem *base) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci printk(KERN_INFO "Dove PCIe port %d: ", index); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (orion_pcie_link_up(base)) { 19762306a36Sopenharmony_ci struct pcie_port *pp = &pcie_port[num_pcie_ports++]; 19862306a36Sopenharmony_ci struct clk *clk = clk_get_sys("pcie", (index ? "1" : "0")); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!IS_ERR(clk)) 20162306a36Sopenharmony_ci clk_prepare_enable(clk); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci printk(KERN_INFO "link up\n"); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci pp->index = index; 20662306a36Sopenharmony_ci pp->root_bus_nr = -1; 20762306a36Sopenharmony_ci pp->base = base; 20862306a36Sopenharmony_ci spin_lock_init(&pp->conf_lock); 20962306a36Sopenharmony_ci memset(&pp->res, 0, sizeof(pp->res)); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci printk(KERN_INFO "link down, ignoring\n"); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_civoid __init dove_pcie_init(int init_port0, int init_port1) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci vga_base = DOVE_PCIE0_MEM_PHYS_BASE; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (init_port0) 22062306a36Sopenharmony_ci add_pcie_port(0, DOVE_PCIE0_VIRT_BASE); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (init_port1) 22362306a36Sopenharmony_ci add_pcie_port(1, DOVE_PCIE1_VIRT_BASE); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pci_common_init(&dove_pci); 22662306a36Sopenharmony_ci} 227