From: olof@austin.ibm.com (Olof Johansson) Here's a fix to deal with p630 systems in LPAR mode. They're to date the only system that in some cases might lack a dma-window property for the bus, but contain an overriding property in the device node for the specific adapter/slot. This makes the device setup code a bit more complex since it needs to do some of the things that the bus setup code has already done. Signed-off-by: Olof Johansson Signed-off-by: Andrew Morton --- 25-akpm/arch/ppc64/kernel/pSeries_iommu.c | 55 +++++++++++++++++++++++++++++- 1 files changed, 54 insertions(+), 1 deletion(-) diff -puN arch/ppc64/kernel/pSeries_iommu.c~ppc64-fix-lpar-iommu-setup-code-for-p630 arch/ppc64/kernel/pSeries_iommu.c --- 25/arch/ppc64/kernel/pSeries_iommu.c~ppc64-fix-lpar-iommu-setup-code-for-p630 Thu Mar 24 15:33:20 2005 +++ 25-akpm/arch/ppc64/kernel/pSeries_iommu.c Thu Mar 24 15:33:20 2005 @@ -402,6 +402,8 @@ static void iommu_bus_setup_pSeriesLP(st struct device_node *dn, *pdn; unsigned int *dma_window = NULL; + DBG("iommu_bus_setup_pSeriesLP, bus %p, bus->self %p\n", bus, bus->self); + dn = pci_bus_to_OF_node(bus); /* Find nearest ibm,dma-window, walking up the device tree */ @@ -478,6 +480,56 @@ static struct notifier_block iommu_recon .notifier_call = iommu_reconfig_notifier, }; +static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) +{ + struct device_node *pdn, *dn; + struct iommu_table *tbl; + int *dma_window = NULL; + + DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, dev->pretty_name); + + /* dev setup for LPAR is a little tricky, since the device tree might + * contain the dma-window properties per-device and not neccesarily + * for the bus. So we need to search upwards in the tree until we + * either hit a dma-window property, OR find a parent with a table + * already allocated. + */ + dn = pci_device_to_OF_node(dev); + + for (pdn = dn; pdn && !pdn->iommu_table; pdn = pdn->parent) { + dma_window = (unsigned int *)get_property(pdn, "ibm,dma-window", NULL); + if (dma_window) + break; + } + + /* Check for parent == NULL so we don't try to setup the empty EADS + * slots on POWER4 machines. + */ + if (dma_window == NULL || pdn->parent == NULL) { + /* Fall back to regular (non-LPAR) dev setup */ + DBG("No dma window for device, falling back to regular setup\n"); + iommu_dev_setup_pSeries(dev); + return; + } else { + DBG("Found DMA window, allocating table\n"); + } + + if (!pdn->iommu_table) { + /* iommu_table_setparms_lpar needs bussubno. */ + pdn->bussubno = pdn->phb->bus->number; + + tbl = (struct iommu_table *)kmalloc(sizeof(struct iommu_table), + GFP_KERNEL); + + iommu_table_setparms_lpar(pdn->phb, pdn, tbl, dma_window); + + pdn->iommu_table = iommu_init_table(tbl); + } + + if (pdn != dn) + dn->iommu_table = pdn->iommu_table; +} + static void iommu_bus_setup_null(struct pci_bus *b) { } static void iommu_dev_setup_null(struct pci_dev *d) { } @@ -502,13 +554,14 @@ void iommu_init_early_pSeries(void) ppc_md.tce_free = tce_free_pSeriesLP; } ppc_md.iommu_bus_setup = iommu_bus_setup_pSeriesLP; + ppc_md.iommu_dev_setup = iommu_dev_setup_pSeriesLP; } else { ppc_md.tce_build = tce_build_pSeries; ppc_md.tce_free = tce_free_pSeries; ppc_md.iommu_bus_setup = iommu_bus_setup_pSeries; + ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries; } - ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries; pSeries_reconfig_notifier_register(&iommu_reconfig_nb); _