18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * arch/arm/mach-orion5x/pci.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * PCI and PCIe functions for Marvell Orion System On Chip 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Maintainer: Tzachi Perelstein <tzachi@marvell.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 98c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 108c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/mbus.h> 178c2ecf20Sopenharmony_ci#include <video/vga.h> 188c2ecf20Sopenharmony_ci#include <asm/irq.h> 198c2ecf20Sopenharmony_ci#include <asm/mach/pci.h> 208c2ecf20Sopenharmony_ci#include <plat/pcie.h> 218c2ecf20Sopenharmony_ci#include <plat/addr-map.h> 228c2ecf20Sopenharmony_ci#include "common.h" 238c2ecf20Sopenharmony_ci#include "orion5x.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/***************************************************************************** 268c2ecf20Sopenharmony_ci * Orion has one PCIe controller and one PCI controller. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Note1: The local PCIe bus number is '0'. The local PCI bus number 298c2ecf20Sopenharmony_ci * follows the scanned PCIe bridged busses, if any. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Note2: It is possible for PCI/PCIe agents to access many subsystem's 328c2ecf20Sopenharmony_ci * space, by configuring BARs and Address Decode Windows, e.g. flashes on 338c2ecf20Sopenharmony_ci * device bus, Orion registers, etc. However this code only enable the 348c2ecf20Sopenharmony_ci * access to DDR banks. 358c2ecf20Sopenharmony_ci ****************************************************************************/ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/***************************************************************************** 398c2ecf20Sopenharmony_ci * PCIe controller 408c2ecf20Sopenharmony_ci ****************************************************************************/ 418c2ecf20Sopenharmony_ci#define PCIE_BASE (ORION5X_PCIE_VIRT_BASE) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_civoid __init orion5x_pcie_id(u32 *dev, u32 *rev) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci *dev = orion_pcie_dev_id(PCIE_BASE); 468c2ecf20Sopenharmony_ci *rev = orion_pcie_rev(PCIE_BASE); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int pcie_valid_config(int bus, int dev) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * Don't go out when trying to access -- 538c2ecf20Sopenharmony_ci * 1. nonexisting device on local bus 548c2ecf20Sopenharmony_ci * 2. where there's no device connected (no link) 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci if (bus == 0 && dev == 0) 578c2ecf20Sopenharmony_ci return 1; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (!orion_pcie_link_up(PCIE_BASE)) 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (bus == 0 && dev != 1) 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 1; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * PCIe config cycles are done by programming the PCIE_CONF_ADDR register 718c2ecf20Sopenharmony_ci * and then reading the PCIE_CONF_DATA register. Need to make sure these 728c2ecf20Sopenharmony_ci * transactions are atomic. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(orion5x_pcie_lock); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 778c2ecf20Sopenharmony_ci int size, u32 *val) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned long flags; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { 838c2ecf20Sopenharmony_ci *val = 0xffffffff; 848c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci spin_lock_irqsave(&orion5x_pcie_lock, flags); 888c2ecf20Sopenharmony_ci ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val); 898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pcie_lock, flags); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int pcie_rd_conf_wa(struct pci_bus *bus, u32 devfn, 958c2ecf20Sopenharmony_ci int where, int size, u32 *val) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int ret; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { 1008c2ecf20Sopenharmony_ci *val = 0xffffffff; 1018c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * We only support access to the non-extended configuration 1068c2ecf20Sopenharmony_ci * space when using the WA access method (or we would have to 1078c2ecf20Sopenharmony_ci * sacrifice 256M of CPU virtual address space.) 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci if (where >= 0x100) { 1108c2ecf20Sopenharmony_ci *val = 0xffffffff; 1118c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = orion_pcie_rd_conf_wa(ORION5X_PCIE_WA_VIRT_BASE, 1158c2ecf20Sopenharmony_ci bus, devfn, where, size, val); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return ret; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int pcie_wr_conf(struct pci_bus *bus, u32 devfn, 1218c2ecf20Sopenharmony_ci int where, int size, u32 val) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci unsigned long flags; 1248c2ecf20Sopenharmony_ci int ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) 1278c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci spin_lock_irqsave(&orion5x_pcie_lock, flags); 1308c2ecf20Sopenharmony_ci ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val); 1318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pcie_lock, flags); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct pci_ops pcie_ops = { 1378c2ecf20Sopenharmony_ci .read = pcie_rd_conf, 1388c2ecf20Sopenharmony_ci .write = pcie_wr_conf, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int __init pcie_setup(struct pci_sys_data *sys) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct resource *res; 1458c2ecf20Sopenharmony_ci int dev; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Generic PCIe unit setup. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci orion_pcie_setup(PCIE_BASE); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Check whether to apply Orion-1/Orion-NAS PCIe config 1548c2ecf20Sopenharmony_ci * read transaction workaround. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci dev = orion_pcie_dev_id(PCIE_BASE); 1578c2ecf20Sopenharmony_ci if (dev == MV88F5181_DEV_ID || dev == MV88F5182_DEV_ID) { 1588c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Applying Orion-1/Orion-NAS PCIe config " 1598c2ecf20Sopenharmony_ci "read transaction workaround\n"); 1608c2ecf20Sopenharmony_ci mvebu_mbus_add_window_by_id(ORION_MBUS_PCIE_WA_TARGET, 1618c2ecf20Sopenharmony_ci ORION_MBUS_PCIE_WA_ATTR, 1628c2ecf20Sopenharmony_ci ORION5X_PCIE_WA_PHYS_BASE, 1638c2ecf20Sopenharmony_ci ORION5X_PCIE_WA_SIZE); 1648c2ecf20Sopenharmony_ci pcie_ops.read = pcie_rd_conf_wa; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCIE_IO_PHYS_BASE); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Request resources. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci res = kzalloc(sizeof(struct resource), GFP_KERNEL); 1738c2ecf20Sopenharmony_ci if (!res) 1748c2ecf20Sopenharmony_ci panic("pcie_setup unable to alloc resources"); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * IORESOURCE_MEM 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci res->name = "PCIe Memory Space"; 1808c2ecf20Sopenharmony_ci res->flags = IORESOURCE_MEM; 1818c2ecf20Sopenharmony_ci res->start = ORION5X_PCIE_MEM_PHYS_BASE; 1828c2ecf20Sopenharmony_ci res->end = res->start + ORION5X_PCIE_MEM_SIZE - 1; 1838c2ecf20Sopenharmony_ci if (request_resource(&iomem_resource, res)) 1848c2ecf20Sopenharmony_ci panic("Request PCIe Memory resource failed\n"); 1858c2ecf20Sopenharmony_ci pci_add_resource_offset(&sys->resources, res, sys->mem_offset); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 1; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/***************************************************************************** 1918c2ecf20Sopenharmony_ci * PCI controller 1928c2ecf20Sopenharmony_ci ****************************************************************************/ 1938c2ecf20Sopenharmony_ci#define ORION5X_PCI_REG(x) (ORION5X_PCI_VIRT_BASE + (x)) 1948c2ecf20Sopenharmony_ci#define PCI_MODE ORION5X_PCI_REG(0xd00) 1958c2ecf20Sopenharmony_ci#define PCI_CMD ORION5X_PCI_REG(0xc00) 1968c2ecf20Sopenharmony_ci#define PCI_P2P_CONF ORION5X_PCI_REG(0x1d14) 1978c2ecf20Sopenharmony_ci#define PCI_CONF_ADDR ORION5X_PCI_REG(0xc78) 1988c2ecf20Sopenharmony_ci#define PCI_CONF_DATA ORION5X_PCI_REG(0xc7c) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * PCI_MODE bits 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci#define PCI_MODE_64BIT (1 << 2) 2048c2ecf20Sopenharmony_ci#define PCI_MODE_PCIX ((1 << 4) | (1 << 5)) 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * PCI_CMD bits 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci#define PCI_CMD_HOST_REORDER (1 << 29) 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * PCI_P2P_CONF bits 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci#define PCI_P2P_BUS_OFFS 16 2158c2ecf20Sopenharmony_ci#define PCI_P2P_BUS_MASK (0xff << PCI_P2P_BUS_OFFS) 2168c2ecf20Sopenharmony_ci#define PCI_P2P_DEV_OFFS 24 2178c2ecf20Sopenharmony_ci#define PCI_P2P_DEV_MASK (0x1f << PCI_P2P_DEV_OFFS) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* 2208c2ecf20Sopenharmony_ci * PCI_CONF_ADDR bits 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci#define PCI_CONF_REG(reg) ((reg) & 0xfc) 2238c2ecf20Sopenharmony_ci#define PCI_CONF_FUNC(func) (((func) & 0x3) << 8) 2248c2ecf20Sopenharmony_ci#define PCI_CONF_DEV(dev) (((dev) & 0x1f) << 11) 2258c2ecf20Sopenharmony_ci#define PCI_CONF_BUS(bus) (((bus) & 0xff) << 16) 2268c2ecf20Sopenharmony_ci#define PCI_CONF_ADDR_EN (1 << 31) 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * Internal configuration space 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci#define PCI_CONF_FUNC_STAT_CMD 0 2328c2ecf20Sopenharmony_ci#define PCI_CONF_REG_STAT_CMD 4 2338c2ecf20Sopenharmony_ci#define PCIX_STAT 0x64 2348c2ecf20Sopenharmony_ci#define PCIX_STAT_BUS_OFFS 8 2358c2ecf20Sopenharmony_ci#define PCIX_STAT_BUS_MASK (0xff << PCIX_STAT_BUS_OFFS) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * PCI Address Decode Windows registers 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci#define PCI_BAR_SIZE_DDR_CS(n) (((n) == 0) ? ORION5X_PCI_REG(0xc08) : \ 2418c2ecf20Sopenharmony_ci ((n) == 1) ? ORION5X_PCI_REG(0xd08) : \ 2428c2ecf20Sopenharmony_ci ((n) == 2) ? ORION5X_PCI_REG(0xc0c) : \ 2438c2ecf20Sopenharmony_ci ((n) == 3) ? ORION5X_PCI_REG(0xd0c) : NULL) 2448c2ecf20Sopenharmony_ci#define PCI_BAR_REMAP_DDR_CS(n) (((n) == 0) ? ORION5X_PCI_REG(0xc48) : \ 2458c2ecf20Sopenharmony_ci ((n) == 1) ? ORION5X_PCI_REG(0xd48) : \ 2468c2ecf20Sopenharmony_ci ((n) == 2) ? ORION5X_PCI_REG(0xc4c) : \ 2478c2ecf20Sopenharmony_ci ((n) == 3) ? ORION5X_PCI_REG(0xd4c) : NULL) 2488c2ecf20Sopenharmony_ci#define PCI_BAR_ENABLE ORION5X_PCI_REG(0xc3c) 2498c2ecf20Sopenharmony_ci#define PCI_ADDR_DECODE_CTRL ORION5X_PCI_REG(0xd3c) 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * PCI configuration helpers for BAR settings 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci#define PCI_CONF_FUNC_BAR_CS(n) ((n) >> 1) 2558c2ecf20Sopenharmony_ci#define PCI_CONF_REG_BAR_LO_CS(n) (((n) & 1) ? 0x18 : 0x10) 2568c2ecf20Sopenharmony_ci#define PCI_CONF_REG_BAR_HI_CS(n) (((n) & 1) ? 0x1c : 0x14) 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * PCI config cycles are done by programming the PCI_CONF_ADDR register 2608c2ecf20Sopenharmony_ci * and then reading the PCI_CONF_DATA register. Need to make sure these 2618c2ecf20Sopenharmony_ci * transactions are atomic. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(orion5x_pci_lock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int orion5x_pci_cardbus_mode; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int orion5x_pci_local_bus_nr(void) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci u32 conf = readl(PCI_P2P_CONF); 2708c2ecf20Sopenharmony_ci return((conf & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int orion5x_pci_hw_rd_conf(int bus, int dev, u32 func, 2748c2ecf20Sopenharmony_ci u32 where, u32 size, u32 *val) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci unsigned long flags; 2778c2ecf20Sopenharmony_ci spin_lock_irqsave(&orion5x_pci_lock, flags); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci writel(PCI_CONF_BUS(bus) | 2808c2ecf20Sopenharmony_ci PCI_CONF_DEV(dev) | PCI_CONF_REG(where) | 2818c2ecf20Sopenharmony_ci PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci *val = readl(PCI_CONF_DATA); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (size == 1) 2868c2ecf20Sopenharmony_ci *val = (*val >> (8*(where & 0x3))) & 0xff; 2878c2ecf20Sopenharmony_ci else if (size == 2) 2888c2ecf20Sopenharmony_ci *val = (*val >> (8*(where & 0x3))) & 0xffff; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pci_lock, flags); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int orion5x_pci_hw_wr_conf(int bus, int dev, u32 func, 2968c2ecf20Sopenharmony_ci u32 where, u32 size, u32 val) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci unsigned long flags; 2998c2ecf20Sopenharmony_ci int ret = PCIBIOS_SUCCESSFUL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci spin_lock_irqsave(&orion5x_pci_lock, flags); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci writel(PCI_CONF_BUS(bus) | 3048c2ecf20Sopenharmony_ci PCI_CONF_DEV(dev) | PCI_CONF_REG(where) | 3058c2ecf20Sopenharmony_ci PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (size == 4) { 3088c2ecf20Sopenharmony_ci __raw_writel(val, PCI_CONF_DATA); 3098c2ecf20Sopenharmony_ci } else if (size == 2) { 3108c2ecf20Sopenharmony_ci __raw_writew(val, PCI_CONF_DATA + (where & 0x3)); 3118c2ecf20Sopenharmony_ci } else if (size == 1) { 3128c2ecf20Sopenharmony_ci __raw_writeb(val, PCI_CONF_DATA + (where & 0x3)); 3138c2ecf20Sopenharmony_ci } else { 3148c2ecf20Sopenharmony_ci ret = PCIBIOS_BAD_REGISTER_NUMBER; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pci_lock, flags); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int orion5x_pci_valid_config(int bus, u32 devfn) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci if (bus == orion5x_pci_local_bus_nr()) { 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Don't go out for local device 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci if (PCI_SLOT(devfn) == 0 && PCI_FUNC(devfn) != 0) 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * When the PCI signals are directly connected to a 3338c2ecf20Sopenharmony_ci * Cardbus slot, ignore all but device IDs 0 and 1. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci if (orion5x_pci_cardbus_mode && PCI_SLOT(devfn) > 1) 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 1; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int orion5x_pci_rd_conf(struct pci_bus *bus, u32 devfn, 3438c2ecf20Sopenharmony_ci int where, int size, u32 *val) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci if (!orion5x_pci_valid_config(bus->number, devfn)) { 3468c2ecf20Sopenharmony_ci *val = 0xffffffff; 3478c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return orion5x_pci_hw_rd_conf(bus->number, PCI_SLOT(devfn), 3518c2ecf20Sopenharmony_ci PCI_FUNC(devfn), where, size, val); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int orion5x_pci_wr_conf(struct pci_bus *bus, u32 devfn, 3558c2ecf20Sopenharmony_ci int where, int size, u32 val) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci if (!orion5x_pci_valid_config(bus->number, devfn)) 3588c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return orion5x_pci_hw_wr_conf(bus->number, PCI_SLOT(devfn), 3618c2ecf20Sopenharmony_ci PCI_FUNC(devfn), where, size, val); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic struct pci_ops pci_ops = { 3658c2ecf20Sopenharmony_ci .read = orion5x_pci_rd_conf, 3668c2ecf20Sopenharmony_ci .write = orion5x_pci_wr_conf, 3678c2ecf20Sopenharmony_ci}; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void __init orion5x_pci_set_bus_nr(int nr) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci u32 p2p = readl(PCI_P2P_CONF); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (readl(PCI_MODE) & PCI_MODE_PCIX) { 3748c2ecf20Sopenharmony_ci /* 3758c2ecf20Sopenharmony_ci * PCI-X mode 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci u32 pcix_status, bus, dev; 3788c2ecf20Sopenharmony_ci bus = (p2p & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS; 3798c2ecf20Sopenharmony_ci dev = (p2p & PCI_P2P_DEV_MASK) >> PCI_P2P_DEV_OFFS; 3808c2ecf20Sopenharmony_ci orion5x_pci_hw_rd_conf(bus, dev, 0, PCIX_STAT, 4, &pcix_status); 3818c2ecf20Sopenharmony_ci pcix_status &= ~PCIX_STAT_BUS_MASK; 3828c2ecf20Sopenharmony_ci pcix_status |= (nr << PCIX_STAT_BUS_OFFS); 3838c2ecf20Sopenharmony_ci orion5x_pci_hw_wr_conf(bus, dev, 0, PCIX_STAT, 4, pcix_status); 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * PCI Conventional mode 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci p2p &= ~PCI_P2P_BUS_MASK; 3898c2ecf20Sopenharmony_ci p2p |= (nr << PCI_P2P_BUS_OFFS); 3908c2ecf20Sopenharmony_ci writel(p2p, PCI_P2P_CONF); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void __init orion5x_pci_master_slave_enable(void) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci int bus_nr, func, reg; 3978c2ecf20Sopenharmony_ci u32 val; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci bus_nr = orion5x_pci_local_bus_nr(); 4008c2ecf20Sopenharmony_ci func = PCI_CONF_FUNC_STAT_CMD; 4018c2ecf20Sopenharmony_ci reg = PCI_CONF_REG_STAT_CMD; 4028c2ecf20Sopenharmony_ci orion5x_pci_hw_rd_conf(bus_nr, 0, func, reg, 4, &val); 4038c2ecf20Sopenharmony_ci val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); 4048c2ecf20Sopenharmony_ci orion5x_pci_hw_wr_conf(bus_nr, 0, func, reg, 4, val | 0x7); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void __init orion5x_setup_pci_wins(void) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci const struct mbus_dram_target_info *dram = mv_mbus_dram_info(); 4108c2ecf20Sopenharmony_ci u32 win_enable; 4118c2ecf20Sopenharmony_ci int bus; 4128c2ecf20Sopenharmony_ci int i; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * First, disable windows. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci win_enable = 0xffffffff; 4188c2ecf20Sopenharmony_ci writel(win_enable, PCI_BAR_ENABLE); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Setup windows for DDR banks. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci bus = orion5x_pci_local_bus_nr(); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (i = 0; i < dram->num_cs; i++) { 4268c2ecf20Sopenharmony_ci const struct mbus_dram_window *cs = dram->cs + i; 4278c2ecf20Sopenharmony_ci u32 func = PCI_CONF_FUNC_BAR_CS(cs->cs_index); 4288c2ecf20Sopenharmony_ci u32 reg; 4298c2ecf20Sopenharmony_ci u32 val; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * Write DRAM bank base address register. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci reg = PCI_CONF_REG_BAR_LO_CS(cs->cs_index); 4358c2ecf20Sopenharmony_ci orion5x_pci_hw_rd_conf(bus, 0, func, reg, 4, &val); 4368c2ecf20Sopenharmony_ci val = (cs->base & 0xfffff000) | (val & 0xfff); 4378c2ecf20Sopenharmony_ci orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, val); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * Write DRAM bank size register. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci reg = PCI_CONF_REG_BAR_HI_CS(cs->cs_index); 4438c2ecf20Sopenharmony_ci orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, 0); 4448c2ecf20Sopenharmony_ci writel((cs->size - 1) & 0xfffff000, 4458c2ecf20Sopenharmony_ci PCI_BAR_SIZE_DDR_CS(cs->cs_index)); 4468c2ecf20Sopenharmony_ci writel(cs->base & 0xfffff000, 4478c2ecf20Sopenharmony_ci PCI_BAR_REMAP_DDR_CS(cs->cs_index)); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * Enable decode window for this chip select. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci win_enable &= ~(1 << cs->cs_index); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* 4568c2ecf20Sopenharmony_ci * Re-enable decode windows. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci writel(win_enable, PCI_BAR_ENABLE); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* 4618c2ecf20Sopenharmony_ci * Disable automatic update of address remapping when writing to BARs. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci orion5x_setbits(PCI_ADDR_DECODE_CTRL, 1); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int __init pci_setup(struct pci_sys_data *sys) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct resource *res; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Point PCI unit MBUS decode windows to DRAM space. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci orion5x_setup_pci_wins(); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * Master + Slave enable 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci orion5x_pci_master_slave_enable(); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * Force ordering 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCI_IO_PHYS_BASE); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * Request resources 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci res = kzalloc(sizeof(struct resource), GFP_KERNEL); 4918c2ecf20Sopenharmony_ci if (!res) 4928c2ecf20Sopenharmony_ci panic("pci_setup unable to alloc resources"); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* 4958c2ecf20Sopenharmony_ci * IORESOURCE_MEM 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_ci res->name = "PCI Memory Space"; 4988c2ecf20Sopenharmony_ci res->flags = IORESOURCE_MEM; 4998c2ecf20Sopenharmony_ci res->start = ORION5X_PCI_MEM_PHYS_BASE; 5008c2ecf20Sopenharmony_ci res->end = res->start + ORION5X_PCI_MEM_SIZE - 1; 5018c2ecf20Sopenharmony_ci if (request_resource(&iomem_resource, res)) 5028c2ecf20Sopenharmony_ci panic("Request PCI Memory resource failed\n"); 5038c2ecf20Sopenharmony_ci pci_add_resource_offset(&sys->resources, res, sys->mem_offset); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 1; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/***************************************************************************** 5108c2ecf20Sopenharmony_ci * General PCIe + PCI 5118c2ecf20Sopenharmony_ci ****************************************************************************/ 5128c2ecf20Sopenharmony_cistatic void rc_pci_fixup(struct pci_dev *dev) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * Prevent enumeration of root complex. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci if (dev->bus->parent == NULL && dev->devfn == 0) { 5188c2ecf20Sopenharmony_ci int i; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 5218c2ecf20Sopenharmony_ci dev->resource[i].start = 0; 5228c2ecf20Sopenharmony_ci dev->resource[i].end = 0; 5238c2ecf20Sopenharmony_ci dev->resource[i].flags = 0; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int orion5x_pci_disabled __initdata; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_civoid __init orion5x_pci_disable(void) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci orion5x_pci_disabled = 1; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_civoid __init orion5x_pci_set_cardbus_mode(void) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci orion5x_pci_cardbus_mode = 1; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciint __init orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci vga_base = ORION5X_PCIE_MEM_PHYS_BASE; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (nr == 0) { 5468c2ecf20Sopenharmony_ci orion_pcie_set_local_bus_nr(PCIE_BASE, sys->busnr); 5478c2ecf20Sopenharmony_ci return pcie_setup(sys); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (nr == 1 && !orion5x_pci_disabled) { 5518c2ecf20Sopenharmony_ci orion5x_pci_set_bus_nr(sys->busnr); 5528c2ecf20Sopenharmony_ci return pci_setup(sys); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciint __init orion5x_pci_sys_scan_bus(int nr, struct pci_host_bridge *bridge) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct pci_sys_data *sys = pci_host_bridge_priv(bridge); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci list_splice_init(&sys->resources, &bridge->windows); 5638c2ecf20Sopenharmony_ci bridge->dev.parent = NULL; 5648c2ecf20Sopenharmony_ci bridge->sysdata = sys; 5658c2ecf20Sopenharmony_ci bridge->busnr = sys->busnr; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (nr == 0) { 5688c2ecf20Sopenharmony_ci bridge->ops = &pcie_ops; 5698c2ecf20Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (nr == 1 && !orion5x_pci_disabled) { 5738c2ecf20Sopenharmony_ci bridge->ops = &pci_ops; 5748c2ecf20Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci BUG(); 5788c2ecf20Sopenharmony_ci return -ENODEV; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ciint __init orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci int bus = dev->bus->number; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* 5868c2ecf20Sopenharmony_ci * PCIe endpoint? 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (orion5x_pci_disabled || bus < orion5x_pci_local_bus_nr()) 5898c2ecf20Sopenharmony_ci return IRQ_ORION5X_PCIE0_INT; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return -1; 5928c2ecf20Sopenharmony_ci} 593