diff options
-rw-r--r-- | arch/powerpc/include/asm/tce.h | 8 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/iommu.c | 61 |
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 | ||
55 | static 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 | |||
55 | static int tce_build_pSeries(struct iommu_table *tbl, long index, | 84 | static 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 | ||
83 | static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) | 115 | static 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 | ||
93 | static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) | 128 | static 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 | /* |