summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/include/mach/pci.h323
-rw-r--r--arch/arm/mach-tegra/include/mach/platform.h1
-rw-r--r--arch/arm/mach-tegra/pci-enum.c637
-rw-r--r--arch/arm/mach-tegra/pci.c561
-rw-r--r--arch/arm/mach-tegra/tegra_sysmap.c2
6 files changed, 1513 insertions, 12 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 6b8ac3027872..075d18a45193 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -65,4 +65,5 @@ obj-$(CONFIG_TEGRA_NVEC_USER) += nvec_user.o
# PCIe support
obj-$(CONFIG_TEGRA_PCI) += pci.o
+obj-$(CONFIG_TEGRA_PCI) += pci-enum.o
diff --git a/arch/arm/mach-tegra/include/mach/pci.h b/arch/arm/mach-tegra/include/mach/pci.h
new file mode 100644
index 000000000000..e5eac3ad5640
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/pci.h
@@ -0,0 +1,323 @@
+/*
+ * arch/arm/mach-tegra/include/mach/pci.h
+ *
+ * Header file containing constants for the tegra PCIe driver.
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_PCI_H
+
+#include <linux/pci.h>
+#include <mach/platform.h>
+
+#include "nvrm_drf.h"
+#include "ap20/dev_ap_pcie2_root_port.h"
+#include "ap20/dev_ap_pcie2_pads.h"
+#include "ap20/arafi.h"
+
+extern void __iomem * volatile pci_tegra_regs;
+
+/*
+ * AXI address map for the PCIe aperture. AP20, defines 1GB in the AXI
+ * address map for PCIe.
+ *
+ * That address space is split into different regions, with sizes and
+ * offsets as follows. Exepct for the Register space, SW is free to slice the
+ * regions as it chooces.
+ *
+ * The split below seems to work fine for now.
+ *
+ * 0x8000_0000 to 0x80ff_ffff - Register space 16MB.
+ * 0x8100_0000 to 0x81ff_ffff - Config space 16MB.
+ * 0x8200_0000 to 0x82ff_ffff - Extended config space 16MB.
+ * 0x8300_0000 to 0x83ff_ffff - Downstream IO space
+ * ... Will be filled with other BARS like MSI/upstream IO etc.
+ * 0x9000_0000 to 0x9fff_ffff - non-prefetchable memory aperture
+ * 0xa000_0000 to 0xbfff_ffff - Prefetchable memory aperture
+ *
+ * Config and Extended config sizes are choosen to support
+ * maximum of 256 devices,
+ * which is good enough for all the AP20 use cases.
+ * */
+
+#define PCIE_REGS_SIZE 0x01000000UL
+#define PCIE_CONFIG_OFFSET PCIE_REGS_SIZE
+#define PCIE_CONFIG_SIZE 0x01000000UL
+#define PCIE_EXTENDED_CONFIG_OFFSET (PCIE_CONFIG_SIZE + PCIE_CONFIG_OFFSET)
+#define PCIE_EXTENDED_CONFIG_SIZE 0x01000000UL
+#define PCIE_DOWNSTREAM_IO_OFFSET (PCIE_EXTENDED_CONFIG_SIZE + \
+ PCIE_EXTENDED_CONFIG_OFFSET)
+#define PCIE_DOWNSTREAM_IO_SIZE 0x00100000UL
+
+#define PCIE_NON_PREFETCH_MEMORY_OFFSET 0x10000000UL
+#define PCIE_NON_PREFETCH_MEMORY_SIZE 0x10000000UL
+#define PCIE_PREFETCH_MEMORY_OFFSET (PCIE_NON_PREFETCH_MEMORY_OFFSET + \
+ PCIE_NON_PREFETCH_MEMORY_SIZE)
+#define PCIE_PREFETCH_MEMORY_SIZE 0x20000000UL
+
+/* PCIe registers can be classified into 4 regions.
+ *
+ * 1. AFI registers - AFI is a wrapper between PCIE and ARM AXI bus. These
+ * registers define the address translation registers, interrupt registers and
+ * some configuration (a.k.a CYA) registers.
+ * 2. PAD registers - PAD control registers which are inside the PCIE CORE.
+ * 3. Configuration 0 and Configuration 1 registers - These registers are PCIe
+ * configuration registers of Root port 0 and root port 1.
+ *
+ * Check the PcieRegType enumeration for the list of Registers banks inside the
+ * PCIE aperture.
+ *
+ * */
+#define NV_PCIE_AXI_AFI_REGS_OFSET 0x3800UL
+#define NV_PCIE_AXI_PADS_OFSET 0x3000UL
+#define NV_PCIE_AXI_RP_T0C0_OFFSET 0x0000UL
+#define NV_PCIE_AXI_RP_T0C1_OFFSET 0x1000UL
+
+/* During the boot only registers/config and extended config apertures are
+ * mapped. Rest are mapped on demand by the PCI device drivers.
+ */
+#define PCI_TEGRA_IOMAPPED_REG_APERTURE_SIZE \
+ (PCIE_REGS_SIZE + PCIE_CONFIG_SIZE + PCIE_EXTENDED_CONFIG_SIZE)
+
+/*
+ * PCI address map for memory mapped devices. Still using 32-bit aperture.
+ *
+ * 1GB for the system memory.
+ * Everything mapped as cpu physical = pci
+ *
+ */
+#define FPCI_SYSTEM_MEMORY_OFFSET 0x0UL
+#define FPCI_SYSTEM_MEMORY_SIZE 0x40000000UL
+#define FPCI_NON_PREFETCH_MEMORY_OFFSET 0x90000000UL
+#define FPCI_NON_PREFETCH_MEMORY_SIZE PCIE_NON_PREFETCH_MEMORY_SIZE
+#define FPCI_PREFETCH_MEMORY_OFFSET (FPCI_NON_PREFETCH_MEMORY_OFFSET+ \
+ FPCI_NON_PREFETCH_MEMORY_SIZE)
+#define FPCI_PREFETCH_MEMORY_SIZE 0x40000000UL
+
+
+
+
+/* PCIE DRF macros to read and write PRI registers */
+
+/** NVPCIE_DRF_DEF - define a new register value.
+
+ @param d register domain (hardware block)
+ @param r register name
+ @param f register field
+ @param c defined value for the field
+ */
+#define NVPCIE_DRF_DEF(d,r,f,c) \
+ ((NV_PROJ__PCIE2_##d##_##r##_##f##_##c) \
+ << NV_FIELD_SHIFT(NV_PROJ__PCIE2_##d##_##r##_##f))
+
+/** NVPCIE_DRF_NUM - define a new register value.
+
+ @param d register domain (hardware block)
+ @param r register name
+ @param f register field
+ @param n numeric value for the field
+ */
+#define NVPCIE_DRF_NUM(d,r,f,n) \
+ (((n)& NV_FIELD_MASK(NV_PROJ__PCIE2_##d##_##r##_##f)) << \
+ NV_FIELD_SHIFT(NV_PROJ__PCIE2_##d##_##r##_##f))
+
+/** NVPCIE_DRF_VAL - read a field from a register.
+
+ @param d register domain (hardware block)
+ @param r register name
+ @param f register field
+ @param v register value
+ */
+#define NVPCIE_DRF_VAL(d,r,f,v) \
+ (((v)>> NV_FIELD_SHIFT(NV_PROJ__PCIE2_##d##_##r##_##f)) & \
+ NV_FIELD_MASK(NV_PROJ__PCIE2_##d##_##r##_##f))
+
+/** NVPCIE_FLD_SET_DRF_NUM - modify a register field.
+
+ @param d register domain (hardware block)
+ @param r register name
+ @param f register field
+ @param n numeric field value
+ @param v register value
+ */
+#define NVPCIE_FLD_SET_DRF_NUM(d,r,f,n,v) \
+ ((v & ~NV_FIELD_SHIFTMASK(NV_PROJ__PCIE2_##d##_##r##_##f)) | \
+ NVPCIE_DRF_NUM(d,r,f,n))
+
+/** NVPCIE_FLD_SET_DRF_DEF - modify a register field.
+
+ @param d register domain (hardware block)
+ @param r register name
+ @param f register field
+ @param c defined field value
+ @param v register value
+ */
+#define NVPCIE_FLD_SET_DRF_DEF(d,r,f,c,v) \
+ (((v) & ~NV_FIELD_SHIFTMASK(NV_PROJ__PCIE2_##d##_##r##_##f)) | \
+ NVPCIE_DRF_DEF(d,r,f,c))
+
+/** NVPCIE_RESETVAL - get the reset value for a register.
+
+ @param d register domain (hardware block)
+ @param r register name
+ */
+#define NVPCIE_RESETVAL(d,r) (d##_##r##_0_RESET_VAL)
+
+/* Register access inline functions */
+
+static inline void pci_tegra_afi_writel(u32 value,unsigned long offset)
+{
+ writel(value, offset + NV_PCIE_AXI_AFI_REGS_OFSET + pci_tegra_regs);
+}
+
+static inline void pci_tegra_rp_writel(u32 value, unsigned long offset, int rp)
+{
+ BUG_ON(rp != 0 && rp != 1);
+
+ if (rp == 0) offset += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) offset += NV_PCIE_AXI_RP_T0C1_OFFSET;
+
+ writel(value, offset + pci_tegra_regs);
+}
+
+static inline void pci_tegra_rp_writew(u16 value, unsigned long offset, int rp)
+{
+ u32 reg;
+
+ BUG_ON(rp != 0 && rp != 1);
+
+ if (rp == 0) offset += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) offset += NV_PCIE_AXI_RP_T0C1_OFFSET;
+
+ reg = readl((offset & ~0x3) + pci_tegra_regs);
+ reg &= ~(0xffff << ((offset & 0x3) * 8));
+ reg |= (u32)value << ((offset & 0x3) * 8);
+ writel(reg, (offset & ~0x3) + pci_tegra_regs);
+}
+
+static inline void pci_tegra_rp_writeb(u8 value, unsigned long offset, int rp)
+{
+ u32 reg;
+
+ BUG_ON(rp != 0 && rp != 1);
+
+ if (rp == 0) offset += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) offset += NV_PCIE_AXI_RP_T0C1_OFFSET;
+
+ reg = readl((offset & ~0x3) + pci_tegra_regs);
+ reg &= ~(0xff << ((offset & 0x3) * 8));
+ reg |= (u32)value << ((offset & 0x3) * 8);
+ writel(reg, (offset & ~0x3) + pci_tegra_regs);
+}
+
+static inline void pci_tegra_pads_writel(u32 value, unsigned long offset)
+{
+ writel(value, offset + NV_PCIE_AXI_PADS_OFSET + pci_tegra_regs);
+}
+
+static inline u32 pci_tegra_afi_readl(unsigned long offset)
+{
+ return readl(offset + NV_PCIE_AXI_AFI_REGS_OFSET + pci_tegra_regs);
+}
+
+static inline u32 pci_tegra_rp_readl(unsigned long offset, int rp)
+{
+ BUG_ON(rp != 0 && rp != 1);
+
+ if (rp == 0) offset += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) offset += NV_PCIE_AXI_RP_T0C1_OFFSET;
+
+ return readl(offset + pci_tegra_regs);
+}
+
+static inline u16 pci_tegra_rp_readw(unsigned long offset, int rp)
+{
+ u32 val;
+
+ BUG_ON(rp != 0 && rp != 1);
+
+ if (rp == 0) offset += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) offset += NV_PCIE_AXI_RP_T0C1_OFFSET;
+
+ val = readl((offset & ~0x3) + pci_tegra_regs);
+ val >>= 8 * (offset & 3);
+ val &= 0xffff;
+
+ return (u16)val;
+}
+
+static inline u8 pci_tegra_rp_readb(unsigned long offset, int rp)
+{
+ u32 val;
+
+ BUG_ON(rp != 0 && rp != 1);
+
+ if (rp == 0) offset += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) offset += NV_PCIE_AXI_RP_T0C1_OFFSET;
+
+ val = readl((offset & ~0x3) + pci_tegra_regs);
+ val >>= 8 * (offset & 3);
+ val &= 0xff;
+
+ return (u8)val;
+}
+
+static inline u32 pci_tegra_pads_reedl(unsigned long offset)
+{
+ return readl(offset + NV_PCIE_AXI_PADS_OFSET + pci_tegra_regs);
+}
+
+static inline bool pci_tegra_is_rp(u32 bus_number, int *rp)
+{
+ if (bus_number == pci_tegra_rp_readb(PCI_PRIMARY_BUS, 0)) {
+ *rp = 0;
+ return true;
+ } else if (bus_number == pci_tegra_rp_readb(PCI_PRIMARY_BUS, 1)) {
+ *rp = 1;
+ return true;
+ } else
+ return false;
+}
+
+/*
+ * Given the bus number, devfn and the offset this API returns the mapped
+ * address of the config space.
+ */
+static inline void __iomem *pci_tegra_config_addr(u8 bus_number,
+ u32 devfn, u32 where)
+{
+ void *addr;
+ u32 function;
+ u32 device;
+
+ function = PCI_FUNC(devfn);
+ device = PCI_SLOT(devfn);
+
+ addr = pci_tegra_regs;
+ addr += (where < 256) ? PCIE_CONFIG_OFFSET
+ : PCIE_EXTENDED_CONFIG_OFFSET;
+ addr += bus_number << 16;
+ addr += device << 11;
+ addr += function << 8;
+ addr += where;
+ return addr;
+}
+
+void pci_tegra_enumerate(void);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/platform.h b/arch/arm/mach-tegra/include/mach/platform.h
index 54cb086e58b3..f80632aff0a8 100644
--- a/arch/arm/mach-tegra/include/mach/platform.h
+++ b/arch/arm/mach-tegra/include/mach/platform.h
@@ -27,6 +27,7 @@ extern unsigned int tegra_get_module_inst_irq(const char *, int, int);
#define TEGRA_PL310_IRQ (tegra_get_module_inst_irq("pl310", 0, 0))
#define TEGRA_SCU_BASE (tegra_get_module_inst_base("scu", 0))
+#define TEGRA_PCIE_BASE (tegra_get_module_inst_base("pcie", 0))
#define TEGRA_SCU0_IRQ NO_IRQ
#define TEGRA_SCU1_IRQ NO_IRQ
diff --git a/arch/arm/mach-tegra/pci-enum.c b/arch/arm/mach-tegra/pci-enum.c
new file mode 100644
index 000000000000..1dab1de0a86d
--- /dev/null
+++ b/arch/arm/mach-tegra/pci-enum.c
@@ -0,0 +1,637 @@
+/*
+ * arch/arm/mach-tegra/pci-enum.c
+ *
+ * Code to enumerate the PCI devices on the PCI bus. Unlike x86 we cannot
+ * rely on BIOS to allocate the PCIe resources for the devices.
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <mach/pci.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+
+struct pci_tegra_device
+{
+ /* Bus number */
+ u8 bus;
+
+ /* Device + function encoding.
+ * Use macros PCI_DEVFN/PCI_SLOT/PCI_FUNC to encode and decode
+ * */
+ u32 devfn;
+
+ /* Secondary bus nummber. Non-zero only for bridge devices. */
+ u32 sec_bus;
+
+ /* Subordinate bus number. Non-zero only for the bridge devices. */
+ u32 sub_bus;
+
+ /* Device ID/vendor ID of the PCI device/bridge.
+ * Upper 16 bits are device ID and lower 16 bits are vendor ID.
+ */
+ u32 id;
+
+ /* For a bridge device only 3 bars are used.
+ */
+#define PCI_BRIDGE_IO_RES 0
+#define PCI_BRIDGE_MEM_RES 1
+#define PCI_BRIDGE_PREFETCH_RES 2
+
+ /* Here we are limiting to the standard PCI resources */
+ struct resource res[PCI_STD_RESOURCE_END + 1];
+
+ bool disabled;
+
+ struct pci_tegra_device *parent;
+ struct pci_tegra_device *next;
+ struct pci_tegra_device *prev;
+ struct pci_tegra_device *child;
+ bool root_port;
+};
+
+#define TEGRA_MAX_PCI_DEVICES 64
+static struct pci_tegra_device pci_devices[TEGRA_MAX_PCI_DEVICES];
+static int max_devices;
+static struct pci_tegra_device *pci_root;
+
+static u32 pci_tegra_io_base;
+static u32 pci_tegra_mem_base;
+static u32 pci_tegra_prefetech_base;
+
+static u32 pci_tegra_io_limt;
+static u32 pci_tegra_mem_limit;
+static u32 pci_tegra_prefetech_limit;
+
+static void pci_tegra_print_device_tree(struct pci_tegra_device *dev);
+static void pcie_scanbus(struct pci_tegra_device *dev_parent);
+static void pci_tegra_allocate_resources(struct pci_tegra_device *dev);
+
+
+static struct pci_tegra_device *alloc_pci_tegra_device(void)
+{
+ static u32 index = 0;
+ struct pci_tegra_device *dev;
+
+ if (index == 0)
+ memset(pci_devices, 0, sizeof(pci_devices));
+
+ dev = &pci_devices[index];
+ index++;
+ max_devices = index;
+ return dev;
+}
+
+static inline void pci_conf_write8(u8 bus, u32 devfn, u32 where , u8 val)
+{
+ u32 addr;
+ u32 temp;
+
+ addr = (u32)pci_tegra_config_addr(bus, devfn, where);
+ pr_err("Issuing pci_conf_write8 at addr 0x%x with data 0x%x\n",
+ addr, val);
+
+ temp = readl((addr & ~0x3));
+ temp &= ~(0xff << ((addr & 0x3) * 8));
+ temp |= (u32)val << ((addr & 0x3) * 8);
+ writel(temp, (addr & ~0x3));
+}
+
+static inline void pci_conf_write16(u8 bus, u32 devfn, u32 where, u16 val)
+{
+ u32 addr;
+ u32 temp;
+
+ BUG_ON(where & 0x1);
+
+ addr = (u32)pci_tegra_config_addr(bus, devfn, where);
+ pr_err("Issuing pci_conf_write16 at addr 0x%x with data 0x%x\n",
+ addr, val);
+
+ temp = readl((addr & ~0x3));
+ temp &= ~(0xffff << ((addr& 0x3) * 8));
+ temp |= (u32)val << ((addr & 0x3) * 8);
+ writel(temp, (addr & ~0x3));
+}
+
+static inline void pci_conf_write32(u8 bus, u32 devfn, u32 where, u32 val)
+{
+ u32 addr;
+
+ BUG_ON(where & 0x3);
+
+ addr = (u32)pci_tegra_config_addr(bus, devfn, where);
+ pr_err("Issuing pci_conf_write32 at addr 0x%x with data 0x%x\n",
+ addr, val);
+ writel(val, addr);
+}
+
+static inline u8 pci_conf_read8(u8 bus, u32 devfn, u32 where)
+{
+ u32 temp;
+ u32 addr;
+
+ addr = (u32)pci_tegra_config_addr(bus, devfn, where);
+ pr_err("Issuing pci_conf_read8 at 0x%x\n", addr);
+ temp = readl(addr & ~0x3);
+ temp >>= 8 * (addr & 3);
+ temp &= 0xff;
+ pr_err("pci_conf_read8 at 0x%x = %d\n", addr, temp);
+
+ return (u8)temp;
+}
+
+static u16 pci_conf_read16(u8 bus, u32 devfn, u32 where)
+{
+ u32 temp;
+ u32 addr;
+
+ BUG_ON(where & 0x1);
+
+ addr = (u32)pci_tegra_config_addr(bus, devfn, where);
+ pr_err("Issuing pci_conf_read16 at 0x%x\n", addr);
+ temp = readl(addr & ~0x3);
+ temp >>= 8 * (addr & 3);
+ temp &= 0xffff;
+ pr_err("pci_conf_read16 at 0x%x = %d\n", addr, temp);
+
+ return (u16)temp;
+}
+
+static u32 pci_conf_read32(u8 bus, u32 devfn, u32 where)
+{
+ u32 temp;
+
+ BUG_ON(where & 0x3);
+
+ pr_err("Issuing pci_conf_read32 at 0x%x\n",
+ (u32)(pci_tegra_config_addr(bus, devfn, where)));
+
+ temp = readl(pci_tegra_config_addr(bus, devfn, where));
+
+ pr_err("pci_conf_read32 at 0x%x = %d\n", where, temp);
+ return temp;
+}
+
+static void pcie_scanbus(struct pci_tegra_device *dev_parent)
+{
+ u8 subordinate_bus;
+ u8 hdr_type;
+ u8 next_bus_number;
+ u32 device = 0;
+ u32 id;
+ struct pci_tegra_device *dev;
+ u32 retry_count;
+
+ next_bus_number = dev_parent->sec_bus;
+
+next_device:
+ retry_count = 6;
+ if (device == 0x20) {
+ /* Termination condition: Max number of devices reached.
+ * PCIe bus segment can only have 32 devices.
+ * */
+ dev_parent->sub_bus = next_bus_number;
+ if (!dev_parent->root_port) {
+ /* Change the subordinate bus-number to the actual
+ * value of all buses on the hierarcy.
+ *
+ * Do this execpt for the root port.
+ */
+ pci_conf_write8(dev_parent->bus, dev_parent->devfn,
+ PCI_SUBORDINATE_BUS, next_bus_number);
+ }
+ return;
+ }
+
+ if (dev_parent->root_port && device != 0) {
+ /* Sepcial Exit condition for root port.
+ * Root port only connect to one bridge or device.
+ */
+ dev_parent->sub_bus = dev_parent->sec_bus;
+ return;
+ }
+
+ while (--retry_count) {
+ id = pci_conf_read32(dev_parent->sec_bus,
+ PCI_DEVFN(device, 0), 0);
+ if (id != 0xFFFFFFFF)
+ {
+ /* Found a valid device, break. Otherwise, retry a couple of
+ * times. It is possible that the bridges can take some time
+ * to settle and it will take couple of transcations to find
+ * the devcies behind the bridge.
+ * */
+ /* FIXME: What should be the delay? */
+ msleep(100);
+ break;
+ }
+ }
+ if (id == 0xFFFFFFFF) {
+ /* Invalid device. Skip that one and look for next device */
+ device++;
+ goto next_device;
+ }
+
+ dev = alloc_pci_tegra_device();
+
+ /* Fill the device information */
+ dev->parent = dev_parent;
+ dev->id = id;
+ dev->bus = dev_parent->sec_bus;
+ dev->devfn = PCI_DEVFN(device, 0);
+ if (dev_parent->child == NULL) {
+ dev_parent->child = dev;
+ dev->prev = NULL;
+ } else {
+ /* Add dev to the list of devices on the same bus */
+ struct pci_tegra_device *temp;
+
+ temp = dev_parent->child;
+ BUG_ON(temp != NULL);
+ while (temp->next != NULL)
+ temp = temp->next;
+ temp->next = dev;
+ dev->prev = temp;
+ }
+
+ hdr_type = pci_conf_read8(dev->bus, dev->devfn, PCI_HEADER_TYPE);
+ if ((hdr_type & 0x7f) == 0x1) {
+ /* Bridge device */
+
+ /* Temporarily assign 0xff for the subordinate bus number as
+ * we don't * know how many devices are preset behind this
+ * bridge.
+ * */
+ subordinate_bus = 0xff;
+ dev->sec_bus = next_bus_number + 1;
+
+ pci_conf_write8(dev->bus, dev->devfn, PCI_PRIMARY_BUS,
+ dev_parent->sec_bus);
+ pci_conf_write8(dev->bus, dev->devfn, PCI_SECONDARY_BUS,
+ dev->sec_bus);
+ pci_conf_write8(dev->bus, dev->devfn, PCI_SUBORDINATE_BUS,
+ subordinate_bus);
+
+ /* Scan all the buses behind this bridge */
+ pcie_scanbus(dev);
+
+ next_bus_number = dev->sub_bus;
+ } else if ((hdr_type & 0x7f) == 0x0) {
+
+ /* PCI endpoint - Can be single function or multie function */
+ pr_info("PCI endpoint (0x%x) is on bus = %d, device = %d\n",
+ id, dev_parent->sec_bus, device);
+
+ } else if ((hdr_type & 0x7f) == 0x2) {
+ /* PC card device - Not handled */
+ BUG();
+ } else {
+ BUG();
+ }
+ device++;
+ goto next_device;
+}
+
+static void pci_tegra_enumerate_root_port(int rp)
+{
+ struct pci_tegra_device *root;
+ u32 reg;
+
+ root = alloc_pci_tegra_device();
+
+ if (pci_root) {
+ pci_root->next = root;
+ root->bus = pci_root->sub_bus + 1;
+ } else {
+ pci_root = root;
+ root->bus = 0;
+ }
+
+ root->sec_bus = root->bus + 1;
+ root->root_port = true;
+ /* Set the Inital value to the max bus number */
+ root->sub_bus = 0xff;
+ root->id = pci_tegra_rp_readl(0, rp);
+
+ pci_tegra_rp_writeb(root->bus, PCI_PRIMARY_BUS, rp);
+ pci_tegra_rp_writeb(root->sec_bus, PCI_SECONDARY_BUS, rp);
+ pci_tegra_rp_writeb(root->sub_bus, PCI_SUBORDINATE_BUS, rp);
+
+ /* Just assigns the bus numbers and sets up the SW hirerarchy */
+ pcie_scanbus(root);
+
+ /* Write the udpated root port subordinate bus number */
+ pci_tegra_rp_writeb(root->sub_bus, PCI_SUBORDINATE_BUS, rp);
+
+ pci_tegra_allocate_resources(root);
+
+ /* IO base and limits */
+ reg = root->res[PCI_BRIDGE_IO_RES].start;
+ reg = ALIGN(reg, 0x1000);
+ pci_tegra_rp_writeb((((reg & 0xf000) >> 8) | PCI_IO_RANGE_TYPE_32),
+ PCI_IO_BASE, rp);
+ pci_tegra_rp_writew(reg>>16, PCI_IO_BASE_UPPER16, rp);
+
+ reg = root->res[PCI_BRIDGE_IO_RES].end;
+ reg = ALIGN(reg, 0x1000);
+ pci_tegra_rp_writeb((((reg & 0xf000) >> 8) | PCI_IO_RANGE_TYPE_32),
+ PCI_IO_LIMIT, rp);
+ pci_tegra_rp_writew(reg>>16, PCI_IO_LIMIT_UPPER16, rp);
+
+ /* Memory base and limits */
+ reg = root->res[PCI_BRIDGE_MEM_RES].start;
+ reg = ALIGN(reg, 0x100000);
+ pci_tegra_rp_writew(reg >> 16, PCI_MEMORY_BASE, rp);
+ reg = root->res[PCI_BRIDGE_MEM_RES].end;
+ reg = ALIGN(reg, 0x100000);
+ pci_tegra_rp_writew(reg >> 16, PCI_MEMORY_LIMIT, rp);
+
+ /* Prefetch base and limit - 32 bit addressing */
+ reg = root->res[PCI_BRIDGE_PREFETCH_RES].start;
+ reg = ALIGN(reg, 0x100000);
+ pci_tegra_rp_writew(reg >> 16, PCI_PREF_MEMORY_BASE, rp);
+ pci_tegra_rp_writel(0, PCI_PREF_BASE_UPPER32, rp);
+ reg = root->res[PCI_BRIDGE_PREFETCH_RES].end;
+ reg = ALIGN(reg, 0x100000);
+ pci_tegra_rp_writew(reg >> 16, PCI_PREF_MEMORY_LIMIT, rp);
+ pci_tegra_rp_writel(0, PCI_PREF_LIMIT_UPPER32, rp);
+
+ reg = 0;
+ reg |= PCI_COMMAND_IO;
+ reg |= PCI_COMMAND_MEMORY;
+ reg |= PCI_COMMAND_MASTER;
+ reg |= PCI_COMMAND_SERR;
+ pci_tegra_rp_writew(reg, PCI_COMMAND, rp);
+}
+
+static void pci_tegra_setup_pci_bridge(struct pci_tegra_device *dev)
+{
+ u32 reg;
+
+ dev->res[PCI_BRIDGE_IO_RES].end = pci_tegra_io_base;
+ dev->res[PCI_BRIDGE_MEM_RES].end = pci_tegra_mem_base;
+ dev->res[PCI_BRIDGE_PREFETCH_RES].end =
+ pci_tegra_prefetech_base;
+
+ /* Only set here for the non-root port devices */
+ if (dev->root_port)
+ return;
+
+ /* IO base and limits */
+ reg = dev->res[PCI_BRIDGE_IO_RES].start;
+ reg = ALIGN(reg, 0x1000);
+ pci_conf_write8(dev->bus, dev->devfn, PCI_IO_BASE,
+ (((reg & 0xf000) >> 8) | PCI_IO_RANGE_TYPE_32));
+ pci_conf_write16(dev->bus, dev->devfn, PCI_IO_BASE_UPPER16, reg>>16);
+
+ reg = dev->res[PCI_BRIDGE_IO_RES].end;
+ reg = ALIGN(reg, 0x1000);
+ pci_conf_write8(dev->bus, dev->devfn, PCI_IO_LIMIT,
+ (((reg & 0xf000) >> 8) | PCI_IO_RANGE_TYPE_32));
+ pci_conf_write16(dev->bus, dev->devfn, PCI_IO_LIMIT_UPPER16, reg>>16);
+
+ /* Memory base and limits */
+ reg = dev->res[PCI_BRIDGE_MEM_RES].start;
+ reg = ALIGN(reg, 0x100000);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_MEMORY_BASE, reg >> 16);
+
+ reg = dev->res[PCI_BRIDGE_MEM_RES].end;
+ reg = ALIGN(reg, 0x100000);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_MEMORY_LIMIT, reg >> 16);
+
+ /* Prefetch base and limit - 32 bit addressing */
+ reg = dev->res[PCI_BRIDGE_PREFETCH_RES].start;
+ reg = ALIGN(reg, 0x100000);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_PREF_MEMORY_BASE,
+ reg >> 16);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_PREF_BASE_UPPER32, 0);
+
+ reg = dev->res[PCI_BRIDGE_PREFETCH_RES].end;
+ reg = ALIGN(reg, 0x100000);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_PREF_MEMORY_LIMIT,
+ reg >> 16);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_PREF_LIMIT_UPPER32, 0);
+
+ reg = 0;
+ reg |= PCI_COMMAND_IO;
+ reg |= PCI_COMMAND_MEMORY;
+ reg |= PCI_COMMAND_MASTER;
+ reg |= PCI_COMMAND_SERR;
+ pci_conf_write16(dev->bus, dev->devfn, PCI_COMMAND, reg);
+
+ /* FIXME how to handle interrutps */
+ pci_conf_write8(dev->bus, dev->devfn, PCI_INTERRUPT_LINE, 0x82);
+ pci_conf_write8(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 0xa);
+}
+
+static void pci_tegra_setup_pci_device(struct pci_tegra_device *dev)
+{
+ u8 flags;
+ u32 bar_index;
+ u32 reg;
+ u32 addr;
+
+ for (bar_index = 0x0; bar_index <= PCI_STD_RESOURCE_END;
+ bar_index ++) {
+ u32 size;
+ pci_conf_write32(dev->bus, dev->devfn, bar_index * 4
+ + PCI_BASE_ADDRESS_0, 0xFFFFFFFFUL);
+
+ size = pci_conf_read32(dev->bus, dev->devfn, bar_index * 4
+ + PCI_BASE_ADDRESS_0);
+
+ if (size == 0xFFFFFFFFUL) continue;
+ if (size == 0) continue; /* A broken device? */
+ flags = (size & 0x000f);
+
+ /* Size align the addr and write that BAR offset */
+ if (flags & 0x1) {
+ size &= ~0xF; /* Ignore the last 4 bits */
+ /* some devices hardwire the high bits of IO bars to 0
+ * So, ignore those bits.
+ */
+ size |= 0xffff0000;
+ size = ~size + 1; /* Do the 1's complement */
+
+ addr = ALIGN(pci_tegra_io_base, size);
+
+ if (addr + size > pci_tegra_io_limt) {
+ pr_err("pci_tegra: "
+ "Cannot asign IO res\n");
+ continue;
+ }
+ dev->res[bar_index].flags = IORESOURCE_IO;
+ dev->res[bar_index].start = addr;
+ dev->res[bar_index].end = addr + size -1;
+
+ pci_tegra_io_base = addr + size;
+ } else {
+ size &= ~0xF; /* Ignore the last 4 bits */
+ size = ~size + 1; /* Do the 1's complement */
+
+ if (flags & 0x08) {
+ addr = ALIGN(pci_tegra_mem_base, size);
+
+ if (addr + size > pci_tegra_mem_limit) {
+ pr_err("pci_tegra: "
+ "Cannot asign mem res\n");
+ continue;
+ }
+
+ dev->res[bar_index].flags = IORESOURCE_MEM;
+ dev->res[bar_index].start = 0;
+ dev->res[bar_index].end =
+ dev->res[bar_index].start + size - 1;
+
+ pci_tegra_mem_base = addr + size;
+ } else {
+ addr = ALIGN(pci_tegra_prefetech_base, size);
+
+ if (addr + size > pci_tegra_prefetech_limit) {
+ pr_err("pci_tegra: "
+ "Cannot asign prefetch res\n");
+ continue;
+ }
+
+ dev->res[bar_index].flags =
+ IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ dev->res[bar_index].start = addr;
+ dev->res[bar_index].end = addr + size - 1;
+
+ pci_tegra_prefetech_base = addr + size;
+ }
+ }
+ pci_conf_write32(dev->bus, dev->devfn, bar_index * 4
+ + PCI_BASE_ADDRESS_0, dev->res[bar_index].start);
+
+ /* Handle 64 bit addresses by forcing to 32 bit addresses */
+ if ((flags == 0x0c) || (flags==0x04)) {
+ bar_index++;
+ BUG_ON(bar_index < 6);
+ pci_conf_write32(dev->bus, dev->devfn, bar_index * 4
+ + PCI_BASE_ADDRESS_0, 0);
+ }
+ }
+
+ reg = 0;
+ reg |= PCI_COMMAND_IO;
+ reg |= PCI_COMMAND_MEMORY;
+ reg |= PCI_COMMAND_MASTER;
+ reg |= PCI_COMMAND_SERR;
+ pci_conf_write16(dev->bus, dev->devfn, PCI_COMMAND, reg);
+
+ /* FIXME how to handle interrutps */
+
+}
+
+static void pci_tegra_print_device_tree(struct pci_tegra_device *dev)
+{
+ u32 i;
+
+ if (!dev)
+ return;
+
+ if (dev->sub_bus)
+ pr_err("PCIe bridge/Root port\n");
+ else
+ pr_err("PCIe device\n");
+
+ pr_err(" Vendor/Device = 0x%x bus = %d sec bus %d sub bus %d\n",
+ dev->id, dev->bus, dev->sec_bus, dev->sub_bus);
+ if (dev->disabled) {
+ pr_err(" Slot disabled\n");
+ } else {
+ for (i=0; i<= PCI_STD_RESOURCE_END; i++) {
+ /* Skip printing the empty ones */
+ if (!dev->res[i].start)
+ continue;
+ pr_err(" bar(%d) \n", i);
+ pr_err(" start = 0x%x\n", dev->res[i].start);
+ pr_err(" end = 0x%x\n", dev->res[i].end);
+ pr_err(" flags = 0x%lx\n", dev->res[i].flags);
+ }
+ }
+
+ if (dev->child != NULL)
+ pci_tegra_print_device_tree(dev->child);
+
+ if (dev->next != NULL)
+ pci_tegra_print_device_tree(dev->next);
+}
+
+static void pci_tegra_allocate_resources(struct pci_tegra_device *dev)
+{
+ /* Employing a depth first search for resource allocation. */
+ if (!dev)
+ return;
+
+ if (dev->sub_bus) {
+
+ dev->res[PCI_BRIDGE_IO_RES].flags = IORESOURCE_IO;
+ dev->res[PCI_BRIDGE_IO_RES].start = pci_tegra_io_base;
+
+ dev->res[PCI_BRIDGE_PREFETCH_RES].flags =
+ IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ dev->res[PCI_BRIDGE_PREFETCH_RES].start =
+ pci_tegra_prefetech_base;
+
+ dev->res[PCI_BRIDGE_MEM_RES].flags = IORESOURCE_MEM;
+ dev->res[PCI_BRIDGE_MEM_RES].start = pci_tegra_mem_base;
+ }
+
+ if (dev->child)
+ pci_tegra_allocate_resources(dev->child);
+ if (dev->next)
+ pci_tegra_allocate_resources(dev->next);
+
+ if (dev->sub_bus)
+ pci_tegra_setup_pci_bridge(dev);
+ else
+ pci_tegra_setup_pci_device(dev);
+}
+
+void pci_tegra_enumerate(void)
+{
+ u32 reg;
+
+ /* Disable all execptions */
+ pci_tegra_afi_writel(0, AFI_FPCI_ERROR_MASKS_0);
+
+ /* Set the base and limits of the resources */
+ pci_tegra_io_base = TEGRA_PCIE_BASE + PCIE_DOWNSTREAM_IO_OFFSET;
+ pci_tegra_io_limt = pci_tegra_io_base + PCIE_DOWNSTREAM_IO_SIZE;
+
+ pci_tegra_mem_base = FPCI_NON_PREFETCH_MEMORY_OFFSET;
+ pci_tegra_mem_limit = FPCI_NON_PREFETCH_MEMORY_OFFSET
+ + PCIE_NON_PREFETCH_MEMORY_SIZE;
+
+ pci_tegra_prefetech_base = FPCI_PREFETCH_MEMORY_OFFSET;
+ pci_tegra_prefetech_limit = FPCI_PREFETCH_MEMORY_OFFSET
+ + PCIE_PREFETCH_MEMORY_SIZE;
+
+ /* Enumerate only if the Link is UP. */
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_VEND_XP, 0);
+ if (NVPCIE_DRF_VAL(RP, VEND_XP, DL_UP, reg) == 1)
+ pci_tegra_enumerate_root_port(0);
+
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_VEND_XP, 1);
+ if (NVPCIE_DRF_VAL(RP, VEND_XP, DL_UP, reg) == 1)
+ pci_tegra_enumerate_root_port(1);
+
+ pci_tegra_print_device_tree(pci_root);
+}
diff --git a/arch/arm/mach-tegra/pci.c b/arch/arm/mach-tegra/pci.c
index dcfdb7981464..2ecba6015229 100644
--- a/arch/arm/mach-tegra/pci.c
+++ b/arch/arm/mach-tegra/pci.c
@@ -20,11 +20,29 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#define DEBUG
+#define VERBOSE_DEBUG
+
+
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/mach/pci.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <mach/pci.h>
+#include <mach/nvrm_linux.h>
+
+#include "nvrm_pmu.h"
+#include "nvodm_query_discovery.h"
+#include "nvrm_power.h"
+#include "nvrm_interrupt.h"
+
+
+void __iomem * volatile pci_tegra_regs;
+static bool pci_tegra_device_attached = false;
+static unsigned int pci_tegra_powerid;
static int __init pcie_tegra_init(void);
@@ -40,12 +58,20 @@ static int pci_tegra_read_conf(struct pci_bus *bus, u32 devfn,
static int __init pcie_tegra_init(void);
+
+static void pci_tegra_power(int on);
+static void pci_tegra_setup_translations(void);
+static irqreturn_t pci_tegra_isr(int irq, void *arg);
+static bool pci_tegra_check_rp(int rp);
+
unsigned long pci_tegra_get_base(char *aperture)
{
if (!strcmp(aperture, "mem"))
- return 0x90000000;
+ return TEGRA_PCIE_BASE +
+ PCIE_NON_PREFETCH_MEMORY_OFFSET;
else if (!strcmp(aperture, "io"))
- return 0x82000000;
+ return TEGRA_PCIE_BASE +
+ PCIE_DOWNSTREAM_IO_OFFSET;
else
return (unsigned long)-1;
}
@@ -54,32 +80,302 @@ static int pci_tegra_read_conf(struct pci_bus *bus, u32 devfn,
int where, int size, u32 *val)
{
int i;
+ u32 v;
+ int rp;
+
+ pr_debug("Issuing read conf: bus %d, devfn 0x%x, where 0x%x size %d\n",
+ bus->number, devfn, where, size);
+
+ if (!pci_tegra_device_attached) goto fail;
+ if (where > 4096) goto fail;
+
+ /* Root port config registers are directly mapped in different
+ * aperture - not same as the config registers for devices on the PCI
+ * bus
+ */
+ if (pci_tegra_is_rp(bus->number, &rp)) {
+ /* Root port is just one bridge! */
+ if (devfn != 0) goto fail;
+ v = pci_tegra_rp_readl(where & ~3, rp);
+ } else {
+ void __iomem *addr;
+
+ /* Root is only attached to one device/bridge */
+ if (bus->number == 1 && PCI_SLOT(devfn) != 0) goto fail;
+ addr = pci_tegra_config_addr(bus->number, devfn, where & ~3);
+ v = readl(addr);
+ }
+
+ switch (size) {
+ case 1:
+ if (where & 2) v >>= 16;
+ if (where & 1) v >>= 8;
+ v &= 0xff;
+ break;
+ case 2:
+ if (where & 2) v >>= 16;
+ v &= 0xffff;
+ break;
+ default:
+ break;
+ }
+ *val = v;
+ pr_debug(" Value = 0x%x\n", v);
+
+ return PCIBIOS_SUCCESSFUL;
+fail:
for (i=0; i<size; i++)
((__u8 *)val)[i] = 0xff;
-
return PCIBIOS_SUCCESSFUL;
}
static int pci_tegra_write_conf(struct pci_bus *bus, u32 devfn,
int where, int size, u32 val)
{
+ void __iomem *addr;
+ int rp;
+ u32 temp;
+
+ pr_debug("Issuing write conf: bus %d, devfn 0x%x, "
+ "where 0x%x size %d value = 0x%x\n",
+ bus->number, devfn, where, size, val);
+
+ if (!pci_tegra_device_attached) goto fail;
+
+ if (where > 4096) goto fail;
+
+ /* Root port config registers are directly mapped in different
+ * aperture - not same as the config registers for devices on the PCI
+ * bus.
+ */
+ if (pci_tegra_is_rp(bus->number, &rp)) {
+ /* Root port is just one bridge! */
+ if (devfn != 0) goto fail;
+ addr = pci_tegra_regs;
+ if (rp == 0) addr += NV_PCIE_AXI_RP_T0C0_OFFSET;
+ if (rp == 1) addr += NV_PCIE_AXI_RP_T0C1_OFFSET;
+ addr += where;
+ } else {
+ /* Root is only attached to one device/bridge */
+ if (bus->number == 1 && PCI_SLOT(devfn) != 0) goto fail;
+
+ addr = pci_tegra_config_addr(bus->number, devfn, where);
+ }
+
+ switch (size) {
+ case 1:
+ temp = readl((u32)addr & ~0x3);
+ temp &= ~(0xff << ((where & 0x3) * 8));
+ temp |= val << ((where & 0x3) * 8);
+ writel(temp, (u32)addr & ~0x3);
+ break;
+ case 2:
+ temp = readl((u32)addr & ~0x3);
+ temp &= ~(0xffff << ((where & 0x3) * 8));
+ temp |= val << ((where & 0x3) * 8);
+ writel(temp, (u32)addr & ~0x3);
+ break;
+ default:
+ writel(val, addr);
+ break;
+ }
+fail:
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops pci_tegra_ops = {
- .read = pci_tegra_read_conf,
- .write = pci_tegra_write_conf,
+ .read = pci_tegra_read_conf,
+ .write = pci_tegra_write_conf,
};
static void __init pci_tegra_preinit(void)
{
-
+ pcibios_setup("firmware");
}
static int __init pci_tegra_setup(int nr, struct pci_sys_data *data)
{
- return (nr == 0);
+ u32 volatile reg;
+ unsigned int irq;
+
+ if (nr != 0) return 0;
+
+ pci_tegra_power(1);
+ if (NvRmSetModuleTristate(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmPrivModuleID_Pcie, 0), NV_FALSE)
+ != NvSuccess) {
+ /* No valid Pinmux for PCIe? */
+ return 0;
+ }
+
+ pci_tegra_regs = ioremap_nocache(TEGRA_PCIE_BASE,
+ PCI_TEGRA_IOMAPPED_REG_APERTURE_SIZE);
+ if (pci_tegra_regs == NULL) {
+ pr_err("pci_tegra_setup: Failed to map the PCI/AFI regs\n");
+ return 0;
+ }
+
+ if (NvRmPowerRegister(s_hRmGlobal, 0, &pci_tegra_powerid) != NvSuccess)
+ goto fail;
+ if (NvRmPowerModuleClockControl(s_hRmGlobal, NvRmPrivModuleID_Pcie,
+ pci_tegra_powerid, NV_TRUE) != NvSuccess)
+ goto fail;
+
+ NvRmModuleReset(s_hRmGlobal, NvRmPrivModuleID_Afi);
+ NvRmModuleReset(s_hRmGlobal, NvRmPrivModuleID_Pcie);
+ NvRmModuleResetWithHold(s_hRmGlobal, NvRmPrivModuleID_PcieXclk,
+ NV_TRUE);
+
+ /* Enable slot clock and pulse the reset signals */
+ reg = pci_tegra_afi_readl(AFI_PEX0_CTRL_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX0_CTRL, PEX0_REFCLK_EN, 1, reg);
+ pci_tegra_afi_writel(reg, AFI_PEX0_CTRL_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX0_CTRL, PEX0_RST_L, 0, reg);
+ pci_tegra_afi_writel(reg, AFI_PEX0_CTRL_0);
+
+ reg = pci_tegra_afi_readl(AFI_PEX1_CTRL_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX1_CTRL, PEX1_REFCLK_EN, 1, reg);
+ pci_tegra_afi_writel(reg, AFI_PEX1_CTRL_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX1_CTRL, PEX1_RST_L, 0, reg);
+ pci_tegra_afi_writel(reg, AFI_PEX1_CTRL_0);
+
+ msleep(100);
+
+ reg = pci_tegra_afi_readl(AFI_PEX0_CTRL_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX0_CTRL, PEX0_RST_L, 1, reg);
+ pci_tegra_afi_writel(reg, AFI_PEX0_CTRL_0);
+
+ reg = pci_tegra_afi_readl(AFI_PEX1_CTRL_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX1_CTRL, PEX1_RST_L, 1, reg);
+ pci_tegra_afi_writel(reg, AFI_PEX1_CTRL_0);
+
+ /* Validate by reading the ROOT port IDs that we are infact working on
+ * the TEGRA root port */
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_DEV_ID, 0);
+ BUG_ON((NVPCIE_DRF_VAL(RP, DEV_ID, VENDOR_ID, reg)) !=
+ NV_PROJ__PCIE2_RP_DEV_ID_VENDOR_ID_NVIDIA);
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_DEV_ID, 1);
+ BUG_ON((NVPCIE_DRF_VAL(RP, DEV_ID, VENDOR_ID, reg)) !=
+ NV_PROJ__PCIE2_RP_DEV_ID_VENDOR_ID_NVIDIA);
+
+ /* Enable dual controller and both ports*/
+ reg = pci_tegra_afi_readl(AFI_PCIE_CONFIG_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PCIE_CONFIG, PCIEC0_DISABLE_DEVICE, 0,
+ reg);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PCIE_CONFIG, SM2TMS0_XBAR_CONFIG, 1,
+ reg);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PCIE_CONFIG, PCIEC1_DISABLE_DEVICE, 0,
+ reg);
+ pci_tegra_afi_writel(reg, AFI_PCIE_CONFIG_0);
+
+ reg = pci_tegra_afi_readl(AFI_FUSE_0);
+ reg = NV_FLD_SET_DRF_NUM(AFI, FUSE, FUSE_PCIE_T0_GEN2_DIS, 0, reg);
+ pci_tegra_afi_writel(reg, AFI_FUSE_0);
+
+ /* Initialze AP20 internal PHY */
+ /* ENABLE up to 16 PCIE lanes */
+ pci_tegra_pads_writel(0x0, NV_PROJ__PCIE2_PADS_CTL_SEL_1);
+
+ /* override IDDQ to 1 on all 4 lanes */
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_CTL_1);
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, CTL_1, IDDQ_1L, 1, reg);
+ pci_tegra_pads_writel(reg, NV_PROJ__PCIE2_PADS_CTL_1);
+
+ /* set up PHY PLL inputs select PLLE output as refclock */
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_PLL_CTL1);
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, PLL_CTL1, PLL_REFCLK_SEL,
+ NV_PROJ__PCIE2_PADS_PLL_CTL1_PLL_REFCLK_SEL_INTERNAL_CML, reg);
+
+ /* set TX ref sel to div10 (not div5) */
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, PLL_CTL1, PLL_TXCLKREF_SEL,
+ NV_PROJ__PCIE2_PADS_PLL_CTL1_PLL_TXCLKREF_SEL_DIV10, reg);
+ pci_tegra_pads_writel(reg, NV_PROJ__PCIE2_PADS_PLL_CTL1);
+
+ /* take PLL out of reset */
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_PLL_CTL1);
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, PLL_CTL1, PLL_RST_B4SM,
+ NV_PROJ__PCIE2_PADS_PLL_CTL1_PLL_RST_B4SM_DEASSERT, reg);
+ pci_tegra_pads_writel(reg, NV_PROJ__PCIE2_PADS_PLL_CTL1);
+
+ /* Hack, set the clock voltage to the DEFAULT provided by hw folks.
+ * This doesn't exist in the documentation
+ * */
+ reg = 0xFA5CFA5C;
+ pci_tegra_pads_writel(reg, 0xc8);
+
+ /* Wait for the PLL to lock */
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_PLL_CTL1);
+ while (NVPCIE_DRF_VAL(PADS, PLL_CTL1, PLL_LOCKDET, reg)
+ != NV_PROJ__PCIE2_PADS_PLL_CTL1_PLL_LOCKDET_LOCKED)
+ {
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_PLL_CTL1);
+ }
+
+ /* turn off IDDQ override */
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_CTL_1);
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, CTL_1, IDDQ_1L, 0, reg);
+ pci_tegra_pads_writel(reg, NV_PROJ__PCIE2_PADS_CTL_1);
+
+ /* ENABLE TX/RX data */
+ reg = pci_tegra_pads_reedl(NV_PROJ__PCIE2_PADS_CTL_1);
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, CTL_1, TX_DATA_EN_1L,
+ NV_PROJ__PCIE2_PADS_CTL_1_TX_DATA_EN_1L_ENABLE, reg);
+ reg = NVPCIE_FLD_SET_DRF_NUM(PADS, CTL_1, RX_DATA_EN_1L,
+ NV_PROJ__PCIE2_PADS_CTL_1_RX_DATA_EN_1L_ENABLE, reg);
+ pci_tegra_pads_writel(reg, NV_PROJ__PCIE2_PADS_CTL_1);
+
+ irq = tegra_get_module_inst_irq("pcie", 0, 0);
+ BUG_ON(irq == NO_IRQ);
+ if (request_irq(irq, pci_tegra_isr, IRQF_SHARED, "PCIE", s_hRmGlobal))
+ {
+ pr_err("pci_tegra_setup: Cannot register the IRQ %d\n", irq);
+ }
+ set_irq_flags(irq, IRQF_VALID);
+
+ /* setup the AFI address translations */
+ pci_tegra_setup_translations();
+
+ /* Take the PCIe interface module out of reset to start the PCIe
+ * training sequence */
+ NvRmModuleResetWithHold(s_hRmGlobal, NvRmPrivModuleID_PcieXclk,
+ NV_FALSE);
+
+ /* Finally enable PCIe */
+ reg = pci_tegra_afi_readl(AFI_CONFIGURATION_0);
+ reg = reg | AFI_CONFIGURATION_0_EN_FPCI_DEFAULT_MASK;
+ pci_tegra_afi_writel(reg, AFI_CONFIGURATION_0);
+
+ if (!pci_tegra_check_rp(0) && !pci_tegra_check_rp(1)) {
+ pci_tegra_device_attached = false;
+ return 0;
+ }
+ pci_tegra_device_attached = true;
+
+ /* Enable PCIe interrupts */
+ reg = 0;
+ reg |= NV_DRF_NUM(AFI, AFI_INTR_ENABLE, EN_INI_SLVERR, 1);
+ reg |= NV_DRF_NUM(AFI, AFI_INTR_ENABLE, EN_INI_DECERR, 1);
+ reg |= NV_DRF_NUM(AFI, AFI_INTR_ENABLE, EN_TGT_SLVERR, 1);
+ reg |= NV_DRF_NUM(AFI, AFI_INTR_ENABLE, EN_TGT_DECERR, 1);
+ reg |= NV_DRF_NUM(AFI, AFI_INTR_ENABLE, EN_TGT_WRERR, 1);
+ reg |= NV_DRF_NUM(AFI, AFI_INTR_ENABLE, EN_DFPCI_DECERR, 1);
+ pci_tegra_afi_writel(reg, AFI_AFI_INTR_ENABLE_0);
+ pci_tegra_afi_writel(0xffffffff, AFI_SM_INTR_ENABLE_0);
+
+ reg = 0;
+ reg |= NV_DRF_NUM(AFI, INTR_MASK, INT_MASK, 1);
+ /* FIXME: No MSI for now */
+ reg |= NV_DRF_NUM(AFI, INTR_MASK, MSI_MASK, 0);
+ pci_tegra_afi_writel(reg, AFI_INTR_MASK_0);
+
+ pci_tegra_enumerate();
+
+ pr_err("pci_tegra_setup: Successfull\n");
+ return 1;
+fail:
+ pr_err("pci_tegra_setup: failed\n");
+ return 0;
}
static struct pci_bus __init *pci_tegra_scan_bus(int nr,
@@ -92,11 +388,11 @@ static struct pci_bus __init *pci_tegra_scan_bus(int nr,
}
static struct hw_pci pci_tegra_data __initdata = {
- .nr_controllers = 2,
- .preinit = pci_tegra_preinit,
- .setup = pci_tegra_setup,
- .scan = pci_tegra_scan_bus,
- .swizzle = pci_std_swizzle,
+ .nr_controllers = 2,
+ .preinit = pci_tegra_preinit,
+ .setup = pci_tegra_setup,
+ .scan = pci_tegra_scan_bus,
+ .swizzle = pci_std_swizzle,
};
late_initcall(pcie_tegra_init);
@@ -106,3 +402,244 @@ static int __init pcie_tegra_init(void)
pci_common_init(&pci_tegra_data);
return 0;
}
+
+/*
+ * PCIe support functions
+ */
+
+static void pci_tegra_power(int on)
+{
+ u32 settling_time;
+ const NvOdmPeripheralConnectivity *con = NULL;
+ int i;
+
+ con = NvOdmPeripheralGetGuid(NV_VDD_PEX_CLK_ODM_ID);
+ if (con == NULL)
+ return;
+
+ for (i = 0; i < con->NumAddress; i++) {
+ if (con->AddressList[i].Interface != NvOdmIoModule_Vdd)
+ continue;
+ if (on) {
+ NvRmPmuVddRailCapabilities rail;
+ NvRmPmuGetCapabilities(s_hRmGlobal,
+ con->AddressList[i].Address, &rail);
+ NvRmPmuSetVoltage(s_hRmGlobal,
+ con->AddressList[i].Address,
+ rail.requestMilliVolts, &settling_time);
+ } else
+ NvRmPmuSetVoltage(s_hRmGlobal,
+ con->AddressList[i].Address, NVODM_VOLTAGE_OFF,
+ &settling_time);
+ udelay(settling_time);
+ }
+}
+
+static void pci_tegra_setup_translations(void)
+{
+ u32 fpci_bar;
+ u32 size;
+ u32 axi_address;
+
+ /* Bar 0: Config Bar */
+ fpci_bar = ((u32)0xfdff << 16);
+ size = PCIE_CONFIG_SIZE;
+ axi_address = TEGRA_PCIE_BASE + PCIE_CONFIG_OFFSET;
+ pci_tegra_afi_writel(axi_address, AFI_AXI_BAR0_START_0);
+ pci_tegra_afi_writel(size>>12, AFI_AXI_BAR0_SZ_0);
+ pci_tegra_afi_writel(fpci_bar, AFI_FPCI_BAR0_0);
+
+ /* Bar 1: Extended config Bar */
+ fpci_bar = ((u32)0xfe1 << 20);
+ size = PCIE_EXTENDED_CONFIG_SIZE;
+ axi_address = TEGRA_PCIE_BASE + PCIE_EXTENDED_CONFIG_OFFSET;
+ pci_tegra_afi_writel(axi_address, AFI_AXI_BAR1_START_0);
+ pci_tegra_afi_writel(size >>12, AFI_AXI_BAR1_SZ_0);
+ pci_tegra_afi_writel(fpci_bar, AFI_FPCI_BAR1_0);
+
+ /* Bar 2: Downstream IO bar */
+ fpci_bar = ((__u32)0xfdfc << 16);
+ size = PCIE_DOWNSTREAM_IO_SIZE;
+ axi_address = TEGRA_PCIE_BASE + PCIE_DOWNSTREAM_IO_OFFSET;
+ pci_tegra_afi_writel(axi_address, AFI_AXI_BAR2_START_0);
+ pci_tegra_afi_writel(size>>12, AFI_AXI_BAR2_SZ_0);
+ pci_tegra_afi_writel(fpci_bar, AFI_FPCI_BAR2_0);
+
+ /* Bar 3: Pre-fetchable memory BAR */
+ /* Bits 39:12 of 40 bit FPCI address goes to bits 31:4 */
+ fpci_bar = (((FPCI_PREFETCH_MEMORY_OFFSET >> 12) & 0x0FFFFFFF) << 4);
+ fpci_bar |= 0x1;
+ size = PCIE_PREFETCH_MEMORY_SIZE;
+ axi_address = TEGRA_PCIE_BASE + PCIE_PREFETCH_MEMORY_OFFSET;
+ pci_tegra_afi_writel(axi_address, AFI_AXI_BAR3_START_0);
+ pci_tegra_afi_writel(size >> 12, AFI_AXI_BAR3_SZ_0);
+ pci_tegra_afi_writel(fpci_bar, AFI_FPCI_BAR3_0);
+
+ /* Bar 4: Non pre-fetchable memory BAR */
+ /* Bits 39:12 of 40 bit FPCI address goes to bits 31:4 */
+ fpci_bar = (((FPCI_NON_PREFETCH_MEMORY_OFFSET >> 12)
+ & 0x0FFFFFFF) << 4);
+ fpci_bar |= 0x1;
+ size = PCIE_NON_PREFETCH_MEMORY_SIZE;
+ axi_address = TEGRA_PCIE_BASE
+ + PCIE_NON_PREFETCH_MEMORY_OFFSET;
+ pci_tegra_afi_writel(axi_address, AFI_AXI_BAR4_START_0);
+ pci_tegra_afi_writel(size >> 12, AFI_AXI_BAR4_SZ_0);
+ pci_tegra_afi_writel(fpci_bar, AFI_FPCI_BAR4_0);
+
+ /* Bar 5: NULL out the remaining BAR as it is not used */
+ fpci_bar = 0;
+ size = 0;
+ axi_address = 0;
+ pci_tegra_afi_writel(axi_address, AFI_AXI_BAR5_START_0);
+ pci_tegra_afi_writel(size >> 12, AFI_AXI_BAR5_SZ_0);
+ pci_tegra_afi_writel(fpci_bar, AFI_FPCI_BAR5_0);
+
+ /* map all upstream transactions as uncached */
+ pci_tegra_afi_writel(FPCI_SYSTEM_MEMORY_OFFSET, AFI_CACHE_BAR0_ST_0);
+ pci_tegra_afi_writel(0, AFI_CACHE_BAR0_SZ_0);
+ pci_tegra_afi_writel(0, AFI_CACHE_BAR1_ST_0);
+ pci_tegra_afi_writel(0, AFI_CACHE_BAR1_SZ_0);
+
+ /* Map MSI bar */
+ pci_tegra_afi_writel(0, AFI_MSI_FPCI_BAR_ST_0);
+ pci_tegra_afi_writel(0, AFI_MSI_BAR_SZ_0);
+ pci_tegra_afi_writel(0, AFI_MSI_AXI_BAR_ST_0);
+ pci_tegra_afi_writel(0, AFI_MSI_BAR_SZ_0);
+}
+
+static irqreturn_t pci_tegra_isr(int irq, void *arg)
+{
+ u32 intr_info, intr_extended_info;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ intr_info = pci_tegra_afi_readl(AFI_INTR_CODE_0);
+ intr_info = NV_DRF_VAL(AFI, INTR_CODE, INT_CODE, intr_info);
+ intr_extended_info = pci_tegra_afi_readl(AFI_INTR_SIGNATURE_0);
+
+ /* pr_err("+pci_tegra_isr\n"); */
+
+ switch (intr_info) {
+ case 6: /* legacy */
+ ret = IRQ_NONE;
+ break;
+ case 1: /* SLVERR */
+ pr_err("pci_tegra_isr: AXI slave error\n");
+ break;
+ case 2: /* DECERR */
+ pr_err("pci_tegra_isr: AXI decode error\n");
+ break;
+ case 3: /* PCIE target abort */
+ pr_err("pci_tegra_isr: Target abort\n");
+ break;
+ case 4: /* PCIE master abort */
+ /* Don't print this, as this error is a common error during
+ * enumeration.
+ */
+ /* pr_err("pci_tegra_isr: Master abort\n"); */
+ break;
+ case 5: /* Bufferable write to non-posted write */
+ pr_err("pci_tegra_isr: Invalid write"
+ " - Bufferable write to non-posted region\n");
+ break;
+ case 7: /* Response address mapping error */
+ pr_err("pci_tegra_isr: Response decoding error \n");
+ break;
+ case 8: /* Response address mapping error */
+ pr_err("pci_tegra_isr: AXI response decoding error\n");
+ break;
+ case 9: /* PCIE timeout */
+ pr_err("pci_tegra_isr: Transcation timeout\n");
+ break;
+ default:
+ pr_err("pci_tegra_isr: Unknown interrupt\n");
+ break;
+ }
+
+ /* Clear the interrupt code register to sample the next interrupt */
+ pci_tegra_afi_writel(0, AFI_INTR_CODE_0);
+
+ /* pr_err("-pci_tegra_isr\n"); */
+ return ret;
+}
+
+
+/*
+ * FIXME: If there are no PCIe cards attached, then calling this function
+ * can result in the increase of the bootup time as there are big timeout
+ * loops.
+ */
+static bool pci_tegra_check_rp(int rp)
+{
+#define PCI_TEGRA_LINKUP_TIMEOUT 50 /* i.e .2 seconds */
+ u32 reg;
+ int retry_count = 0;
+ int loop_counter;
+
+ BUG_ON(rp != 0 && rp != 1);
+retry:
+ if (retry_count > 2 ) {
+ pr_err("pci_tegra_check_rp: RP %d Failed\n", rp);
+ return false;
+ }
+
+ if (retry_count != 0) {
+ u32 offset;
+
+ /* Reset before retrying again. */
+ if (rp == 0) offset = AFI_PEX0_CTRL_0;
+ if (rp == 1) offset = AFI_PEX1_CTRL_0;
+
+ BUG_ON(AFI_PEX0_CTRL_0_PEX0_RST_L_SHIFT !=
+ AFI_PEX1_CTRL_0_PEX1_RST_L_SHIFT);
+
+ /* Pulse the PEX reset */
+ reg = pci_tegra_afi_readl(offset);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX0_CTRL, PEX0_RST_L, 1, reg);
+ pci_tegra_afi_writel(reg, offset);
+
+ msleep(100);
+
+ reg = pci_tegra_afi_readl(offset);
+ reg = NV_FLD_SET_DRF_NUM(AFI, PEX0_CTRL, PEX0_RST_L, 0, reg);
+ pci_tegra_afi_writel(reg, offset);
+ }
+
+ loop_counter = PCI_TEGRA_LINKUP_TIMEOUT;
+
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_VEND_XP, rp);
+ while (loop_counter && (NVPCIE_DRF_VAL(RP, VEND_XP, DL_UP, reg)!= 1)) {
+ msleep(1);
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_VEND_XP, rp);
+ loop_counter --;
+ }
+
+ if (!loop_counter) {
+ retry_count++;
+ pr_err("pci_tegra_check_rp: "
+ "RP %d LINK is not up...retrying...\n", rp);
+ goto retry;
+ }
+
+ loop_counter = PCI_TEGRA_LINKUP_TIMEOUT;
+
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_LINK_CONTROL_STATUS, rp);
+ reg = NVPCIE_DRF_VAL(RP, LINK_CONTROL_STATUS, LINKSTAT, reg);
+ while (loop_counter && ((reg & 0x2000) != 0x2000)) {
+ msleep(1);
+ reg = pci_tegra_rp_readl(NV_PROJ__PCIE2_RP_LINK_CONTROL_STATUS,
+ rp);
+ reg = NVPCIE_DRF_VAL(RP, LINK_CONTROL_STATUS, LINKSTAT, reg);
+ pr_err("pci_tegra_check_rp: "
+ "RP %d LINK status 0x%x...retrying...\n", rp, reg);
+ loop_counter --;
+ }
+
+ if (!loop_counter) {
+ retry_count++;
+ goto retry;
+ }
+
+ pr_info("pci_tegra_check_rp: RP %d success\n", rp);
+ return true;
+}
diff --git a/arch/arm/mach-tegra/tegra_sysmap.c b/arch/arm/mach-tegra/tegra_sysmap.c
index 13550ff45322..cb5b88ced984 100644
--- a/arch/arm/mach-tegra/tegra_sysmap.c
+++ b/arch/arm/mach-tegra/tegra_sysmap.c
@@ -34,6 +34,8 @@ static NvRmModuleID tegra_map_name_to_mod(const char *name, int inst)
return NVRM_MODULE_ID(NvRmPrivModuleID_Pl310, inst);
else if (!strcmp(name, "scu"))
return NVRM_MODULE_ID(NvRmPrivModuleID_ArmPerif, inst);
+ else if (!strcmp(name, "pcie"))
+ return NVRM_MODULE_ID(NvRmPrivModuleID_Pcie, inst);
return (NvRmModuleID) 0;
}