|
@@ -72,34 +72,57 @@ static inline void iommu_batch_start(struct device *dev, unsigned long prot, uns
|
|
}
|
|
}
|
|
|
|
|
|
/* Interrupts must be disabled. */
|
|
/* Interrupts must be disabled. */
|
|
-static long iommu_batch_flush(struct iommu_batch *p)
|
|
|
|
|
|
+static long iommu_batch_flush(struct iommu_batch *p, u64 mask)
|
|
{
|
|
{
|
|
struct pci_pbm_info *pbm = p->dev->archdata.host_controller;
|
|
struct pci_pbm_info *pbm = p->dev->archdata.host_controller;
|
|
|
|
+ u64 *pglist = p->pglist;
|
|
|
|
+ u64 index_count;
|
|
unsigned long devhandle = pbm->devhandle;
|
|
unsigned long devhandle = pbm->devhandle;
|
|
unsigned long prot = p->prot;
|
|
unsigned long prot = p->prot;
|
|
unsigned long entry = p->entry;
|
|
unsigned long entry = p->entry;
|
|
- u64 *pglist = p->pglist;
|
|
|
|
unsigned long npages = p->npages;
|
|
unsigned long npages = p->npages;
|
|
|
|
+ unsigned long iotsb_num;
|
|
|
|
+ unsigned long ret;
|
|
|
|
+ long num;
|
|
|
|
|
|
/* VPCI maj=1, min=[0,1] only supports read and write */
|
|
/* VPCI maj=1, min=[0,1] only supports read and write */
|
|
if (vpci_major < 2)
|
|
if (vpci_major < 2)
|
|
prot &= (HV_PCI_MAP_ATTR_READ | HV_PCI_MAP_ATTR_WRITE);
|
|
prot &= (HV_PCI_MAP_ATTR_READ | HV_PCI_MAP_ATTR_WRITE);
|
|
|
|
|
|
while (npages != 0) {
|
|
while (npages != 0) {
|
|
- long num;
|
|
|
|
-
|
|
|
|
- num = pci_sun4v_iommu_map(devhandle, HV_PCI_TSBID(0, entry),
|
|
|
|
- npages, prot, __pa(pglist));
|
|
|
|
- if (unlikely(num < 0)) {
|
|
|
|
- if (printk_ratelimit())
|
|
|
|
- printk("iommu_batch_flush: IOMMU map of "
|
|
|
|
- "[%08lx:%08llx:%lx:%lx:%lx] failed with "
|
|
|
|
- "status %ld\n",
|
|
|
|
- devhandle, HV_PCI_TSBID(0, entry),
|
|
|
|
- npages, prot, __pa(pglist), num);
|
|
|
|
- return -1;
|
|
|
|
|
|
+ if (mask <= DMA_BIT_MASK(32)) {
|
|
|
|
+ num = pci_sun4v_iommu_map(devhandle,
|
|
|
|
+ HV_PCI_TSBID(0, entry),
|
|
|
|
+ npages,
|
|
|
|
+ prot,
|
|
|
|
+ __pa(pglist));
|
|
|
|
+ if (unlikely(num < 0)) {
|
|
|
|
+ pr_err_ratelimited("%s: IOMMU map of [%08lx:%08llx:%lx:%lx:%lx] failed with status %ld\n",
|
|
|
|
+ __func__,
|
|
|
|
+ devhandle,
|
|
|
|
+ HV_PCI_TSBID(0, entry),
|
|
|
|
+ npages, prot, __pa(pglist),
|
|
|
|
+ num);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ index_count = HV_PCI_IOTSB_INDEX_COUNT(npages, entry),
|
|
|
|
+ iotsb_num = pbm->iommu->atu->iotsb->iotsb_num;
|
|
|
|
+ ret = pci_sun4v_iotsb_map(devhandle,
|
|
|
|
+ iotsb_num,
|
|
|
|
+ index_count,
|
|
|
|
+ prot,
|
|
|
|
+ __pa(pglist),
|
|
|
|
+ &num);
|
|
|
|
+ if (unlikely(ret != HV_EOK)) {
|
|
|
|
+ pr_err_ratelimited("%s: ATU map of [%08lx:%lx:%llx:%lx:%lx] failed with status %ld\n",
|
|
|
|
+ __func__,
|
|
|
|
+ devhandle, iotsb_num,
|
|
|
|
+ index_count, prot,
|
|
|
|
+ __pa(pglist), ret);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
entry += num;
|
|
entry += num;
|
|
npages -= num;
|
|
npages -= num;
|
|
pglist += num;
|
|
pglist += num;
|
|
@@ -111,19 +134,19 @@ static long iommu_batch_flush(struct iommu_batch *p)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void iommu_batch_new_entry(unsigned long entry)
|
|
|
|
|
|
+static inline void iommu_batch_new_entry(unsigned long entry, u64 mask)
|
|
{
|
|
{
|
|
struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
|
|
struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
|
|
|
|
|
|
if (p->entry + p->npages == entry)
|
|
if (p->entry + p->npages == entry)
|
|
return;
|
|
return;
|
|
if (p->entry != ~0UL)
|
|
if (p->entry != ~0UL)
|
|
- iommu_batch_flush(p);
|
|
|
|
|
|
+ iommu_batch_flush(p, mask);
|
|
p->entry = entry;
|
|
p->entry = entry;
|
|
}
|
|
}
|
|
|
|
|
|
/* Interrupts must be disabled. */
|
|
/* Interrupts must be disabled. */
|
|
-static inline long iommu_batch_add(u64 phys_page)
|
|
|
|
|
|
+static inline long iommu_batch_add(u64 phys_page, u64 mask)
|
|
{
|
|
{
|
|
struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
|
|
struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
|
|
|
|
|
|
@@ -131,28 +154,31 @@ static inline long iommu_batch_add(u64 phys_page)
|
|
|
|
|
|
p->pglist[p->npages++] = phys_page;
|
|
p->pglist[p->npages++] = phys_page;
|
|
if (p->npages == PGLIST_NENTS)
|
|
if (p->npages == PGLIST_NENTS)
|
|
- return iommu_batch_flush(p);
|
|
|
|
|
|
+ return iommu_batch_flush(p, mask);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Interrupts must be disabled. */
|
|
/* Interrupts must be disabled. */
|
|
-static inline long iommu_batch_end(void)
|
|
|
|
|
|
+static inline long iommu_batch_end(u64 mask)
|
|
{
|
|
{
|
|
struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
|
|
struct iommu_batch *p = this_cpu_ptr(&iommu_batch);
|
|
|
|
|
|
BUG_ON(p->npages >= PGLIST_NENTS);
|
|
BUG_ON(p->npages >= PGLIST_NENTS);
|
|
|
|
|
|
- return iommu_batch_flush(p);
|
|
|
|
|
|
+ return iommu_batch_flush(p, mask);
|
|
}
|
|
}
|
|
|
|
|
|
static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
|
|
static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
|
|
dma_addr_t *dma_addrp, gfp_t gfp,
|
|
dma_addr_t *dma_addrp, gfp_t gfp,
|
|
unsigned long attrs)
|
|
unsigned long attrs)
|
|
{
|
|
{
|
|
|
|
+ u64 mask;
|
|
unsigned long flags, order, first_page, npages, n;
|
|
unsigned long flags, order, first_page, npages, n;
|
|
unsigned long prot = 0;
|
|
unsigned long prot = 0;
|
|
struct iommu *iommu;
|
|
struct iommu *iommu;
|
|
|
|
+ struct atu *atu;
|
|
|
|
+ struct iommu_map_table *tbl;
|
|
struct page *page;
|
|
struct page *page;
|
|
void *ret;
|
|
void *ret;
|
|
long entry;
|
|
long entry;
|
|
@@ -177,14 +203,21 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
|
|
memset((char *)first_page, 0, PAGE_SIZE << order);
|
|
memset((char *)first_page, 0, PAGE_SIZE << order);
|
|
|
|
|
|
iommu = dev->archdata.iommu;
|
|
iommu = dev->archdata.iommu;
|
|
|
|
+ atu = iommu->atu;
|
|
|
|
+
|
|
|
|
+ mask = dev->coherent_dma_mask;
|
|
|
|
+ if (mask <= DMA_BIT_MASK(32))
|
|
|
|
+ tbl = &iommu->tbl;
|
|
|
|
+ else
|
|
|
|
+ tbl = &atu->tbl;
|
|
|
|
|
|
- entry = iommu_tbl_range_alloc(dev, &iommu->tbl, npages, NULL,
|
|
|
|
|
|
+ entry = iommu_tbl_range_alloc(dev, tbl, npages, NULL,
|
|
(unsigned long)(-1), 0);
|
|
(unsigned long)(-1), 0);
|
|
|
|
|
|
if (unlikely(entry == IOMMU_ERROR_CODE))
|
|
if (unlikely(entry == IOMMU_ERROR_CODE))
|
|
goto range_alloc_fail;
|
|
goto range_alloc_fail;
|
|
|
|
|
|
- *dma_addrp = (iommu->tbl.table_map_base + (entry << IO_PAGE_SHIFT));
|
|
|
|
|
|
+ *dma_addrp = (tbl->table_map_base + (entry << IO_PAGE_SHIFT));
|
|
ret = (void *) first_page;
|
|
ret = (void *) first_page;
|
|
first_page = __pa(first_page);
|
|
first_page = __pa(first_page);
|
|
|
|
|
|
@@ -196,12 +229,12 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
|
|
entry);
|
|
entry);
|
|
|
|
|
|
for (n = 0; n < npages; n++) {
|
|
for (n = 0; n < npages; n++) {
|
|
- long err = iommu_batch_add(first_page + (n * PAGE_SIZE));
|
|
|
|
|
|
+ long err = iommu_batch_add(first_page + (n * PAGE_SIZE), mask);
|
|
if (unlikely(err < 0L))
|
|
if (unlikely(err < 0L))
|
|
goto iommu_map_fail;
|
|
goto iommu_map_fail;
|
|
}
|
|
}
|
|
|
|
|
|
- if (unlikely(iommu_batch_end() < 0L))
|
|
|
|
|
|
+ if (unlikely(iommu_batch_end(mask) < 0L))
|
|
goto iommu_map_fail;
|
|
goto iommu_map_fail;
|
|
|
|
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
@@ -209,7 +242,7 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
iommu_map_fail:
|
|
iommu_map_fail:
|
|
- iommu_tbl_range_free(&iommu->tbl, *dma_addrp, npages, IOMMU_ERROR_CODE);
|
|
|
|
|
|
+ iommu_tbl_range_free(tbl, *dma_addrp, npages, IOMMU_ERROR_CODE);
|
|
|
|
|
|
range_alloc_fail:
|
|
range_alloc_fail:
|
|
free_pages(first_page, order);
|
|
free_pages(first_page, order);
|
|
@@ -253,18 +286,27 @@ unsigned long dma_4v_iotsb_bind(unsigned long devhandle,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void dma_4v_iommu_demap(void *demap_arg, unsigned long entry,
|
|
|
|
- unsigned long npages)
|
|
|
|
|
|
+static void dma_4v_iommu_demap(struct device *dev, unsigned long devhandle,
|
|
|
|
+ dma_addr_t dvma, unsigned long iotsb_num,
|
|
|
|
+ unsigned long entry, unsigned long npages)
|
|
{
|
|
{
|
|
- u32 devhandle = *(u32 *)demap_arg;
|
|
|
|
unsigned long num, flags;
|
|
unsigned long num, flags;
|
|
|
|
+ unsigned long ret;
|
|
|
|
|
|
local_irq_save(flags);
|
|
local_irq_save(flags);
|
|
do {
|
|
do {
|
|
- num = pci_sun4v_iommu_demap(devhandle,
|
|
|
|
- HV_PCI_TSBID(0, entry),
|
|
|
|
- npages);
|
|
|
|
-
|
|
|
|
|
|
+ if (dvma <= DMA_BIT_MASK(32)) {
|
|
|
|
+ num = pci_sun4v_iommu_demap(devhandle,
|
|
|
|
+ HV_PCI_TSBID(0, entry),
|
|
|
|
+ npages);
|
|
|
|
+ } else {
|
|
|
|
+ ret = pci_sun4v_iotsb_demap(devhandle, iotsb_num,
|
|
|
|
+ entry, npages, &num);
|
|
|
|
+ if (unlikely(ret != HV_EOK)) {
|
|
|
|
+ pr_err_ratelimited("pci_iotsb_demap() failed with error: %ld\n",
|
|
|
|
+ ret);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
entry += num;
|
|
entry += num;
|
|
npages -= num;
|
|
npages -= num;
|
|
} while (npages != 0);
|
|
} while (npages != 0);
|
|
@@ -276,16 +318,28 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu,
|
|
{
|
|
{
|
|
struct pci_pbm_info *pbm;
|
|
struct pci_pbm_info *pbm;
|
|
struct iommu *iommu;
|
|
struct iommu *iommu;
|
|
|
|
+ struct atu *atu;
|
|
|
|
+ struct iommu_map_table *tbl;
|
|
unsigned long order, npages, entry;
|
|
unsigned long order, npages, entry;
|
|
|
|
+ unsigned long iotsb_num;
|
|
u32 devhandle;
|
|
u32 devhandle;
|
|
|
|
|
|
npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
|
|
npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
|
|
iommu = dev->archdata.iommu;
|
|
iommu = dev->archdata.iommu;
|
|
pbm = dev->archdata.host_controller;
|
|
pbm = dev->archdata.host_controller;
|
|
|
|
+ atu = iommu->atu;
|
|
devhandle = pbm->devhandle;
|
|
devhandle = pbm->devhandle;
|
|
- entry = ((dvma - iommu->tbl.table_map_base) >> IO_PAGE_SHIFT);
|
|
|
|
- dma_4v_iommu_demap(&devhandle, entry, npages);
|
|
|
|
- iommu_tbl_range_free(&iommu->tbl, dvma, npages, IOMMU_ERROR_CODE);
|
|
|
|
|
|
+
|
|
|
|
+ if (dvma <= DMA_BIT_MASK(32)) {
|
|
|
|
+ tbl = &iommu->tbl;
|
|
|
|
+ iotsb_num = 0; /* we don't care for legacy iommu */
|
|
|
|
+ } else {
|
|
|
|
+ tbl = &atu->tbl;
|
|
|
|
+ iotsb_num = atu->iotsb->iotsb_num;
|
|
|
|
+ }
|
|
|
|
+ entry = ((dvma - tbl->table_map_base) >> IO_PAGE_SHIFT);
|
|
|
|
+ dma_4v_iommu_demap(dev, devhandle, dvma, iotsb_num, entry, npages);
|
|
|
|
+ iommu_tbl_range_free(tbl, dvma, npages, IOMMU_ERROR_CODE);
|
|
order = get_order(size);
|
|
order = get_order(size);
|
|
if (order < 10)
|
|
if (order < 10)
|
|
free_pages((unsigned long)cpu, order);
|
|
free_pages((unsigned long)cpu, order);
|
|
@@ -297,13 +351,17 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
|
|
unsigned long attrs)
|
|
unsigned long attrs)
|
|
{
|
|
{
|
|
struct iommu *iommu;
|
|
struct iommu *iommu;
|
|
|
|
+ struct atu *atu;
|
|
|
|
+ struct iommu_map_table *tbl;
|
|
|
|
+ u64 mask;
|
|
unsigned long flags, npages, oaddr;
|
|
unsigned long flags, npages, oaddr;
|
|
unsigned long i, base_paddr;
|
|
unsigned long i, base_paddr;
|
|
- u32 bus_addr, ret;
|
|
|
|
unsigned long prot;
|
|
unsigned long prot;
|
|
|
|
+ dma_addr_t bus_addr, ret;
|
|
long entry;
|
|
long entry;
|
|
|
|
|
|
iommu = dev->archdata.iommu;
|
|
iommu = dev->archdata.iommu;
|
|
|
|
+ atu = iommu->atu;
|
|
|
|
|
|
if (unlikely(direction == DMA_NONE))
|
|
if (unlikely(direction == DMA_NONE))
|
|
goto bad;
|
|
goto bad;
|
|
@@ -312,13 +370,19 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
|
|
npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK);
|
|
npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK);
|
|
npages >>= IO_PAGE_SHIFT;
|
|
npages >>= IO_PAGE_SHIFT;
|
|
|
|
|
|
- entry = iommu_tbl_range_alloc(dev, &iommu->tbl, npages, NULL,
|
|
|
|
|
|
+ mask = *dev->dma_mask;
|
|
|
|
+ if (mask <= DMA_BIT_MASK(32))
|
|
|
|
+ tbl = &iommu->tbl;
|
|
|
|
+ else
|
|
|
|
+ tbl = &atu->tbl;
|
|
|
|
+
|
|
|
|
+ entry = iommu_tbl_range_alloc(dev, tbl, npages, NULL,
|
|
(unsigned long)(-1), 0);
|
|
(unsigned long)(-1), 0);
|
|
|
|
|
|
if (unlikely(entry == IOMMU_ERROR_CODE))
|
|
if (unlikely(entry == IOMMU_ERROR_CODE))
|
|
goto bad;
|
|
goto bad;
|
|
|
|
|
|
- bus_addr = (iommu->tbl.table_map_base + (entry << IO_PAGE_SHIFT));
|
|
|
|
|
|
+ bus_addr = (tbl->table_map_base + (entry << IO_PAGE_SHIFT));
|
|
ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
|
|
ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
|
|
base_paddr = __pa(oaddr & IO_PAGE_MASK);
|
|
base_paddr = __pa(oaddr & IO_PAGE_MASK);
|
|
prot = HV_PCI_MAP_ATTR_READ;
|
|
prot = HV_PCI_MAP_ATTR_READ;
|
|
@@ -333,11 +397,11 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
|
|
iommu_batch_start(dev, prot, entry);
|
|
iommu_batch_start(dev, prot, entry);
|
|
|
|
|
|
for (i = 0; i < npages; i++, base_paddr += IO_PAGE_SIZE) {
|
|
for (i = 0; i < npages; i++, base_paddr += IO_PAGE_SIZE) {
|
|
- long err = iommu_batch_add(base_paddr);
|
|
|
|
|
|
+ long err = iommu_batch_add(base_paddr, mask);
|
|
if (unlikely(err < 0L))
|
|
if (unlikely(err < 0L))
|
|
goto iommu_map_fail;
|
|
goto iommu_map_fail;
|
|
}
|
|
}
|
|
- if (unlikely(iommu_batch_end() < 0L))
|
|
|
|
|
|
+ if (unlikely(iommu_batch_end(mask) < 0L))
|
|
goto iommu_map_fail;
|
|
goto iommu_map_fail;
|
|
|
|
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
@@ -350,7 +414,7 @@ bad:
|
|
return DMA_ERROR_CODE;
|
|
return DMA_ERROR_CODE;
|
|
|
|
|
|
iommu_map_fail:
|
|
iommu_map_fail:
|
|
- iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, IOMMU_ERROR_CODE);
|
|
|
|
|
|
+ iommu_tbl_range_free(tbl, bus_addr, npages, IOMMU_ERROR_CODE);
|
|
return DMA_ERROR_CODE;
|
|
return DMA_ERROR_CODE;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -360,7 +424,10 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
|
|
{
|
|
{
|
|
struct pci_pbm_info *pbm;
|
|
struct pci_pbm_info *pbm;
|
|
struct iommu *iommu;
|
|
struct iommu *iommu;
|
|
|
|
+ struct atu *atu;
|
|
|
|
+ struct iommu_map_table *tbl;
|
|
unsigned long npages;
|
|
unsigned long npages;
|
|
|
|
+ unsigned long iotsb_num;
|
|
long entry;
|
|
long entry;
|
|
u32 devhandle;
|
|
u32 devhandle;
|
|
|
|
|
|
@@ -372,14 +439,23 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
|
|
|
|
|
|
iommu = dev->archdata.iommu;
|
|
iommu = dev->archdata.iommu;
|
|
pbm = dev->archdata.host_controller;
|
|
pbm = dev->archdata.host_controller;
|
|
|
|
+ atu = iommu->atu;
|
|
devhandle = pbm->devhandle;
|
|
devhandle = pbm->devhandle;
|
|
|
|
|
|
npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
|
|
npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
|
|
npages >>= IO_PAGE_SHIFT;
|
|
npages >>= IO_PAGE_SHIFT;
|
|
bus_addr &= IO_PAGE_MASK;
|
|
bus_addr &= IO_PAGE_MASK;
|
|
- entry = (bus_addr - iommu->tbl.table_map_base) >> IO_PAGE_SHIFT;
|
|
|
|
- dma_4v_iommu_demap(&devhandle, entry, npages);
|
|
|
|
- iommu_tbl_range_free(&iommu->tbl, bus_addr, npages, IOMMU_ERROR_CODE);
|
|
|
|
|
|
+
|
|
|
|
+ if (bus_addr <= DMA_BIT_MASK(32)) {
|
|
|
|
+ iotsb_num = 0; /* we don't care for legacy iommu */
|
|
|
|
+ tbl = &iommu->tbl;
|
|
|
|
+ } else {
|
|
|
|
+ iotsb_num = atu->iotsb->iotsb_num;
|
|
|
|
+ tbl = &atu->tbl;
|
|
|
|
+ }
|
|
|
|
+ entry = (bus_addr - tbl->table_map_base) >> IO_PAGE_SHIFT;
|
|
|
|
+ dma_4v_iommu_demap(dev, devhandle, bus_addr, iotsb_num, entry, npages);
|
|
|
|
+ iommu_tbl_range_free(tbl, bus_addr, npages, IOMMU_ERROR_CODE);
|
|
}
|
|
}
|
|
|
|
|
|
static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
|
|
static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
|
|
@@ -393,12 +469,17 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
|
|
unsigned long seg_boundary_size;
|
|
unsigned long seg_boundary_size;
|
|
int outcount, incount, i;
|
|
int outcount, incount, i;
|
|
struct iommu *iommu;
|
|
struct iommu *iommu;
|
|
|
|
+ struct atu *atu;
|
|
|
|
+ struct iommu_map_table *tbl;
|
|
|
|
+ u64 mask;
|
|
unsigned long base_shift;
|
|
unsigned long base_shift;
|
|
long err;
|
|
long err;
|
|
|
|
|
|
BUG_ON(direction == DMA_NONE);
|
|
BUG_ON(direction == DMA_NONE);
|
|
|
|
|
|
iommu = dev->archdata.iommu;
|
|
iommu = dev->archdata.iommu;
|
|
|
|
+ atu = iommu->atu;
|
|
|
|
+
|
|
if (nelems == 0 || !iommu)
|
|
if (nelems == 0 || !iommu)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -424,7 +505,15 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
|
|
max_seg_size = dma_get_max_seg_size(dev);
|
|
max_seg_size = dma_get_max_seg_size(dev);
|
|
seg_boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
|
|
seg_boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
|
|
IO_PAGE_SIZE) >> IO_PAGE_SHIFT;
|
|
IO_PAGE_SIZE) >> IO_PAGE_SHIFT;
|
|
- base_shift = iommu->tbl.table_map_base >> IO_PAGE_SHIFT;
|
|
|
|
|
|
+
|
|
|
|
+ mask = *dev->dma_mask;
|
|
|
|
+ if (mask <= DMA_BIT_MASK(32))
|
|
|
|
+ tbl = &iommu->tbl;
|
|
|
|
+ else
|
|
|
|
+ tbl = &atu->tbl;
|
|
|
|
+
|
|
|
|
+ base_shift = tbl->table_map_base >> IO_PAGE_SHIFT;
|
|
|
|
+
|
|
for_each_sg(sglist, s, nelems, i) {
|
|
for_each_sg(sglist, s, nelems, i) {
|
|
unsigned long paddr, npages, entry, out_entry = 0, slen;
|
|
unsigned long paddr, npages, entry, out_entry = 0, slen;
|
|
|
|
|
|
@@ -437,27 +526,26 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
|
|
/* Allocate iommu entries for that segment */
|
|
/* Allocate iommu entries for that segment */
|
|
paddr = (unsigned long) SG_ENT_PHYS_ADDRESS(s);
|
|
paddr = (unsigned long) SG_ENT_PHYS_ADDRESS(s);
|
|
npages = iommu_num_pages(paddr, slen, IO_PAGE_SIZE);
|
|
npages = iommu_num_pages(paddr, slen, IO_PAGE_SIZE);
|
|
- entry = iommu_tbl_range_alloc(dev, &iommu->tbl, npages,
|
|
|
|
|
|
+ entry = iommu_tbl_range_alloc(dev, tbl, npages,
|
|
&handle, (unsigned long)(-1), 0);
|
|
&handle, (unsigned long)(-1), 0);
|
|
|
|
|
|
/* Handle failure */
|
|
/* Handle failure */
|
|
if (unlikely(entry == IOMMU_ERROR_CODE)) {
|
|
if (unlikely(entry == IOMMU_ERROR_CODE)) {
|
|
- if (printk_ratelimit())
|
|
|
|
- printk(KERN_INFO "iommu_alloc failed, iommu %p paddr %lx"
|
|
|
|
- " npages %lx\n", iommu, paddr, npages);
|
|
|
|
|
|
+ pr_err_ratelimited("iommu_alloc failed, iommu %p paddr %lx npages %lx\n",
|
|
|
|
+ tbl, paddr, npages);
|
|
goto iommu_map_failed;
|
|
goto iommu_map_failed;
|
|
}
|
|
}
|
|
|
|
|
|
- iommu_batch_new_entry(entry);
|
|
|
|
|
|
+ iommu_batch_new_entry(entry, mask);
|
|
|
|
|
|
/* Convert entry to a dma_addr_t */
|
|
/* Convert entry to a dma_addr_t */
|
|
- dma_addr = iommu->tbl.table_map_base + (entry << IO_PAGE_SHIFT);
|
|
|
|
|
|
+ dma_addr = tbl->table_map_base + (entry << IO_PAGE_SHIFT);
|
|
dma_addr |= (s->offset & ~IO_PAGE_MASK);
|
|
dma_addr |= (s->offset & ~IO_PAGE_MASK);
|
|
|
|
|
|
/* Insert into HW table */
|
|
/* Insert into HW table */
|
|
paddr &= IO_PAGE_MASK;
|
|
paddr &= IO_PAGE_MASK;
|
|
while (npages--) {
|
|
while (npages--) {
|
|
- err = iommu_batch_add(paddr);
|
|
|
|
|
|
+ err = iommu_batch_add(paddr, mask);
|
|
if (unlikely(err < 0L))
|
|
if (unlikely(err < 0L))
|
|
goto iommu_map_failed;
|
|
goto iommu_map_failed;
|
|
paddr += IO_PAGE_SIZE;
|
|
paddr += IO_PAGE_SIZE;
|
|
@@ -492,7 +580,7 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
|
|
dma_next = dma_addr + slen;
|
|
dma_next = dma_addr + slen;
|
|
}
|
|
}
|
|
|
|
|
|
- err = iommu_batch_end();
|
|
|
|
|
|
+ err = iommu_batch_end(mask);
|
|
|
|
|
|
if (unlikely(err < 0L))
|
|
if (unlikely(err < 0L))
|
|
goto iommu_map_failed;
|
|
goto iommu_map_failed;
|
|
@@ -515,7 +603,7 @@ iommu_map_failed:
|
|
vaddr = s->dma_address & IO_PAGE_MASK;
|
|
vaddr = s->dma_address & IO_PAGE_MASK;
|
|
npages = iommu_num_pages(s->dma_address, s->dma_length,
|
|
npages = iommu_num_pages(s->dma_address, s->dma_length,
|
|
IO_PAGE_SIZE);
|
|
IO_PAGE_SIZE);
|
|
- iommu_tbl_range_free(&iommu->tbl, vaddr, npages,
|
|
|
|
|
|
+ iommu_tbl_range_free(tbl, vaddr, npages,
|
|
IOMMU_ERROR_CODE);
|
|
IOMMU_ERROR_CODE);
|
|
/* XXX demap? XXX */
|
|
/* XXX demap? XXX */
|
|
s->dma_address = DMA_ERROR_CODE;
|
|
s->dma_address = DMA_ERROR_CODE;
|
|
@@ -536,13 +624,16 @@ static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
|
struct pci_pbm_info *pbm;
|
|
struct pci_pbm_info *pbm;
|
|
struct scatterlist *sg;
|
|
struct scatterlist *sg;
|
|
struct iommu *iommu;
|
|
struct iommu *iommu;
|
|
|
|
+ struct atu *atu;
|
|
unsigned long flags, entry;
|
|
unsigned long flags, entry;
|
|
|
|
+ unsigned long iotsb_num;
|
|
u32 devhandle;
|
|
u32 devhandle;
|
|
|
|
|
|
BUG_ON(direction == DMA_NONE);
|
|
BUG_ON(direction == DMA_NONE);
|
|
|
|
|
|
iommu = dev->archdata.iommu;
|
|
iommu = dev->archdata.iommu;
|
|
pbm = dev->archdata.host_controller;
|
|
pbm = dev->archdata.host_controller;
|
|
|
|
+ atu = iommu->atu;
|
|
devhandle = pbm->devhandle;
|
|
devhandle = pbm->devhandle;
|
|
|
|
|
|
local_irq_save(flags);
|
|
local_irq_save(flags);
|
|
@@ -552,15 +643,24 @@ static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
|
dma_addr_t dma_handle = sg->dma_address;
|
|
dma_addr_t dma_handle = sg->dma_address;
|
|
unsigned int len = sg->dma_length;
|
|
unsigned int len = sg->dma_length;
|
|
unsigned long npages;
|
|
unsigned long npages;
|
|
- struct iommu_map_table *tbl = &iommu->tbl;
|
|
|
|
|
|
+ struct iommu_map_table *tbl;
|
|
unsigned long shift = IO_PAGE_SHIFT;
|
|
unsigned long shift = IO_PAGE_SHIFT;
|
|
|
|
|
|
if (!len)
|
|
if (!len)
|
|
break;
|
|
break;
|
|
npages = iommu_num_pages(dma_handle, len, IO_PAGE_SIZE);
|
|
npages = iommu_num_pages(dma_handle, len, IO_PAGE_SIZE);
|
|
|
|
+
|
|
|
|
+ if (dma_handle <= DMA_BIT_MASK(32)) {
|
|
|
|
+ iotsb_num = 0; /* we don't care for legacy iommu */
|
|
|
|
+ tbl = &iommu->tbl;
|
|
|
|
+ } else {
|
|
|
|
+ iotsb_num = atu->iotsb->iotsb_num;
|
|
|
|
+ tbl = &atu->tbl;
|
|
|
|
+ }
|
|
entry = ((dma_handle - tbl->table_map_base) >> shift);
|
|
entry = ((dma_handle - tbl->table_map_base) >> shift);
|
|
- dma_4v_iommu_demap(&devhandle, entry, npages);
|
|
|
|
- iommu_tbl_range_free(&iommu->tbl, dma_handle, npages,
|
|
|
|
|
|
+ dma_4v_iommu_demap(dev, devhandle, dma_handle, iotsb_num,
|
|
|
|
+ entry, npages);
|
|
|
|
+ iommu_tbl_range_free(tbl, dma_handle, npages,
|
|
IOMMU_ERROR_CODE);
|
|
IOMMU_ERROR_CODE);
|
|
sg = sg_next(sg);
|
|
sg = sg_next(sg);
|
|
}
|
|
}
|