diff options
| author | Olof Johansson <olof@lixom.net> | 2006-04-12 22:05:59 -0400 |
|---|---|---|
| committer | Paul Mackerras <paulus@samba.org> | 2006-04-21 08:28:55 -0400 |
| commit | 7daa411b810d7eadfaabe3765ec5f827893dbb30 (patch) | |
| tree | c7cef2c78faa74928fb32942d9e9aaf262fe98ab | |
| parent | f4ffaa452e71495a06376f12f772342bc57051fc (diff) | |
[PATCH] powerpc: IOMMU support for honoring dma_mask
Some devices don't support full 32-bit DMA address space, which we currently
assume. Add the required mask-passing to the IOMMU allocators.
Signed-off-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Paul Mackerras <paulus@samba.org>
| -rw-r--r-- | arch/powerpc/kernel/iommu.c | 36 | ||||
| -rw-r--r-- | arch/powerpc/kernel/pci_iommu.c | 40 | ||||
| -rw-r--r-- | arch/powerpc/kernel/vio.c | 6 | ||||
| -rw-r--r-- | include/asm-powerpc/iommu.h | 7 |
4 files changed, 69 insertions, 20 deletions
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index d9a7fdef59..4eba60a328 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c | |||
| @@ -61,6 +61,7 @@ __setup("iommu=", setup_iommu); | |||
| 61 | static unsigned long iommu_range_alloc(struct iommu_table *tbl, | 61 | static unsigned long iommu_range_alloc(struct iommu_table *tbl, |
| 62 | unsigned long npages, | 62 | unsigned long npages, |
| 63 | unsigned long *handle, | 63 | unsigned long *handle, |
| 64 | unsigned long mask, | ||
| 64 | unsigned int align_order) | 65 | unsigned int align_order) |
| 65 | { | 66 | { |
| 66 | unsigned long n, end, i, start; | 67 | unsigned long n, end, i, start; |
| @@ -97,9 +98,21 @@ static unsigned long iommu_range_alloc(struct iommu_table *tbl, | |||
| 97 | */ | 98 | */ |
| 98 | if (start >= limit) | 99 | if (start >= limit) |
| 99 | start = largealloc ? tbl->it_largehint : tbl->it_hint; | 100 | start = largealloc ? tbl->it_largehint : tbl->it_hint; |
| 100 | 101 | ||
| 101 | again: | 102 | again: |
| 102 | 103 | ||
| 104 | if (limit + tbl->it_offset > mask) { | ||
| 105 | limit = mask - tbl->it_offset + 1; | ||
| 106 | /* If we're constrained on address range, first try | ||
| 107 | * at the masked hint to avoid O(n) search complexity, | ||
| 108 | * but on second pass, start at 0. | ||
| 109 | */ | ||
| 110 | if ((start & mask) >= limit || pass > 0) | ||
| 111 | start = 0; | ||
| 112 | else | ||
| 113 | start &= mask; | ||
| 114 | } | ||
| 115 | |||
| 103 | n = find_next_zero_bit(tbl->it_map, limit, start); | 116 | n = find_next_zero_bit(tbl->it_map, limit, start); |
| 104 | 117 | ||
| 105 | /* Align allocation */ | 118 | /* Align allocation */ |
| @@ -150,14 +163,14 @@ static unsigned long iommu_range_alloc(struct iommu_table *tbl, | |||
| 150 | 163 | ||
| 151 | static dma_addr_t iommu_alloc(struct iommu_table *tbl, void *page, | 164 | static dma_addr_t iommu_alloc(struct iommu_table *tbl, void *page, |
| 152 | unsigned int npages, enum dma_data_direction direction, | 165 | unsigned int npages, enum dma_data_direction direction, |
| 153 | unsigned int align_order) | 166 | unsigned long mask, unsigned int align_order) |
| 154 | { | 167 | { |
| 155 | unsigned long entry, flags; | 168 | unsigned long entry, flags; |
| 156 | dma_addr_t ret = DMA_ERROR_CODE; | 169 | dma_addr_t ret = DMA_ERROR_CODE; |
| 157 | 170 | ||
| 158 | spin_lock_irqsave(&(tbl->it_lock), flags); | 171 | spin_lock_irqsave(&(tbl->it_lock), flags); |
| 159 | 172 | ||
| 160 | entry = iommu_range_alloc(tbl, npages, NULL, align_order); | 173 | entry = iommu_range_alloc(tbl, npages, NULL, mask, align_order); |
| 161 | 174 | ||
| 162 | if (unlikely(entry == DMA_ERROR_CODE)) { | 175 | if (unlikely(entry == DMA_ERROR_CODE)) { |
| 163 | spin_unlock_irqrestore(&(tbl->it_lock), flags); | 176 | spin_unlock_irqrestore(&(tbl->it_lock), flags); |
| @@ -236,7 +249,7 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, | |||
| 236 | 249 | ||
| 237 | int iommu_map_sg(struct device *dev, struct iommu_table *tbl, | 250 | int iommu_map_sg(struct device *dev, struct iommu_table *tbl, |
| 238 | struct scatterlist *sglist, int nelems, | 251 | struct scatterlist *sglist, int nelems, |
| 239 | enum dma_data_direction direction) | 252 | unsigned long mask, enum dma_data_direction direction) |
| 240 | { | 253 | { |
| 241 | dma_addr_t dma_next = 0, dma_addr; | 254 | dma_addr_t dma_next = 0, dma_addr; |
| 242 | unsigned long flags; | 255 | unsigned long flags; |
| @@ -274,7 +287,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl, | |||
| 274 | vaddr = (unsigned long)page_address(s->page) + s->offset; | 287 | vaddr = (unsigned long)page_address(s->page) + s->offset; |
| 275 | npages = PAGE_ALIGN(vaddr + slen) - (vaddr & PAGE_MASK); | 288 | npages = PAGE_ALIGN(vaddr + slen) - (vaddr & PAGE_MASK); |
| 276 | npages >>= PAGE_SHIFT; | 289 | npages >>= PAGE_SHIFT; |
| 277 | entry = iommu_range_alloc(tbl, npages, &handle, 0); | 290 | entry = iommu_range_alloc(tbl, npages, &handle, mask >> PAGE_SHIFT, 0); |
| 278 | 291 | ||
| 279 | DBG(" - vaddr: %lx, size: %lx\n", vaddr, slen); | 292 | DBG(" - vaddr: %lx, size: %lx\n", vaddr, slen); |
| 280 | 293 | ||
| @@ -479,7 +492,8 @@ void iommu_free_table(struct device_node *dn) | |||
| 479 | * byte within the page as vaddr. | 492 | * byte within the page as vaddr. |
| 480 | */ | 493 | */ |
| 481 | dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr, | 494 | dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr, |
| 482 | size_t size, enum dma_data_direction direction) | 495 | size_t size, unsigned long mask, |
| 496 | enum dma_data_direction direction) | ||
| 483 | { | 497 | { |
| 484 | dma_addr_t dma_handle = DMA_ERROR_CODE; | 498 | dma_addr_t dma_handle = DMA_ERROR_CODE; |
| 485 | unsigned long uaddr; | 499 | unsigned long uaddr; |
| @@ -492,7 +506,8 @@ dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr, | |||
| 492 | npages >>= PAGE_SHIFT; | 506 | npages >>= PAGE_SHIFT; |
| 493 | 507 | ||
| 494 | if (tbl) { | 508 | if (tbl) { |
| 495 | dma_handle = iommu_alloc(tbl, vaddr, npages, direction, 0); | 509 | dma_handle = iommu_alloc(tbl, vaddr, npages, direction, |
| 510 | mask >> PAGE_SHIFT, 0); | ||
| 496 | if (dma_handle == DMA_ERROR_CODE) { | 511 | if (dma_handle == DMA_ERROR_CODE) { |
| 497 | if (printk_ratelimit()) { | 512 | if (printk_ratelimit()) { |
| 498 | printk(KERN_INFO "iommu_alloc failed, " | 513 | printk(KERN_INFO "iommu_alloc failed, " |
| @@ -521,7 +536,7 @@ void iommu_unmap_single(struct iommu_table *tbl, dma_addr_t dma_handle, | |||
| 521 | * to the dma address (mapping) of the first page. | 536 | * to the dma address (mapping) of the first page. |
| 522 | */ | 537 | */ |
| 523 | void *iommu_alloc_coherent(struct iommu_table *tbl, size_t size, | 538 | void *iommu_alloc_coherent(struct iommu_table *tbl, size_t size, |
| 524 | dma_addr_t *dma_handle, gfp_t flag) | 539 | dma_addr_t *dma_handle, unsigned long mask, gfp_t flag) |
| 525 | { | 540 | { |
| 526 | void *ret = NULL; | 541 | void *ret = NULL; |
| 527 | dma_addr_t mapping; | 542 | dma_addr_t mapping; |
| @@ -551,7 +566,8 @@ void *iommu_alloc_coherent(struct iommu_table *tbl, size_t size, | |||
| 551 | memset(ret, 0, size); | 566 | memset(ret, 0, size); |
| 552 | 567 | ||
| 553 | /* Set up tces to cover the allocated range */ | 568 | /* Set up tces to cover the allocated range */ |
| 554 | mapping = iommu_alloc(tbl, ret, npages, DMA_BIDIRECTIONAL, order); | 569 | mapping = iommu_alloc(tbl, ret, npages, DMA_BIDIRECTIONAL, |
| 570 | mask >> PAGE_SHIFT, order); | ||
| 555 | if (mapping == DMA_ERROR_CODE) { | 571 | if (mapping == DMA_ERROR_CODE) { |
| 556 | free_pages((unsigned long)ret, order); | 572 | free_pages((unsigned long)ret, order); |
| 557 | ret = NULL; | 573 | ret = NULL; |
diff --git a/arch/powerpc/kernel/pci_iommu.c b/arch/powerpc/kernel/pci_iommu.c index c336f3e31c..c1d95e14bb 100644 --- a/arch/powerpc/kernel/pci_iommu.c +++ b/arch/powerpc/kernel/pci_iommu.c | |||
| @@ -59,6 +59,25 @@ static inline struct iommu_table *devnode_table(struct device *dev) | |||
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | 61 | ||
| 62 | static inline unsigned long device_to_mask(struct device *hwdev) | ||
| 63 | { | ||
| 64 | struct pci_dev *pdev; | ||
| 65 | |||
| 66 | if (!hwdev) { | ||
| 67 | pdev = ppc64_isabridge_dev; | ||
| 68 | if (!pdev) /* This is the best guess we can do */ | ||
| 69 | return 0xfffffffful; | ||
| 70 | } else | ||
| 71 | pdev = to_pci_dev(hwdev); | ||
| 72 | |||
| 73 | if (pdev->dma_mask) | ||
| 74 | return pdev->dma_mask; | ||
| 75 | |||
| 76 | /* Assume devices without mask can take 32 bit addresses */ | ||
| 77 | return 0xfffffffful; | ||
| 78 | } | ||
| 79 | |||
| 80 | |||
| 62 | /* Allocates a contiguous real buffer and creates mappings over it. | 81 | /* Allocates a contiguous real buffer and creates mappings over it. |
| 63 | * Returns the virtual address of the buffer and sets dma_handle | 82 | * Returns the virtual address of the buffer and sets dma_handle |
| 64 | * to the dma address (mapping) of the first page. | 83 | * to the dma address (mapping) of the first page. |
| @@ -67,7 +86,7 @@ static void *pci_iommu_alloc_coherent(struct device *hwdev, size_t size, | |||
| 67 | dma_addr_t *dma_handle, gfp_t flag) | 86 | dma_addr_t *dma_handle, gfp_t flag) |
| 68 | { | 87 | { |
| 69 | return iommu_alloc_coherent(devnode_table(hwdev), size, dma_handle, | 88 | return iommu_alloc_coherent(devnode_table(hwdev), size, dma_handle, |
| 70 | flag); | 89 | device_to_mask(hwdev), flag); |
| 71 | } | 90 | } |
| 72 | 91 | ||
| 73 | static void pci_iommu_free_coherent(struct device *hwdev, size_t size, | 92 | static void pci_iommu_free_coherent(struct device *hwdev, size_t size, |
| @@ -85,7 +104,8 @@ static void pci_iommu_free_coherent(struct device *hwdev, size_t size, | |||
| 85 | static dma_addr_t pci_iommu_map_single(struct device *hwdev, void *vaddr, | 104 | static dma_addr_t pci_iommu_map_single(struct device *hwdev, void *vaddr, |
| 86 | size_t size, enum dma_data_direction direction) | 105 | size_t size, enum dma_data_direction direction) |
| 87 | { | 106 | { |
| 88 | return iommu_map_single(devnode_table(hwdev), vaddr, size, direction); | 107 | return iommu_map_single(devnode_table(hwdev), vaddr, size, |
| 108 | device_to_mask(hwdev), direction); | ||
| 89 | } | 109 | } |
| 90 | 110 | ||
| 91 | 111 | ||
| @@ -100,7 +120,7 @@ static int pci_iommu_map_sg(struct device *pdev, struct scatterlist *sglist, | |||
| 100 | int nelems, enum dma_data_direction direction) | 120 | int nelems, enum dma_data_direction direction) |
| 101 | { | 121 | { |
| 102 | return iommu_map_sg(pdev, devnode_table(pdev), sglist, | 122 | return iommu_map_sg(pdev, devnode_table(pdev), sglist, |
| 103 | nelems, direction); | 123 | nelems, device_to_mask(pdev), direction); |
| 104 | } | 124 | } |
| 105 | 125 | ||
| 106 | static void pci_iommu_unmap_sg(struct device *pdev, struct scatterlist *sglist, | 126 | static void pci_iommu_unmap_sg(struct device *pdev, struct scatterlist *sglist, |
| @@ -112,7 +132,19 @@ static void pci_iommu_unmap_sg(struct device *pdev, struct scatterlist *sglist, | |||
| 112 | /* We support DMA to/from any memory page via the iommu */ | 132 | /* We support DMA to/from any memory page via the iommu */ |
| 113 | static int pci_iommu_dma_supported(struct device *dev, u64 mask) | 133 | static int pci_iommu_dma_supported(struct device *dev, u64 mask) |
| 114 | { | 134 | { |
| 115 | return 1; | 135 | struct iommu_table *tbl = devnode_table(dev); |
