#include <asm/dma.h>
#include <asm/rio.h>
+#ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT
+int use_calgary __read_mostly = 1;
+#else
+int use_calgary __read_mostly = 0;
+#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
+
#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
#define PCI_VENDOR_DEVICE_ID_CALGARY \
(PCI_VENDOR_ID_IBM | PCI_DEVICE_ID_IBM_CALGARY << 16)
#define PHB_DEBUG_STUFF_OFFSET 0x0020
+#define EMERGENCY_PAGES 32 /* = 128KB */
+
unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
static int translate_empty_slots __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;
+static struct rio_detail *rio_devs[MAX_NUMNODES * 4] __initdata;
struct calgary_bus_info {
void *tce_space;
{
unsigned long entry;
unsigned long badbit;
+ unsigned long badend;
+
+ /* were we called with bad_dma_address? */
+ badend = bad_dma_address + (EMERGENCY_PAGES * PAGE_SIZE);
+ if (unlikely((dma_addr >= bad_dma_address) && (dma_addr < badend))) {
+ printk(KERN_ERR "Calgary: driver tried unmapping bad DMA "
+ "address 0x%Lx\n", dma_addr);
+ WARN_ON(1);
+ return;
+ }
entry = dma_addr >> PAGE_SHIFT;
static void __init calgary_reserve_regions(struct pci_dev *dev)
{
unsigned int npages;
- void __iomem *bbar;
- unsigned char busnum;
u64 start;
struct iommu_table *tbl = dev->sysdata;
- bbar = tbl->bbar;
- busnum = dev->bus->number;
-
- /* reserve bad_dma_address in case it's a legal address */
- iommu_range_reserve(tbl, bad_dma_address, 1);
+ /* reserve EMERGENCY_PAGES from bad_dma_address and up */
+ iommu_range_reserve(tbl, bad_dma_address, EMERGENCY_PAGES);
/* avoid the BIOS/VGA first 640KB-1MB region */
start = (640 * 1024);
del_timer_sync(&tbl->watchdog_timer);
}
-static inline void __iomem * __init locate_register_space(struct pci_dev *dev)
-{
- return busno_to_bbar(dev->bus->number);
-}
-
static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
{
pci_dev_get(dev);
BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM);
- bbar = locate_register_space(dev);
- if (!bbar) {
- ret = -ENODATA;
- goto done;
- }
-
+ bbar = busno_to_bbar(dev->bus->number);
ret = calgary_setup_tar(dev, bbar);
if (ret)
- goto iounmap;
+ goto done;
pci_dev_get(dev);
dev->bus->self = dev;
return 0;
-iounmap:
- iounmap(bbar);
done:
return ret;
}
-static int __init calgary_init(void)
+static int __init calgary_locate_bbars(void)
{
- int ret = -ENODEV;
- struct pci_dev *dev = NULL;
- int rio, phb, bus;
+ int ret;
+ int rioidx, phb, bus;
void __iomem *bbar;
void __iomem *target;
+ unsigned long offset;
u8 start_bus, end_bus;
u32 val;
- for (rio = 0; rio < rio_table_hdr->num_rio_dev; rio++) {
+ ret = -ENODATA;
+ for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) {
+ struct rio_detail *rio = rio_devs[rioidx];
- if ( (rio_devs[rio]->type != COMPAT_CALGARY) &&
- (rio_devs[rio]->type != ALT_CALGARY) )
+ if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY))
continue;
/* map entire 1MB of Calgary config space */
- bbar = ioremap_nocache(rio_devs[rio]->BBAR, 1024 * 1024);
+ bbar = ioremap_nocache(rio->BBAR, 1024 * 1024);
+ if (!bbar)
+ goto error;
for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {
+ offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET;
+ target = calgary_reg(bbar, offset);
- 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);
+ end_bus = (u8)((val & 0x0000FF00) >> 8);
for (bus = start_bus; bus <= end_bus; bus++) {
bus_info[bus].bbar = bbar;
bus_info[bus].phbid = phb;
}
}
+ return 0;
+
+error:
+ /* scan bus_info and iounmap any bbars we previously ioremap'd */
+ for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++)
+ if (bus_info[bus].bbar)
+ iounmap(bus_info[bus].bbar);
+
+ return ret;
+}
+
+static int __init calgary_init(void)
+{
+ int ret;
+ struct pci_dev *dev = NULL;
+
+ ret = calgary_locate_bbars();
+ if (ret)
+ return ret;
do {
dev = pci_get_device(PCI_VENDOR_ID_IBM,
if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){
printk(KERN_WARNING
- "Calgary: MAX_NUMNODES too low! Defined as %d, "
+ "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;
scal_detail_size = 12;
rio_detail_size = 15;
break;
+ default:
+ printk(KERN_WARNING
+ "Calgary: Invalid Rio Grande Table Version: %d\n",
+ rio_table_hdr->version);
+ return -EPROTO;
}
ptr = ((unsigned long)rio_table_hdr) + 3;
void *tbl;
int calgary_found = 0;
unsigned long ptr;
- int offset;
+ unsigned int offset, prev_offset;
+ int ret;
/*
* if the user specified iommu=off or iommu=soft or we found
if (swiotlb || no_iommu || iommu_detected)
return;
+ if (!use_calgary)
+ return;
+
if (!early_pci_allowed())
return;
+ printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n");
+
ptr = (unsigned long)phys_to_virt(get_bios_ebda());
rio_table_hdr = NULL;
+ prev_offset = 0;
offset = 0x180;
- while (offset) {
+ /*
+ * The next offset is stored in the 1st word.
+ * Only parse up until the offset increases:
+ */
+ while (offset > prev_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);
+ rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4);
break;
}
- /* The next offset is stored in the 1st word. 0 means no more */
+ prev_offset = offset;
offset = *((unsigned short *)(ptr + offset));
}
- if (!rio_table_hdr){
- printk(KERN_ERR "Calgary: Unable to locate "
- "Rio Grande Table in EBDA - bailing!\n");
+ if (!rio_table_hdr) {
+ printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table "
+ "in EBDA - bailing!\n");
return;
}
- if (build_detail_arrays())
+ ret = build_detail_arrays();
+ if (ret) {
+ printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n", ret);
return;
+ }
specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);
for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
int dev;
struct calgary_bus_info *info = &bus_info[bus];
- info->phbid = -1;
if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY)
continue;
}
}
+ printk(KERN_DEBUG "Calgary: finished detection, Calgary %s\n",
+ calgary_found ? "found" : "not found");
+
if (calgary_found) {
iommu_detected = 1;
calgary_detected = 1;
}
force_iommu = 1;
+ bad_dma_address = 0x0;
dma_ops = &calgary_dma_ops;
return 0;