diff options
author | Jeremy Alves <jalves@nvidia.com> | 2010-03-03 21:06:37 -0800 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-03-09 19:11:03 -0800 |
commit | b54fb83786bf9c0807577cdb912b016a9f012f3a (patch) | |
tree | d33e9fcb70884becbee952d37266a70113ad626f | |
parent | 02259089c0deaff0f89a9c8408497c3aeccbe0dd (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.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/pci.h | 15 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pci-enum.c | 82 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pci.c | 69 |
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); |