18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * arch/arm/mach-dove/pcie.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * PCIe functions for Marvell Dove 88AP510 SoC 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <video/vga.h> 158c2ecf20Sopenharmony_ci#include <asm/mach/pci.h> 168c2ecf20Sopenharmony_ci#include <asm/mach/arch.h> 178c2ecf20Sopenharmony_ci#include <asm/setup.h> 188c2ecf20Sopenharmony_ci#include <asm/delay.h> 198c2ecf20Sopenharmony_ci#include <plat/pcie.h> 208c2ecf20Sopenharmony_ci#include <plat/addr-map.h> 218c2ecf20Sopenharmony_ci#include "irqs.h" 228c2ecf20Sopenharmony_ci#include "bridge-regs.h" 238c2ecf20Sopenharmony_ci#include "common.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct pcie_port { 268c2ecf20Sopenharmony_ci u8 index; 278c2ecf20Sopenharmony_ci u8 root_bus_nr; 288c2ecf20Sopenharmony_ci void __iomem *base; 298c2ecf20Sopenharmony_ci spinlock_t conf_lock; 308c2ecf20Sopenharmony_ci char mem_space_name[16]; 318c2ecf20Sopenharmony_ci struct resource res; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct pcie_port pcie_port[2]; 358c2ecf20Sopenharmony_cistatic int num_pcie_ports; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int __init dove_pcie_setup(int nr, struct pci_sys_data *sys) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct pcie_port *pp; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (nr >= num_pcie_ports) 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci pp = &pcie_port[nr]; 468c2ecf20Sopenharmony_ci sys->private_data = pp; 478c2ecf20Sopenharmony_ci pp->root_bus_nr = sys->busnr; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * Generic PCIe unit setup. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci orion_pcie_set_local_bus_nr(pp->base, sys->busnr); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci orion_pcie_setup(pp->base); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (pp->index == 0) 578c2ecf20Sopenharmony_ci pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE0_IO_PHYS_BASE); 588c2ecf20Sopenharmony_ci else 598c2ecf20Sopenharmony_ci pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE1_IO_PHYS_BASE); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* 628c2ecf20Sopenharmony_ci * IORESOURCE_MEM 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), 658c2ecf20Sopenharmony_ci "PCIe %d MEM", pp->index); 668c2ecf20Sopenharmony_ci pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; 678c2ecf20Sopenharmony_ci pp->res.name = pp->mem_space_name; 688c2ecf20Sopenharmony_ci if (pp->index == 0) { 698c2ecf20Sopenharmony_ci pp->res.start = DOVE_PCIE0_MEM_PHYS_BASE; 708c2ecf20Sopenharmony_ci pp->res.end = pp->res.start + DOVE_PCIE0_MEM_SIZE - 1; 718c2ecf20Sopenharmony_ci } else { 728c2ecf20Sopenharmony_ci pp->res.start = DOVE_PCIE1_MEM_PHYS_BASE; 738c2ecf20Sopenharmony_ci pp->res.end = pp->res.start + DOVE_PCIE1_MEM_SIZE - 1; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci pp->res.flags = IORESOURCE_MEM; 768c2ecf20Sopenharmony_ci if (request_resource(&iomem_resource, &pp->res)) 778c2ecf20Sopenharmony_ci panic("Request PCIe Memory resource failed\n"); 788c2ecf20Sopenharmony_ci pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 1; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int pcie_valid_config(struct pcie_port *pp, int bus, int dev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * Don't go out when trying to access nonexisting devices 878c2ecf20Sopenharmony_ci * on the local bus. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci if (bus == pp->root_bus_nr && dev > 1) 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 1; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 968c2ecf20Sopenharmony_ci int size, u32 *val) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct pci_sys_data *sys = bus->sysdata; 998c2ecf20Sopenharmony_ci struct pcie_port *pp = sys->private_data; 1008c2ecf20Sopenharmony_ci unsigned long flags; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { 1048c2ecf20Sopenharmony_ci *val = 0xffffffff; 1058c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci spin_lock_irqsave(&pp->conf_lock, flags); 1098c2ecf20Sopenharmony_ci ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); 1108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pp->conf_lock, flags); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int pcie_wr_conf(struct pci_bus *bus, u32 devfn, 1168c2ecf20Sopenharmony_ci int where, int size, u32 val) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct pci_sys_data *sys = bus->sysdata; 1198c2ecf20Sopenharmony_ci struct pcie_port *pp = sys->private_data; 1208c2ecf20Sopenharmony_ci unsigned long flags; 1218c2ecf20Sopenharmony_ci int ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) 1248c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci spin_lock_irqsave(&pp->conf_lock, flags); 1278c2ecf20Sopenharmony_ci ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); 1288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pp->conf_lock, flags); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return ret; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct pci_ops pcie_ops = { 1348c2ecf20Sopenharmony_ci .read = pcie_rd_conf, 1358c2ecf20Sopenharmony_ci .write = pcie_wr_conf, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void rc_pci_fixup(struct pci_dev *dev) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * Prevent enumeration of root complex. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci if (dev->bus->parent == NULL && dev->devfn == 0) { 1448c2ecf20Sopenharmony_ci int i; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 1478c2ecf20Sopenharmony_ci dev->resource[i].start = 0; 1488c2ecf20Sopenharmony_ci dev->resource[i].end = 0; 1498c2ecf20Sopenharmony_ci dev->resource[i].flags = 0; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int __init 1568c2ecf20Sopenharmony_cidove_pcie_scan_bus(int nr, struct pci_host_bridge *bridge) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct pci_sys_data *sys = pci_host_bridge_priv(bridge); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (nr >= num_pcie_ports) { 1618c2ecf20Sopenharmony_ci BUG(); 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci list_splice_init(&sys->resources, &bridge->windows); 1668c2ecf20Sopenharmony_ci bridge->dev.parent = NULL; 1678c2ecf20Sopenharmony_ci bridge->sysdata = sys; 1688c2ecf20Sopenharmony_ci bridge->busnr = sys->busnr; 1698c2ecf20Sopenharmony_ci bridge->ops = &pcie_ops; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int __init dove_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct pci_sys_data *sys = dev->sysdata; 1778c2ecf20Sopenharmony_ci struct pcie_port *pp = sys->private_data; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return pp->index ? IRQ_DOVE_PCIE1 : IRQ_DOVE_PCIE0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct hw_pci dove_pci __initdata = { 1838c2ecf20Sopenharmony_ci .nr_controllers = 2, 1848c2ecf20Sopenharmony_ci .setup = dove_pcie_setup, 1858c2ecf20Sopenharmony_ci .scan = dove_pcie_scan_bus, 1868c2ecf20Sopenharmony_ci .map_irq = dove_pcie_map_irq, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void __init add_pcie_port(int index, void __iomem *base) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci printk(KERN_INFO "Dove PCIe port %d: ", index); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (orion_pcie_link_up(base)) { 1948c2ecf20Sopenharmony_ci struct pcie_port *pp = &pcie_port[num_pcie_ports++]; 1958c2ecf20Sopenharmony_ci struct clk *clk = clk_get_sys("pcie", (index ? "1" : "0")); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) 1988c2ecf20Sopenharmony_ci clk_prepare_enable(clk); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci printk(KERN_INFO "link up\n"); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pp->index = index; 2038c2ecf20Sopenharmony_ci pp->root_bus_nr = -1; 2048c2ecf20Sopenharmony_ci pp->base = base; 2058c2ecf20Sopenharmony_ci spin_lock_init(&pp->conf_lock); 2068c2ecf20Sopenharmony_ci memset(&pp->res, 0, sizeof(pp->res)); 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci printk(KERN_INFO "link down, ignoring\n"); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid __init dove_pcie_init(int init_port0, int init_port1) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci vga_base = DOVE_PCIE0_MEM_PHYS_BASE; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (init_port0) 2178c2ecf20Sopenharmony_ci add_pcie_port(0, DOVE_PCIE0_VIRT_BASE); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (init_port1) 2208c2ecf20Sopenharmony_ci add_pcie_port(1, DOVE_PCIE1_VIRT_BASE); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pci_common_init(&dove_pci); 2238c2ecf20Sopenharmony_ci} 224