aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/kernel/pci-dma_32.c34
1 files changed, 29 insertions, 5 deletions
diff --git a/arch/x86/kernel/pci-dma_32.c b/arch/x86/kernel/pci-dma_32.c
index debe9119b724..11f100a5f034 100644
--- a/arch/x86/kernel/pci-dma_32.c
+++ b/arch/x86/kernel/pci-dma_32.c
@@ -76,6 +76,8 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
76 struct page *page; 76 struct page *page;
77 dma_addr_t bus; 77 dma_addr_t bus;
78 int order = get_order(size); 78 int order = get_order(size);
79 unsigned long dma_mask = 0;
80
79 /* ignore region specifiers */ 81 /* ignore region specifiers */
80 gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); 82 gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
81 83
@@ -85,15 +87,37 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
85 if (!dev) 87 if (!dev)
86 dev = &fallback_dev; 88 dev = &fallback_dev;
87 89
90 dma_mask = dev->coherent_dma_mask;
91 if (dma_mask == 0)
92 dma_mask = DMA_32BIT_MASK;
93
94 again:
88 page = dma_alloc_pages(dev, gfp, order); 95 page = dma_alloc_pages(dev, gfp, order);
89 if (page == NULL) 96 if (page == NULL)
90 return NULL; 97 return NULL;
91 98
92 ret = page_address(page); 99 {
93 bus = page_to_phys(page); 100 int high, mmu;
94 101 bus = page_to_phys(page);
95 memset(ret, 0, size); 102 ret = page_address(page);
96 *dma_handle = bus; 103 high = (bus + size) >= dma_mask;
104 mmu = high;
105 if (force_iommu && !(gfp & GFP_DMA))
106 mmu = 1;
107 else if (high) {
108 free_pages((unsigned long)ret,
109 get_order(size));
110
111 /* Don't use the 16MB ZONE_DMA unless absolutely
112 needed. It's better to use remapping first. */
113 if (dma_mask < DMA_32BIT_MASK && !(gfp & GFP_DMA)) {
114 gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
115 goto again;
116 }
117 }
118 memset(ret, 0, size);
119 *dma_handle = bus;
120 }
97 121
98 return ret; 122 return ret;
99} 123}