diff options
| author | Olof Johansson <olof@lixom.net> | 2012-09-13 01:00:07 -0400 |
|---|---|---|
| committer | Olof Johansson <olof@lixom.net> | 2012-09-13 01:00:07 -0400 |
| commit | 2bc733e8b443e77db5bb18e1bb8f1c535a92eb8c (patch) | |
| tree | a6985ec4a761efbf2bdbaf7e75110669bf52bc15 /arch/arm/mm/dma-mapping.c | |
| parent | f5a60d4efc7a3928d6e7d37202a8560ce76cb838 (diff) | |
| parent | 48540058612786d365602f3324ed97f9071092de (diff) | |
Merge tag 'imx-fixes' of git://git.pengutronix.de/git/imx/linux-2.6 into fixes
ARM: i.MX: Fix SSI clock associations for i.MX25/i.MX35
* tag 'imx-fixes' of git://git.pengutronix.de/git/imx/linux-2.6:
ARM: clk-imx35: Fix SSI clock registration
ARM: clk-imx25: Fix SSI clock registration
+ Linux 3.6-rc5
Diffstat (limited to 'arch/arm/mm/dma-mapping.c')
| -rw-r--r-- | arch/arm/mm/dma-mapping.c | 114 |
1 files changed, 104 insertions, 10 deletions
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 4e7d1182e8a3..051204fc4617 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c | |||
| @@ -267,17 +267,19 @@ static void __dma_free_remap(void *cpu_addr, size_t size) | |||
| 267 | vunmap(cpu_addr); | 267 | vunmap(cpu_addr); |
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K | ||
| 271 | |||
| 270 | struct dma_pool { | 272 | struct dma_pool { |
| 271 | size_t size; | 273 | size_t size; |
| 272 | spinlock_t lock; | 274 | spinlock_t lock; |
| 273 | unsigned long *bitmap; | 275 | unsigned long *bitmap; |
| 274 | unsigned long nr_pages; | 276 | unsigned long nr_pages; |
| 275 | void *vaddr; | 277 | void *vaddr; |
| 276 | struct page *page; | 278 | struct page **pages; |
| 277 | }; | 279 | }; |
| 278 | 280 | ||
| 279 | static struct dma_pool atomic_pool = { | 281 | static struct dma_pool atomic_pool = { |
| 280 | .size = SZ_256K, | 282 | .size = DEFAULT_DMA_COHERENT_POOL_SIZE, |
| 281 | }; | 283 | }; |
| 282 | 284 | ||
| 283 | static int __init early_coherent_pool(char *p) | 285 | static int __init early_coherent_pool(char *p) |
| @@ -287,6 +289,21 @@ static int __init early_coherent_pool(char *p) | |||
| 287 | } | 289 | } |
| 288 | early_param("coherent_pool", early_coherent_pool); | 290 | early_param("coherent_pool", early_coherent_pool); |
| 289 | 291 | ||
| 292 | void __init init_dma_coherent_pool_size(unsigned long size) | ||
| 293 | { | ||
| 294 | /* | ||
| 295 | * Catch any attempt to set the pool size too late. | ||
| 296 | */ | ||
| 297 | BUG_ON(atomic_pool.vaddr); | ||
| 298 | |||
| 299 | /* | ||
| 300 | * Set architecture specific coherent pool size only if | ||
| 301 | * it has not been changed by kernel command line parameter. | ||
| 302 | */ | ||
| 303 | if (atomic_pool.size == DEFAULT_DMA_COHERENT_POOL_SIZE) | ||
| 304 | atomic_pool.size = size; | ||
| 305 | } | ||
| 306 | |||
| 290 | /* | 307 | /* |
| 291 | * Initialise the coherent pool for atomic allocations. | 308 | * Initialise the coherent pool for atomic allocations. |
| 292 | */ | 309 | */ |
| @@ -297,6 +314,7 @@ static int __init atomic_pool_init(void) | |||
| 297 | unsigned long nr_pages = pool->size >> PAGE_SHIFT; | 314 | unsigned long nr_pages = pool->size >> PAGE_SHIFT; |
| 298 | unsigned long *bitmap; | 315 | unsigned long *bitmap; |
| 299 | struct page *page; | 316 | struct page *page; |
| 317 | struct page **pages; | ||
| 300 | void *ptr; | 318 | void *ptr; |
| 301 | int bitmap_size = BITS_TO_LONGS(nr_pages) * sizeof(long); | 319 | int bitmap_size = BITS_TO_LONGS(nr_pages) * sizeof(long); |
| 302 | 320 | ||
| @@ -304,21 +322,31 @@ static int __init atomic_pool_init(void) | |||
| 304 | if (!bitmap) | 322 | if (!bitmap) |
| 305 | goto no_bitmap; | 323 | goto no_bitmap; |
| 306 | 324 | ||
| 325 | pages = kzalloc(nr_pages * sizeof(struct page *), GFP_KERNEL); | ||
| 326 | if (!pages) | ||
| 327 | goto no_pages; | ||
| 328 | |||
| 307 | if (IS_ENABLED(CONFIG_CMA)) | 329 | if (IS_ENABLED(CONFIG_CMA)) |
| 308 | ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page); | 330 | ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page); |
| 309 | else | 331 | else |
| 310 | ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot, | 332 | ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot, |
| 311 | &page, NULL); | 333 | &page, NULL); |
| 312 | if (ptr) { | 334 | if (ptr) { |
| 335 | int i; | ||
| 336 | |||
| 337 | for (i = 0; i < nr_pages; i++) | ||
| 338 | pages[i] = page + i; | ||
| 339 | |||
| 313 | spin_lock_init(&pool->lock); | 340 | spin_lock_init(&pool->lock); |
| 314 | pool->vaddr = ptr; | 341 | pool->vaddr = ptr; |
| 315 | pool->page = page; | 342 | pool->pages = pages; |
| 316 | pool->bitmap = bitmap; | 343 | pool->bitmap = bitmap; |
| 317 | pool->nr_pages = nr_pages; | 344 | pool->nr_pages = nr_pages; |
| 318 | pr_info("DMA: preallocated %u KiB pool for atomic coherent allocations\n", | 345 | pr_info("DMA: preallocated %u KiB pool for atomic coherent allocations\n", |
| 319 | (unsigned)pool->size / 1024); | 346 | (unsigned)pool->size / 1024); |
| 320 | return 0; | 347 | return 0; |
| 321 | } | 348 | } |
| 349 | no_pages: | ||
| 322 | kfree(bitmap); | 350 | kfree(bitmap); |
| 323 | no_bitmap: | 351 | no_bitmap: |
| 324 | pr_err("DMA: failed to allocate %u KiB pool for atomic coherent allocation\n", | 352 | pr_err("DMA: failed to allocate %u KiB pool for atomic coherent allocation\n", |
| @@ -443,27 +471,45 @@ static void *__alloc_from_pool(size_t size, struct page **ret_page) | |||
| 443 | if (pageno < pool->nr_pages) { | 471 | if (pageno < pool->nr_pages) { |
| 444 | bitmap_set(pool->bitmap, pageno, count); | 472 | bitmap_set(pool->bitmap, pageno, count); |
| 445 | ptr = pool->vaddr + PAGE_SIZE * pageno; | 473 | ptr = pool->vaddr + PAGE_SIZE * pageno; |
| 446 | *ret_page = pool->page + pageno; | 474 | *ret_page = pool->pages[pageno]; |
| 475 | } else { | ||
| 476 | pr_err_once("ERROR: %u KiB atomic DMA coherent pool is too small!\n" | ||
| 477 | "Please increase it with coherent_pool= kernel parameter!\n", | ||
| 478 | (unsigned)pool->size / 1024); | ||
| 447 | } | 479 | } |
| 448 | spin_unlock_irqrestore(&pool->lock, flags); | 480 | spin_unlock_irqrestore(&pool->lock, flags); |
| 449 | 481 | ||
| 450 | return ptr; | 482 | return ptr; |
| 451 | } | 483 | } |
| 452 | 484 | ||
| 485 | static bool __in_atomic_pool(void *start, size_t size) | ||
| 486 | { | ||
| 487 | struct dma_pool *pool = &atomic_pool; | ||
| 488 | void *end = start + size; | ||
| 489 | void *pool_start = pool->vaddr; | ||
| 490 | void *pool_end = pool->vaddr + pool->size; | ||
| 491 | |||
| 492 | if (start < pool_start || start > pool_end) | ||
| 493 | return false; | ||
| 494 | |||
| 495 | if (end <= pool_end) | ||
| 496 | return true; | ||
| 497 | |||
| 498 | WARN(1, "Wrong coherent size(%p-%p) from atomic pool(%p-%p)\n", | ||
| 499 | start, end - 1, pool_start, pool_end - 1); | ||
| 500 | |||
| 501 | return false; | ||
| 502 | } | ||
| 503 | |||
| 453 | static int __free_from_pool(void *start, size_t size) | 504 | static int __free_from_pool(void *start, size_t size) |
| 454 | { | 505 | { |
| 455 | struct dma_pool *pool = &atomic_pool; | 506 | struct dma_pool *pool = &atomic_pool; |
| 456 | unsigned long pageno, count; | 507 | unsigned long pageno, count; |
| 457 | unsigned long flags; | 508 | unsigned long flags; |
| 458 | 509 | ||
| 459 | if (start < pool->vaddr || start > pool->vaddr + pool->size) | 510 | if (!__in_atomic_pool(start, size)) |
| 460 | return 0; | 511 | return 0; |
| 461 | 512 | ||
| 462 | if (start + size > pool->vaddr + pool->size) { | ||
| 463 | WARN(1, "freeing wrong coherent size from pool\n"); | ||
| 464 | return 0; | ||
| 465 | } | ||
| 466 | |||
| 467 | pageno = (start - pool->vaddr) >> PAGE_SHIFT; | 513 | pageno = (start - pool->vaddr) >> PAGE_SHIFT; |
| 468 | count = size >> PAGE_SHIFT; | 514 | count = size >> PAGE_SHIFT; |
| 469 | 515 | ||
| @@ -1090,10 +1136,22 @@ static int __iommu_remove_mapping(struct device *dev, dma_addr_t iova, size_t si | |||
| 1090 | return 0; | 1136 | return 0; |
| 1091 | } | 1137 | } |
| 1092 | 1138 | ||
| 1139 | static struct page **__atomic_get_pages(void *addr) | ||
| 1140 | { | ||
| 1141 | struct dma_pool *pool = &atomic_pool; | ||
| 1142 | struct page **pages = pool->pages; | ||
| 1143 | int offs = (addr - pool->vaddr) >> PAGE_SHIFT; | ||
| 1144 | |||
| 1145 | return pages + offs; | ||
| 1146 | } | ||
| 1147 | |||
| 1093 | static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs) | 1148 | static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs) |
| 1094 | { | 1149 | { |
| 1095 | struct vm_struct *area; | 1150 | struct vm_struct *area; |
| 1096 | 1151 | ||
| 1152 | if (__in_atomic_pool(cpu_addr, PAGE_SIZE)) | ||
| 1153 | return __atomic_get_pages(cpu_addr); | ||
| 1154 | |||
| 1097 | if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) | 1155 | if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) |
| 1098 | return cpu_addr; | 1156 | return cpu_addr; |
| 1099 | 1157 | ||
| @@ -1103,6 +1161,34 @@ static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs) | |||
| 1103 | return NULL; | 1161 | return NULL; |
| 1104 | } | 1162 | } |
| 1105 | 1163 | ||
| 1164 | static void *__iommu_alloc_atomic(struct device *dev, size_t size, | ||
| 1165 | dma_addr_t *handle) | ||
| 1166 | { | ||
| 1167 | struct page *page; | ||
| 1168 | void *addr; | ||
| 1169 | |||
| 1170 | addr = __alloc_from_pool(size, &page); | ||
| 1171 | if (!addr) | ||
| 1172 | return NULL; | ||
| 1173 | |||
| 1174 | *handle = __iommu_create_mapping(dev, &page, size); | ||
| 1175 | if (*handle == DMA_ERROR_CODE) | ||
| 1176 | goto err_mapping; | ||
| 1177 | |||
| 1178 | return addr; | ||
| 1179 | |||
| 1180 | err_mapping: | ||
| 1181 | __free_from_pool(addr, size); | ||
| 1182 | return NULL; | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | static void __iommu_free_atomic(struct device *dev, struct page **pages, | ||
| 1186 | dma_addr_t handle, size_t size) | ||
| 1187 | { | ||
| 1188 | __iommu_remove_mapping(dev, handle, size); | ||
| 1189 | __free_from_pool(page_address(pages[0]), size); | ||
| 1190 | } | ||
| 1191 | |||
| 1106 | static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, | 1192 | static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, |
| 1107 | dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) | 1193 | dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) |
| 1108 | { | 1194 | { |
| @@ -1113,6 +1199,9 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, | |||
| 1113 | *handle = DMA_ERROR_CODE; | 1199 | *handle = DMA_ERROR_CODE; |
| 1114 | size = PAGE_ALIGN(size); | 1200 | size = PAGE_ALIGN(size); |
| 1115 | 1201 | ||
| 1202 | if (gfp & GFP_ATOMIC) | ||
| 1203 | return __iommu_alloc_atomic(dev, size, handle); | ||
| 1204 | |||
| 1116 | pages = __iommu_alloc_buffer(dev, size, gfp); | 1205 | pages = __iommu_alloc_buffer(dev, size, gfp); |
| 1117 | if (!pages) | 1206 | if (!pages) |
| 1118 | return NULL; | 1207 | return NULL; |
| @@ -1179,6 +1268,11 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr, | |||
| 1179 | return; | 1268 | return; |
| 1180 | } | 1269 | } |
| 1181 | 1270 | ||
| 1271 | if (__in_atomic_pool(cpu_addr, size)) { | ||
| 1272 | __iommu_free_atomic(dev, pages, handle, size); | ||
| 1273 | return; | ||
| 1274 | } | ||
| 1275 | |||
| 1182 | if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) { | 1276 | if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) { |
| 1183 | unmap_kernel_range((unsigned long)cpu_addr, size); | 1277 | unmap_kernel_range((unsigned long)cpu_addr, size); |
| 1184 | vunmap(cpu_addr); | 1278 | vunmap(cpu_addr); |
