aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pseries/iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/pseries/iommu.c')
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c226
1 files changed, 72 insertions, 154 deletions
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 0307901e413..33b552ffbe5 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -52,7 +52,7 @@
static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
- u64 *startp, u64 *endp)
+ __be64 *startp, __be64 *endp)
{
u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index;
unsigned long start, end, inc;
@@ -86,7 +86,7 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index,
struct dma_attrs *attrs)
{
u64 proto_tce;
- u64 *tcep, *tces;
+ __be64 *tcep, *tces;
u64 rpn;
proto_tce = TCE_PCI_READ; // Read allowed
@@ -94,12 +94,12 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index,
if (direction != DMA_TO_DEVICE)
proto_tce |= TCE_PCI_WRITE;
- tces = tcep = ((u64 *)tbl->it_base) + index;
+ tces = tcep = ((__be64 *)tbl->it_base) + index;
while (npages--) {
/* can't move this out since we might cross MEMBLOCK boundary */
rpn = __pa(uaddr) >> TCE_SHIFT;
- *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
+ *tcep = cpu_to_be64(proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT);
uaddr += TCE_PAGE_SIZE;
tcep++;
@@ -113,9 +113,9 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index,
static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages)
{
- u64 *tcep, *tces;
+ __be64 *tcep, *tces;
- tces = tcep = ((u64 *)tbl->it_base) + index;
+ tces = tcep = ((__be64 *)tbl->it_base) + index;
while (npages--)
*(tcep++) = 0;
@@ -126,11 +126,11 @@ static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages)
static unsigned long tce_get_pseries(struct iommu_table *tbl, long index)
{
- u64 *tcep;
+ __be64 *tcep;
- tcep = ((u64 *)tbl->it_base) + index;
+ tcep = ((__be64 *)tbl->it_base) + index;
- return *tcep;
+ return be64_to_cpu(*tcep);
}
static void tce_free_pSeriesLP(struct iommu_table*, long, long);
@@ -177,7 +177,7 @@ static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
return ret;
}
-static DEFINE_PER_CPU(u64 *, tce_page);
+static DEFINE_PER_CPU(__be64 *, tce_page);
static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
long npages, unsigned long uaddr,
@@ -186,7 +186,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
{
u64 rc = 0;
u64 proto_tce;
- u64 *tcep;
+ __be64 *tcep;
u64 rpn;
long l, limit;
long tcenum_start = tcenum, npages_start = npages;
@@ -206,7 +206,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
* from iommu_alloc{,_sg}()
*/
if (!tcep) {
- tcep = (u64 *)__get_free_page(GFP_ATOMIC);
+ tcep = (__be64 *)__get_free_page(GFP_ATOMIC);
/* If allocation fails, fall back to the loop implementation */
if (!tcep) {
local_irq_restore(flags);
@@ -230,7 +230,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
limit = min_t(long, npages, 4096/TCE_ENTRY_SIZE);
for (l = 0; l < limit; l++) {
- tcep[l] = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
+ tcep[l] = cpu_to_be64(proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT);
rpn++;
}
@@ -329,16 +329,16 @@ struct direct_window {
/* Dynamic DMA Window support */
struct ddw_query_response {
- u32 windows_available;
- u32 largest_available_block;
- u32 page_size;
- u32 migration_capable;
+ __be32 windows_available;
+ __be32 largest_available_block;
+ __be32 page_size;
+ __be32 migration_capable;
};
struct ddw_create_response {
- u32 liobn;
- u32 addr_hi;
- u32 addr_lo;
+ __be32 liobn;
+ __be32 addr_hi;
+ __be32 addr_lo;
};
static LIST_HEAD(direct_window_list);
@@ -392,7 +392,8 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
unsigned long num_pfn, const void *arg)
{
const struct dynamic_dma_window_prop *maprange = arg;
- u64 *tcep, tce_size, num_tce, dma_offset, next, proto_tce, liobn;
+ u64 tce_size, num_tce, dma_offset, next, proto_tce, liobn;
+ __be64 *tcep;
u32 tce_shift;
u64 rc = 0;
long l, limit;
@@ -401,7 +402,7 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
tcep = __get_cpu_var(tce_page);
if (!tcep) {
- tcep = (u64 *)__get_free_page(GFP_ATOMIC);
+ tcep = (__be64 *)__get_free_page(GFP_ATOMIC);
if (!tcep) {
local_irq_enable();
return -ENOMEM;
@@ -435,7 +436,7 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
dma_offset = next + be64_to_cpu(maprange->dma_base);
for (l = 0; l < limit; l++) {
- tcep[l] = proto_tce | next;
+ tcep[l] = cpu_to_be64(proto_tce | next);
next += tce_size;
}
@@ -485,9 +486,10 @@ static void iommu_table_setparms(struct pci_controller *phb,
memset((void *)tbl->it_base, 0, *sizep);
tbl->it_busno = phb->bus->number;
+ tbl->it_page_shift = IOMMU_PAGE_SHIFT_4K;
/* Units of tce entries */
- tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT;
+ tbl->it_offset = phb->dma_window_base_cur >> tbl->it_page_shift;
/* Test if we are going over 2GB of DMA space */
if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) {
@@ -498,7 +500,7 @@ static void iommu_table_setparms(struct pci_controller *phb,
phb->dma_window_base_cur += phb->dma_window_size;
/* Set the tce table size - measured in entries */
- tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT;
+ tbl->it_size = phb->dma_window_size >> tbl->it_page_shift;
tbl->it_index = 0;
tbl->it_blocksize = 16;
@@ -536,11 +538,12 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb,
of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size);
tbl->it_busno = phb->bus->number;
+ tbl->it_page_shift = IOMMU_PAGE_SHIFT_4K;
tbl->it_base = 0;
tbl->it_blocksize = 16;
tbl->it_type = TCE_PCI;
- tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
- tbl->it_size = size >> IOMMU_PAGE_SHIFT;
+ tbl->it_offset = offset >> tbl->it_page_shift;
+ tbl->it_size = size >> tbl->it_page_shift;
}
static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
@@ -686,7 +689,8 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
iommu_table_setparms(phb, dn, tbl);
PCI_DN(dn)->iommu_table = iommu_init_table(tbl, phb->node);
iommu_register_group(tbl, pci_domain_nr(phb->bus), 0);
- set_iommu_table_base(&dev->dev, PCI_DN(dn)->iommu_table);
+ set_iommu_table_base_and_group(&dev->dev,
+ PCI_DN(dn)->iommu_table);
return;
}
@@ -698,7 +702,8 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
dn = dn->parent;
if (dn && PCI_DN(dn))
- set_iommu_table_base(&dev->dev, PCI_DN(dn)->iommu_table);
+ set_iommu_table_base_and_group(&dev->dev,
+ PCI_DN(dn)->iommu_table);
else
printk(KERN_WARNING "iommu: Device %s has no iommu table\n",
pci_name(dev));
@@ -716,21 +721,6 @@ static int __init disable_ddw_setup(char *str)
early_param("disable_ddw", disable_ddw_setup);
-static inline void __remove_ddw(struct device_node *np, const u32 *ddw_avail, u64 liobn)
-{
- int ret;
-
- ret = rtas_call(ddw_avail[2], 1, 1, NULL, liobn);
- if (ret)
- pr_warning("%s: failed to remove DMA window: rtas returned "
- "%d to ibm,remove-pe-dma-window(%x) %llx\n",
- np->full_name, ret, ddw_avail[2], liobn);
- else
- pr_debug("%s: successfully removed DMA window: rtas returned "
- "%d to ibm,remove-pe-dma-window(%x) %llx\n",
- np->full_name, ret, ddw_avail[2], liobn);
-}
-
static void remove_ddw(struct device_node *np)
{
struct dynamic_dma_window_prop *dwp;
@@ -760,7 +750,15 @@ static void remove_ddw(struct device_node *np)
pr_debug("%s successfully cleared tces in window.\n",
np->full_name);
- __remove_ddw(np, ddw_avail, liobn);
+ ret = rtas_call(ddw_avail[2], 1, 1, NULL, liobn);
+ if (ret)
+ pr_warning("%s: failed to remove direct window: rtas returned "
+ "%d to ibm,remove-pe-dma-window(%x) %llx\n",
+ np->full_name, ret, ddw_avail[2], liobn);
+ else
+ pr_debug("%s: successfully removed direct window: rtas returned "
+ "%d to ibm,remove-pe-dma-window(%x) %llx\n",
+ np->full_name, ret, ddw_avail[2], liobn);
delprop:
ret = of_remove_property(np, win64);
@@ -780,7 +778,7 @@ static u64 find_existing_ddw(struct device_node *pdn)
list_for_each_entry(window, &direct_window_list, list) {
if (window->device == pdn) {
direct64 = window->prop;
- dma_addr = direct64->dma_base;
+ dma_addr = be64_to_cpu(direct64->dma_base);
break;
}
}
@@ -789,68 +787,33 @@ static u64 find_existing_ddw(struct device_node *pdn)
return dma_addr;
}
-static void __restore_default_window(struct eeh_dev *edev,
- u32 ddw_restore_token)
-{
- u32 cfg_addr;
- u64 buid;
- int ret;
-
- /*
- * Get the config address and phb buid of the PE window.
- * Rely on eeh to retrieve this for us.
- * Retrieve them from the pci device, not the node with the
- * dma-window property
- */
- cfg_addr = edev->config_addr;
- if (edev->pe_config_addr)
- cfg_addr = edev->pe_config_addr;
- buid = edev->phb->buid;
-
- do {
- ret = rtas_call(ddw_restore_token, 3, 1, NULL, cfg_addr,
- BUID_HI(buid), BUID_LO(buid));
- } while (rtas_busy_delay(ret));
- pr_info("ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n",
- ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret);
-}
-
static int find_existing_ddw_windows(void)
{
+ int len;
struct device_node *pdn;
+ struct direct_window *window;
const struct dynamic_dma_window_prop *direct64;
- const u32 *ddw_extensions;
if (!firmware_has_feature(FW_FEATURE_LPAR))
return 0;
for_each_node_with_property(pdn, DIRECT64_PROPNAME) {
- direct64 = of_get_property(pdn, DIRECT64_PROPNAME, NULL);
+ direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len);
if (!direct64)
continue;
- /*
- * We need to ensure the IOMMU table is active when we
- * return from the IOMMU setup so that the common code
- * can clear the table or find the holes. To that end,
- * first, remove any existing DDW configuration.
- */
- remove_ddw(pdn);
+ window = kzalloc(sizeof(*window), GFP_KERNEL);
+ if (!window || len < sizeof(struct dynamic_dma_window_prop)) {
+ kfree(window);
+ remove_ddw(pdn);
+ continue;
+ }
- /*
- * Second, if we are running on a new enough level of
- * firmware where the restore API is present, use it to
- * restore the 32-bit window, which was removed in
- * create_ddw.
- * If the API is not present, then create_ddw couldn't
- * have removed the 32-bit window in the first place, so
- * removing the DDW configuration should be sufficient.
- */
- ddw_extensions = of_get_property(pdn, "ibm,ddw-extensions",
- NULL);
- if (ddw_extensions && ddw_extensions[0] > 0)
- __restore_default_window(of_node_to_eeh_dev(pdn),
- ddw_extensions[1]);
+ window->device = pdn;
+ window->prop = direct64;
+ spin_lock(&direct_window_list_lock);
+ list_add(&window->list, &direct_window_list);
+ spin_unlock(&direct_window_list_lock);
}
return 0;
@@ -920,12 +883,6 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail,
return ret;
}
-static void restore_default_window(struct pci_dev *dev,
- u32 ddw_restore_token)
-{
- __restore_default_window(pci_dev_to_eeh_dev(dev), ddw_restore_token);
-}
-
struct failed_ddw_pdn {
struct device_node *pdn;
struct list_head list;
@@ -953,13 +910,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
u64 dma_addr, max_addr;
struct device_node *dn;
const u32 *uninitialized_var(ddw_avail);
- const u32 *uninitialized_var(ddw_extensions);
- u32 ddw_restore_token = 0;
struct direct_window *window;
struct property *win64;
struct dynamic_dma_window_prop *ddwprop;
- const void *dma_window = NULL;
- unsigned long liobn, offset, size;
struct failed_ddw_pdn *fpdn;
mutex_lock(&direct_window_init_mutex);
@@ -990,42 +943,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
*/
ddw_avail = of_get_property(pdn, "ibm,ddw-applicable", &len);
if (!ddw_avail || len < 3 * sizeof(u32))
- goto out_unlock;
-
- /*
- * the extensions property is only required to exist in certain
- * levels of firmware and later
- * the ibm,ddw-extensions property is a list with the first
- * element containing the number of extensions and each
- * subsequent entry is a value corresponding to that extension
- */
- ddw_extensions = of_get_property(pdn, "ibm,ddw-extensions", &len);
- if (ddw_extensions) {
- /*
- * each new defined extension length should be added to
- * the top of the switch so the "earlier" entries also
- * get picked up
- */
- switch (ddw_extensions[0]) {
- /* ibm,reset-pe-dma-windows */
- case 1:
- ddw_restore_token = ddw_extensions[1];
- break;
- }
- }
-
- /*
- * Only remove the existing DMA window if we can restore back to
- * the default state. Removing the existing window maximizes the
- * resources available to firmware for dynamic window creation.
- */
- if (ddw_restore_token) {
- dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
- of_parse_dma_window(pdn, dma_window, &liobn, &offset, &size);
- __remove_ddw(pdn, ddw_avail, liobn);
- }
+ goto out_failed;
- /*
+ /*
* Query if there is a second window of size to map the
* whole partition. Query returns number of windows, largest
* block assigned to PE (partition endpoint), and two bitmasks
@@ -1034,7 +954,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
dn = pci_device_to_OF_node(dev);
ret = query_ddw(dev, ddw_avail, &query);
if (ret != 0)
- goto out_restore_window;
+ goto out_failed;
if (query.windows_available == 0) {
/*
@@ -1043,34 +963,34 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
* trading in for a larger page size.
*/
dev_dbg(&dev->dev, "no free dynamic windows");
- goto out_restore_window;
+ goto out_failed;
}
- if (query.page_size & 4) {
+ if (be32_to_cpu(query.page_size) & 4) {
page_shift = 24; /* 16MB */
- } else if (query.page_size & 2) {
+ } else if (be32_to_cpu(query.page_size) & 2) {
page_shift = 16; /* 64kB */
- } else if (query.page_size & 1) {
+ } else if (be32_to_cpu(query.page_size) & 1) {
page_shift = 12; /* 4kB */
} else {
dev_dbg(&dev->dev, "no supported direct page size in mask %x",
query.page_size);
- goto out_restore_window;
+ goto out_failed;
}
/* verify the window * number of ptes will map the partition */
/* check largest block * page size > max memory hotplug addr */
max_addr = memory_hotplug_max();
- if (query.largest_available_block < (max_addr >> page_shift)) {
+ if (be32_to_cpu(query.largest_available_block) < (max_addr >> page_shift)) {
dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u "
"%llu-sized pages\n", max_addr, query.largest_available_block,
1ULL << page_shift);
- goto out_restore_window;
+ goto out_failed;
}
len = order_base_2(max_addr);
win64 = kzalloc(sizeof(struct property), GFP_KERNEL);
if (!win64) {
dev_info(&dev->dev,
"couldn't allocate property for 64bit dma window\n");
- goto out_restore_window;
+ goto out_failed;
}
win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL);
win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL);
@@ -1085,7 +1005,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
if (ret != 0)
goto out_free_prop;
- ddwprop->liobn = cpu_to_be32(create.liobn);
+ ddwprop->liobn = create.liobn;
ddwprop->dma_base = cpu_to_be64(of_read_number(&create.addr_hi, 2));
ddwprop->tce_shift = cpu_to_be32(page_shift);
ddwprop->window_shift = cpu_to_be32(len);
@@ -1132,9 +1052,7 @@ out_free_prop:
kfree(win64->value);
kfree(win64);
-out_restore_window:
- if (ddw_restore_token)
- restore_default_window(dev, ddw_restore_token);
+out_failed:
fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL);
if (!fpdn)
@@ -1192,7 +1110,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
pr_debug(" found DMA window, table: %p\n", pci->iommu_table);
}
- set_iommu_table_base(&dev->dev, pci->iommu_table);
+ set_iommu_table_base_and_group(&dev->dev, pci->iommu_table);
}
static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)