diff -r d3bde3445259 arch/x86_64/kernel/pci-calgary.c --- a/arch/x86_64/kernel/pci-calgary.c Sun Nov 12 14:06:55 2006 +0200 +++ b/arch/x86_64/kernel/pci-calgary.c Tue Nov 14 09:50:22 2006 +0200 @@ -50,8 +50,7 @@ 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 PCI_DEVICE_ID_IBM_CALIOC2 0x0308 /* we need these for register space address calculation */ #define START_ADDRESS 0xfe000000 @@ -326,6 +325,32 @@ static void iommu_free(struct iommu_tabl spin_unlock_irqrestore(&tbl->it_lock, flags); } +static inline struct iommu_table *find_iommu_table(struct device *dev) +{ + struct pci_dev *pdev; + struct pci_bus *pbus; + struct iommu_table *tbl; + + pdev = to_pci_dev(dev); + + /* is the device behind a bridge? */ + if (unlikely(pdev->bus->parent)) + pbus = pdev->bus->parent; + else + pbus = pdev->bus; + + tbl = pbus->self->sysdata; + BUG_ON(pdev->bus->parent && (tbl->it_busno != pdev->bus->parent->number)); + return tbl; +} + +static inline int trace_device(struct device *dev) +{ + BUG_ON(!dev); + BUG_ON(!to_pci_dev(dev)); + return (to_pci_dev(dev)->device == 0x105e); +} + static void __calgary_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, int nelems, int direction) { @@ -347,10 +372,14 @@ void calgary_unmap_sg(struct device *dev int nelems, int direction) { unsigned long flags; - struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata; + struct iommu_table *tbl = find_iommu_table(dev); if (!translate_phb(to_pci_dev(dev))) return; + + if (trace_device(dev)) + printk("Calg: unmap tbl %p list %p nelems %d dir %d\n", + tbl, sglist, nelems, direction); spin_lock_irqsave(&tbl->it_lock, flags); @@ -376,7 +405,7 @@ int calgary_map_sg(struct device *dev, s int calgary_map_sg(struct device *dev, struct scatterlist *sg, int nelems, int direction) { - struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata; + struct iommu_table *tbl = find_iommu_table(dev); unsigned long flags; unsigned long vaddr; unsigned int npages; @@ -385,6 +414,10 @@ int calgary_map_sg(struct device *dev, s if (!translate_phb(to_pci_dev(dev))) return calgary_nontranslate_map_sg(dev, sg, nelems, direction); + + if (trace_device(dev)) + printk("Calg: map tbl %p list %p nelems %d dir %d\n", + tbl, sg, nelems, direction); spin_lock_irqsave(&tbl->it_lock, flags); @@ -430,7 +463,11 @@ dma_addr_t calgary_map_single(struct dev dma_addr_t dma_handle = bad_dma_address; unsigned long uaddr; unsigned int npages; - struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata; + struct iommu_table *tbl = find_iommu_table(dev); + + if (trace_device(dev)) + printk("Calg: mapsingle tbl %p vaddr %p size %lu dir %d\n", + tbl, vaddr, size, direction); uaddr = (unsigned long)vaddr; npages = num_dma_pages(uaddr, size); @@ -446,11 +483,15 @@ void calgary_unmap_single(struct device void calgary_unmap_single(struct device *dev, dma_addr_t dma_handle, size_t size, int direction) { - struct iommu_table *tbl = to_pci_dev(dev)->bus->self->sysdata; + struct iommu_table *tbl = find_iommu_table(dev); unsigned int npages; if (!translate_phb(to_pci_dev(dev))) return; + + if (trace_device(dev)) + printk("Calg: unmapsingle tbl %p handle 0x%Lx size %lu dir %d\n", + tbl, dma_handle, size, direction); npages = num_dma_pages(dma_handle, size); iommu_free(tbl, dma_handle, npages); @@ -462,9 +503,10 @@ void* calgary_alloc_coherent(struct devi void *ret = NULL; dma_addr_t mapping; unsigned int npages, order; - struct iommu_table *tbl; - - tbl = to_pci_dev(dev)->bus->self->sysdata; + struct iommu_table *tbl = find_iommu_table(dev); + + if (trace_device(dev)) + printk("Calg: alloc tbl %p size %lu\n", tbl, size); size = PAGE_ALIGN(size); /* size rounded up to full pages */ npages = size >> PAGE_SHIFT; @@ -699,9 +741,8 @@ static int __init calgary_setup_tar(stru /* zero out all TAR bits under sw control */ val64 &= ~TAR_SW_BITS; - - tbl = dev->sysdata; table_phys = (u64)__pa(tbl->it_base); + val64 |= table_phys; BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M); @@ -817,7 +858,9 @@ static void __init calgary_enable_transl val32 = be32_to_cpu(readl(target)); val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE; - printk(KERN_INFO "Calgary: enabling translation on PHB %#x\n", busnum); + printk(KERN_INFO "Calgary: enabling translation on %s PHB %#x\n", + (dev->device == PCI_DEVICE_ID_IBM_CALGARY) ? + "Calgary" : "CalIOC2", busnum); printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this " "bus.\n"); @@ -876,6 +919,13 @@ static int __init calgary_init_one(struc BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM); bbar = busno_to_bbar(dev->bus->number); + if (!bbar) { + ret = -ENODATA; + printk(KERN_ERR "Calgary: no bbar found for bus 0x%x\n", + dev->bus->number); + goto done; + } + ret = calgary_setup_tar(dev, bbar); if (ret) goto done; @@ -917,11 +967,17 @@ static int __init calgary_locate_bbars(v target = calgary_reg(bbar, 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; + if (end_bus) { + for (bus = start_bus; bus <= end_bus; bus++) { + bus_info[bus].bbar = bbar; + bus_info[bus].phbid = phb; + } + } else { + bus_info[start_bus].bbar = bbar; + bus_info[start_bus].phbid = phb; } } } @@ -937,28 +993,41 @@ error: return ret; } +static inline int is_calgary_pci_dev(unsigned short device) +{ + int ret; + + ret = ((device == PCI_DEVICE_ID_IBM_CALGARY) || + (device == PCI_DEVICE_ID_IBM_CALIOC2)); + + return ret; +} + static int __init calgary_init(void) { int ret; struct pci_dev *dev = NULL; + void* tce_space; ret = calgary_locate_bbars(); if (ret) return ret; do { - dev = pci_get_device(PCI_VENDOR_ID_IBM, - PCI_DEVICE_ID_IBM_CALGARY, - dev); + dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev); if (!dev) break; + if (!is_calgary_pci_dev(dev->device)) + continue; if (!translate_phb(dev)) { calgary_init_one_nontraslated(dev); continue; } - if (!bus_info[dev->bus->number].tce_space && !translate_empty_slots) + tce_space = bus_info[dev->bus->number].tce_space; + if (!tce_space && !translate_empty_slots) { + printk("Calg: %p failed tce_space check\n", dev); continue; - + } ret = calgary_init_one(dev); if (ret) goto error; @@ -969,10 +1038,11 @@ error: error: do { dev = pci_find_device_reverse(PCI_VENDOR_ID_IBM, - PCI_DEVICE_ID_IBM_CALGARY, - dev); + PCI_ANY_ID, dev); if (!dev) break; + if (!is_calgary_pci_dev(dev->device)) + continue; if (!translate_phb(dev)) { pci_dev_put(dev); continue; @@ -1052,7 +1122,6 @@ static int __init build_detail_arrays(vo void __init detect_calgary(void) { - u32 val; int bus; void *tbl; int calgary_found = 0; @@ -1103,26 +1172,36 @@ void __init detect_calgary(void) for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { int dev; + u32 val; struct calgary_bus_info *info = &bus_info[bus]; - - if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) + unsigned short pci_device; + + val = read_pci_config(bus, 0, 0, 0); + pci_device = (val & 0xFFFF0000) >> 16; + + if (!is_calgary_pci_dev(pci_device)) continue; if (info->translation_disabled) continue; + printk("Calg: scanning slots of bus 0x%x\n", bus); /* * Scan the slots of the PCI bus to see if there is a device present. * The parent bus will be the zero-ith device, so start at 1. */ for (dev = 1; dev < 8; dev++) { val = read_pci_config(bus, dev, 0, 0); + printk("Calg: bus 0x%x dev 0x%x returned 0x%x\n", + bus, dev, val); if (val != 0xffffffff || translate_empty_slots) { tbl = alloc_tce_table(); if (!tbl) goto cleanup; info->tce_space = tbl; calgary_found = 1; + printk("Calg: allocated tce_table %p for bus 0x%x\n", + info->tce_space, bus); break; } }