162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/mach-orion5x/pci.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * PCI and PCIe functions for Marvell Orion System On Chip 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Maintainer: Tzachi Perelstein <tzachi@marvell.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/mbus.h> 1462306a36Sopenharmony_ci#include <video/vga.h> 1562306a36Sopenharmony_ci#include <asm/irq.h> 1662306a36Sopenharmony_ci#include <asm/mach/pci.h> 1762306a36Sopenharmony_ci#include <plat/pcie.h> 1862306a36Sopenharmony_ci#include <plat/addr-map.h> 1962306a36Sopenharmony_ci#include "common.h" 2062306a36Sopenharmony_ci#include "orion5x.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/***************************************************************************** 2362306a36Sopenharmony_ci * Orion has one PCIe controller and one PCI controller. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Note1: The local PCIe bus number is '0'. The local PCI bus number 2662306a36Sopenharmony_ci * follows the scanned PCIe bridged busses, if any. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Note2: It is possible for PCI/PCIe agents to access many subsystem's 2962306a36Sopenharmony_ci * space, by configuring BARs and Address Decode Windows, e.g. flashes on 3062306a36Sopenharmony_ci * device bus, Orion registers, etc. However this code only enable the 3162306a36Sopenharmony_ci * access to DDR banks. 3262306a36Sopenharmony_ci ****************************************************************************/ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/***************************************************************************** 3662306a36Sopenharmony_ci * PCIe controller 3762306a36Sopenharmony_ci ****************************************************************************/ 3862306a36Sopenharmony_ci#define PCIE_BASE (ORION5X_PCIE_VIRT_BASE) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_civoid __init orion5x_pcie_id(u32 *dev, u32 *rev) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci *dev = orion_pcie_dev_id(PCIE_BASE); 4362306a36Sopenharmony_ci *rev = orion_pcie_rev(PCIE_BASE); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int pcie_valid_config(int bus, int dev) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * Don't go out when trying to access -- 5062306a36Sopenharmony_ci * 1. nonexisting device on local bus 5162306a36Sopenharmony_ci * 2. where there's no device connected (no link) 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci if (bus == 0 && dev == 0) 5462306a36Sopenharmony_ci return 1; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!orion_pcie_link_up(PCIE_BASE)) 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (bus == 0 && dev != 1) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 1; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * PCIe config cycles are done by programming the PCIE_CONF_ADDR register 6862306a36Sopenharmony_ci * and then reading the PCIE_CONF_DATA register. Need to make sure these 6962306a36Sopenharmony_ci * transactions are atomic. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(orion5x_pcie_lock); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 7462306a36Sopenharmony_ci int size, u32 *val) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci unsigned long flags; 7762306a36Sopenharmony_ci int ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { 8062306a36Sopenharmony_ci *val = 0xffffffff; 8162306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci spin_lock_irqsave(&orion5x_pcie_lock, flags); 8562306a36Sopenharmony_ci ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val); 8662306a36Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pcie_lock, flags); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int pcie_rd_conf_wa(struct pci_bus *bus, u32 devfn, 9262306a36Sopenharmony_ci int where, int size, u32 *val) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { 9762306a36Sopenharmony_ci *val = 0xffffffff; 9862306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * We only support access to the non-extended configuration 10362306a36Sopenharmony_ci * space when using the WA access method (or we would have to 10462306a36Sopenharmony_ci * sacrifice 256M of CPU virtual address space.) 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (where >= 0x100) { 10762306a36Sopenharmony_ci *val = 0xffffffff; 10862306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ret = orion_pcie_rd_conf_wa(ORION5X_PCIE_WA_VIRT_BASE, 11262306a36Sopenharmony_ci bus, devfn, where, size, val); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int pcie_wr_conf(struct pci_bus *bus, u32 devfn, 11862306a36Sopenharmony_ci int where, int size, u32 val) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long flags; 12162306a36Sopenharmony_ci int ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) 12462306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spin_lock_irqsave(&orion5x_pcie_lock, flags); 12762306a36Sopenharmony_ci ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val); 12862306a36Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pcie_lock, flags); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct pci_ops pcie_ops = { 13462306a36Sopenharmony_ci .read = pcie_rd_conf, 13562306a36Sopenharmony_ci .write = pcie_wr_conf, 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int __init pcie_setup(struct pci_sys_data *sys) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct resource *res; 14262306a36Sopenharmony_ci struct resource realio; 14362306a36Sopenharmony_ci int dev; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * Generic PCIe unit setup. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci orion_pcie_setup(PCIE_BASE); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Check whether to apply Orion-1/Orion-NAS PCIe config 15262306a36Sopenharmony_ci * read transaction workaround. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci dev = orion_pcie_dev_id(PCIE_BASE); 15562306a36Sopenharmony_ci if (dev == MV88F5181_DEV_ID || dev == MV88F5182_DEV_ID) { 15662306a36Sopenharmony_ci printk(KERN_NOTICE "Applying Orion-1/Orion-NAS PCIe config " 15762306a36Sopenharmony_ci "read transaction workaround\n"); 15862306a36Sopenharmony_ci mvebu_mbus_add_window_by_id(ORION_MBUS_PCIE_WA_TARGET, 15962306a36Sopenharmony_ci ORION_MBUS_PCIE_WA_ATTR, 16062306a36Sopenharmony_ci ORION5X_PCIE_WA_PHYS_BASE, 16162306a36Sopenharmony_ci ORION5X_PCIE_WA_SIZE); 16262306a36Sopenharmony_ci pcie_ops.read = pcie_rd_conf_wa; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci realio.start = sys->busnr * SZ_64K; 16662306a36Sopenharmony_ci realio.end = realio.start + SZ_64K - 1; 16762306a36Sopenharmony_ci pci_remap_iospace(&realio, ORION5X_PCIE_IO_PHYS_BASE); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Request resources. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci res = kzalloc(sizeof(struct resource), GFP_KERNEL); 17362306a36Sopenharmony_ci if (!res) 17462306a36Sopenharmony_ci panic("pcie_setup unable to alloc resources"); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * IORESOURCE_MEM 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci res->name = "PCIe Memory Space"; 18062306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 18162306a36Sopenharmony_ci res->start = ORION5X_PCIE_MEM_PHYS_BASE; 18262306a36Sopenharmony_ci res->end = res->start + ORION5X_PCIE_MEM_SIZE - 1; 18362306a36Sopenharmony_ci if (request_resource(&iomem_resource, res)) 18462306a36Sopenharmony_ci panic("Request PCIe Memory resource failed\n"); 18562306a36Sopenharmony_ci pci_add_resource_offset(&sys->resources, res, sys->mem_offset); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 1; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/***************************************************************************** 19162306a36Sopenharmony_ci * PCI controller 19262306a36Sopenharmony_ci ****************************************************************************/ 19362306a36Sopenharmony_ci#define ORION5X_PCI_REG(x) (ORION5X_PCI_VIRT_BASE + (x)) 19462306a36Sopenharmony_ci#define PCI_MODE ORION5X_PCI_REG(0xd00) 19562306a36Sopenharmony_ci#define PCI_CMD ORION5X_PCI_REG(0xc00) 19662306a36Sopenharmony_ci#define PCI_P2P_CONF ORION5X_PCI_REG(0x1d14) 19762306a36Sopenharmony_ci#define PCI_CONF_ADDR ORION5X_PCI_REG(0xc78) 19862306a36Sopenharmony_ci#define PCI_CONF_DATA ORION5X_PCI_REG(0xc7c) 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * PCI_MODE bits 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci#define PCI_MODE_64BIT (1 << 2) 20462306a36Sopenharmony_ci#define PCI_MODE_PCIX ((1 << 4) | (1 << 5)) 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * PCI_CMD bits 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci#define PCI_CMD_HOST_REORDER (1 << 29) 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * PCI_P2P_CONF bits 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci#define PCI_P2P_BUS_OFFS 16 21562306a36Sopenharmony_ci#define PCI_P2P_BUS_MASK (0xff << PCI_P2P_BUS_OFFS) 21662306a36Sopenharmony_ci#define PCI_P2P_DEV_OFFS 24 21762306a36Sopenharmony_ci#define PCI_P2P_DEV_MASK (0x1f << PCI_P2P_DEV_OFFS) 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* 22062306a36Sopenharmony_ci * PCI_CONF_ADDR bits 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci#define PCI_CONF_REG(reg) ((reg) & 0xfc) 22362306a36Sopenharmony_ci#define PCI_CONF_FUNC(func) (((func) & 0x3) << 8) 22462306a36Sopenharmony_ci#define PCI_CONF_DEV(dev) (((dev) & 0x1f) << 11) 22562306a36Sopenharmony_ci#define PCI_CONF_BUS(bus) (((bus) & 0xff) << 16) 22662306a36Sopenharmony_ci#define PCI_CONF_ADDR_EN (1 << 31) 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* 22962306a36Sopenharmony_ci * Internal configuration space 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci#define PCI_CONF_FUNC_STAT_CMD 0 23262306a36Sopenharmony_ci#define PCI_CONF_REG_STAT_CMD 4 23362306a36Sopenharmony_ci#define PCIX_STAT 0x64 23462306a36Sopenharmony_ci#define PCIX_STAT_BUS_OFFS 8 23562306a36Sopenharmony_ci#define PCIX_STAT_BUS_MASK (0xff << PCIX_STAT_BUS_OFFS) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * PCI Address Decode Windows registers 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci#define PCI_BAR_SIZE_DDR_CS(n) (((n) == 0) ? ORION5X_PCI_REG(0xc08) : \ 24162306a36Sopenharmony_ci ((n) == 1) ? ORION5X_PCI_REG(0xd08) : \ 24262306a36Sopenharmony_ci ((n) == 2) ? ORION5X_PCI_REG(0xc0c) : \ 24362306a36Sopenharmony_ci ((n) == 3) ? ORION5X_PCI_REG(0xd0c) : NULL) 24462306a36Sopenharmony_ci#define PCI_BAR_REMAP_DDR_CS(n) (((n) == 0) ? ORION5X_PCI_REG(0xc48) : \ 24562306a36Sopenharmony_ci ((n) == 1) ? ORION5X_PCI_REG(0xd48) : \ 24662306a36Sopenharmony_ci ((n) == 2) ? ORION5X_PCI_REG(0xc4c) : \ 24762306a36Sopenharmony_ci ((n) == 3) ? ORION5X_PCI_REG(0xd4c) : NULL) 24862306a36Sopenharmony_ci#define PCI_BAR_ENABLE ORION5X_PCI_REG(0xc3c) 24962306a36Sopenharmony_ci#define PCI_ADDR_DECODE_CTRL ORION5X_PCI_REG(0xd3c) 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* 25262306a36Sopenharmony_ci * PCI configuration helpers for BAR settings 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci#define PCI_CONF_FUNC_BAR_CS(n) ((n) >> 1) 25562306a36Sopenharmony_ci#define PCI_CONF_REG_BAR_LO_CS(n) (((n) & 1) ? 0x18 : 0x10) 25662306a36Sopenharmony_ci#define PCI_CONF_REG_BAR_HI_CS(n) (((n) & 1) ? 0x1c : 0x14) 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* 25962306a36Sopenharmony_ci * PCI config cycles are done by programming the PCI_CONF_ADDR register 26062306a36Sopenharmony_ci * and then reading the PCI_CONF_DATA register. Need to make sure these 26162306a36Sopenharmony_ci * transactions are atomic. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(orion5x_pci_lock); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int orion5x_pci_cardbus_mode; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int orion5x_pci_local_bus_nr(void) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci u32 conf = readl(PCI_P2P_CONF); 27062306a36Sopenharmony_ci return((conf & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int orion5x_pci_hw_rd_conf(int bus, int dev, u32 func, 27462306a36Sopenharmony_ci u32 where, u32 size, u32 *val) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci unsigned long flags; 27762306a36Sopenharmony_ci spin_lock_irqsave(&orion5x_pci_lock, flags); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci writel(PCI_CONF_BUS(bus) | 28062306a36Sopenharmony_ci PCI_CONF_DEV(dev) | PCI_CONF_REG(where) | 28162306a36Sopenharmony_ci PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci *val = readl(PCI_CONF_DATA); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (size == 1) 28662306a36Sopenharmony_ci *val = (*val >> (8*(where & 0x3))) & 0xff; 28762306a36Sopenharmony_ci else if (size == 2) 28862306a36Sopenharmony_ci *val = (*val >> (8*(where & 0x3))) & 0xffff; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pci_lock, flags); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int orion5x_pci_hw_wr_conf(int bus, int dev, u32 func, 29662306a36Sopenharmony_ci u32 where, u32 size, u32 val) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci unsigned long flags; 29962306a36Sopenharmony_ci int ret = PCIBIOS_SUCCESSFUL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci spin_lock_irqsave(&orion5x_pci_lock, flags); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci writel(PCI_CONF_BUS(bus) | 30462306a36Sopenharmony_ci PCI_CONF_DEV(dev) | PCI_CONF_REG(where) | 30562306a36Sopenharmony_ci PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (size == 4) { 30862306a36Sopenharmony_ci __raw_writel(val, PCI_CONF_DATA); 30962306a36Sopenharmony_ci } else if (size == 2) { 31062306a36Sopenharmony_ci __raw_writew(val, PCI_CONF_DATA + (where & 0x3)); 31162306a36Sopenharmony_ci } else if (size == 1) { 31262306a36Sopenharmony_ci __raw_writeb(val, PCI_CONF_DATA + (where & 0x3)); 31362306a36Sopenharmony_ci } else { 31462306a36Sopenharmony_ci ret = PCIBIOS_BAD_REGISTER_NUMBER; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci spin_unlock_irqrestore(&orion5x_pci_lock, flags); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int orion5x_pci_valid_config(int bus, u32 devfn) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci if (bus == orion5x_pci_local_bus_nr()) { 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Don't go out for local device 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci if (PCI_SLOT(devfn) == 0 && PCI_FUNC(devfn) != 0) 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * When the PCI signals are directly connected to a 33362306a36Sopenharmony_ci * Cardbus slot, ignore all but device IDs 0 and 1. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci if (orion5x_pci_cardbus_mode && PCI_SLOT(devfn) > 1) 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return 1; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int orion5x_pci_rd_conf(struct pci_bus *bus, u32 devfn, 34362306a36Sopenharmony_ci int where, int size, u32 *val) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci if (!orion5x_pci_valid_config(bus->number, devfn)) { 34662306a36Sopenharmony_ci *val = 0xffffffff; 34762306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return orion5x_pci_hw_rd_conf(bus->number, PCI_SLOT(devfn), 35162306a36Sopenharmony_ci PCI_FUNC(devfn), where, size, val); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int orion5x_pci_wr_conf(struct pci_bus *bus, u32 devfn, 35562306a36Sopenharmony_ci int where, int size, u32 val) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci if (!orion5x_pci_valid_config(bus->number, devfn)) 35862306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return orion5x_pci_hw_wr_conf(bus->number, PCI_SLOT(devfn), 36162306a36Sopenharmony_ci PCI_FUNC(devfn), where, size, val); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic struct pci_ops pci_ops = { 36562306a36Sopenharmony_ci .read = orion5x_pci_rd_conf, 36662306a36Sopenharmony_ci .write = orion5x_pci_wr_conf, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void __init orion5x_pci_set_bus_nr(int nr) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci u32 p2p = readl(PCI_P2P_CONF); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (readl(PCI_MODE) & PCI_MODE_PCIX) { 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * PCI-X mode 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci u32 pcix_status, bus, dev; 37862306a36Sopenharmony_ci bus = (p2p & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS; 37962306a36Sopenharmony_ci dev = (p2p & PCI_P2P_DEV_MASK) >> PCI_P2P_DEV_OFFS; 38062306a36Sopenharmony_ci orion5x_pci_hw_rd_conf(bus, dev, 0, PCIX_STAT, 4, &pcix_status); 38162306a36Sopenharmony_ci pcix_status &= ~PCIX_STAT_BUS_MASK; 38262306a36Sopenharmony_ci pcix_status |= (nr << PCIX_STAT_BUS_OFFS); 38362306a36Sopenharmony_ci orion5x_pci_hw_wr_conf(bus, dev, 0, PCIX_STAT, 4, pcix_status); 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * PCI Conventional mode 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci p2p &= ~PCI_P2P_BUS_MASK; 38962306a36Sopenharmony_ci p2p |= (nr << PCI_P2P_BUS_OFFS); 39062306a36Sopenharmony_ci writel(p2p, PCI_P2P_CONF); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void __init orion5x_pci_master_slave_enable(void) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int bus_nr, func, reg; 39762306a36Sopenharmony_ci u32 val; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci bus_nr = orion5x_pci_local_bus_nr(); 40062306a36Sopenharmony_ci func = PCI_CONF_FUNC_STAT_CMD; 40162306a36Sopenharmony_ci reg = PCI_CONF_REG_STAT_CMD; 40262306a36Sopenharmony_ci orion5x_pci_hw_rd_conf(bus_nr, 0, func, reg, 4, &val); 40362306a36Sopenharmony_ci val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); 40462306a36Sopenharmony_ci orion5x_pci_hw_wr_conf(bus_nr, 0, func, reg, 4, val | 0x7); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void __init orion5x_setup_pci_wins(void) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci const struct mbus_dram_target_info *dram = mv_mbus_dram_info(); 41062306a36Sopenharmony_ci u32 win_enable; 41162306a36Sopenharmony_ci int bus; 41262306a36Sopenharmony_ci int i; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * First, disable windows. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci win_enable = 0xffffffff; 41862306a36Sopenharmony_ci writel(win_enable, PCI_BAR_ENABLE); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * Setup windows for DDR banks. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci bus = orion5x_pci_local_bus_nr(); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (i = 0; i < dram->num_cs; i++) { 42662306a36Sopenharmony_ci const struct mbus_dram_window *cs = dram->cs + i; 42762306a36Sopenharmony_ci u32 func = PCI_CONF_FUNC_BAR_CS(cs->cs_index); 42862306a36Sopenharmony_ci u32 reg; 42962306a36Sopenharmony_ci u32 val; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* 43262306a36Sopenharmony_ci * Write DRAM bank base address register. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci reg = PCI_CONF_REG_BAR_LO_CS(cs->cs_index); 43562306a36Sopenharmony_ci orion5x_pci_hw_rd_conf(bus, 0, func, reg, 4, &val); 43662306a36Sopenharmony_ci val = (cs->base & 0xfffff000) | (val & 0xfff); 43762306a36Sopenharmony_ci orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, val); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * Write DRAM bank size register. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci reg = PCI_CONF_REG_BAR_HI_CS(cs->cs_index); 44362306a36Sopenharmony_ci orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, 0); 44462306a36Sopenharmony_ci writel((cs->size - 1) & 0xfffff000, 44562306a36Sopenharmony_ci PCI_BAR_SIZE_DDR_CS(cs->cs_index)); 44662306a36Sopenharmony_ci writel(cs->base & 0xfffff000, 44762306a36Sopenharmony_ci PCI_BAR_REMAP_DDR_CS(cs->cs_index)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* 45062306a36Sopenharmony_ci * Enable decode window for this chip select. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci win_enable &= ~(1 << cs->cs_index); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Re-enable decode windows. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci writel(win_enable, PCI_BAR_ENABLE); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * Disable automatic update of address remapping when writing to BARs. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci orion5x_setbits(PCI_ADDR_DECODE_CTRL, 1); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int __init pci_setup(struct pci_sys_data *sys) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct resource *res; 46962306a36Sopenharmony_ci struct resource realio; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * Point PCI unit MBUS decode windows to DRAM space. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci orion5x_setup_pci_wins(); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci * Master + Slave enable 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci orion5x_pci_master_slave_enable(); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* 48262306a36Sopenharmony_ci * Force ordering 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci realio.start = sys->busnr * SZ_64K; 48762306a36Sopenharmony_ci realio.end = realio.start + SZ_64K - 1; 48862306a36Sopenharmony_ci pci_remap_iospace(&realio, ORION5X_PCI_IO_PHYS_BASE); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * Request resources 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci res = kzalloc(sizeof(struct resource), GFP_KERNEL); 49462306a36Sopenharmony_ci if (!res) 49562306a36Sopenharmony_ci panic("pci_setup unable to alloc resources"); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * IORESOURCE_MEM 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci res->name = "PCI Memory Space"; 50162306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 50262306a36Sopenharmony_ci res->start = ORION5X_PCI_MEM_PHYS_BASE; 50362306a36Sopenharmony_ci res->end = res->start + ORION5X_PCI_MEM_SIZE - 1; 50462306a36Sopenharmony_ci if (request_resource(&iomem_resource, res)) 50562306a36Sopenharmony_ci panic("Request PCI Memory resource failed\n"); 50662306a36Sopenharmony_ci pci_add_resource_offset(&sys->resources, res, sys->mem_offset); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return 1; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/***************************************************************************** 51362306a36Sopenharmony_ci * General PCIe + PCI 51462306a36Sopenharmony_ci ****************************************************************************/ 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* 51762306a36Sopenharmony_ci * The root complex has a hardwired class of PCI_CLASS_MEMORY_OTHER, when it 51862306a36Sopenharmony_ci * is operating as a root complex this needs to be switched to 51962306a36Sopenharmony_ci * PCI_CLASS_BRIDGE_HOST or Linux will errantly try to process the BAR's on 52062306a36Sopenharmony_ci * the device. Decoding setup is handled by the orion code. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic void rc_pci_fixup(struct pci_dev *dev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci if (dev->bus->parent == NULL && dev->devfn == 0) { 52562306a36Sopenharmony_ci struct resource *r; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dev->class &= 0xff; 52862306a36Sopenharmony_ci dev->class |= PCI_CLASS_BRIDGE_HOST << 8; 52962306a36Sopenharmony_ci pci_dev_for_each_resource(dev, r) { 53062306a36Sopenharmony_ci r->start = 0; 53162306a36Sopenharmony_ci r->end = 0; 53262306a36Sopenharmony_ci r->flags = 0; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int orion5x_pci_disabled __initdata; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid __init orion5x_pci_disable(void) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci orion5x_pci_disabled = 1; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_civoid __init orion5x_pci_set_cardbus_mode(void) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci orion5x_pci_cardbus_mode = 1; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciint __init orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci vga_base = ORION5X_PCIE_MEM_PHYS_BASE; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (nr == 0) { 55562306a36Sopenharmony_ci orion_pcie_set_local_bus_nr(PCIE_BASE, sys->busnr); 55662306a36Sopenharmony_ci return pcie_setup(sys); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (nr == 1 && !orion5x_pci_disabled) { 56062306a36Sopenharmony_ci orion5x_pci_set_bus_nr(sys->busnr); 56162306a36Sopenharmony_ci return pci_setup(sys); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciint __init orion5x_pci_sys_scan_bus(int nr, struct pci_host_bridge *bridge) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct pci_sys_data *sys = pci_host_bridge_priv(bridge); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci list_splice_init(&sys->resources, &bridge->windows); 57262306a36Sopenharmony_ci bridge->dev.parent = NULL; 57362306a36Sopenharmony_ci bridge->sysdata = sys; 57462306a36Sopenharmony_ci bridge->busnr = sys->busnr; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (nr == 0) { 57762306a36Sopenharmony_ci bridge->ops = &pcie_ops; 57862306a36Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (nr == 1 && !orion5x_pci_disabled) { 58262306a36Sopenharmony_ci bridge->ops = &pci_ops; 58362306a36Sopenharmony_ci return pci_scan_root_bus_bridge(bridge); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci BUG(); 58762306a36Sopenharmony_ci return -ENODEV; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ciint __init orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci int bus = dev->bus->number; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* 59562306a36Sopenharmony_ci * PCIe endpoint? 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci if (orion5x_pci_disabled || bus < orion5x_pci_local_bus_nr()) 59862306a36Sopenharmony_ci return IRQ_ORION5X_PCIE0_INT; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return -1; 60162306a36Sopenharmony_ci} 602