diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-11-06 13:55:59 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-11-24 22:32:57 -0500 |
commit | 1f1616e864972fc82e1a144c7d23622d2aa2c6f2 (patch) | |
tree | 8e0439953be68803a6fc86384e7031212f5b1170 /arch | |
parent | 48c2ce97fa406607ca5e11a76cf507d171452dd9 (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>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/include/asm/tce.h | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.c | 84 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/iommu.c | 6 |
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 | |||
261 | static 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 | |||
260 | static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, | 302 | static 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 | ||
286 | static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) | 331 | static 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 | ||
294 | void pnv_pci_setup_iommu_table(struct iommu_table *tbl, | 344 | void pnv_pci_setup_iommu_table(struct iommu_table *tbl, |
@@ -308,13 +358,14 @@ static struct iommu_table * __devinit | |||
308 | pnv_pci_setup_bml_iommu(struct pci_controller *hose) | 358 | pnv_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 | ||