diff options
-rw-r--r-- | Documentation/DMA-attributes.txt | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/iommu.c | 113 | ||||
-rw-r--r-- | include/linux/dma-attrs.h | 1 |
3 files changed, 118 insertions, 5 deletions
diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index 6d772f84b477..b768cc0e402b 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt | |||
@@ -22,3 +22,12 @@ ready and available in memory. The DMA of the "completion indication" | |||
22 | could race with data DMA. Mapping the memory used for completion | 22 | could race with data DMA. Mapping the memory used for completion |
23 | indications with DMA_ATTR_WRITE_BARRIER would prevent the race. | 23 | indications with DMA_ATTR_WRITE_BARRIER would prevent the race. |
24 | 24 | ||
25 | DMA_ATTR_WEAK_ORDERING | ||
26 | ---------------------- | ||
27 | |||
28 | DMA_ATTR_WEAK_ORDERING specifies that reads and writes to the mapping | ||
29 | may be weakly ordered, that is that reads and writes may pass each other. | ||
30 | |||
31 | Since it is optional for platforms to implement DMA_ATTR_WEAK_ORDERING, | ||
32 | those that do not will simply ignore the attribute and exhibit default | ||
33 | behavior. | ||
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 3b7078453e7f..208005ca262c 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c | |||
@@ -199,6 +199,8 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages, | |||
199 | base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | | 199 | base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | |
200 | (window->ioid & IOPTE_IOID_Mask); | 200 | (window->ioid & IOPTE_IOID_Mask); |
201 | #endif | 201 | #endif |
202 | if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))) | ||
203 | base_pte &= ~IOPTE_SO_RW; | ||
202 | 204 | ||
203 | io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); | 205 | io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); |
204 | 206 | ||
@@ -539,7 +541,9 @@ static struct cbe_iommu *cell_iommu_for_node(int nid) | |||
539 | static unsigned long cell_dma_direct_offset; | 541 | static unsigned long cell_dma_direct_offset; |
540 | 542 | ||
541 | static unsigned long dma_iommu_fixed_base; | 543 | static unsigned long dma_iommu_fixed_base; |
542 | struct dma_mapping_ops dma_iommu_fixed_ops; | 544 | |
545 | /* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */ | ||
546 | static int iommu_fixed_is_weak; | ||
543 | 547 | ||
544 | static struct iommu_table *cell_get_iommu_table(struct device *dev) | 548 | static struct iommu_table *cell_get_iommu_table(struct device *dev) |
545 | { | 549 | { |
@@ -563,6 +567,98 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) | |||
563 | return &window->table; | 567 | return &window->table; |
564 | } | 568 | } |
565 | 569 | ||
570 | /* A coherent allocation implies strong ordering */ | ||
571 | |||
572 | static void *dma_fixed_alloc_coherent(struct device *dev, size_t size, | ||
573 | dma_addr_t *dma_handle, gfp_t flag) | ||
574 | { | ||
575 | if (iommu_fixed_is_weak) | ||
576 | return iommu_alloc_coherent(dev, cell_get_iommu_table(dev), | ||
577 | size, dma_handle, | ||
578 | device_to_mask(dev), flag, | ||
579 | dev->archdata.numa_node); | ||
580 | else | ||
581 | return dma_direct_ops.alloc_coherent(dev, size, dma_handle, | ||
582 | flag); | ||
583 | } | ||
584 | |||
585 | static void dma_fixed_free_coherent(struct device *dev, size_t size, | ||
586 | void *vaddr, dma_addr_t dma_handle) | ||
587 | { | ||
588 | if (iommu_fixed_is_weak) | ||
589 | iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr, | ||
590 | dma_handle); | ||
591 | else | ||
592 | dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle); | ||
593 | } | ||
594 | |||
595 | static dma_addr_t dma_fixed_map_single(struct device *dev, void *ptr, | ||
596 | size_t size, | ||
597 | enum dma_data_direction direction, | ||
598 | struct dma_attrs *attrs) | ||
599 | { | ||
600 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) | ||
601 | return dma_direct_ops.map_single(dev, ptr, size, direction, | ||
602 | attrs); | ||
603 | else | ||
604 | return iommu_map_single(dev, cell_get_iommu_table(dev), ptr, | ||
605 | size, device_to_mask(dev), direction, | ||
606 | attrs); | ||
607 | } | ||
608 | |||
609 | static void dma_fixed_unmap_single(struct device *dev, dma_addr_t dma_addr, | ||
610 | size_t size, | ||
611 | enum dma_data_direction direction, | ||
612 | struct dma_attrs *attrs) | ||
613 | { | ||
614 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) | ||
615 | dma_direct_ops.unmap_single(dev, dma_addr, size, direction, | ||
616 | attrs); | ||
617 | else | ||
618 | iommu_unmap_single(cell_get_iommu_table(dev), dma_addr, size, | ||
619 | direction, attrs); | ||
620 | } | ||
621 | |||
622 | static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg, | ||
623 | int nents, enum dma_data_direction direction, | ||
624 | struct dma_attrs *attrs) | ||
625 | { | ||
626 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) | ||
627 | return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); | ||
628 | else | ||
629 | return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents, | ||
630 | device_to_mask(dev), direction, attrs); | ||
631 | } | ||
632 | |||
633 | static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, | ||
634 | int nents, enum dma_data_direction direction, | ||
635 | struct dma_attrs *attrs) | ||
636 | { | ||
637 | if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) | ||
638 | dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); | ||
639 | else | ||
640 | iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction, | ||
641 | attrs); | ||
642 | } | ||
643 | |||
644 | static int dma_fixed_dma_supported(struct device *dev, u64 mask) | ||
645 | { | ||
646 | return mask == DMA_64BIT_MASK; | ||
647 | } | ||
648 | |||
649 | static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask); | ||
650 | |||
651 | struct dma_mapping_ops dma_iommu_fixed_ops = { | ||
652 | .alloc_coherent = dma_fixed_alloc_coherent, | ||
653 | .free_coherent = dma_fixed_free_coherent, | ||
654 | .map_single = dma_fixed_map_single, | ||
655 | .unmap_single = dma_fixed_unmap_single, | ||
656 | .map_sg = dma_fixed_map_sg, | ||
657 | .unmap_sg = dma_fixed_unmap_sg, | ||
658 | .dma_supported = dma_fixed_dma_supported, | ||
659 | .set_dma_mask = dma_set_mask_and_switch, | ||
660 | }; | ||
661 | |||
566 | static void cell_dma_dev_setup_fixed(struct device *dev); | 662 | static void cell_dma_dev_setup_fixed(struct device *dev); |
567 | 663 | ||
568 | static void cell_dma_dev_setup(struct device *dev) | 664 | static void cell_dma_dev_setup(struct device *dev) |
@@ -919,9 +1015,16 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu, | |||
919 | 1015 | ||
920 | pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase); | 1016 | pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase); |
921 | 1017 | ||
922 | base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | 1018 | base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M |
923 | | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask); | 1019 | | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask); |
924 | 1020 | ||
1021 | if (iommu_fixed_is_weak) | ||
1022 | pr_info("IOMMU: Using weak ordering for fixed mapping\n"); | ||
1023 | else { | ||
1024 | pr_info("IOMMU: Using strong ordering for fixed mapping\n"); | ||
1025 | base_pte |= IOPTE_SO_RW; | ||
1026 | } | ||
1027 | |||
925 | for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) { | 1028 | for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) { |
926 | /* Don't touch the dynamic region */ | 1029 | /* Don't touch the dynamic region */ |
927 | ioaddr = uaddr + fbase; | 1030 | ioaddr = uaddr + fbase; |
@@ -1037,9 +1140,6 @@ static int __init cell_iommu_fixed_mapping_init(void) | |||
1037 | cell_iommu_setup_window(iommu, np, dbase, dsize, 0); | 1140 | cell_iommu_setup_window(iommu, np, dbase, dsize, 0); |
1038 | } | 1141 | } |
1039 | 1142 | ||
1040 | dma_iommu_fixed_ops = dma_direct_ops; | ||
1041 | dma_iommu_fixed_ops.set_dma_mask = dma_set_mask_and_switch; | ||
1042 | |||
1043 | dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; | 1143 | dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; |
1044 | set_pci_dma_ops(&dma_iommu_ops); | 1144 | set_pci_dma_ops(&dma_iommu_ops); |
1045 | 1145 | ||
@@ -1053,6 +1153,9 @@ static int __init setup_iommu_fixed(char *str) | |||
1053 | if (strcmp(str, "off") == 0) | 1153 | if (strcmp(str, "off") == 0) |
1054 | iommu_fixed_disabled = 1; | 1154 | iommu_fixed_disabled = 1; |
1055 | 1155 | ||
1156 | else if (strcmp(str, "weak") == 0) | ||
1157 | iommu_fixed_is_weak = 1; | ||
1158 | |||
1056 | return 1; | 1159 | return 1; |
1057 | } | 1160 | } |
1058 | __setup("iommu_fixed=", setup_iommu_fixed); | 1161 | __setup("iommu_fixed=", setup_iommu_fixed); |
diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index 1677e2bfa00c..71ad34eca6e3 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h | |||
@@ -12,6 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | enum dma_attr { | 13 | enum dma_attr { |
14 | DMA_ATTR_WRITE_BARRIER, | 14 | DMA_ATTR_WRITE_BARRIER, |
15 | DMA_ATTR_WEAK_ORDERING, | ||
15 | DMA_ATTR_MAX, | 16 | DMA_ATTR_MAX, |
16 | }; | 17 | }; |
17 | 18 | ||