aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/include/asm/tce.h8
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c61
2 files changed, 61 insertions, 8 deletions
diff --git a/arch/powerpc/include/asm/tce.h b/arch/powerpc/include/asm/tce.h
index f663634cccc..3dead82d597 100644
--- a/arch/powerpc/include/asm/tce.h
+++ b/arch/powerpc/include/asm/tce.h
@@ -26,10 +26,12 @@
26 26
27/* 27/*
28 * Tces come in two formats, one for the virtual bus and a different 28 * Tces come in two formats, one for the virtual bus and a different
29 * format for PCI 29 * format for PCI. PCI TCEs can have hardware or software maintianed
30 * coherency.
30 */ 31 */
31#define TCE_VB 0 32#define TCE_VB 0
32#define TCE_PCI 1 33#define TCE_PCI 1
34#define TCE_PCI_SW_INVAL 2
33 35
34/* TCE page size is 4096 bytes (1 << 12) */ 36/* TCE page size is 4096 bytes (1 << 12) */
35 37
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index b719d970973..ea70667d4fc 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -52,13 +52,42 @@
52#include "plpar_wrappers.h" 52#include "plpar_wrappers.h"
53 53
54 54
55static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
56 u64 *startp, u64 *endp)
57{
58 u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index;
59 unsigned long start, end, inc;
60
61 start = __pa(startp);
62 end = __pa(endp);
63 inc = L1_CACHE_BYTES; /* invalidate a cacheline of TCEs at a time */
64
65 /* If this is non-zero, change the format. We shift the
66 * address and or in the magic from the device tree. */
67 if (tbl->it_busno) {
68 start <<= 12;
69 end <<= 12;
70 inc <<= 12;
71 start |= tbl->it_busno;
72 end |= tbl->it_busno;
73 }
74
75 end |= inc - 1; /* round up end to be different than start */
76
77 mb(); /* Make sure TCEs in memory are written */
78 while (start <= end) {
79 out_be64(invalidate, start);
80 start += inc;
81 }
82}
83
55static int tce_build_pSeries(struct iommu_table *tbl, long index, 84static int tce_build_pSeries(struct iommu_table *tbl, long index,
56 long npages, unsigned long uaddr, 85 long npages, unsigned long uaddr,
57 enum dma_data_direction direction, 86 enum dma_data_direction direction,
58 struct dma_attrs *attrs) 87 struct dma_attrs *attrs)
59{ 88{
60 u64 proto_tce; 89 u64 proto_tce;
61 u64 *tcep; 90 u64 *tcep, *tces;
62 u64 rpn; 91 u64 rpn;
63 92
64 proto_tce = TCE_PCI_READ; // Read allowed 93 proto_tce = TCE_PCI_READ; // Read allowed
@@ -66,7 +95,7 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index,
66 if (direction != DMA_TO_DEVICE) 95 if (direction != DMA_TO_DEVICE)
67 proto_tce |= TCE_PCI_WRITE; 96 proto_tce |= TCE_PCI_WRITE;
68 97
69 tcep = ((u64 *)tbl->it_base) + index; 98 tces = tcep = ((u64 *)tbl->it_base) + index;
70 99
71 while (npages--) { 100 while (npages--) {
72 /* can't move this out since we might cross MEMBLOCK boundary */ 101 /* can't move this out since we might cross MEMBLOCK boundary */
@@ -76,18 +105,24 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index,
76 uaddr += TCE_PAGE_SIZE; 105 uaddr += TCE_PAGE_SIZE;
77 tcep++; 106 tcep++;
78 } 107 }
108
109 if (tbl->it_type == TCE_PCI_SW_INVAL)
110 tce_invalidate_pSeries_sw(tbl, tces, tcep - 1);
79 return 0; 111 return 0;
80} 112}
81 113
82 114
83static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) 115static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages)
84{ 116{
85 u64 *tcep; 117 u64 *tcep, *tces;
86 118
87 tcep = ((u64 *)tbl->it_base) + index; 119 tces = tcep = ((u64 *)tbl->it_base) + index;
88 120
89 while (npages--) 121 while (npages--)
90 *(tcep++) = 0; 122 *(tcep++) = 0;
123
124 if (tbl->it_type == TCE_PCI_SW_INVAL)
125 tce_invalidate_pSeries_sw(tbl, tces, tcep - 1);
91} 126}
92 127
93static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) 128static unsigned long tce_get_pseries(struct iommu_table *tbl, long index)
@@ -425,7 +460,7 @@ static void iommu_table_setparms(struct pci_controller *phb,
425 struct iommu_table *tbl) 460 struct iommu_table *tbl)
426{ 461{
427 struct device_node *node; 462 struct device_node *node;
428 const unsigned long *basep; 463 const unsigned long *basep, *sw_inval;
429 const u32 *sizep; 464 const u32 *sizep;
430 465
431 node = phb->dn; 466 node = phb->dn;
@@ -462,6 +497,22 @@ static void iommu_table_setparms(struct pci_controller *phb,
462 tbl->it_index = 0; 497 tbl->it_index = 0;
463 tbl->it_blocksize = 16; 498 tbl->it_blocksize = 16;
464 tbl->it_type = TCE_PCI; 499 tbl->it_type = TCE_PCI;
500
501 sw_inval = of_get_property(node, "linux,tce-sw-invalidate-info", NULL);
502 if (sw_inval) {
503 /*
504 * This property contains information on how to
505 * invalidate the TCE entry. The first property is
506 * the base MMIO address used to invalidate entries.
507 * The second property tells us the format of the TCE
508 * invalidate (whether it needs to be shifted) and
509 * some magic routing info to add to our invalidate
510 * command.
511 */
512 tbl->it_index = (unsigned long) ioremap(sw_inval[0], 8);
513 tbl->it_busno = sw_inval[1]; /* overload this with magic */
514 tbl->it_type = TCE_PCI_SW_INVAL;
515 }
465} 516}
466 517
467/* 518/*