aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMilton Miller <miltonm@bga.com>2011-06-29 16:58:33 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-11-24 22:11:26 -0500
commit8d3d589a7901dd93743e98a60a0bacde485437f3 (patch)
tree8c8d201d35f1eb606dd38c51620448ac7691fdd6
parent7df1027542c9353bef4d027cb4ab8e99f69017b7 (diff)
powerpc/pseries: Software invalidatation of TCEs
Some pseries IOMMUs cache TCEs but don't snoop when the TCEs are changed in memory, hence we need manually invalidate in software. This adds code to do the invalidate. It keys off a device tree property to say where the to do the MMIO for the invalidate and some information on what the format of the invalidate including some magic routing info. it_busno get overloaded with this magic routing info and it_index with the MMIO address for the invalidate command. This then gets hooked into the building and freeing of TCEs. This is only useful on bare metal pseries. pHyp takes care of this when virtualised. Based on patch from Milton with cleanups from Mikey. Signed-off-by: Milton Miller <miltonm@bga.com> Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-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/*