|
@@ -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);
|