#include "plpar_wrappers.h"
 
 
+static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
+                                     u64 *startp, u64 *endp)
+{
+       u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index;
+       unsigned long start, end, inc;
+
+       start = __pa(startp);
+       end = __pa(endp);
+       inc = L1_CACHE_BYTES; /* invalidate a cacheline of TCEs at a time */
+
+       /* If this is non-zero, change the format.  We shift the
+        * address and or in the magic from the device tree. */
+       if (tbl->it_busno) {
+               start <<= 12;
+               end <<= 12;
+               inc <<= 12;
+               start |= tbl->it_busno;
+               end |= tbl->it_busno;
+       }
+
+       end |= inc - 1; /* round up end to be different than start */
+
+       mb(); /* Make sure TCEs in memory are written */
+       while (start <= end) {
+               out_be64(invalidate, start);
+               start += inc;
+       }
+}
+
 static int tce_build_pSeries(struct iommu_table *tbl, long index,
                              long npages, unsigned long uaddr,
                              enum dma_data_direction direction,
                              struct dma_attrs *attrs)
 {
        u64 proto_tce;
-       u64 *tcep;
+       u64 *tcep, *tces;
        u64 rpn;
 
        proto_tce = TCE_PCI_READ; // Read allowed
        if (direction != DMA_TO_DEVICE)
                proto_tce |= TCE_PCI_WRITE;
 
-       tcep = ((u64 *)tbl->it_base) + index;
+       tces = tcep = ((u64 *)tbl->it_base) + index;
 
        while (npages--) {
                /* can't move this out since we might cross MEMBLOCK boundary */
                uaddr += TCE_PAGE_SIZE;
                tcep++;
        }
+
+       if (tbl->it_type == TCE_PCI_SW_INVAL)
+               tce_invalidate_pSeries_sw(tbl, tces, tcep - 1);
        return 0;
 }
 
 
 static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages)
 {
-       u64 *tcep;
+       u64 *tcep, *tces;
 
-       tcep = ((u64 *)tbl->it_base) + index;
+       tces = tcep = ((u64 *)tbl->it_base) + index;
 
        while (npages--)
                *(tcep++) = 0;
+
+       if (tbl->it_type == TCE_PCI_SW_INVAL)
+               tce_invalidate_pSeries_sw(tbl, tces, tcep - 1);
 }
 
 static unsigned long tce_get_pseries(struct iommu_table *tbl, long index)
                                 struct iommu_table *tbl)
 {
        struct device_node *node;
-       const unsigned long *basep;
+       const unsigned long *basep, *sw_inval;
        const u32 *sizep;
 
        node = phb->dn;
        tbl->it_index = 0;
        tbl->it_blocksize = 16;
        tbl->it_type = TCE_PCI;
+
+       sw_inval = of_get_property(node, "linux,tce-sw-invalidate-info", NULL);
+       if (sw_inval) {
+               /*
+                * This property contains information on how to
+                * invalidate the TCE entry.  The first property is
+                * the base MMIO address used to invalidate entries.
+                * The second property tells us the format of the TCE
+                * invalidate (whether it needs to be shifted) and
+                * some magic routing info to add to our invalidate
+                * command.
+                */
+               tbl->it_index = (unsigned long) ioremap(sw_inval[0], 8);
+               tbl->it_busno = sw_inval[1]; /* overload this with magic */
+               tbl->it_type = TCE_PCI_SW_INVAL;
+       }
 }
 
 /*