162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/mach-mv78xx0/pcie.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * PCIe functions for Marvell MV78xx0 SoCs 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/mbus.h> 1162306a36Sopenharmony_ci#include <video/vga.h> 1262306a36Sopenharmony_ci#include <asm/irq.h> 1362306a36Sopenharmony_ci#include <asm/mach/pci.h> 1462306a36Sopenharmony_ci#include <plat/pcie.h> 1562306a36Sopenharmony_ci#include "mv78xx0.h" 1662306a36Sopenharmony_ci#include "common.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MV78XX0_MBUS_PCIE_MEM_TARGET(port, lane) ((port) ? 8 : 4) 1962306a36Sopenharmony_ci#define MV78XX0_MBUS_PCIE_MEM_ATTR(port, lane) (0xf8 & ~(0x10 << (lane))) 2062306a36Sopenharmony_ci#define MV78XX0_MBUS_PCIE_IO_TARGET(port, lane) ((port) ? 8 : 4) 2162306a36Sopenharmony_ci#define MV78XX0_MBUS_PCIE_IO_ATTR(port, lane) (0xf0 & ~(0x10 << (lane))) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct pcie_port { 2462306a36Sopenharmony_ci u8 maj; 2562306a36Sopenharmony_ci u8 min; 2662306a36Sopenharmony_ci u8 root_bus_nr; 2762306a36Sopenharmony_ci void __iomem *base; 2862306a36Sopenharmony_ci spinlock_t conf_lock; 2962306a36Sopenharmony_ci char mem_space_name[20]; 3062306a36Sopenharmony_ci struct resource res; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct pcie_port pcie_port[8]; 3462306a36Sopenharmony_cistatic int num_pcie_ports; 3562306a36Sopenharmony_cistatic struct resource pcie_io_space; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_civoid __init mv78xx0_pcie_id(u32 *dev, u32 *rev) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci *dev = orion_pcie_dev_id(PCIE00_VIRT_BASE); 4062306a36Sopenharmony_ci *rev = orion_pcie_rev(PCIE00_VIRT_BASE); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciu32 pcie_port_size[8] = { 4462306a36Sopenharmony_ci 0, 4562306a36Sopenharmony_ci 0x20000000, 4662306a36Sopenharmony_ci 0x10000000, 4762306a36Sopenharmony_ci 0x10000000, 4862306a36Sopenharmony_ci 0x08000000, 4962306a36Sopenharmony_ci 0x08000000, 5062306a36Sopenharmony_ci 0x08000000, 5162306a36Sopenharmony_ci 0x04000000, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void __init mv78xx0_pcie_preinit(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int i; 5762306a36Sopenharmony_ci u32 size_each; 5862306a36Sopenharmony_ci u32 start; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci pcie_io_space.name = "PCIe I/O Space"; 6162306a36Sopenharmony_ci pcie_io_space.start = MV78XX0_PCIE_IO_PHYS_BASE(0); 6262306a36Sopenharmony_ci pcie_io_space.end = 6362306a36Sopenharmony_ci MV78XX0_PCIE_IO_PHYS_BASE(0) + MV78XX0_PCIE_IO_SIZE * 8 - 1; 6462306a36Sopenharmony_ci pcie_io_space.flags = IORESOURCE_MEM; 6562306a36Sopenharmony_ci if (request_resource(&iomem_resource, &pcie_io_space)) 6662306a36Sopenharmony_ci panic("can't allocate PCIe I/O space"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (num_pcie_ports > 7) 6962306a36Sopenharmony_ci panic("invalid number of PCIe ports"); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci size_each = pcie_port_size[num_pcie_ports]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci start = MV78XX0_PCIE_MEM_PHYS_BASE; 7462306a36Sopenharmony_ci for (i = 0; i < num_pcie_ports; i++) { 7562306a36Sopenharmony_ci struct pcie_port *pp = pcie_port + i; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), 7862306a36Sopenharmony_ci "PCIe %d.%d MEM", pp->maj, pp->min); 7962306a36Sopenharmony_ci pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; 8062306a36Sopenharmony_ci pp->res.name = pp->mem_space_name; 8162306a36Sopenharmony_ci pp->res.flags = IORESOURCE_MEM; 8262306a36Sopenharmony_ci pp->res.start = start; 8362306a36Sopenharmony_ci pp->res.end = start + size_each - 1; 8462306a36Sopenharmony_ci start += size_each; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (request_resource(&iomem_resource, &pp->res)) 8762306a36Sopenharmony_ci panic("can't allocate PCIe MEM sub-space"); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci mvebu_mbus_add_window_by_id(MV78XX0_MBUS_PCIE_MEM_TARGET(pp->maj, pp->min), 9062306a36Sopenharmony_ci MV78XX0_MBUS_PCIE_MEM_ATTR(pp->maj, pp->min), 9162306a36Sopenharmony_ci pp->res.start, resource_size(&pp->res)); 9262306a36Sopenharmony_ci mvebu_mbus_add_window_remap_by_id(MV78XX0_MBUS_PCIE_IO_TARGET(pp->maj, pp->min), 9362306a36Sopenharmony_ci MV78XX0_MBUS_PCIE_IO_ATTR(pp->maj, pp->min), 9462306a36Sopenharmony_ci i * SZ_64K, SZ_64K, 0); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct pcie_port *pp; 10162306a36Sopenharmony_ci struct resource realio; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (nr >= num_pcie_ports) 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci pp = &pcie_port[nr]; 10762306a36Sopenharmony_ci sys->private_data = pp; 10862306a36Sopenharmony_ci pp->root_bus_nr = sys->busnr; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* 11162306a36Sopenharmony_ci * Generic PCIe unit setup. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci orion_pcie_set_local_bus_nr(pp->base, sys->busnr); 11462306a36Sopenharmony_ci orion_pcie_setup(pp->base); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci realio.start = nr * SZ_64K; 11762306a36Sopenharmony_ci realio.end = realio.start + SZ_64K - 1; 11862306a36Sopenharmony_ci pci_remap_iospace(&realio, MV78XX0_PCIE_IO_PHYS_BASE(nr)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 1; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int pcie_valid_config(struct pcie_port *pp, int bus, int dev) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * Don't go out when trying to access nonexisting devices 12962306a36Sopenharmony_ci * on the local bus. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (bus == pp->root_bus_nr && dev > 1) 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return 1; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 13862306a36Sopenharmony_ci int size, u32 *val) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct pci_sys_data *sys = bus->sysdata; 14162306a36Sopenharmony_ci struct pcie_port *pp = sys->private_data; 14262306a36Sopenharmony_ci unsigned long flags; 14362306a36Sopenharmony_ci int ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { 14662306a36Sopenharmony_ci *val = 0xffffffff; 14762306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci spin_lock_irqsave(&pp->conf_lock, flags); 15162306a36Sopenharmony_ci ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); 15262306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->conf_lock, flags); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int pcie_wr_conf(struct pci_bus *bus, u32 devfn, 15862306a36Sopenharmony_ci int where, int size, u32 val) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct pci_sys_data *sys = bus->sysdata; 16162306a36Sopenharmony_ci struct pcie_port *pp = sys->private_data; 16262306a36Sopenharmony_ci unsigned long flags; 16362306a36Sopenharmony_ci int ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) 16662306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_lock_irqsave(&pp->conf_lock, flags); 16962306a36Sopenharmony_ci ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); 17062306a36Sopenharmony_ci spin_unlock_irqrestore(&pp->conf_lock, flags); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct pci_ops pcie_ops = { 17662306a36Sopenharmony_ci .read = pcie_rd_conf, 17762306a36Sopenharmony_ci .write = pcie_wr_conf, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * The root complex has a hardwired class of PCI_CLASS_MEMORY_OTHER, when it 18262306a36Sopenharmony_ci * is operating as a root complex this needs to be switched to 18362306a36Sopenharmony_ci * PCI_CLASS_BRIDGE_HOST or Linux will errantly try to process the BAR's on 18462306a36Sopenharmony_ci * the device. Decoding setup is handled by the orion code. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic void rc_pci_fixup(struct pci_dev *dev) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (dev->bus->parent == NULL && dev->devfn == 0) { 18962306a36Sopenharmony_ci struct resource *r; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci dev->class &= 0xff; 19262306a36Sopenharmony_ci dev->class |= PCI_CLASS_BRIDGE_HOST << 8; 19362306a36Sopenharmony_ci pci_dev_for_each_resource(dev, r) { 19462306a36Sopenharmony_ci r->start = 0; 19562306a36Sopenharmony_ci r->end = 0; 19662306a36Sopenharmony_ci r->flags = 0; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int __init mv78xx0_pcie_scan_bus(int nr, struct pci_host_bridge *bridge) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct pci_sys_data *sys = pci_host_bridge_priv(bridge); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (nr >= num_pcie_ports) { 20762306a36Sopenharmony_ci BUG(); 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci list_splice_init(&sys->resources, &bridge->windows); 21262306a36Sopenharmony_ci bridge->dev.parent = NULL; 21362306a36Sopenharmony_ci bridge->sysdata = sys; 21462306a36Sopenharmony_ci bridge->busnr = sys->busnr; 21562306a36Sopenharmony_ci bridge->ops = &pcie_ops; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int __init mv78xx0_pcie_map_irq(const struct pci_dev *dev, u8 slot, 22162306a36Sopenharmony_ci u8 pin) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct pci_sys_data *sys = dev->bus->sysdata; 22462306a36Sopenharmony_ci struct pcie_port *pp = sys->private_data; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return IRQ_MV78XX0_PCIE_00 + (pp->maj << 2) + pp->min; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct hw_pci mv78xx0_pci __initdata = { 23062306a36Sopenharmony_ci .nr_controllers = 8, 23162306a36Sopenharmony_ci .preinit = mv78xx0_pcie_preinit, 23262306a36Sopenharmony_ci .setup = mv78xx0_pcie_setup, 23362306a36Sopenharmony_ci .scan = mv78xx0_pcie_scan_bus, 23462306a36Sopenharmony_ci .map_irq = mv78xx0_pcie_map_irq, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void __init add_pcie_port(int maj, int min, void __iomem *base) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci printk(KERN_INFO "MV78xx0 PCIe port %d.%d: ", maj, min); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (orion_pcie_link_up(base)) { 24262306a36Sopenharmony_ci struct pcie_port *pp = &pcie_port[num_pcie_ports++]; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci printk("link up\n"); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci pp->maj = maj; 24762306a36Sopenharmony_ci pp->min = min; 24862306a36Sopenharmony_ci pp->root_bus_nr = -1; 24962306a36Sopenharmony_ci pp->base = base; 25062306a36Sopenharmony_ci spin_lock_init(&pp->conf_lock); 25162306a36Sopenharmony_ci memset(&pp->res, 0, sizeof(pp->res)); 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci printk("link down, ignoring\n"); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_civoid __init mv78xx0_pcie_init(int init_port0, int init_port1) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci vga_base = MV78XX0_PCIE_MEM_PHYS_BASE; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (init_port0) { 26262306a36Sopenharmony_ci add_pcie_port(0, 0, PCIE00_VIRT_BASE); 26362306a36Sopenharmony_ci if (!orion_pcie_x4_mode(PCIE00_VIRT_BASE)) { 26462306a36Sopenharmony_ci add_pcie_port(0, 1, PCIE01_VIRT_BASE); 26562306a36Sopenharmony_ci add_pcie_port(0, 2, PCIE02_VIRT_BASE); 26662306a36Sopenharmony_ci add_pcie_port(0, 3, PCIE03_VIRT_BASE); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (init_port1) { 27162306a36Sopenharmony_ci add_pcie_port(1, 0, PCIE10_VIRT_BASE); 27262306a36Sopenharmony_ci if (!orion_pcie_x4_mode((void __iomem *)PCIE10_VIRT_BASE)) { 27362306a36Sopenharmony_ci add_pcie_port(1, 1, PCIE11_VIRT_BASE); 27462306a36Sopenharmony_ci add_pcie_port(1, 2, PCIE12_VIRT_BASE); 27562306a36Sopenharmony_ci add_pcie_port(1, 3, PCIE13_VIRT_BASE); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci pci_common_init(&mv78xx0_pci); 28062306a36Sopenharmony_ci} 281