| 
					
				 | 
			
			
				@@ -0,0 +1,131 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * arch/arm/mach-orion5x/pci.c 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * PCI and PCIe functions for Marvell Orion System On Chip 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Maintainer: Tzachi Perelstein <tzachi@marvell.com> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * This file is licensed under the terms of the GNU General Public 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * License version 2.  This program is licensed "as is" without any 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * warranty of any kind, whether express or implied. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <linux/kernel.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <linux/pci.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <linux/slab.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <linux/mbus.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <video/vga.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <asm/irq.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <asm/mach/pci.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <plat/pcie.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <plat/addr-map.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <mach/orion5x.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "common.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/***************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Orion has one PCIe controller and one PCI controller. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Note1: The local PCIe bus number is '0'. The local PCI bus number 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * follows the scanned PCIe bridged busses, if any. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Note2: It is possible for PCI/PCIe agents to access many subsystem's 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * space, by configuring BARs and Address Decode Windows, e.g. flashes on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * device bus, Orion registers, etc. However this code only enable the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * access to DDR banks. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ ****************************************************************************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/***************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * PCIe controller 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ ****************************************************************************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define PCIE_BASE	(ORION5X_PCIE_VIRT_BASE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void __init orion5x_pcie_id(u32 *dev, u32 *rev) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	*dev = orion_pcie_dev_id(PCIE_BASE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	*rev = orion_pcie_rev(PCIE_BASE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int pcie_valid_config(int bus, int dev) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * Don't go out when trying to access -- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 1. nonexisting device on local bus 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 2. where there's no device connected (no link) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (bus == 0 && dev == 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (!orion_pcie_link_up(PCIE_BASE)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (bus == 0 && dev != 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * PCIe config cycles are done by programming the PCIE_CONF_ADDR register 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * and then reading the PCIE_CONF_DATA register. Need to make sure these 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * transactions are atomic. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static DEFINE_SPINLOCK(orion5x_pcie_lock); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			int size, u32 *val) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	unsigned long flags; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		*val = 0xffffffff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return PCIBIOS_DEVICE_NOT_FOUND; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	spin_lock_irqsave(&orion5x_pcie_lock, flags); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	spin_unlock_irqrestore(&orion5x_pcie_lock, flags); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int pcie_rd_conf_wa(struct pci_bus *bus, u32 devfn, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			   int where, int size, u32 *val) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		*val = 0xffffffff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return PCIBIOS_DEVICE_NOT_FOUND; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * We only support access to the non-extended configuration 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * space when using the WA access method (or we would have to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * sacrifice 256M of CPU virtual address space.) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (where >= 0x100) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		*val = 0xffffffff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return PCIBIOS_DEVICE_NOT_FOUND; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ret = orion_pcie_rd_conf_wa(ORION5X_PCIE_WA_VIRT_BASE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				    bus, devfn, where, size, val); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			int where, int size, u32 val) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	unsigned long flags; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	int ret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return PCIBIOS_DEVICE_NOT_FOUND; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	spin_lock_irqsave(&orion5x_pcie_lock, flags); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	spin_unlock_irqrestore(&orion5x_pcie_lock, flags); 
			 |