summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Alves <jalves@nvidia.com>2010-03-03 21:06:37 -0800
committerGary King <gking@nvidia.com>2010-03-09 19:11:03 -0800
commitb54fb83786bf9c0807577cdb912b016a9f012f3a (patch)
treed33e9fcb70884becbee952d37266a70113ad626f
parent02259089c0deaff0f89a9c8408497c3aeccbe0dd (diff)
tegra pcie: fix support for simultaneous root ports
eliminate overlapping memory ranges for the root ports reduce config accesses Change-Id: I9eb2af226a88bcd27ee445ddee01c18663fe4449 Reviewed-on: http://git-master/r/762 Reviewed-by: Gary King <gking@nvidia.com> Tested-by: Gary King <gking@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/hardware.h3
-rw-r--r--arch/arm/mach-tegra/include/mach/pci.h15
-rw-r--r--arch/arm/mach-tegra/pci-enum.c82
-rw-r--r--arch/arm/mach-tegra/pci.c69
4 files changed, 129 insertions, 40 deletions
diff --git a/arch/arm/mach-tegra/include/mach/hardware.h b/arch/arm/mach-tegra/include/mach/hardware.h
index 911f7a70ee83..3e3a6eaa244e 100644
--- a/arch/arm/mach-tegra/include/mach/hardware.h
+++ b/arch/arm/mach-tegra/include/mach/hardware.h
@@ -20,7 +20,8 @@
#ifndef __MACH_TEGRA_HARDWARE_H
-#define pcibios_assign_all_busses() 1
+//do not let linux override bios's bus assignments
+#define pcibios_assign_all_busses() 0
extern unsigned long pci_tegra_get_base(char *aperture);
diff --git a/arch/arm/mach-tegra/include/mach/pci.h b/arch/arm/mach-tegra/include/mach/pci.h
index e5eac3ad5640..7a4cbaccd837 100644
--- a/arch/arm/mach-tegra/include/mach/pci.h
+++ b/arch/arm/mach-tegra/include/mach/pci.h
@@ -294,6 +294,21 @@ static inline bool pci_tegra_is_rp(u32 bus_number, int *rp)
return false;
}
+// return true if this is the first device on an (active) rootport
+static inline bool pci_tegra_is_rp_first_dev(u32 bus_number, int rp)
+{
+ u8 primary, secondary;
+
+ BUG_ON((rp != 0) && (rp != 1));
+
+ primary = pci_tegra_rp_readb(PCI_PRIMARY_BUS, rp);
+ secondary = pci_tegra_rp_readb(PCI_SECONDARY_BUS, rp);
+ if ((primary < secondary) && (bus_number == secondary))
+ return true;
+
+ return false;
+}
+
/*
* Given the bus number, devfn and the offset this API returns the mapped
* address of the config space.
diff --git a/arch/arm/mach-tegra/pci-enum.c b/arch/arm/mach-tegra/pci-enum.c
index 1bcf659fe696..28dad224a868 100644
--- a/arch/arm/mach-tegra/pci-enum.c
+++ b/arch/arm/mach-tegra/pci-enum.c
@@ -274,7 +274,7 @@ next_device:
/* Bridge device */
/* Temporarily assign 0xff for the subordinate bus number as
- * we don't * know how many devices are preset behind this
+ * we don't * know how many devices are present behind this
* bridge.
* */
subordinate_bus = 0xff;
@@ -354,21 +354,31 @@ static void pci_tegra_enumerate_root_port(int 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);
+ if (root->res[PCI_BRIDGE_MEM_RES].start != root->res[PCI_BRIDGE_MEM_RES].end) {
+ 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);
+ } else {
+ pci_tegra_rp_writew(0xffff, PCI_MEMORY_BASE, rp);
+ pci_tegra_rp_writew(0x0000, 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);
+ if (root->res[PCI_BRIDGE_PREFETCH_RES].start != root->res[PCI_BRIDGE_PREFETCH_RES].end) {
+ reg = root->res[PCI_BRIDGE_PREFETCH_RES].start;
+ reg = ALIGN(reg, 0x100000);
+ pci_tegra_rp_writew(reg >> 16, PCI_PREF_MEMORY_BASE, rp);
+ reg = root->res[PCI_BRIDGE_PREFETCH_RES].end;
+ reg = ALIGN(reg, 0x100000);
+ pci_tegra_rp_writew(reg >> 16, PCI_PREF_MEMORY_LIMIT, rp);
+ } else {
+ pci_tegra_rp_writew(0xffff, PCI_PREF_MEMORY_BASE, rp);
+ pci_tegra_rp_writew(0, PCI_PREF_MEMORY_LIMIT, 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;
@@ -406,26 +416,36 @@ static void pci_tegra_setup_pci_bridge(struct pci_tegra_device *dev)
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);
+ if (dev->res[PCI_BRIDGE_MEM_RES].start != dev->res[PCI_BRIDGE_MEM_RES].end) {
+ 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);
+ } else {
+ pci_conf_write16(dev->bus, dev->devfn, PCI_MEMORY_BASE, 0xffff);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_MEMORY_LIMIT, 0);
+ }
/* 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);
+ if (dev->res[PCI_BRIDGE_PREFETCH_RES].start != dev->res[PCI_BRIDGE_PREFETCH_RES].end) {
+ 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);
+ } else {
+ pci_conf_write16(dev->bus, dev->devfn, PCI_PREF_MEMORY_BASE, 0xffff);
+ pci_conf_write16(dev->bus, dev->devfn, PCI_PREF_MEMORY_LIMIT, 0);
+ }
reg = 0;
reg |= PCI_COMMAND_IO;
diff --git a/arch/arm/mach-tegra/pci.c b/arch/arm/mach-tegra/pci.c
index 5118692b1b79..7882082f1a3c 100644
--- a/arch/arm/mach-tegra/pci.c
+++ b/arch/arm/mach-tegra/pci.c
@@ -43,6 +43,8 @@
void __iomem * volatile pci_tegra_regs;
static bool pci_tegra_device_attached = false;
+static bool pci_tegra_rp0_up = false;
+static bool pci_tegra_rp1_up = false;
static unsigned int pci_tegra_powerid;
static int __init pcie_tegra_init(void);
@@ -75,6 +77,31 @@ unsigned long pci_tegra_get_base(char *aperture)
return (unsigned long)-1;
}
+static inline bool pci_tegra_is_within_rp_range(u32 bus_number, int rp)
+{
+ bool ret=false;
+ u8 primary;
+ u8 secondary;
+ u8 subordinate;
+ u32 busrange;
+
+ busrange = pci_tegra_rp_readl(PCI_PRIMARY_BUS, rp);
+
+ primary = (u8)((busrange & 0x000000ff)>>0);
+ secondary = (u8)((busrange & 0x0000ff00)>>8);
+ subordinate = (u8)((busrange & 0x00ff0000)>>16);
+
+ if (((rp==0) && pci_tegra_rp0_up) || ((rp==1) && pci_tegra_rp1_up)) {
+ if(primary != subordinate) {
+ //otherwise it's not configured
+ if((bus_number >= secondary) && (bus_number <= subordinate))
+ ret=true;
+ }
+ }
+
+ return ret;
+}
+
static int pci_tegra_read_conf(struct pci_bus *bus, u32 devfn,
int where, int size, u32 *val)
{
@@ -98,9 +125,19 @@ static int pci_tegra_read_conf(struct pci_bus *bus, u32 devfn,
v = pci_tegra_rp_readl(where & ~3, rp);
} else {
void __iomem *addr;
+ bool is_rp_firstdev;
+ bool is_valid_dev;
+
+ /*Make sure the bus falls within one of the root ports*/
+ is_valid_dev = pci_tegra_is_within_rp_range(bus->number, 0)
+ || pci_tegra_is_within_rp_range(bus->number, 1);
+ if (! is_valid_dev) goto fail;
/* Root is only attached to one device/bridge */
- if (bus->number == 1 && PCI_SLOT(devfn) != 0) goto fail;
+ is_rp_firstdev = pci_tegra_is_rp_first_dev(bus->number, 0)
+ || pci_tegra_is_rp_first_dev(bus->number, 1);
+ if (is_rp_firstdev && PCI_SLOT(devfn) != 0) goto fail;
+
addr = pci_tegra_config_addr(bus->number, devfn, where & ~3);
v = readl(addr);
}
@@ -159,8 +196,18 @@ static int pci_tegra_write_conf(struct pci_bus *bus, u32 devfn,
if (rp == 1) addr += NV_PCIE_AXI_RP_T0C1_OFFSET;
addr += where;
} else {
+ bool is_rp_firstdev;
+ bool is_valid_dev;
+
+ /*Make sure the bus falls within one of the root ports*/
+ is_valid_dev = pci_tegra_is_within_rp_range(bus->number, 0)
+ || pci_tegra_is_within_rp_range(bus->number, 1);
+ if (!is_valid_dev) goto fail;
+
/* Root is only attached to one device/bridge */
- if (bus->number == 1 && PCI_SLOT(devfn) != 0) goto fail;
+ is_rp_firstdev = pci_tegra_is_rp_first_dev(bus->number, 0)
+ || pci_tegra_is_rp_first_dev(bus->number, 1);
+ if (is_rp_firstdev && PCI_SLOT(devfn) != 0) goto fail;
addr = pci_tegra_config_addr(bus->number, devfn, where);
}
@@ -204,7 +251,8 @@ static int __init pci_tegra_setup(int nr, struct pci_sys_data *data)
u32 volatile reg;
unsigned int irq;
- if (nr != 0) return 0;
+ if ((nr == 1) && pci_tegra_device_attached) return 1;
+ else if (nr >= 1) return 0;
if (NvRmSetModuleTristate(s_hRmGlobal,
NVRM_MODULE_ID(NvRmPrivModuleID_Pcie, 0), NV_FALSE)
@@ -356,7 +404,11 @@ static int __init pci_tegra_setup(int nr, struct pci_sys_data *data)
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_rp0_up = pci_tegra_check_rp(0);
+ pci_tegra_rp1_up = pci_tegra_check_rp(1);
+
+ if(!pci_tegra_rp0_up && !pci_tegra_rp1_up)
+ {
pci_tegra_device_attached = false;
NvRmPowerVoltageControl(s_hRmGlobal, NvRmPrivModuleID_Pcie,
pci_tegra_powerid, NvRmVoltsOff, NvRmVoltsOff, NULL,
@@ -397,7 +449,7 @@ fail:
static struct pci_bus __init *pci_tegra_scan_bus(int nr,
struct pci_sys_data *sys)
{
- if (nr == 0)
+ if ((nr == 0) || (nr == 1))
return pci_scan_bus(sys->busnr, &pci_tegra_ops, sys);
return NULL;
@@ -417,7 +469,7 @@ static struct hw_pci pci_tegra_data __initdata = {
.setup = pci_tegra_setup,
.scan = pci_tegra_scan_bus,
.swizzle = pci_std_swizzle,
- .map_irq = pci_tegra_map_irq,
+ .map_irq = pci_tegra_map_irq,
};
late_initcall(pcie_tegra_init);
@@ -581,11 +633,12 @@ retry:
}
if (retry_count != 0) {
- u32 offset;
+ u32 offset=0;
/* Reset before retrying again. */
if (rp == 0) offset = AFI_PEX0_CTRL_0;
- if (rp == 1) offset = AFI_PEX1_CTRL_0;
+ else if (rp == 1) offset = AFI_PEX1_CTRL_0;
+ else BUG();
BUG_ON(AFI_PEX0_CTRL_0_PEX0_RST_L_SHIFT !=
AFI_PEX1_CTRL_0_PEX1_RST_L_SHIFT);