Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc: add real mode support for dma operations on powernv

The existing TCE machine calls (tce_build and tce_free) only support
virtual mode as they call __raw_writeq for TCE invalidation what
fails in real mode.

This introduces tce_build_rm and tce_free_rm real mode versions
which do mostly the same but use "Store Doubleword Caching Inhibited
Indexed" instruction for TCE invalidation.

This new feature is going to be utilized by real mode support of VFIO.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Alexey Kardashevskiy and committed by
Benjamin Herrenschmidt
8e0a1611 8e0861fa

+87 -19
+12
arch/powerpc/include/asm/machdep.h
··· 78 78 long index); 79 79 void (*tce_flush)(struct iommu_table *tbl); 80 80 81 + /* _rm versions are for real mode use only */ 82 + int (*tce_build_rm)(struct iommu_table *tbl, 83 + long index, 84 + long npages, 85 + unsigned long uaddr, 86 + enum dma_data_direction direction, 87 + struct dma_attrs *attrs); 88 + void (*tce_free_rm)(struct iommu_table *tbl, 89 + long index, 90 + long npages); 91 + void (*tce_flush_rm)(struct iommu_table *tbl); 92 + 81 93 void __iomem * (*ioremap)(phys_addr_t addr, unsigned long size, 82 94 unsigned long flags, void *caller); 83 95 void (*iounmap)(volatile void __iomem *token);
+37 -12
arch/powerpc/platforms/powernv/pci-ioda.c
··· 70 70 define_pe_printk_level(pe_warn, KERN_WARNING); 71 71 define_pe_printk_level(pe_info, KERN_INFO); 72 72 73 + /* 74 + * stdcix is only supposed to be used in hypervisor real mode as per 75 + * the architecture spec 76 + */ 77 + static inline void __raw_rm_writeq(u64 val, volatile void __iomem *paddr) 78 + { 79 + __asm__ __volatile__("stdcix %0,0,%1" 80 + : : "r" (val), "r" (paddr) : "memory"); 81 + } 82 + 73 83 static int pnv_ioda_alloc_pe(struct pnv_phb *phb) 74 84 { 75 85 unsigned long pe; ··· 464 454 } 465 455 } 466 456 467 - static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, 468 - u64 *startp, u64 *endp) 457 + static void pnv_pci_ioda1_tce_invalidate(struct pnv_ioda_pe *pe, 458 + struct iommu_table *tbl, 459 + u64 *startp, u64 *endp, bool rm) 469 460 { 470 - u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; 461 + u64 __iomem *invalidate = rm ? 462 + (u64 __iomem *)pe->tce_inval_reg_phys : 463 + (u64 __iomem *)tbl->it_index; 471 464 unsigned long start, end, inc; 472 465 473 466 start = __pa(startp); ··· 497 484 498 485 mb(); /* Ensure above stores are visible */ 499 486 while (start <= end) { 500 - __raw_writeq(start, invalidate); 487 + if (rm) 488 + __raw_rm_writeq(start, invalidate); 489 + else 490 + __raw_writeq(start, invalidate); 501 491 start += inc; 502 492 } 503 493 ··· 512 496 513 497 static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe, 514 498 struct iommu_table *tbl, 515 - u64 *startp, u64 *endp) 499 + u64 *startp, u64 *endp, bool rm) 516 500 { 517 501 unsigned long start, end, inc; 518 - u64 __iomem *invalidate = (u64 __iomem *)tbl->it_index; 502 + u64 __iomem *invalidate = rm ? 503 + (u64 __iomem *)pe->tce_inval_reg_phys : 504 + (u64 __iomem *)tbl->it_index; 519 505 520 506 /* We'll invalidate DMA address in PE scope */ 521 507 start = 0x2ul << 60; ··· 533 515 mb(); 534 516 535 517 while (start <= end) { 536 - __raw_writeq(start, invalidate); 518 + if (rm) 519 + __raw_rm_writeq(start, invalidate); 520 + else 521 + __raw_writeq(start, invalidate); 537 522 start += inc; 538 523 } 539 524 } 540 525 541 526 void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl, 542 - u64 *startp, u64 *endp) 527 + u64 *startp, u64 *endp, bool rm) 543 528 { 544 529 struct pnv_ioda_pe *pe = container_of(tbl, struct pnv_ioda_pe, 545 530 tce32_table); 546 531 struct pnv_phb *phb = pe->phb; 547 532 548 533 if (phb->type == PNV_PHB_IODA1) 549 - pnv_pci_ioda1_tce_invalidate(tbl, startp, endp); 534 + pnv_pci_ioda1_tce_invalidate(pe, tbl, startp, endp, rm); 550 535 else 551 - pnv_pci_ioda2_tce_invalidate(pe, tbl, startp, endp); 536 + pnv_pci_ioda2_tce_invalidate(pe, tbl, startp, endp, rm); 552 537 } 553 538 554 539 static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, ··· 624 603 * bus number, print that out instead. 625 604 */ 626 605 tbl->it_busno = 0; 627 - tbl->it_index = (unsigned long)ioremap(be64_to_cpup(swinvp), 8); 606 + pe->tce_inval_reg_phys = be64_to_cpup(swinvp); 607 + tbl->it_index = (unsigned long)ioremap(pe->tce_inval_reg_phys, 608 + 8); 628 609 tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE | 629 610 TCE_PCI_SWINV_PAIR; 630 611 } ··· 704 681 * bus number, print that out instead. 705 682 */ 706 683 tbl->it_busno = 0; 707 - tbl->it_index = (unsigned long)ioremap(be64_to_cpup(swinvp), 8); 684 + pe->tce_inval_reg_phys = be64_to_cpup(swinvp); 685 + tbl->it_index = (unsigned long)ioremap(pe->tce_inval_reg_phys, 686 + 8); 708 687 tbl->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE; 709 688 } 710 689 iommu_init_table(tbl, phb->hose->node);
+36 -6
arch/powerpc/platforms/powernv/pci.c
··· 401 401 402 402 static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, 403 403 unsigned long uaddr, enum dma_data_direction direction, 404 - struct dma_attrs *attrs) 404 + struct dma_attrs *attrs, bool rm) 405 405 { 406 406 u64 proto_tce; 407 407 u64 *tcep, *tces; ··· 423 423 * of flags if that becomes the case 424 424 */ 425 425 if (tbl->it_type & TCE_PCI_SWINV_CREATE) 426 - pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1); 426 + pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm); 427 427 428 428 return 0; 429 429 } 430 430 431 - static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) 431 + static int pnv_tce_build_vm(struct iommu_table *tbl, long index, long npages, 432 + unsigned long uaddr, 433 + enum dma_data_direction direction, 434 + struct dma_attrs *attrs) 435 + { 436 + return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, 437 + false); 438 + } 439 + 440 + static void pnv_tce_free(struct iommu_table *tbl, long index, long npages, 441 + bool rm) 432 442 { 433 443 u64 *tcep, *tces; 434 444 ··· 448 438 *(tcep++) = 0; 449 439 450 440 if (tbl->it_type & TCE_PCI_SWINV_FREE) 451 - pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1); 441 + pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm); 442 + } 443 + 444 + static void pnv_tce_free_vm(struct iommu_table *tbl, long index, long npages) 445 + { 446 + pnv_tce_free(tbl, index, npages, false); 452 447 } 453 448 454 449 static unsigned long pnv_tce_get(struct iommu_table *tbl, long index) 455 450 { 456 451 return ((u64 *)tbl->it_base)[index - tbl->it_offset]; 452 + } 453 + 454 + static int pnv_tce_build_rm(struct iommu_table *tbl, long index, long npages, 455 + unsigned long uaddr, 456 + enum dma_data_direction direction, 457 + struct dma_attrs *attrs) 458 + { 459 + return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, true); 460 + } 461 + 462 + static void pnv_tce_free_rm(struct iommu_table *tbl, long index, long npages) 463 + { 464 + pnv_tce_free(tbl, index, npages, true); 457 465 } 458 466 459 467 void pnv_pci_setup_iommu_table(struct iommu_table *tbl, ··· 638 610 639 611 /* Configure IOMMU DMA hooks */ 640 612 ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup; 641 - ppc_md.tce_build = pnv_tce_build; 642 - ppc_md.tce_free = pnv_tce_free; 613 + ppc_md.tce_build = pnv_tce_build_vm; 614 + ppc_md.tce_free = pnv_tce_free_vm; 615 + ppc_md.tce_build_rm = pnv_tce_build_rm; 616 + ppc_md.tce_free_rm = pnv_tce_free_rm; 643 617 ppc_md.tce_get = pnv_tce_get; 644 618 ppc_md.pci_probe_mode = pnv_pci_probe_mode; 645 619 set_pci_dma_ops(&dma_iommu_ops);
+2 -1
arch/powerpc/platforms/powernv/pci.h
··· 52 52 int tce32_seg; 53 53 int tce32_segcount; 54 54 struct iommu_table tce32_table; 55 + phys_addr_t tce_inval_reg_phys; 55 56 56 57 /* XXX TODO: Add support for additional 64-bit iommus */ 57 58 ··· 194 193 extern void pnv_pci_init_ioda_hub(struct device_node *np); 195 194 extern void pnv_pci_init_ioda2_phb(struct device_node *np); 196 195 extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl, 197 - u64 *startp, u64 *endp); 196 + u64 *startp, u64 *endp, bool rm); 198 197 199 198 #endif /* __POWERNV_PCI_H */