diff options
Diffstat (limited to 'arch/sparc64/kernel/pci_sun4v.c')
-rw-r--r-- | arch/sparc64/kernel/pci_sun4v.c | 278 |
1 files changed, 144 insertions, 134 deletions
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index 61baf8dc095e..ddca6c6c0b49 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* pci_sun4v.c: SUN4V specific PCI controller support. | 1 | /* pci_sun4v.c: SUN4V specific PCI controller support. |
2 | * | 2 | * |
3 | * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) | 3 | * Copyright (C) 2006, 2007, 2008 David S. Miller (davem@davemloft.net) |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> |
@@ -89,6 +89,17 @@ static long iommu_batch_flush(struct iommu_batch *p) | |||
89 | return 0; | 89 | return 0; |
90 | } | 90 | } |
91 | 91 | ||
92 | static inline void iommu_batch_new_entry(unsigned long entry) | ||
93 | { | ||
94 | struct iommu_batch *p = &__get_cpu_var(iommu_batch); | ||
95 | |||
96 | if (p->entry + p->npages == entry) | ||
97 | return; | ||
98 | if (p->entry != ~0UL) | ||
99 | iommu_batch_flush(p); | ||
100 | p->entry = entry; | ||
101 | } | ||
102 | |||
92 | /* Interrupts must be disabled. */ | 103 | /* Interrupts must be disabled. */ |
93 | static inline long iommu_batch_add(u64 phys_page) | 104 | static inline long iommu_batch_add(u64 phys_page) |
94 | { | 105 | { |
@@ -113,54 +124,6 @@ static inline long iommu_batch_end(void) | |||
113 | return iommu_batch_flush(p); | 124 | return iommu_batch_flush(p); |
114 | } | 125 | } |
115 | 126 | ||
116 | static long arena_alloc(struct iommu_arena *arena, unsigned long npages) | ||
117 | { | ||
118 | unsigned long n, i, start, end, limit; | ||
119 | int pass; | ||
120 | |||
121 | limit = arena->limit; | ||
122 | start = arena->hint; | ||
123 | pass = 0; | ||
124 | |||
125 | again: | ||
126 | n = find_next_zero_bit(arena->map, limit, start); | ||
127 | end = n + npages; | ||
128 | if (unlikely(end >= limit)) { | ||
129 | if (likely(pass < 1)) { | ||
130 | limit = start; | ||
131 | start = 0; | ||
132 | pass++; | ||
133 | goto again; | ||
134 | } else { | ||
135 | /* Scanned the whole thing, give up. */ | ||
136 | return -1; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | for (i = n; i < end; i++) { | ||
141 | if (test_bit(i, arena->map)) { | ||
142 | start = i + 1; | ||
143 | goto again; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | for (i = n; i < end; i++) | ||
148 | __set_bit(i, arena->map); | ||
149 | |||
150 | arena->hint = end; | ||
151 | |||
152 | return n; | ||
153 | } | ||
154 | |||
155 | static void arena_free(struct iommu_arena *arena, unsigned long base, | ||
156 | unsigned long npages) | ||
157 | { | ||
158 | unsigned long i; | ||
159 | |||
160 | for (i = base; i < (base + npages); i++) | ||
161 | __clear_bit(i, arena->map); | ||
162 | } | ||
163 | |||
164 | static void *dma_4v_alloc_coherent(struct device *dev, size_t size, | 127 | static void *dma_4v_alloc_coherent(struct device *dev, size_t size, |
165 | dma_addr_t *dma_addrp, gfp_t gfp) | 128 | dma_addr_t *dma_addrp, gfp_t gfp) |
166 | { | 129 | { |
@@ -185,11 +148,11 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size, | |||
185 | iommu = dev->archdata.iommu; | 148 | iommu = dev->archdata.iommu; |
186 | 149 | ||
187 | spin_lock_irqsave(&iommu->lock, flags); | 150 | spin_lock_irqsave(&iommu->lock, flags); |
188 | entry = arena_alloc(&iommu->arena, npages); | 151 | entry = iommu_range_alloc(dev, iommu, npages, NULL); |
189 | spin_unlock_irqrestore(&iommu->lock, flags); | 152 | spin_unlock_irqrestore(&iommu->lock, flags); |
190 | 153 | ||
191 | if (unlikely(entry < 0L)) | 154 | if (unlikely(entry == DMA_ERROR_CODE)) |
192 | goto arena_alloc_fail; | 155 | goto range_alloc_fail; |
193 | 156 | ||
194 | *dma_addrp = (iommu->page_table_map_base + | 157 | *dma_addrp = (iommu->page_table_map_base + |
195 | (entry << IO_PAGE_SHIFT)); | 158 | (entry << IO_PAGE_SHIFT)); |
@@ -219,10 +182,10 @@ static void *dma_4v_alloc_coherent(struct device *dev, size_t size, | |||
219 | iommu_map_fail: | 182 | iommu_map_fail: |
220 | /* Interrupts are disabled. */ | 183 | /* Interrupts are disabled. */ |
221 | spin_lock(&iommu->lock); | 184 | spin_lock(&iommu->lock); |
222 | arena_free(&iommu->arena, entry, npages); | 185 | iommu_range_free(iommu, *dma_addrp, npages); |
223 | spin_unlock_irqrestore(&iommu->lock, flags); | 186 | spin_unlock_irqrestore(&iommu->lock, flags); |
224 | 187 | ||
225 | arena_alloc_fail: | 188 | range_alloc_fail: |
226 | free_pages(first_page, order); | 189 | free_pages(first_page, order); |
227 | return NULL; | 190 | return NULL; |
228 | } | 191 | } |
@@ -243,7 +206,7 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu, | |||
243 | 206 | ||
244 | spin_lock_irqsave(&iommu->lock, flags); | 207 | spin_lock_irqsave(&iommu->lock, flags); |
245 | 208 | ||
246 | arena_free(&iommu->arena, entry, npages); | 209 | iommu_range_free(iommu, dvma, npages); |
247 | 210 | ||
248 | do { | 211 | do { |
249 | unsigned long num; | 212 | unsigned long num; |
@@ -281,10 +244,10 @@ static dma_addr_t dma_4v_map_single(struct device *dev, void *ptr, size_t sz, | |||
281 | npages >>= IO_PAGE_SHIFT; | 244 | npages >>= IO_PAGE_SHIFT; |
282 | 245 | ||
283 | spin_lock_irqsave(&iommu->lock, flags); | 246 | spin_lock_irqsave(&iommu->lock, flags); |
284 | entry = arena_alloc(&iommu->arena, npages); | 247 | entry = iommu_range_alloc(dev, iommu, npages, NULL); |
285 | spin_unlock_irqrestore(&iommu->lock, flags); | 248 | spin_unlock_irqrestore(&iommu->lock, flags); |
286 | 249 | ||
287 | if (unlikely(entry < 0L)) | 250 | if (unlikely(entry == DMA_ERROR_CODE)) |
288 | goto bad; | 251 | goto bad; |
289 | 252 | ||
290 | bus_addr = (iommu->page_table_map_base + | 253 | bus_addr = (iommu->page_table_map_base + |
@@ -319,7 +282,7 @@ bad: | |||
319 | iommu_map_fail: | 282 | iommu_map_fail: |
320 | /* Interrupts are disabled. */ | 283 | /* Interrupts are disabled. */ |
321 | spin_lock(&iommu->lock); | 284 | spin_lock(&iommu->lock); |
322 | arena_free(&iommu->arena, entry, npages); | 285 | iommu_range_free(iommu, bus_addr, npages); |
323 | spin_unlock_irqrestore(&iommu->lock, flags); | 286 | spin_unlock_irqrestore(&iommu->lock, flags); |
324 | 287 | ||
325 | return DMA_ERROR_CODE; | 288 | return DMA_ERROR_CODE; |
@@ -350,9 +313,9 @@ static void dma_4v_unmap_single(struct device *dev, dma_addr_t bus_addr, | |||
350 | 313 | ||
351 | spin_lock_irqsave(&iommu->lock, flags); | 314 | spin_lock_irqsave(&iommu->lock, flags); |
352 | 315 | ||
353 | entry = (bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT; | 316 | iommu_range_free(iommu, bus_addr, npages); |
354 | arena_free(&iommu->arena, entry, npages); | ||
355 | 317 | ||
318 | entry = (bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT; | ||
356 | do { | 319 | do { |
357 | unsigned long num; | 320 | unsigned long num; |
358 | 321 | ||
@@ -368,88 +331,131 @@ static void dma_4v_unmap_single(struct device *dev, dma_addr_t bus_addr, | |||
368 | static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, | 331 | static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, |
369 | int nelems, enum dma_data_direction direction) | 332 | int nelems, enum dma_data_direction direction) |
370 | { | 333 | { |
371 | unsigned long flags, npages, i, prot; | 334 | struct scatterlist *s, *outs, *segstart; |
372 | struct scatterlist *sg; | 335 | unsigned long flags, handle, prot; |
336 | dma_addr_t dma_next = 0, dma_addr; | ||
337 | unsigned int max_seg_size; | ||
338 | int outcount, incount, i; | ||
373 | struct iommu *iommu; | 339 | struct iommu *iommu; |
374 | long entry, err; | 340 | long err; |
375 | u32 dma_base; | 341 | |
376 | 342 | BUG_ON(direction == DMA_NONE); | |
377 | /* Fast path single entry scatterlists. */ | ||
378 | if (nelems == 1) { | ||
379 | sglist->dma_address = | ||
380 | dma_4v_map_single(dev, sg_virt(sglist), | ||
381 | sglist->length, direction); | ||
382 | if (unlikely(sglist->dma_address == DMA_ERROR_CODE)) | ||
383 | return 0; | ||
384 | sglist->dma_length = sglist->length; | ||
385 | return 1; | ||
386 | } | ||
387 | 343 | ||
388 | iommu = dev->archdata.iommu; | 344 | iommu = dev->archdata.iommu; |
345 | if (nelems == 0 || !iommu) | ||
346 | return 0; | ||
389 | 347 | ||
390 | if (unlikely(direction == DMA_NONE)) | 348 | prot = HV_PCI_MAP_ATTR_READ; |
391 | goto bad; | 349 | if (direction != DMA_TO_DEVICE) |
392 | 350 | prot |= HV_PCI_MAP_ATTR_WRITE; | |
393 | npages = calc_npages(sglist, nelems); | ||
394 | 351 | ||
395 | spin_lock_irqsave(&iommu->lock, flags); | 352 | outs = s = segstart = &sglist[0]; |
396 | entry = arena_alloc(&iommu->arena, npages); | 353 | outcount = 1; |
397 | spin_unlock_irqrestore(&iommu->lock, flags); | 354 | incount = nelems; |
355 | handle = 0; | ||
398 | 356 | ||
399 | if (unlikely(entry < 0L)) | 357 | /* Init first segment length for backout at failure */ |
400 | goto bad; | 358 | outs->dma_length = 0; |
401 | 359 | ||
402 | dma_base = iommu->page_table_map_base + | 360 | spin_lock_irqsave(&iommu->lock, flags); |
403 | (entry << IO_PAGE_SHIFT); | ||
404 | 361 | ||
405 | prot = HV_PCI_MAP_ATTR_READ; | 362 | iommu_batch_start(dev, prot, ~0UL); |
406 | if (direction != DMA_TO_DEVICE) | ||
407 | prot |= HV_PCI_MAP_ATTR_WRITE; | ||
408 | 363 | ||
409 | local_irq_save(flags); | 364 | max_seg_size = dma_get_max_seg_size(dev); |
365 | for_each_sg(sglist, s, nelems, i) { | ||
366 | unsigned long paddr, npages, entry, slen; | ||
410 | 367 | ||
411 | iommu_batch_start(dev, prot, entry); | 368 | slen = s->length; |
369 | /* Sanity check */ | ||
370 | if (slen == 0) { | ||
371 | dma_next = 0; | ||
372 | continue; | ||
373 | } | ||
374 | /* Allocate iommu entries for that segment */ | ||
375 | paddr = (unsigned long) SG_ENT_PHYS_ADDRESS(s); | ||
376 | npages = iommu_num_pages(paddr, slen); | ||
377 | entry = iommu_range_alloc(dev, iommu, npages, &handle); | ||
412 | 378 | ||
413 | for_each_sg(sglist, sg, nelems, i) { | 379 | /* Handle failure */ |
414 | unsigned long paddr = SG_ENT_PHYS_ADDRESS(sg); | 380 | if (unlikely(entry == DMA_ERROR_CODE)) { |
415 | unsigned long slen = sg->length; | 381 | if (printk_ratelimit()) |
416 | unsigned long this_npages; | 382 | printk(KERN_INFO "iommu_alloc failed, iommu %p paddr %lx" |
383 | " npages %lx\n", iommu, paddr, npages); | ||
384 | goto iommu_map_failed; | ||
385 | } | ||
417 | 386 | ||
418 | this_npages = iommu_num_pages(paddr, slen); | 387 | iommu_batch_new_entry(entry); |
419 | 388 | ||
420 | sg->dma_address = dma_base | (paddr & ~IO_PAGE_MASK); | 389 | /* Convert entry to a dma_addr_t */ |
421 | sg->dma_length = slen; | 390 | dma_addr = iommu->page_table_map_base + |
391 | (entry << IO_PAGE_SHIFT); | ||
392 | dma_addr |= (s->offset & ~IO_PAGE_MASK); | ||
422 | 393 | ||
394 | /* Insert into HW table */ | ||
423 | paddr &= IO_PAGE_MASK; | 395 | paddr &= IO_PAGE_MASK; |
424 | while (this_npages--) { | 396 | while (npages--) { |
425 | err = iommu_batch_add(paddr); | 397 | err = iommu_batch_add(paddr); |
426 | if (unlikely(err < 0L)) { | 398 | if (unlikely(err < 0L)) |
427 | local_irq_restore(flags); | ||
428 | goto iommu_map_failed; | 399 | goto iommu_map_failed; |
400 | paddr += IO_PAGE_SIZE; | ||
401 | } | ||
402 | |||
403 | /* If we are in an open segment, try merging */ | ||
404 | if (segstart != s) { | ||
405 | /* We cannot merge if: | ||
406 | * - allocated dma_addr isn't contiguous to previous allocation | ||
407 | */ | ||
408 | if ((dma_addr != dma_next) || | ||
409 | (outs->dma_length + s->length > max_seg_size)) { | ||
410 | /* Can't merge: create a new segment */ | ||
411 | segstart = s; | ||
412 | outcount++; | ||
413 | outs = sg_next(outs); | ||
414 | } else { | ||
415 | outs->dma_length += s->length; | ||
429 | } | 416 | } |
417 | } | ||
430 | 418 | ||
431 | paddr += IO_PAGE_SIZE; | 419 | if (segstart == s) { |
432 | dma_base += IO_PAGE_SIZE; | 420 | /* This is a new segment, fill entries */ |
421 | outs->dma_address = dma_addr; | ||
422 | outs->dma_length = slen; | ||
433 | } | 423 | } |
424 | |||
425 | /* Calculate next page pointer for contiguous check */ | ||
426 | dma_next = dma_addr + slen; | ||
434 | } | 427 | } |
435 | 428 | ||
436 | err = iommu_batch_end(); | 429 | err = iommu_batch_end(); |
437 | 430 | ||
438 | local_irq_restore(flags); | ||
439 | |||
440 | if (unlikely(err < 0L)) | 431 | if (unlikely(err < 0L)) |
441 | goto iommu_map_failed; | 432 | goto iommu_map_failed; |
442 | 433 | ||
443 | return nelems; | 434 | spin_unlock_irqrestore(&iommu->lock, flags); |
444 | 435 | ||
445 | bad: | 436 | if (outcount < incount) { |
446 | if (printk_ratelimit()) | 437 | outs = sg_next(outs); |
447 | WARN_ON(1); | 438 | outs->dma_address = DMA_ERROR_CODE; |
448 | return 0; | 439 | outs->dma_length = 0; |
440 | } | ||
441 | |||
442 | return outcount; | ||
449 | 443 | ||
450 | iommu_map_failed: | 444 | iommu_map_failed: |
451 | spin_lock_irqsave(&iommu->lock, flags); | 445 | for_each_sg(sglist, s, nelems, i) { |
452 | arena_free(&iommu->arena, entry, npages); | 446 | if (s->dma_length != 0) { |
447 | unsigned long vaddr, npages; | ||
448 | |||
449 | vaddr = s->dma_address & IO_PAGE_MASK; | ||
450 | npages = iommu_num_pages(s->dma_address, s->dma_length); | ||
451 | iommu_range_free(iommu, vaddr, npages); | ||
452 | /* XXX demap? XXX */ | ||
453 | s->dma_address = DMA_ERROR_CODE; | ||
454 | s->dma_length = 0; | ||
455 | } | ||
456 | if (s == outs) | ||
457 | break; | ||
458 | } | ||
453 | spin_unlock_irqrestore(&iommu->lock, flags); | 459 | spin_unlock_irqrestore(&iommu->lock, flags); |
454 | 460 | ||
455 | return 0; | 461 | return 0; |
@@ -458,39 +464,43 @@ iommu_map_failed: | |||
458 | static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist, | 464 | static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist, |
459 | int nelems, enum dma_data_direction direction) | 465 | int nelems, enum dma_data_direction direction) |
460 | { | 466 | { |
461 | unsigned long flags, npages; | ||
462 | struct pci_pbm_info *pbm; | 467 | struct pci_pbm_info *pbm; |
463 | u32 devhandle, bus_addr; | 468 | struct scatterlist *sg; |
464 | struct iommu *iommu; | 469 | struct iommu *iommu; |
465 | long entry; | 470 | unsigned long flags; |
471 | u32 devhandle; | ||
466 | 472 | ||
467 | if (unlikely(direction == DMA_NONE)) { | 473 | BUG_ON(direction == DMA_NONE); |
468 | if (printk_ratelimit()) | ||
469 | WARN_ON(1); | ||
470 | } | ||
471 | 474 | ||
472 | iommu = dev->archdata.iommu; | 475 | iommu = dev->archdata.iommu; |
473 | pbm = dev->archdata.host_controller; | 476 | pbm = dev->archdata.host_controller; |
474 | devhandle = pbm->devhandle; | 477 | devhandle = pbm->devhandle; |
475 | 478 | ||
476 | bus_addr = sglist->dma_address & IO_PAGE_MASK; | ||
477 | |||
478 | npages = calc_npages(sglist, nelems); | ||
479 | |||
480 | entry = ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); | ||
481 | |||
482 | spin_lock_irqsave(&iommu->lock, flags); | 479 | spin_lock_irqsave(&iommu->lock, flags); |
483 | 480 | ||
484 | arena_free(&iommu->arena, entry, npages); | 481 | sg = sglist; |
485 | 482 | while (nelems--) { | |
486 | do { | 483 | dma_addr_t dma_handle = sg->dma_address; |
487 | unsigned long num; | 484 | unsigned int len = sg->dma_length; |
485 | unsigned long npages, entry; | ||
486 | |||
487 | if (!len) | ||
488 | break; | ||
489 | npages = iommu_num_pages(dma_handle, len); | ||
490 | iommu_range_free(iommu, dma_handle, npages); | ||
491 | |||
492 | entry = ((dma_handle - iommu->page_table_map_base) >> IO_PAGE_SHIFT); | ||
493 | while (npages) { | ||
494 | unsigned long num; | ||
495 | |||
496 | num = pci_sun4v_iommu_demap(devhandle, HV_PCI_TSBID(0, entry), | ||
497 | npages); | ||
498 | entry += num; | ||
499 | npages -= num; | ||
500 | } | ||
488 | 501 | ||
489 | num = pci_sun4v_iommu_demap(devhandle, HV_PCI_TSBID(0, entry), | 502 | sg = sg_next(sg); |
490 | npages); | 503 | } |
491 | entry += num; | ||
492 | npages -= num; | ||
493 | } while (npages != 0); | ||
494 | 504 | ||
495 | spin_unlock_irqrestore(&iommu->lock, flags); | 505 | spin_unlock_irqrestore(&iommu->lock, flags); |
496 | } | 506 | } |