summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/ppc64/kernel/Makefile2
-rw-r--r--arch/ppc64/kernel/iSeries_iommu.c11
-rw-r--r--arch/ppc64/kernel/iSeries_setup.c3
-rw-r--r--arch/ppc64/kernel/iommu.c21
-rw-r--r--arch/ppc64/kernel/maple_pci.c3
-rw-r--r--arch/ppc64/kernel/maple_setup.c7
-rw-r--r--arch/ppc64/kernel/pSeries_iommu.c283
-rw-r--r--arch/ppc64/kernel/pSeries_pci.c5
-rw-r--r--arch/ppc64/kernel/pSeries_setup.c5
-rw-r--r--arch/ppc64/kernel/pci.c5
-rw-r--r--arch/ppc64/kernel/pci_direct_iommu.c2
-rw-r--r--arch/ppc64/kernel/pmac_pci.c2
-rw-r--r--arch/ppc64/kernel/pmac_setup.c7
-rw-r--r--arch/ppc64/kernel/prom.c11
-rw-r--r--arch/ppc64/kernel/u3_iommu.c106
-rw-r--r--arch/ppc64/kernel/vio.c18
-rw-r--r--drivers/pci/hotplug/rpaphp_pci.c4
-rw-r--r--include/asm-ppc64/iommu.h13
-rw-r--r--include/asm-ppc64/machdep.h2
-rw-r--r--include/asm-ppc64/pci-bridge.h8
20 files changed, 266 insertions, 252 deletions
diff --git a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile
index af3b7522ca71..bc2a508a4c35 100644
--- a/arch/ppc64/kernel/Makefile
+++ b/arch/ppc64/kernel/Makefile
@@ -16,7 +16,7 @@ obj-y := setup.o entry.o traps.o irq.o idle.o dma.o \
obj-$(CONFIG_PPC_OF) += of_device.o
pci-obj-$(CONFIG_PPC_ISERIES) += iSeries_pci.o iSeries_pci_reset.o
-pci-obj-$(CONFIG_PPC_MULTIPLATFORM) += pci_dn.o pci_dma_direct.o
+pci-obj-$(CONFIG_PPC_MULTIPLATFORM) += pci_dn.o pci_direct_iommu.o
obj-$(CONFIG_PCI) += pci.o pci_iommu.o iomap.o $(pci-obj-y)
diff --git a/arch/ppc64/kernel/iSeries_iommu.c b/arch/ppc64/kernel/iSeries_iommu.c
index 4f0631860300..4e1a47c8a802 100644
--- a/arch/ppc64/kernel/iSeries_iommu.c
+++ b/arch/ppc64/kernel/iSeries_iommu.c
@@ -132,11 +132,11 @@ static void iommu_table_getparms(struct iSeries_Device_Node* dn,
if (parms->itc_size == 0)
panic("PCI_DMA: parms->size is zero, parms is 0x%p", parms);
- tbl->it_size = parms->itc_size;
+ /* itc_size is in pages worth of table, it_size is in # of entries */
+ tbl->it_size = (parms->itc_size * PAGE_SIZE) / sizeof(union tce_entry);
tbl->it_busno = parms->itc_busno;
tbl->it_offset = parms->itc_offset;
tbl->it_index = parms->itc_index;
- tbl->it_entrysize = sizeof(union tce_entry);
tbl->it_blocksize = 1;
tbl->it_type = TCE_PCI;
@@ -160,11 +160,16 @@ void iommu_devnode_init_iSeries(struct iSeries_Device_Node *dn)
kfree(tbl);
}
+static void iommu_dev_setup_iSeries(struct pci_dev *dev) { }
+static void iommu_bus_setup_iSeries(struct pci_bus *bus) { }
-void tce_init_iSeries(void)
+void iommu_init_early_iSeries(void)
{
ppc_md.tce_build = tce_build_iSeries;
ppc_md.tce_free = tce_free_iSeries;
+ ppc_md.iommu_dev_setup = iommu_dev_setup_iSeries;
+ ppc_md.iommu_bus_setup = iommu_bus_setup_iSeries;
+
pci_iommu_init();
}
diff --git a/arch/ppc64/kernel/iSeries_setup.c b/arch/ppc64/kernel/iSeries_setup.c
index b48af5ef347b..7191f57d8856 100644
--- a/arch/ppc64/kernel/iSeries_setup.c
+++ b/arch/ppc64/kernel/iSeries_setup.c
@@ -68,7 +68,6 @@ extern void hvlog(char *fmt, ...);
/* Function Prototypes */
extern void ppcdbg_initialize(void);
-extern void tce_init_iSeries(void);
static void build_iSeries_Memory_Map(void);
static void setup_iSeries_cache_sizes(void);
@@ -344,7 +343,7 @@ static void __init iSeries_parse_cmdline(void)
/*
* Initialize the DMA/TCE management
*/
- tce_init_iSeries();
+ iommu_init_early_iSeries();
/*
* Initialize the table which translate Linux physical addresses to
diff --git a/arch/ppc64/kernel/iommu.c b/arch/ppc64/kernel/iommu.c
index 7221346dc028..0f0ba6b64993 100644
--- a/arch/ppc64/kernel/iommu.c
+++ b/arch/ppc64/kernel/iommu.c
@@ -87,7 +87,7 @@ static unsigned long iommu_range_alloc(struct iommu_table *tbl,
start = largealloc ? tbl->it_largehint : tbl->it_hint;
/* Use only half of the table for small allocs (15 pages or less) */
- limit = largealloc ? tbl->it_mapsize : tbl->it_halfpoint;
+ limit = largealloc ? tbl->it_size : tbl->it_halfpoint;
if (largealloc && start < tbl->it_halfpoint)
start = tbl->it_halfpoint;
@@ -114,7 +114,7 @@ static unsigned long iommu_range_alloc(struct iommu_table *tbl,
* Second failure, rescan the other half of the table.
*/
start = (largealloc ^ pass) ? tbl->it_halfpoint : 0;
- limit = pass ? tbl->it_mapsize : limit;
+ limit = pass ? tbl->it_size : limit;
pass++;
goto again;
} else {
@@ -194,7 +194,7 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
entry = dma_addr >> PAGE_SHIFT;
free_entry = entry - tbl->it_offset;
- if (((free_entry + npages) > tbl->it_mapsize) ||
+ if (((free_entry + npages) > tbl->it_size) ||
(entry < tbl->it_offset)) {
if (printk_ratelimit()) {
printk(KERN_INFO "iommu_free: invalid entry\n");
@@ -202,7 +202,7 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
printk(KERN_INFO "\tdma_addr = 0x%lx\n", (u64)dma_addr);
printk(KERN_INFO "\tTable = 0x%lx\n", (u64)tbl);
printk(KERN_INFO "\tbus# = 0x%lx\n", (u64)tbl->it_busno);
- printk(KERN_INFO "\tmapsize = 0x%lx\n", (u64)tbl->it_mapsize);
+ printk(KERN_INFO "\tsize = 0x%lx\n", (u64)tbl->it_size);
printk(KERN_INFO "\tstartOff = 0x%lx\n", (u64)tbl->it_offset);
printk(KERN_INFO "\tindex = 0x%lx\n", (u64)tbl->it_index);
WARN_ON(1);
@@ -407,14 +407,11 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl)
unsigned long sz;
static int welcomed = 0;
- /* it_size is in pages, it_mapsize in number of entries */
- tbl->it_mapsize = (tbl->it_size << PAGE_SHIFT) / tbl->it_entrysize;
-
/* Set aside 1/4 of the table for large allocations. */
- tbl->it_halfpoint = tbl->it_mapsize * 3 / 4;
+ tbl->it_halfpoint = tbl->it_size * 3 / 4;
/* number of bytes needed for the bitmap */
- sz = (tbl->it_mapsize + 7) >> 3;
+ sz = (tbl->it_size + 7) >> 3;
tbl->it_map = (unsigned long *)__get_free_pages(GFP_ATOMIC, get_order(sz));
if (!tbl->it_map)
@@ -448,8 +445,8 @@ void iommu_free_table(struct device_node *dn)
}
/* verify that table contains no entries */
- /* it_mapsize is in entries, and we're examining 64 at a time */
- for (i = 0; i < (tbl->it_mapsize/64); i++) {
+ /* it_size is in entries, and we're examining 64 at a time */
+ for (i = 0; i < (tbl->it_size/64); i++) {
if (tbl->it_map[i] != 0) {
printk(KERN_WARNING "%s: Unexpected TCEs for %s\n",
__FUNCTION__, dn->full_name);
@@ -458,7 +455,7 @@ void iommu_free_table(struct device_node *dn)
}
/* calculate bitmap size in bytes */
- bitmap_sz = (tbl->it_mapsize + 7) / 8;
+ bitmap_sz = (tbl->it_size + 7) / 8;
/* free bitmap */
order = get_order(bitmap_sz);
diff --git a/arch/ppc64/kernel/maple_pci.c b/arch/ppc64/kernel/maple_pci.c
index bf3a3c05762f..f8cd5b495d6b 100644
--- a/arch/ppc64/kernel/maple_pci.c
+++ b/arch/ppc64/kernel/maple_pci.c
@@ -385,9 +385,6 @@ void __init maple_pcibios_fixup(void)
/* Fixup the pci_bus sysdata pointers */
pci_fix_bus_sysdata();
- /* Setup the iommu */
- iommu_setup_u3();
-
DBG(" <- maple_pcibios_fixup\n");
}
diff --git a/arch/ppc64/kernel/maple_setup.c b/arch/ppc64/kernel/maple_setup.c
index 6c8d19cc7a32..1db6ea0f336f 100644
--- a/arch/ppc64/kernel/maple_setup.c
+++ b/arch/ppc64/kernel/maple_setup.c
@@ -111,11 +111,6 @@ void __init maple_setup_arch(void)
#ifdef CONFIG_SMP
smp_ops = &maple_smp_ops;
#endif
- /* Setup the PCI DMA to "direct" by default. May be overriden
- * by iommu later on
- */
- pci_dma_init_direct();
-
/* Lookup PCI hosts */
maple_pci_init();
@@ -159,6 +154,8 @@ static void __init maple_init_early(void)
/* Setup interrupt mapping options */
ppc64_interrupt_controller = IC_OPEN_PIC;
+ iommu_init_early_u3();
+
DBG(" <- maple_init_early\n");
}
diff --git a/arch/ppc64/kernel/pSeries_iommu.c b/arch/ppc64/kernel/pSeries_iommu.c
index 2d67121e9f6f..3d625552739f 100644
--- a/arch/ppc64/kernel/pSeries_iommu.c
+++ b/arch/ppc64/kernel/pSeries_iommu.c
@@ -46,6 +46,9 @@
#include <asm/systemcfg.h>
#include "pci.h"
+#define DBG(fmt...)
+
+extern int is_python(struct device_node *);
static void tce_build_pSeries(struct iommu_table *tbl, long index,
long npages, unsigned long uaddr,
@@ -121,7 +124,7 @@ static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
}
}
-DEFINE_PER_CPU(void *, tce_page) = NULL;
+static DEFINE_PER_CPU(void *, tce_page) = NULL;
static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
long npages, unsigned long uaddr,
@@ -233,85 +236,6 @@ static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long n
}
}
-
-static void iommu_buses_init(void)
-{
- struct pci_controller *phb, *tmp;
- struct device_node *dn, *first_dn;
- int num_slots, num_slots_ilog2;
- int first_phb = 1;
- unsigned long tcetable_ilog2;
-
- /*
- * We default to a TCE table that maps 2GB (4MB table, 22 bits),
- * however some machines have a 3GB IO hole and for these we
- * create a table that maps 1GB (2MB table, 21 bits)
- */
- if (io_hole_start < 0x80000000UL)
- tcetable_ilog2 = 21;
- else
- tcetable_ilog2 = 22;
-
- /* XXX Should we be using pci_root_buses instead? -ojn
- */
-
- list_for_each_entry_safe(phb, tmp, &hose_list, list_node) {
- first_dn = ((struct device_node *)phb->arch_data)->child;
-
- /* Carve 2GB into the largest dma_window_size possible */
- for (dn = first_dn, num_slots = 0; dn != NULL; dn = dn->sibling)
- num_slots++;
- num_slots_ilog2 = __ilog2(num_slots);
-
- if ((1<<num_slots_ilog2) != num_slots)
- num_slots_ilog2++;
-
- phb->dma_window_size = 1 << (tcetable_ilog2 - num_slots_ilog2);
-
- /* Reserve 16MB of DMA space on the first PHB.
- * We should probably be more careful and use firmware props.
- * In reality this space is remapped, not lost. But we don't
- * want to get that smart to handle it -- too much work.
- */
- phb->dma_window_base_cur = first_phb ? (1 << 12) : 0;
- first_phb = 0;
-
- for (dn = first_dn; dn != NULL; dn = dn->sibling)
- iommu_devnode_init_pSeries(dn);
- }
-}
-
-
-static void iommu_buses_init_lpar(struct list_head *bus_list)
-{
- struct list_head *ln;
- struct pci_bus *bus;
- struct device_node *busdn;
- unsigned int *dma_window;
-
- for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
- bus = pci_bus_b(ln);
-
- if (bus->self)
- busdn = pci_device_to_OF_node(bus->self);
- else
- busdn = bus->sysdata; /* must be a phb */
-
- dma_window = (unsigned int *)get_property(busdn, "ibm,dma-window", NULL);
- if (dma_window) {
- /* Bussubno hasn't been copied yet.
- * Do it now because iommu_table_setparms_lpar needs it.
- */
- busdn->bussubno = bus->number;
- iommu_devnode_init_pSeries(busdn);
- }
-
- /* look for a window on a bridge even if the PHB had one */
- iommu_buses_init_lpar(&bus->children);
- }
-}
-
-
static void iommu_table_setparms(struct pci_controller *phb,
struct device_node *dn,
struct iommu_table *tbl)
@@ -336,27 +260,18 @@ static void iommu_table_setparms(struct pci_controller *phb,
tbl->it_busno = phb->bus->number;
/* Units of tce entries */
- tbl->it_offset = phb->dma_window_base_cur;
-
- /* Adjust the current table offset to the next
- * region. Measured in TCE entries. Force an
- * alignment to the size allotted per IOA. This
- * makes it easier to remove the 1st 16MB.
- */
- phb->dma_window_base_cur += (phb->dma_window_size>>3);
- phb->dma_window_base_cur &=
- ~((phb->dma_window_size>>3)-1);
-
- /* Set the tce table size - measured in pages */
- tbl->it_size = ((phb->dma_window_base_cur -
- tbl->it_offset) << 3) >> PAGE_SHIFT;
+ tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT;
/* Test if we are going over 2GB of DMA space */
- if (phb->dma_window_base_cur > (1 << 19))
+ if (phb->dma_window_base_cur + phb->dma_window_size > (1L << 31))
panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n");
+ phb->dma_window_base_cur += phb->dma_window_size;
+
+ /* Set the tce table size - measured in entries */
+ tbl->it_size = phb->dma_window_size >> PAGE_SHIFT;
+
tbl->it_index = 0;
- tbl->it_entrysize = sizeof(union tce_entry);
tbl->it_blocksize = 16;
tbl->it_type = TCE_PCI;
}
@@ -375,82 +290,174 @@ static void iommu_table_setparms(struct pci_controller *phb,
*/
static void iommu_table_setparms_lpar(struct pci_controller *phb,
struct device_node *dn,
- struct iommu_table *tbl)
+ struct iommu_table *tbl,
+ unsigned int *dma_window)
{
- unsigned int *dma_window;
-
- dma_window = (unsigned int *)get_property(dn, "ibm,dma-window", NULL);
-
if (!dma_window)
panic("iommu_table_setparms_lpar: device %s has no"
" ibm,dma-window property!\n", dn->full_name);
tbl->it_busno = dn->bussubno;
- tbl->it_size = (((((unsigned long)dma_window[4] << 32) |
- (unsigned long)dma_window[5]) >> PAGE_SHIFT) << 3) >> PAGE_SHIFT;
- tbl->it_offset = ((((unsigned long)dma_window[2] << 32) |
- (unsigned long)dma_window[3]) >> 12);
+
+ /* TODO: Parse field size properties properly. */
+ tbl->it_size = (((unsigned long)dma_window[4] << 32) |
+ (unsigned long)dma_window[5]) >> PAGE_SHIFT;
+ tbl->it_offset = (((unsigned long)dma_window[2] << 32) |
+ (unsigned long)dma_window[3]) >> PAGE_SHIFT;
tbl->it_base = 0;
tbl->it_index = dma_window[0];
- tbl->it_entrysize = sizeof(union tce_entry);
tbl->it_blocksize = 16;
tbl->it_type = TCE_PCI;
}
+static void iommu_bus_setup_pSeries(struct pci_bus *bus)
+{
+ struct device_node *dn, *pdn;
+
+ DBG("iommu_bus_setup_pSeries, bus %p, bus->self %p\n", bus, bus->self);
+
+ /* For each (root) bus, we carve up the available DMA space in 256MB
+ * pieces. Since each piece is used by one (sub) bus/device, that would
+ * give a maximum of 7 devices per PHB. In most cases, this is plenty.
+ *
+ * The exception is on Python PHBs (pre-POWER4). Here we don't have EADS
+ * bridges below the PHB to allocate the sectioned tables to, so instead
+ * we allocate a 1GB table at the PHB level.
+ */
+
+ dn = pci_bus_to_OF_node(bus);
+
+ if (!bus->self) {
+ /* Root bus */
+ if (is_python(dn)) {
+ struct iommu_table *tbl;
+
+ DBG("Python root bus %s\n", bus->name);
+
+ /* 1GB window by default */
+ dn->phb->dma_window_size = 1 << 30;
+ dn->phb->dma_window_base_cur = 0;
+
+ tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL);
+
+ iommu_table_setparms(dn->phb, dn, tbl);
+ dn->iommu_table = iommu_init_table(tbl);
+ } else {
+ /* 256 MB window by default */
+ dn->phb->dma_window_size = 1 << 28;
+ /* always skip the first 256MB */
+ dn->phb->dma_window_base_cur = 1 << 28;
+
+ /* No table at PHB level for non-python PHBs */
+ }
+ } else {
+ pdn = pci_bus_to_OF_node(bus->parent);
+
+ if (!pdn->iommu_table) {
+ struct iommu_table *tbl;
+ /* First child, allocate new table (256MB window) */
+
+ tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL);
+
+ iommu_table_setparms(dn->phb, dn, tbl);
+
+ dn->iommu_table = iommu_init_table(tbl);
+ } else {
+ /* Lower than first child or under python, copy parent table */
+ dn->iommu_table = pdn->iommu_table;
+ }
+ }
+}
+
-void iommu_devnode_init_pSeries(struct device_node *dn)
+static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus)
{
struct iommu_table *tbl;
+ struct device_node *dn, *pdn;
+ unsigned int *dma_window = NULL;
- tbl = (struct iommu_table *)kmalloc(sizeof(struct iommu_table),
- GFP_KERNEL);
-
- if (systemcfg->platform == PLATFORM_PSERIES_LPAR)
- iommu_table_setparms_lpar(dn->phb, dn, tbl);
- else
- iommu_table_setparms(dn->phb, dn, tbl);
+ dn = pci_bus_to_OF_node(bus);
+
+ /* Find nearest ibm,dma-window, walking up the device tree */
+ for (pdn = dn; pdn != NULL; pdn = pdn->parent) {
+ dma_window = (unsigned int *)get_property(pdn, "ibm,dma-window", NULL);
+ if (dma_window != NULL)
+ break;
+ }
+
+ WARN_ON(dma_window == NULL);
+
+ if (!pdn->iommu_table) {
+ /* Bussubno hasn't been copied yet.
+ * Do it now because iommu_table_setparms_lpar needs it.
+ */
+ pdn->bussubno = bus->number;
+
+ tbl = (struct iommu_table *)kmalloc(sizeof(struct iommu_table),
+ GFP_KERNEL);
- dn->iommu_table = iommu_init_table(tbl);
+ 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;
}
-void iommu_setup_pSeries(void)
+
+static void iommu_dev_setup_pSeries(struct pci_dev *dev)
{
- struct pci_dev *dev = NULL;
struct device_node *dn, *mydn;
- if (systemcfg->platform == PLATFORM_PSERIES_LPAR)
- iommu_buses_init_lpar(&pci_root_buses);
- else
- iommu_buses_init();
-
- /* Now copy the iommu_table ptr from the bus devices down to every
+ DBG("iommu_dev_setup_pSeries, dev %p (%s)\n", dev, dev->pretty_name);
+ /* Now copy the iommu_table ptr from the bus device down to the
* pci device_node. This means get_iommu_table() won't need to search
* up the device tree to find it.
*/
- for_each_pci_dev(dev) {
- mydn = dn = pci_device_to_OF_node(dev);
+ mydn = dn = pci_device_to_OF_node(dev);
- while (dn && dn->iommu_table == NULL)
- dn = dn->parent;
- if (dn)
- mydn->iommu_table = dn->iommu_table;
- }
+ while (dn && dn->iommu_table == NULL)
+ dn = dn->parent;
+
+ WARN_ON(!dn);
+
+ if (dn)
+ mydn->iommu_table = dn->iommu_table;
}
+static void iommu_bus_setup_null(struct pci_bus *b) { }
+static void iommu_dev_setup_null(struct pci_dev *d) { }
+
/* These are called very early. */
-void tce_init_pSeries(void)
+void iommu_init_early_pSeries(void)
{
- if (!(systemcfg->platform & PLATFORM_LPAR)) {
+ if (of_chosen && get_property(of_chosen, "linux,iommu-off", NULL)) {
+ /* Direct I/O, IOMMU off */
+ ppc_md.iommu_dev_setup = iommu_dev_setup_null;
+ ppc_md.iommu_bus_setup = iommu_bus_setup_null;
+ pci_direct_iommu_init();
+
+ return;
+ }
+
+ if (systemcfg->platform & PLATFORM_LPAR) {
+ if (cur_cpu_spec->firmware_features & FW_FEATURE_MULTITCE) {
+ ppc_md.tce_build = tce_buildmulti_pSeriesLP;
+ ppc_md.tce_free = tce_freemulti_pSeriesLP;
+ } else {
+ ppc_md.tce_build = tce_build_pSeriesLP;
+ ppc_md.tce_free = tce_free_pSeriesLP;
+ }
+ ppc_md.iommu_bus_setup = iommu_bus_setup_pSeriesLP;
+ } else {
ppc_md.tce_build = tce_build_pSeries;
ppc_md.tce_free = tce_free_pSeries;
- } else if (cur_cpu_spec->firmware_features & FW_FEATURE_MULTITCE) {
- ppc_md.tce_build = tce_buildmulti_pSeriesLP;
- ppc_md.tce_free = tce_freemulti_pSeriesLP;
- } else {
- ppc_md.tce_build = tce_build_pSeriesLP;
- ppc_md.tce_free = tce_free_pSeriesLP;
+ ppc_md.iommu_bus_setup = iommu_bus_setup_pSeries;
}
+ ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries;
+
pci_iommu_init();
}
diff --git a/arch/ppc64/kernel/pSeries_pci.c b/arch/ppc64/kernel/pSeries_pci.c
index 1382fa03cb22..2b9a00951f20 100644
--- a/arch/ppc64/kernel/pSeries_pci.c
+++ b/arch/ppc64/kernel/pSeries_pci.c
@@ -148,7 +148,7 @@ struct pci_ops rtas_pci_ops = {
rtas_pci_write_config
};
-static int is_python(struct device_node *dev)
+int is_python(struct device_node *dev)
{
char *model = (char *)get_property(dev, "model", NULL);
@@ -554,9 +554,6 @@ void __init pSeries_final_fixup(void)
pSeries_request_regions();
pci_fix_bus_sysdata();
- if (!of_chosen || !get_property(of_chosen, "linux,iommu-off", NULL))
- iommu_setup_pSeries();
-
pci_addr_cache_build();
}
diff --git a/arch/ppc64/kernel/pSeries_setup.c b/arch/ppc64/kernel/pSeries_setup.c
index 5005eebbf6c2..69591745eabe 100644
--- a/arch/ppc64/kernel/pSeries_setup.c
+++ b/arch/ppc64/kernel/pSeries_setup.c
@@ -379,10 +379,7 @@ static void __init pSeries_init_early(void)
}
- if (iommu_off)
- pci_dma_init_direct();
- else
- tce_init_pSeries();
+ iommu_init_early_pSeries();
pSeries_discover_pic();
diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c
index 9891dbeadf63..25822272542e 100644
--- a/arch/ppc64/kernel/pci.c
+++ b/arch/ppc64/kernel/pci.c
@@ -845,6 +845,11 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus)
pcibios_fixup_device_resources(dev, bus);
}
+ ppc_md.iommu_bus_setup(bus);
+
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ ppc_md.iommu_dev_setup(dev);
+
if (!pci_probe_only)
return;
diff --git a/arch/ppc64/kernel/pci_direct_iommu.c b/arch/ppc64/kernel/pci_direct_iommu.c
index dc96c0cf2b91..8faabca93b29 100644
--- a/arch/ppc64/kernel/pci_direct_iommu.c
+++ b/arch/ppc64/kernel/pci_direct_iommu.c
@@ -78,7 +78,7 @@ static void pci_direct_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg,
{
}
-void __init pci_dma_init_direct(void)
+void __init pci_direct_iommu_init(void)
{
pci_dma_ops.pci_alloc_consistent = pci_direct_alloc_consistent;
pci_dma_ops.pci_free_consistent = pci_direct_free_consistent;
diff --git a/arch/ppc64/kernel/pmac_pci.c b/arch/ppc64/kernel/pmac_pci.c
index 00d1db487cdf..32634216ad78 100644
--- a/arch/ppc64/kernel/pmac_pci.c
+++ b/arch/ppc64/kernel/pmac_pci.c
@@ -666,8 +666,6 @@ void __init pmac_pcibios_fixup(void)
pci_read_irq_line(dev);
pci_fix_bus_sysdata();
-
- iommu_setup_u3();
}
static void __init pmac_fixup_phb_resources(void)
diff --git a/arch/ppc64/kernel/pmac_setup.c b/arch/ppc64/kernel/pmac_setup.c
index 6f851d2d1a6d..41fa6e95a06f 100644
--- a/arch/ppc64/kernel/pmac_setup.c
+++ b/arch/ppc64/kernel/pmac_setup.c
@@ -166,11 +166,6 @@ void __init pmac_setup_arch(void)
pmac_setup_smp();
#endif
- /* Setup the PCI DMA to "direct" by default. May be overriden
- * by iommu later on
- */
- pci_dma_init_direct();
-
/* Lookup PCI hosts */
pmac_pci_init();
@@ -317,6 +312,8 @@ void __init pmac_init_early(void)
/* Setup interrupt mapping options */
ppc64_interrupt_controller = IC_OPEN_PIC;
+ iommu_init_early_u3();
+
DBG(" <- pmac_init_early\n");
}
diff --git a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c
index ca7ad16f84f9..f636c398e331 100644
--- a/arch/ppc64/kernel/prom.c
+++ b/arch/ppc64/kernel/prom.c
@@ -1743,17 +1743,6 @@ static int of_finish_dynamic_node(struct device_node *node)
node->devfn = (regs[0] >> 8) & 0xff;
}
- /* fixing up iommu_table */
-
-#ifdef CONFIG_PPC_PSERIES
- if (strcmp(node->name, "pci") == 0 &&
- get_property(node, "ibm,dma-window", NULL)) {
- node->bussubno = node->busno;
- iommu_devnode_init_pSeries(node);
- } else
- node->iommu_table = parent->iommu_table;
-#endif /* CONFIG_PPC_PSERIES */
-
out:
of_node_put(parent);
return err;
diff --git a/arch/ppc64/kernel/u3_iommu.c b/arch/ppc64/kernel/u3_iommu.c
index 051cac60b46f..b6e3bca4102d 100644
--- a/arch/ppc64/kernel/u3_iommu.c
+++ b/arch/ppc64/kernel/u3_iommu.c
@@ -91,6 +91,7 @@ static unsigned int *dart;
static unsigned int dart_emptyval;
static struct iommu_table iommu_table_u3;
+static int iommu_table_u3_inited;
static int dart_dirty;
#define DBG(...)
@@ -192,7 +193,6 @@ static int dart_init(struct device_node *dart_node)
unsigned int regword;
unsigned int i;
unsigned long tmp;
- struct page *p;
if (dart_tablebase == 0 || dart_tablesize == 0) {
printk(KERN_INFO "U3-DART: table not allocated, using direct DMA\n");
@@ -209,16 +209,15 @@ static int dart_init(struct device_node *dart_node)
* that to work around what looks like a problem with the HT bridge
* prefetching into invalid pages and corrupting data
*/
- tmp = __get_free_pages(GFP_ATOMIC, 1);
- if (tmp == 0)
- panic("U3-DART: Cannot allocate spare page !");
- dart_emptyval = DARTMAP_VALID |
- ((virt_to_abs(tmp) >> PAGE_SHIFT) & DARTMAP_RPNMASK);
+ tmp = lmb_alloc(PAGE_SIZE, PAGE_SIZE);
+ if (!tmp)
+ panic("U3-DART: Cannot allocate spare page!");
+ dart_emptyval = DARTMAP_VALID | ((tmp >> PAGE_SHIFT) & DARTMAP_RPNMASK);
/* Map in DART registers. FIXME: Use device node to get base address */
dart = ioremap(DART_BASE, 0x7000);
if (dart == NULL)
- panic("U3-DART: Cannot map registers !");
+ panic("U3-DART: Cannot map registers!");
/* Set initial control register contents: table base,
* table size and enable bit
@@ -227,7 +226,6 @@ static int dart_init(struct device_node *dart_node)
((dart_tablebase >> PAGE_SHIFT) << DARTCNTL_BASE_SHIFT) |
(((dart_tablesize >> PAGE_SHIFT) & DARTCNTL_SIZE_MASK)
<< DARTCNTL_SIZE_SHIFT);
- p = virt_to_page(dart_tablebase);
dart_vbase = ioremap(virt_to_abs(dart_tablebase), dart_tablesize);
/* Fill initial table */
@@ -240,35 +238,67 @@ static int dart_init(struct device_node *dart_node)
/* Invalidate DART to get rid of possible stale TLBs */
dart_tlb_invalidate_all();
+ printk(KERN_INFO "U3/CPC925 DART IOMMU initialized\n");
+
+ return 0;
+}
+
+static void iommu_table_u3_setup(void)
+{
iommu_table_u3.it_busno = 0;
-
- /* Units of tce entries */
iommu_table_u3.it_offset = 0;
-
- /* Set the tce table size - measured in pages */
- iommu_table_u3.it_size = dart_tablesize >> PAGE_SHIFT;
+ /* it_size is in number of entries */
+ iommu_table_u3.it_size = dart_tablesize / sizeof(u32);
/* Initialize the common IOMMU code */
iommu_table_u3.it_base = (unsigned long)dart_vbase;
iommu_table_u3.it_index = 0;
iommu_table_u3.it_blocksize = 1;
- iommu_table_u3.it_entrysize = sizeof(u32);
iommu_init_table(&iommu_table_u3);
/* Reserve the last page of the DART to avoid possible prefetch
* past the DART mapped area
*/
- set_bit(iommu_table_u3.it_mapsize - 1, iommu_table_u3.it_map);
+ set_bit(iommu_table_u3.it_size - 1, iommu_table_u3.it_map);
+}
- printk(KERN_INFO "U3/CPC925 DART IOMMU initialized\n");
+static void iommu_dev_setup_u3(struct pci_dev *dev)
+{
+ struct device_node *dn;
- return 0;
+ /* We only have one iommu table on the mac for now, which makes
+ * things simple. Setup all PCI devices to point to this table
+ *
+ * We must use pci_device_to_OF_node() to make sure that
+ * we get the real "final" pointer to the device in the
+ * pci_dev sysdata and not the temporary PHB one
+ */
+ dn = pci_device_to_OF_node(dev);
+
+ if (dn)
+ dn->iommu_table = &iommu_table_u3;
}
-void iommu_setup_u3(void)
+static void iommu_bus_setup_u3(struct pci_bus *bus)
+{
+ struct device_node *dn;
+
+ if (!iommu_table_u3_inited) {
+ iommu_table_u3_inited = 1;
+ iommu_table_u3_setup();
+ }
+
+ dn = pci_bus_to_OF_node(bus);
+
+ if (dn)
+ dn->iommu_table = &iommu_table_u3;
+}
+
+static void iommu_dev_setup_null(struct pci_dev *dev) { }
+static void iommu_bus_setup_null(struct pci_bus *bus) { }
+
+void iommu_init_early_u3(void)
{
- struct pci_controller *phb, *tmp;
- struct pci_dev *dev = NULL;
struct device_node *dn;
/* Find the DART in the device-tree */
@@ -282,31 +312,23 @@ void iommu_setup_u3(void)
ppc_md.tce_flush = dart_flush;
/* Initialize the DART HW */
- if (dart_init(dn))
- return;
-
- /* Setup pci_dma ops */
- pci_iommu_init();
-
- /* We only have one iommu table on the mac for now, which makes
- * things simple. Setup all PCI devices to point to this table
- */
- for_each_pci_dev(dev) {
- /* We must use pci_device_to_OF_node() to make sure that
- * we get the real "final" pointer to the device in the
- * pci_dev sysdata and not the temporary PHB one
- */
- struct device_node *dn = pci_device_to_OF_node(dev);
- if (dn)
- dn->iommu_table = &iommu_table_u3;
- }
- /* We also make sure we set all PHBs ... */
- list_for_each_entry_safe(phb, tmp, &hose_list, list_node) {
- dn = (struct device_node *)phb->arch_data;
- dn->iommu_table = &iommu_table_u3;
+ if (dart_init(dn)) {
+ /* If init failed, use direct iommu and null setup functions */
+ ppc_md.iommu_dev_setup = iommu_dev_setup_null;
+ ppc_md.iommu_bus_setup = iommu_bus_setup_null;
+
+ /* Setup pci_dma ops */
+ pci_direct_iommu_init();
+ } else {
+ ppc_md.iommu_dev_setup = iommu_dev_setup_u3;
+ ppc_md.iommu_bus_setup = iommu_bus_setup_u3;
+
+ /* Setup pci_dma ops */
+ pci_iommu_init();
}
}
+
void __init alloc_u3_dart_table(void)
{
/* Only reserve DART space if machine has more than 2GB of RAM
diff --git a/arch/ppc64/kernel/vio.c b/arch/ppc64/kernel/vio.c
index ed20451775b6..8adddd7ca1ad 100644
--- a/arch/ppc64/kernel/vio.c
+++ b/arch/ppc64/kernel/vio.c
@@ -158,6 +158,7 @@ void __init iommu_vio_init(void)
struct iommu_table *t;
struct iommu_table_cb cb;
unsigned long cbp;
+ unsigned long itc_entries;
cb.itc_busno = 255; /* Bus 255 is the virtual bus */
cb.itc_virtbus = 0xff; /* Ask for virtual bus */
@@ -165,12 +166,12 @@ void __init iommu_vio_init(void)
cbp = virt_to_abs(&cb);
HvCallXm_getTceTableParms(cbp);
- veth_iommu_table.it_size = cb.itc_size / 2;
+ itc_entries = cb.itc_size * PAGE_SIZE / sizeof(union tce_entry);
+ veth_iommu_table.it_size = itc_entries / 2;
veth_iommu_table.it_busno = cb.itc_busno;
veth_iommu_table.it_offset = cb.itc_offset;
veth_iommu_table.it_index = cb.itc_index;
veth_iommu_table.it_type = TCE_VB;
- veth_iommu_table.it_entrysize = sizeof(union tce_entry);
veth_iommu_table.it_blocksize = 1;
t = iommu_init_table(&veth_iommu_table);
@@ -178,13 +179,12 @@ void __init iommu_vio_init(void)
if (!t)
printk("Virtual Bus VETH TCE table failed.\n");
- vio_iommu_table.it_size = cb.itc_size - veth_iommu_table.it_size;
+ vio_iommu_table.it_size = itc_entries - veth_iommu_table.it_size;
vio_iommu_table.it_busno = cb.itc_busno;
vio_iommu_table.it_offset = cb.itc_offset +
- veth_iommu_table.it_size * (PAGE_SIZE/sizeof(union tce_entry));
+ veth_iommu_table.it_size;
vio_iommu_table.it_index = cb.itc_index;
vio_iommu_table.it_type = TCE_VB;
- vio_iommu_table.it_entrysize = sizeof(union tce_entry);
vio_iommu_table.it_blocksize = 1;
t = iommu_init_table(&vio_iommu_table);
@@ -511,7 +511,6 @@ static struct iommu_table * vio_build_iommu_table(struct vio_dev *dev)
unsigned int *dma_window;
struct iommu_table *newTceTable;
unsigned long offset;
- unsigned long size;
int dma_window_property_size;
dma_window = (unsigned int *) get_property(dev->dev.platform_data, "ibm,my-dma-window", &dma_window_property_size);
@@ -521,21 +520,18 @@ static struct iommu_table * vio_build_iommu_table(struct vio_dev *dev)
newTceTable = (struct iommu_table *) kmalloc(sizeof(struct iommu_table), GFP_KERNEL);
- size = ((dma_window[4] >> PAGE_SHIFT) << 3) >> PAGE_SHIFT;
-
/* There should be some code to extract the phys-encoded offset
using prom_n_addr_cells(). However, according to a comment
on earlier versions, it's always zero, so we don't bother */
offset = dma_window[1] >> PAGE_SHIFT;
- /* TCE table size - measured in units of pages of tce table */
- newTceTable->it_size = size;
+ /* TCE table size - measured in tce entries */
+ newTceTable->it_size = dma_window[4] >> PAGE_SHIFT;
/* offset for VIO should always be 0 */
newTceTable->it_offset = offset;
newTceTable->it_busno = 0;
newTceTable->it_index = (unsigned long)dma_window[0];
newTceTable->it_type = TCE_VB;
- newTceTable->it_entrysize = sizeof(union tce_entry);
return iommu_init_table(newTceTable);
}
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
index 002c91fff205..9e81bdedca5a 100644
--- a/drivers/pci/hotplug/rpaphp_pci.c
+++ b/drivers/pci/hotplug/rpaphp_pci.c
@@ -25,6 +25,7 @@
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#include <asm/rtas.h>
+#include <asm/machdep.h>
#include "../pci.h" /* for pci_add_new_bus */
#include "rpaphp.h"
@@ -168,6 +169,9 @@ rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus)
if (list_empty(&dev->global_list)) {
int i;
+ /* Need to setup IOMMU tables */
+ ppc_md.iommu_dev_setup(dev);
+
if(fix_bus)
pcibios_fixup_device_resources(dev, bus);
pci_read_irq_line(dev);
diff --git a/include/asm-ppc64/iommu.h b/include/asm-ppc64/iommu.h
index aea9a3b5f23f..19f57ab59641 100644
--- a/include/asm-ppc64/iommu.h
+++ b/include/asm-ppc64/iommu.h
@@ -69,18 +69,16 @@ union tce_entry {
struct iommu_table {
unsigned long it_busno; /* Bus number this table belongs to */
- unsigned long it_size; /* Size in pages of iommu table */
+ unsigned long it_size; /* Size of iommu table in entries */
unsigned long it_offset; /* Offset into global table */
unsigned long it_base; /* mapped address of tce table */
unsigned long it_index; /* which iommu table this is */
unsigned long it_type; /* type: PCI or Virtual Bus */
- unsigned long it_entrysize; /* Size of an entry in bytes */
unsigned long it_blocksize; /* Entries in each block (cacheline) */
unsigned long it_hint; /* Hint for next alloc */
unsigned long it_largehint; /* Hint for large allocs */
unsigned long it_halfpoint; /* Breaking point for small/large allocs */
spinlock_t it_lock; /* Protects it_map */
- unsigned long it_mapsize; /* Size of map in # of entries (bits) */
unsigned long *it_map; /* A simple allocation bitmap for now */
};
@@ -156,14 +154,13 @@ extern dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr,
extern void iommu_unmap_single(struct iommu_table *tbl, dma_addr_t dma_handle,
size_t size, enum dma_data_direction direction);
-extern void tce_init_pSeries(void);
-extern void tce_init_iSeries(void);
+extern void iommu_init_early_pSeries(void);
+extern void iommu_init_early_iSeries(void);
+extern void iommu_init_early_u3(void);
extern void pci_iommu_init(void);
-extern void pci_dma_init_direct(void);
+extern void pci_direct_iommu_init(void);
extern void alloc_u3_dart_table(void);
-extern int ppc64_iommu_off;
-
#endif /* _ASM_IOMMU_H */
diff --git a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h
index 1dd70a419b95..ae1c4fd4a7cf 100644
--- a/include/asm-ppc64/machdep.h
+++ b/include/asm-ppc64/machdep.h
@@ -70,6 +70,8 @@ struct machdep_calls {
long index,
long npages);
void (*tce_flush)(struct iommu_table *tbl);
+ void (*iommu_dev_setup)(struct pci_dev *dev);
+ void (*iommu_bus_setup)(struct pci_bus *bus);
int (*probe)(int platform);
void (*setup_arch)(void);
diff --git a/include/asm-ppc64/pci-bridge.h b/include/asm-ppc64/pci-bridge.h
index 428cc1947d95..51cfa8b72812 100644
--- a/include/asm-ppc64/pci-bridge.h
+++ b/include/asm-ppc64/pci-bridge.h
@@ -79,6 +79,14 @@ static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev)
return fetch_dev_dn(dev);
}
+static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
+{
+ if (bus->self)
+ return pci_device_to_OF_node(bus->self);
+ else
+ return bus->sysdata; /* Must be root bus (PHB) */
+}
+
extern void pci_process_bridge_OF_ranges(struct pci_controller *hose,
struct device_node *dev);