aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-11-06 13:55:59 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-11-24 22:32:57 -0500
commit1f1616e864972fc82e1a144c7d23622d2aa2c6f2 (patch)
tree8e0439953be68803a6fc86384e7031212f5b1170
parent48c2ce97fa406607ca5e11a76cf507d171452dd9 (diff)
powerpc/powernv: Add TCE SW invalidation support
This is used for newer IO Hubs such as p7IOC. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/tce.h4
-rw-r--r--arch/powerpc/platforms/powernv/pci.c84
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c6
3 files changed, 78 insertions, 16 deletions
diff --git a/arch/powerpc/include/asm/tce.h b/arch/powerpc/include/asm/tce.h
index 3dead82d5979..743f36b38e5d 100644
--- a/arch/powerpc/include/asm/tce.h
+++ b/arch/powerpc/include/asm/tce.h
@@ -31,7 +31,9 @@
31 */ 31 */
32#define TCE_VB 0 32#define TCE_VB 0
33#define TCE_PCI 1 33#define TCE_PCI 1
34#define TCE_PCI_SW_INVAL 2 34#define TCE_PCI_SWINV_CREATE 2
35#define TCE_PCI_SWINV_FREE 4
36#define TCE_PCI_SWINV_PAIR 8
35 37
36/* TCE page size is 4096 bytes (1 << 12) */ 38/* TCE page size is 4096 bytes (1 << 12) */
37 39
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 85bb66d7f933..8b90d945399a 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -257,12 +257,54 @@ struct pci_ops pnv_pci_ops = {
257 .write = pnv_pci_write_config, 257 .write = pnv_pci_write_config,
258}; 258};
259 259
260
261static void pnv_tce_invalidate(struct iommu_table *tbl,
262 u64 *startp, u64 *endp)
263{
264 u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index;
265 unsigned long start, end, inc;
266
267 start = __pa(startp);
268 end = __pa(endp);
269
270
271 /* BML uses this case for p6/p7/galaxy2: Shift addr and put in node */
272 if (tbl->it_busno) {
273 start <<= 12;
274 end <<= 12;
275 inc = 128 << 12;
276 start |= tbl->it_busno;
277 end |= tbl->it_busno;
278 }
279 /* p7ioc-style invalidation, 2 TCEs per write */
280 else if (tbl->it_type & TCE_PCI_SWINV_PAIR) {
281 start |= (1ull << 63);
282 end |= (1ull << 63);
283 inc = 16;
284 }
285 /* Default (older HW) */
286 else
287 inc = 128;
288
289 end |= inc - 1; /* round up end to be different than start */
290
291 mb(); /* Ensure above stores are visible */
292 while (start <= end) {
293 __raw_writeq(start, invalidate);
294 start += inc;
295 }
296 /* The iommu layer will do another mb() for us on build() and
297 * we don't care on free()
298 */
299}
300
301
260static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, 302static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
261 unsigned long uaddr, enum dma_data_direction direction, 303 unsigned long uaddr, enum dma_data_direction direction,
262 struct dma_attrs *attrs) 304 struct dma_attrs *attrs)
263{ 305{
264 u64 proto_tce; 306 u64 proto_tce;
265 u64 *tcep; 307 u64 *tcep, *tces;
266 u64 rpn; 308 u64 rpn;
267 309
268 proto_tce = TCE_PCI_READ; // Read allowed 310 proto_tce = TCE_PCI_READ; // Read allowed
@@ -270,25 +312,33 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
270 if (direction != DMA_TO_DEVICE) 312 if (direction != DMA_TO_DEVICE)
271 proto_tce |= TCE_PCI_WRITE; 313 proto_tce |= TCE_PCI_WRITE;
272 314
273 tcep = ((u64 *)tbl->it_base) + index; 315 tces = tcep = ((u64 *)tbl->it_base) + index - tbl->it_offset;
316 rpn = __pa(uaddr) >> TCE_SHIFT;
274 317
275 while (npages--) { 318 while (npages--)
276 /* can't move this out since we might cross LMB boundary */ 319 *(tcep++) = proto_tce | (rpn++ << TCE_RPN_SHIFT);
277 rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; 320
278 *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; 321 /* Some implementations won't cache invalid TCEs and thus may not
322 * need that flush. We'll probably turn it_type into a bit mask
323 * of flags if that becomes the case
324 */
325 if (tbl->it_type & TCE_PCI_SWINV_CREATE)
326 pnv_tce_invalidate(tbl, tces, tcep - 1);
279 327
280 uaddr += TCE_PAGE_SIZE;
281 tcep++;
282 }
283 return 0; 328 return 0;
284} 329}
285 330
286static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) 331static void pnv_tce_free(struct iommu_table *tbl, long index, long npages)
287{ 332{
288 u64 *tcep = ((u64 *)tbl->it_base) + index; 333 u64 *tcep, *tces;
334
335 tces = tcep = ((u64 *)tbl->it_base) + index - tbl->it_offset;
289 336
290 while (npages--) 337 while (npages--)
291 *(tcep++) = 0; 338 *(tcep++) = 0;
339
340 if (tbl->it_type & TCE_PCI_SWINV_FREE)
341 pnv_tce_invalidate(tbl, tces, tcep - 1);
292} 342}
293 343
294void pnv_pci_setup_iommu_table(struct iommu_table *tbl, 344void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
@@ -308,13 +358,14 @@ static struct iommu_table * __devinit
308pnv_pci_setup_bml_iommu(struct pci_controller *hose) 358pnv_pci_setup_bml_iommu(struct pci_controller *hose)
309{ 359{
310 struct iommu_table *tbl; 360 struct iommu_table *tbl;
311 const __be64 *basep; 361 const __be64 *basep, *swinvp;
312 const __be32 *sizep; 362 const __be32 *sizep;
313 363
314 basep = of_get_property(hose->dn, "linux,tce-base", NULL); 364 basep = of_get_property(hose->dn, "linux,tce-base", NULL);
315 sizep = of_get_property(hose->dn, "linux,tce-size", NULL); 365 sizep = of_get_property(hose->dn, "linux,tce-size", NULL);
316 if (basep == NULL || sizep == NULL) { 366 if (basep == NULL || sizep == NULL) {
317 pr_err("PCI: %s has missing tce entries !\n", hose->dn->full_name); 367 pr_err("PCI: %s has missing tce entries !\n",
368 hose->dn->full_name);
318 return NULL; 369 return NULL;
319 } 370 }
320 tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node); 371 tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node);
@@ -323,6 +374,15 @@ pnv_pci_setup_bml_iommu(struct pci_controller *hose)
323 pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)), 374 pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)),
324 be32_to_cpup(sizep), 0); 375 be32_to_cpup(sizep), 0);
325 iommu_init_table(tbl, hose->node); 376 iommu_init_table(tbl, hose->node);
377
378 /* Deal with SW invalidated TCEs when needed (BML way) */
379 swinvp = of_get_property(hose->dn, "linux,tce-sw-invalidate-info",
380 NULL);
381 if (swinvp) {
382 tbl->it_busno = swinvp[1];
383 tbl->it_index = (unsigned long)ioremap(swinvp[0], 8);
384 tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE;
385 }
326 return tbl; 386 return tbl;
327} 387}
328 388
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index ea70667d4fc3..c442f2b1980f 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -106,7 +106,7 @@ static int tce_build_pSeries(struct iommu_table *tbl, long index,
106 tcep++; 106 tcep++;
107 } 107 }
108 108
109 if (tbl->it_type == TCE_PCI_SW_INVAL) 109 if (tbl->it_type == TCE_PCI_SWINV_CREATE)
110 tce_invalidate_pSeries_sw(tbl, tces, tcep - 1); 110 tce_invalidate_pSeries_sw(tbl, tces, tcep - 1);
111 return 0; 111 return 0;
112} 112}
@@ -121,7 +121,7 @@ static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages)
121 while (npages--) 121 while (npages--)
122 *(tcep++) = 0; 122 *(tcep++) = 0;
123 123
124 if (tbl->it_type == TCE_PCI_SW_INVAL) 124 if (tbl->it_type == TCE_PCI_SWINV_FREE)
125 tce_invalidate_pSeries_sw(tbl, tces, tcep - 1); 125 tce_invalidate_pSeries_sw(tbl, tces, tcep - 1);
126} 126}
127 127
@@ -511,7 +511,7 @@ static void iommu_table_setparms(struct pci_controller *phb,
511 */ 511 */
512 tbl->it_index = (unsigned long) ioremap(sw_inval[0], 8); 512 tbl->it_index = (unsigned long) ioremap(sw_inval[0], 8);
513 tbl->it_busno = sw_inval[1]; /* overload this with magic */ 513 tbl->it_busno = sw_inval[1]; /* overload this with magic */
514 tbl->it_type = TCE_PCI_SW_INVAL; 514 tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE;
515 } 515 }
516} 516}
517 517