|
@@ -41,6 +41,7 @@
|
|
#include <asm/pci-direct.h>
|
|
#include <asm/pci-direct.h>
|
|
#include <asm/system.h>
|
|
#include <asm/system.h>
|
|
#include <asm/dma.h>
|
|
#include <asm/dma.h>
|
|
|
|
+#include <asm/rio.h>
|
|
|
|
|
|
#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
|
|
#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
|
|
#define PCI_VENDOR_DEVICE_ID_CALGARY \
|
|
#define PCI_VENDOR_DEVICE_ID_CALGARY \
|
|
@@ -115,14 +116,35 @@ static const unsigned long phb_offsets[] = {
|
|
0xB000 /* PHB3 */
|
|
0xB000 /* PHB3 */
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/* PHB debug registers */
|
|
|
|
+
|
|
|
|
+static const unsigned long phb_debug_offsets[] = {
|
|
|
|
+ 0x4000 /* PHB 0 DEBUG */,
|
|
|
|
+ 0x5000 /* PHB 1 DEBUG */,
|
|
|
|
+ 0x6000 /* PHB 2 DEBUG */,
|
|
|
|
+ 0x7000 /* PHB 3 DEBUG */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * STUFF register for each debug PHB,
|
|
|
|
+ * byte 1 = start bus number, byte 2 = end bus number
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define PHB_DEBUG_STUFF_OFFSET 0x0020
|
|
|
|
+
|
|
unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
|
|
unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
|
|
static int translate_empty_slots __read_mostly = 0;
|
|
static int translate_empty_slots __read_mostly = 0;
|
|
static int calgary_detected __read_mostly = 0;
|
|
static int calgary_detected __read_mostly = 0;
|
|
|
|
|
|
|
|
+static struct rio_table_hdr *rio_table_hdr __initdata;
|
|
|
|
+static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
|
|
|
|
+static struct rio_detail *rio_devs[MAX_NUMNODES*4] __initdata;
|
|
|
|
+
|
|
struct calgary_bus_info {
|
|
struct calgary_bus_info {
|
|
void *tce_space;
|
|
void *tce_space;
|
|
unsigned char translation_disabled;
|
|
unsigned char translation_disabled;
|
|
signed char phbid;
|
|
signed char phbid;
|
|
|
|
+ void __iomem *bbar;
|
|
};
|
|
};
|
|
|
|
|
|
static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, };
|
|
static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, };
|
|
@@ -475,6 +497,11 @@ static struct dma_mapping_ops calgary_dma_ops = {
|
|
.unmap_sg = calgary_unmap_sg,
|
|
.unmap_sg = calgary_unmap_sg,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static inline void __iomem * busno_to_bbar(unsigned char num)
|
|
|
|
+{
|
|
|
|
+ return bus_info[num].bbar;
|
|
|
|
+}
|
|
|
|
+
|
|
static inline int busno_to_phbid(unsigned char num)
|
|
static inline int busno_to_phbid(unsigned char num)
|
|
{
|
|
{
|
|
return bus_info[num].phbid;
|
|
return bus_info[num].phbid;
|
|
@@ -828,31 +855,9 @@ static void __init calgary_disable_translation(struct pci_dev *dev)
|
|
del_timer_sync(&tbl->watchdog_timer);
|
|
del_timer_sync(&tbl->watchdog_timer);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline unsigned int __init locate_register_space(struct pci_dev *dev)
|
|
|
|
|
|
+static inline void __iomem * __init locate_register_space(struct pci_dev *dev)
|
|
{
|
|
{
|
|
- int rionodeid;
|
|
|
|
- u32 address;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Each Calgary has four busses. The first four busses (first Calgary)
|
|
|
|
- * have RIO node ID 2, then the next four (second Calgary) have RIO
|
|
|
|
- * node ID 3, the next four (third Calgary) have node ID 2 again, etc.
|
|
|
|
- * We use a gross hack - relying on the dev->bus->number ordering,
|
|
|
|
- * modulo 14 - to decide which Calgary a given bus is on. Busses 0, 1,
|
|
|
|
- * 2 and 4 are on the first Calgary (id 2), 6, 8, a and c are on the
|
|
|
|
- * second (id 3), and then it repeats modulo 14.
|
|
|
|
- */
|
|
|
|
- rionodeid = (dev->bus->number % 14 > 4) ? 3 : 2;
|
|
|
|
- /*
|
|
|
|
- * register space address calculation as follows:
|
|
|
|
- * FE0MB-8MB*OneBasedChassisNumber+1MB*(RioNodeId-ChassisBase)
|
|
|
|
- * ChassisBase is always zero for x366/x260/x460
|
|
|
|
- * RioNodeId is 2 for first Calgary, 3 for second Calgary
|
|
|
|
- */
|
|
|
|
- address = START_ADDRESS -
|
|
|
|
- (0x800000 * (ONE_BASED_CHASSIS_NUM + dev->bus->number / 14)) +
|
|
|
|
- (0x100000) * (rionodeid - CHASSIS_BASE);
|
|
|
|
- return address;
|
|
|
|
|
|
+ return busno_to_bbar(dev->bus->number);
|
|
}
|
|
}
|
|
|
|
|
|
static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
|
|
static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
|
|
@@ -864,15 +869,12 @@ static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
|
|
|
|
|
|
static int __init calgary_init_one(struct pci_dev *dev)
|
|
static int __init calgary_init_one(struct pci_dev *dev)
|
|
{
|
|
{
|
|
- u32 address;
|
|
|
|
void __iomem *bbar;
|
|
void __iomem *bbar;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM);
|
|
BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM);
|
|
|
|
|
|
- address = locate_register_space(dev);
|
|
|
|
- /* map entire 1MB of Calgary config space */
|
|
|
|
- bbar = ioremap_nocache(address, 1024 * 1024);
|
|
|
|
|
|
+ bbar = locate_register_space(dev);
|
|
if (!bbar) {
|
|
if (!bbar) {
|
|
ret = -ENODATA;
|
|
ret = -ENODATA;
|
|
goto done;
|
|
goto done;
|
|
@@ -898,6 +900,35 @@ static int __init calgary_init(void)
|
|
{
|
|
{
|
|
int ret = -ENODEV;
|
|
int ret = -ENODEV;
|
|
struct pci_dev *dev = NULL;
|
|
struct pci_dev *dev = NULL;
|
|
|
|
+ int rio, phb, bus;
|
|
|
|
+ void __iomem *bbar;
|
|
|
|
+ void __iomem *target;
|
|
|
|
+ u8 start_bus, end_bus;
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ for (rio = 0; rio < rio_table_hdr->num_rio_dev; rio++) {
|
|
|
|
+
|
|
|
|
+ if ( (rio_devs[rio]->type != COMPAT_CALGARY) &&
|
|
|
|
+ (rio_devs[rio]->type != ALT_CALGARY) )
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /* map entire 1MB of Calgary config space */
|
|
|
|
+ bbar = ioremap_nocache(rio_devs[rio]->BBAR, 1024 * 1024);
|
|
|
|
+
|
|
|
|
+ for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {
|
|
|
|
+
|
|
|
|
+ target = calgary_reg(bbar, phb_debug_offsets[phb] |
|
|
|
|
+ PHB_DEBUG_STUFF_OFFSET);
|
|
|
|
+ val = be32_to_cpu(readl(target));
|
|
|
|
+ start_bus = (u8)((val & 0x00FF0000) >> 16);
|
|
|
|
+ end_bus = (u8)((val & 0x0000FF00) >> 8);
|
|
|
|
+ for (bus = start_bus; bus <= end_bus; bus++) {
|
|
|
|
+ bus_info[bus].bbar = bbar;
|
|
|
|
+ bus_info[bus].phbid = phb;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
|
|
do {
|
|
do {
|
|
dev = pci_get_device(PCI_VENDOR_ID_IBM,
|
|
dev = pci_get_device(PCI_VENDOR_ID_IBM,
|
|
@@ -962,13 +993,55 @@ static inline int __init determine_tce_table_size(u64 ram)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int __init build_detail_arrays(void)
|
|
|
|
+{
|
|
|
|
+ unsigned long ptr;
|
|
|
|
+ int i, scal_detail_size, rio_detail_size;
|
|
|
|
+
|
|
|
|
+ if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){
|
|
|
|
+ printk(KERN_WARNING
|
|
|
|
+ "Calgary: MAX_NUMNODES too low! Defined as %d, "
|
|
|
|
+ "but system has %d nodes.\n",
|
|
|
|
+ MAX_NUMNODES, rio_table_hdr->num_scal_dev);
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (rio_table_hdr->version){
|
|
|
|
+ default:
|
|
|
|
+ printk(KERN_WARNING
|
|
|
|
+ "Calgary: Invalid Rio Grande Table Version: %d\n",
|
|
|
|
+ rio_table_hdr->version);
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ case 2:
|
|
|
|
+ scal_detail_size = 11;
|
|
|
|
+ rio_detail_size = 13;
|
|
|
|
+ break;
|
|
|
|
+ case 3:
|
|
|
|
+ scal_detail_size = 12;
|
|
|
|
+ rio_detail_size = 15;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ptr = ((unsigned long)rio_table_hdr) + 3;
|
|
|
|
+ for (i = 0; i < rio_table_hdr->num_scal_dev;
|
|
|
|
+ i++, ptr += scal_detail_size)
|
|
|
|
+ scal_devs[i] = (struct scal_detail *)ptr;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < rio_table_hdr->num_rio_dev;
|
|
|
|
+ i++, ptr += rio_detail_size)
|
|
|
|
+ rio_devs[i] = (struct rio_detail *)ptr;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
void __init detect_calgary(void)
|
|
void __init detect_calgary(void)
|
|
{
|
|
{
|
|
u32 val;
|
|
u32 val;
|
|
int bus;
|
|
int bus;
|
|
void *tbl;
|
|
void *tbl;
|
|
int calgary_found = 0;
|
|
int calgary_found = 0;
|
|
- int phb = -1;
|
|
|
|
|
|
+ unsigned long ptr;
|
|
|
|
+ int offset;
|
|
|
|
|
|
/*
|
|
/*
|
|
* if the user specified iommu=off or iommu=soft or we found
|
|
* if the user specified iommu=off or iommu=soft or we found
|
|
@@ -980,6 +1053,29 @@ void __init detect_calgary(void)
|
|
if (!early_pci_allowed())
|
|
if (!early_pci_allowed())
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ ptr = (unsigned long)phys_to_virt(get_bios_ebda());
|
|
|
|
+
|
|
|
|
+ rio_table_hdr = NULL;
|
|
|
|
+ offset = 0x180;
|
|
|
|
+ while (offset) {
|
|
|
|
+ /* The block id is stored in the 2nd word */
|
|
|
|
+ if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
|
|
|
|
+ /* set the pointer past the offset & block id */
|
|
|
|
+ rio_table_hdr = (struct rio_table_hdr *)(ptr+offset+4);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ /* The next offset is stored in the 1st word. 0 means no more */
|
|
|
|
+ offset = *((unsigned short *)(ptr + offset));
|
|
|
|
+ }
|
|
|
|
+ if (!rio_table_hdr){
|
|
|
|
+ printk(KERN_ERR "Calgary: Unable to locate "
|
|
|
|
+ "Rio Grande Table in EBDA - bailing!\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (build_detail_arrays())
|
|
|
|
+ return;
|
|
|
|
+
|
|
specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);
|
|
specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);
|
|
|
|
|
|
for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
|
|
for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
|
|
@@ -990,12 +1086,6 @@ void __init detect_calgary(void)
|
|
if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY)
|
|
if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- /*
|
|
|
|
- * There are 4 PHBs per Calgary chip. Set phb to which phb (0-3)
|
|
|
|
- * it is connected to releative to the clagary chip.
|
|
|
|
- */
|
|
|
|
- phb = (phb + 1) % PHBS_PER_CALGARY;
|
|
|
|
-
|
|
|
|
if (info->translation_disabled)
|
|
if (info->translation_disabled)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
@@ -1010,7 +1100,6 @@ void __init detect_calgary(void)
|
|
if (!tbl)
|
|
if (!tbl)
|
|
goto cleanup;
|
|
goto cleanup;
|
|
info->tce_space = tbl;
|
|
info->tce_space = tbl;
|
|
- info->phbid = phb;
|
|
|
|
calgary_found = 1;
|
|
calgary_found = 1;
|
|
break;
|
|
break;
|
|
}
|
|
}
|