|
@@ -25,6 +25,7 @@
|
|
|
#include <linux/memblock.h>
|
|
|
#include <linux/iommu.h>
|
|
|
#include <linux/rculist.h>
|
|
|
+#include <linux/sizes.h>
|
|
|
|
|
|
#include <asm/sections.h>
|
|
|
#include <asm/io.h>
|
|
@@ -1870,6 +1871,12 @@ static void pnv_ioda2_tce_free(struct iommu_table *tbl, long index,
|
|
|
pnv_pci_ioda2_tce_invalidate(tbl, index, npages, false);
|
|
|
}
|
|
|
|
|
|
+static void pnv_ioda2_table_free(struct iommu_table *tbl)
|
|
|
+{
|
|
|
+ pnv_pci_ioda2_table_free_pages(tbl);
|
|
|
+ iommu_free_table(tbl, "pnv");
|
|
|
+}
|
|
|
+
|
|
|
static struct iommu_table_ops pnv_ioda2_iommu_ops = {
|
|
|
.set = pnv_ioda2_tce_build,
|
|
|
#ifdef CONFIG_IOMMU_API
|
|
@@ -1877,6 +1884,7 @@ static struct iommu_table_ops pnv_ioda2_iommu_ops = {
|
|
|
#endif
|
|
|
.clear = pnv_ioda2_tce_free,
|
|
|
.get = pnv_tce_get,
|
|
|
+ .free = pnv_ioda2_table_free,
|
|
|
};
|
|
|
|
|
|
static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
|
|
@@ -1947,6 +1955,8 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
|
|
|
TCE_PCI_SWINV_PAIR);
|
|
|
|
|
|
tbl->it_ops = &pnv_ioda1_iommu_ops;
|
|
|
+ pe->table_group.tce32_start = tbl->it_offset << tbl->it_page_shift;
|
|
|
+ pe->table_group.tce32_size = tbl->it_size << tbl->it_page_shift;
|
|
|
iommu_init_table(tbl, phb->hose->node);
|
|
|
|
|
|
if (pe->flags & PNV_IODA_PE_DEV) {
|
|
@@ -1985,7 +1995,7 @@ static long pnv_pci_ioda2_set_window(struct iommu_table_group *table_group,
|
|
|
const __u64 start_addr = tbl->it_offset << tbl->it_page_shift;
|
|
|
const __u64 win_size = tbl->it_size << tbl->it_page_shift;
|
|
|
|
|
|
- pe_info(pe, "Setting up window %llx..%llx pg=%x\n",
|
|
|
+ pe_info(pe, "Setting up window#%d %llx..%llx pg=%x\n", num,
|
|
|
start_addr, start_addr + win_size - 1,
|
|
|
IOMMU_PAGE_SIZE(tbl));
|
|
|
|
|
@@ -1995,7 +2005,7 @@ static long pnv_pci_ioda2_set_window(struct iommu_table_group *table_group,
|
|
|
*/
|
|
|
rc = opal_pci_map_pe_dma_window(phb->opal_id,
|
|
|
pe->pe_number,
|
|
|
- pe->pe_number << 1,
|
|
|
+ (pe->pe_number << 1) + num,
|
|
|
tbl->it_indirect_levels + 1,
|
|
|
__pa(tbl->it_base),
|
|
|
size << 3,
|
|
@@ -2040,7 +2050,67 @@ static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
|
|
|
pe->tce_bypass_enabled = enable;
|
|
|
}
|
|
|
|
|
|
+static long pnv_pci_ioda2_table_alloc_pages(int nid, __u64 bus_offset,
|
|
|
+ __u32 page_shift, __u64 window_size, __u32 levels,
|
|
|
+ struct iommu_table *tbl);
|
|
|
+
|
|
|
+static long pnv_pci_ioda2_create_table(struct iommu_table_group *table_group,
|
|
|
+ int num, __u32 page_shift, __u64 window_size, __u32 levels,
|
|
|
+ struct iommu_table **ptbl)
|
|
|
+{
|
|
|
+ struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
|
|
|
+ table_group);
|
|
|
+ int nid = pe->phb->hose->node;
|
|
|
+ __u64 bus_offset = num ? pe->tce_bypass_base : table_group->tce32_start;
|
|
|
+ long ret;
|
|
|
+ struct iommu_table *tbl;
|
|
|
+
|
|
|
+ tbl = pnv_pci_table_alloc(nid);
|
|
|
+ if (!tbl)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ret = pnv_pci_ioda2_table_alloc_pages(nid,
|
|
|
+ bus_offset, page_shift, window_size,
|
|
|
+ levels, tbl);
|
|
|
+ if (ret) {
|
|
|
+ iommu_free_table(tbl, "pnv");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ tbl->it_ops = &pnv_ioda2_iommu_ops;
|
|
|
+ if (pe->phb->ioda.tce_inval_reg)
|
|
|
+ tbl->it_type |= (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE);
|
|
|
+
|
|
|
+ *ptbl = tbl;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
#ifdef CONFIG_IOMMU_API
|
|
|
+static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group,
|
|
|
+ int num)
|
|
|
+{
|
|
|
+ struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
|
|
|
+ table_group);
|
|
|
+ struct pnv_phb *phb = pe->phb;
|
|
|
+ long ret;
|
|
|
+
|
|
|
+ pe_info(pe, "Removing DMA window #%d\n", num);
|
|
|
+
|
|
|
+ ret = opal_pci_map_pe_dma_window(phb->opal_id, pe->pe_number,
|
|
|
+ (pe->pe_number << 1) + num,
|
|
|
+ 0/* levels */, 0/* table address */,
|
|
|
+ 0/* table size */, 0/* page size */);
|
|
|
+ if (ret)
|
|
|
+ pe_warn(pe, "Unmapping failed, ret = %ld\n", ret);
|
|
|
+ else
|
|
|
+ pnv_pci_ioda2_tce_invalidate_entire(pe);
|
|
|
+
|
|
|
+ pnv_pci_unlink_table_and_group(table_group->tables[num], table_group);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void pnv_ioda2_take_ownership(struct iommu_table_group *table_group)
|
|
|
{
|
|
|
struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
|
|
@@ -2060,6 +2130,9 @@ static void pnv_ioda2_release_ownership(struct iommu_table_group *table_group)
|
|
|
}
|
|
|
|
|
|
static struct iommu_table_group_ops pnv_pci_ioda2_ops = {
|
|
|
+ .create_table = pnv_pci_ioda2_create_table,
|
|
|
+ .set_window = pnv_pci_ioda2_set_window,
|
|
|
+ .unset_window = pnv_pci_ioda2_unset_window,
|
|
|
.take_ownership = pnv_ioda2_take_ownership,
|
|
|
.release_ownership = pnv_ioda2_release_ownership,
|
|
|
};
|
|
@@ -2214,7 +2287,7 @@ static void pnv_pci_ioda2_table_free_pages(struct iommu_table *tbl)
|
|
|
static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
|
|
|
struct pnv_ioda_pe *pe)
|
|
|
{
|
|
|
- struct iommu_table *tbl;
|
|
|
+ struct iommu_table *tbl = NULL;
|
|
|
int64_t rc;
|
|
|
|
|
|
/* We shouldn't already have a 32-bit DMA associated */
|
|
@@ -2224,10 +2297,8 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
|
|
|
/* TVE #1 is selected by PCI address bit 59 */
|
|
|
pe->tce_bypass_base = 1ull << 59;
|
|
|
|
|
|
- tbl = pnv_pci_table_alloc(phb->hose->node);
|
|
|
iommu_register_group(&pe->table_group, phb->hose->global_number,
|
|
|
pe->pe_number);
|
|
|
- pnv_pci_link_table_and_group(phb->hose->node, 0, tbl, &pe->table_group);
|
|
|
|
|
|
/* The PE will reserve all possible 32-bits space */
|
|
|
pe->tce32_seg = 0;
|
|
@@ -2235,13 +2306,22 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
|
|
|
phb->ioda.m32_pci_base);
|
|
|
|
|
|
/* Setup linux iommu table */
|
|
|
- rc = pnv_pci_ioda2_table_alloc_pages(pe->phb->hose->node,
|
|
|
- 0, IOMMU_PAGE_SHIFT_4K, phb->ioda.m32_pci_base,
|
|
|
- POWERNV_IOMMU_DEFAULT_LEVELS, tbl);
|
|
|
+ pe->table_group.tce32_start = 0;
|
|
|
+ pe->table_group.tce32_size = phb->ioda.m32_pci_base;
|
|
|
+ pe->table_group.max_dynamic_windows_supported =
|
|
|
+ IOMMU_TABLE_GROUP_MAX_TABLES;
|
|
|
+ pe->table_group.max_levels = POWERNV_IOMMU_MAX_LEVELS;
|
|
|
+ pe->table_group.pgsizes = SZ_4K | SZ_64K | SZ_16M;
|
|
|
+
|
|
|
+ rc = pnv_pci_ioda2_create_table(&pe->table_group, 0,
|
|
|
+ IOMMU_PAGE_SHIFT_4K,
|
|
|
+ pe->table_group.tce32_size,
|
|
|
+ POWERNV_IOMMU_DEFAULT_LEVELS, &tbl);
|
|
|
if (rc) {
|
|
|
pe_err(pe, "Failed to create 32-bit TCE table, err %ld", rc);
|
|
|
goto fail;
|
|
|
}
|
|
|
+ pnv_pci_link_table_and_group(phb->hose->node, 0, tbl, &pe->table_group);
|
|
|
|
|
|
tbl->it_ops = &pnv_ioda2_iommu_ops;
|
|
|
iommu_init_table(tbl, phb->hose->node);
|