diff options
Diffstat (limited to 'arch/sparc64/kernel/iommu.c')
-rw-r--r-- | arch/sparc64/kernel/iommu.c | 231 |
1 files changed, 158 insertions, 73 deletions
diff --git a/arch/sparc64/kernel/iommu.c b/arch/sparc64/kernel/iommu.c index 90a5907080a1..d3276ebcfb47 100644 --- a/arch/sparc64/kernel/iommu.c +++ b/arch/sparc64/kernel/iommu.c | |||
@@ -512,124 +512,209 @@ static void dma_4u_unmap_single(struct device *dev, dma_addr_t bus_addr, | |||
512 | static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, | 512 | static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist, |
513 | int nelems, enum dma_data_direction direction) | 513 | int nelems, enum dma_data_direction direction) |
514 | { | 514 | { |
515 | unsigned long flags, ctx, i, npages, iopte_protection; | 515 | struct scatterlist *s, *outs, *segstart; |
516 | struct scatterlist *sg; | 516 | unsigned long flags, handle, prot, ctx; |
517 | dma_addr_t dma_next = 0, dma_addr; | ||
518 | unsigned int max_seg_size; | ||
519 | int outcount, incount, i; | ||
517 | struct strbuf *strbuf; | 520 | struct strbuf *strbuf; |
518 | struct iommu *iommu; | 521 | struct iommu *iommu; |
519 | iopte_t *base; | 522 | |
520 | u32 dma_base; | 523 | BUG_ON(direction == DMA_NONE); |
521 | |||
522 | /* Fast path single entry scatterlists. */ | ||
523 | if (nelems == 1) { | ||
524 | sglist->dma_address = | ||
525 | dma_4u_map_single(dev, sg_virt(sglist), | ||
526 | sglist->length, direction); | ||
527 | if (unlikely(sglist->dma_address == DMA_ERROR_CODE)) | ||
528 | return 0; | ||
529 | sglist->dma_length = sglist->length; | ||
530 | return 1; | ||
531 | } | ||
532 | 524 | ||
533 | iommu = dev->archdata.iommu; | 525 | iommu = dev->archdata.iommu; |
534 | strbuf = dev->archdata.stc; | 526 | strbuf = dev->archdata.stc; |
535 | 527 | if (nelems == 0 || !iommu) | |
536 | if (unlikely(direction == DMA_NONE)) | 528 | return 0; |
537 | goto bad_no_ctx; | ||
538 | |||
539 | npages = calc_npages(sglist, nelems); | ||
540 | 529 | ||
541 | spin_lock_irqsave(&iommu->lock, flags); | 530 | spin_lock_irqsave(&iommu->lock, flags); |
542 | 531 | ||
543 | base = alloc_npages(dev, iommu, npages); | ||
544 | ctx = 0; | 532 | ctx = 0; |
545 | if (iommu->iommu_ctxflush) | 533 | if (iommu->iommu_ctxflush) |
546 | ctx = iommu_alloc_ctx(iommu); | 534 | ctx = iommu_alloc_ctx(iommu); |
547 | 535 | ||
548 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
549 | |||
550 | if (base == NULL) | ||
551 | goto bad; | ||
552 | |||
553 | dma_base = iommu->page_table_map_base + | ||
554 | ((base - iommu->page_table) << IO_PAGE_SHIFT); | ||
555 | |||
556 | if (strbuf->strbuf_enabled) | 536 | if (strbuf->strbuf_enabled) |
557 | iopte_protection = IOPTE_STREAMING(ctx); | 537 | prot = IOPTE_STREAMING(ctx); |
558 | else | 538 | else |
559 | iopte_protection = IOPTE_CONSISTENT(ctx); | 539 | prot = IOPTE_CONSISTENT(ctx); |
560 | if (direction != DMA_TO_DEVICE) | 540 | if (direction != DMA_TO_DEVICE) |
561 | iopte_protection |= IOPTE_WRITE; | 541 | prot |= IOPTE_WRITE; |
562 | 542 | ||
563 | for_each_sg(sglist, sg, nelems, i) { | 543 | outs = s = segstart = &sglist[0]; |
564 | unsigned long paddr = SG_ENT_PHYS_ADDRESS(sg); | 544 | outcount = 1; |
565 | unsigned long slen = sg->length; | 545 | incount = nelems; |
566 | unsigned long this_npages; | 546 | handle = 0; |
547 | |||
548 | /* Init first segment length for backout at failure */ | ||
549 | outs->dma_length = 0; | ||
550 | |||
551 | max_seg_size = dma_get_max_seg_size(dev); | ||
552 | for_each_sg(sglist, s, nelems, i) { | ||
553 | unsigned long paddr, npages, entry, slen; | ||
554 | iopte_t *base; | ||
555 | |||
556 | slen = s->length; | ||
557 | /* Sanity check */ | ||
558 | if (slen == 0) { | ||
559 | dma_next = 0; | ||
560 | continue; | ||
561 | } | ||
562 | /* Allocate iommu entries for that segment */ | ||
563 | paddr = (unsigned long) SG_ENT_PHYS_ADDRESS(s); | ||
564 | npages = iommu_num_pages(paddr, slen); | ||
565 | entry = iommu_range_alloc(dev, iommu, npages, &handle); | ||
566 | |||
567 | /* Handle failure */ | ||
568 | if (unlikely(entry == DMA_ERROR_CODE)) { | ||
569 | if (printk_ratelimit()) | ||
570 | printk(KERN_INFO "iommu_alloc failed, iommu %p paddr %lx" | ||
571 | " npages %lx\n", iommu, paddr, npages); | ||
572 | goto iommu_map_failed; | ||
573 | } | ||
567 | 574 | ||
568 | this_npages = iommu_num_pages(paddr, slen); | 575 | base = iommu->page_table + entry; |
569 | 576 | ||
570 | sg->dma_address = dma_base | (paddr & ~IO_PAGE_MASK); | 577 | /* Convert entry to a dma_addr_t */ |
571 | sg->dma_length = slen; | 578 | dma_addr = iommu->page_table_map_base + |
579 | (entry << IO_PAGE_SHIFT); | ||
580 | dma_addr |= (s->offset & ~IO_PAGE_MASK); | ||
572 | 581 | ||
582 | /* Insert into HW table */ | ||
573 | paddr &= IO_PAGE_MASK; | 583 | paddr &= IO_PAGE_MASK; |
574 | while (this_npages--) { | 584 | while (npages--) { |
575 | iopte_val(*base) = iopte_protection | paddr; | 585 | iopte_val(*base) = prot | paddr; |
576 | |||
577 | base++; | 586 | base++; |
578 | paddr += IO_PAGE_SIZE; | 587 | paddr += IO_PAGE_SIZE; |
579 | dma_base += IO_PAGE_SIZE; | ||
580 | } | 588 | } |
589 | |||
590 | /* If we are in an open segment, try merging */ | ||
591 | if (segstart != s) { | ||
592 | /* We cannot merge if: | ||
593 | * - allocated dma_addr isn't contiguous to previous allocation | ||
594 | */ | ||
595 | if ((dma_addr != dma_next) || | ||
596 | (outs->dma_length + s->length > max_seg_size)) { | ||
597 | /* Can't merge: create a new segment */ | ||
598 | segstart = s; | ||
599 | outcount++; | ||
600 | outs = sg_next(outs); | ||
601 | } else { | ||
602 | outs->dma_length += s->length; | ||
603 | } | ||
604 | } | ||
605 | |||
606 | if (segstart == s) { | ||
607 | /* This is a new segment, fill entries */ | ||
608 | outs->dma_address = dma_addr; | ||
609 | outs->dma_length = slen; | ||
610 | } | ||
611 | |||
612 | /* Calculate next page pointer for contiguous check */ | ||
613 | dma_next = dma_addr + slen; | ||
581 | } | 614 | } |
582 | 615 | ||
583 | return nelems; | 616 | spin_unlock_irqrestore(&iommu->lock, flags); |
617 | |||
618 | if (outcount < incount) { | ||
619 | outs = sg_next(outs); | ||
620 | outs->dma_address = DMA_ERROR_CODE; | ||
621 | outs->dma_length = 0; | ||
622 | } | ||
623 | |||
624 | return outcount; | ||
625 | |||
626 | iommu_map_failed: | ||
627 | for_each_sg(sglist, s, nelems, i) { | ||
628 | if (s->dma_length != 0) { | ||
629 | unsigned long vaddr, npages, entry, i; | ||
630 | iopte_t *base; | ||
631 | |||
632 | vaddr = s->dma_address & IO_PAGE_MASK; | ||
633 | npages = iommu_num_pages(s->dma_address, s->dma_length); | ||
634 | iommu_range_free(iommu, vaddr, npages); | ||
635 | |||
636 | entry = (vaddr - iommu->page_table_map_base) | ||
637 | >> IO_PAGE_SHIFT; | ||
638 | base = iommu->page_table + entry; | ||
639 | |||
640 | for (i = 0; i < npages; i++) | ||
641 | iopte_make_dummy(iommu, base + i); | ||
642 | |||
643 | s->dma_address = DMA_ERROR_CODE; | ||
644 | s->dma_length = 0; | ||
645 | } | ||
646 | if (s == outs) | ||
647 | break; | ||
648 | } | ||
649 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
584 | 650 | ||
585 | bad: | ||
586 | iommu_free_ctx(iommu, ctx); | ||
587 | bad_no_ctx: | ||
588 | if (printk_ratelimit()) | ||
589 | WARN_ON(1); | ||
590 | return 0; | 651 | return 0; |
591 | } | 652 | } |
592 | 653 | ||
654 | /* If contexts are being used, they are the same in all of the mappings | ||
655 | * we make for a particular SG. | ||
656 | */ | ||
657 | static unsigned long fetch_sg_ctx(struct iommu *iommu, struct scatterlist *sg) | ||
658 | { | ||
659 | unsigned long ctx = 0; | ||
660 | |||
661 | if (iommu->iommu_ctxflush) { | ||
662 | iopte_t *base; | ||
663 | u32 bus_addr; | ||
664 | |||
665 | bus_addr = sg->dma_address & IO_PAGE_MASK; | ||
666 | base = iommu->page_table + | ||
667 | ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); | ||
668 | |||
669 | ctx = (iopte_val(*base) & IOPTE_CONTEXT) >> 47UL; | ||
670 | } | ||
671 | return ctx; | ||
672 | } | ||
673 | |||
593 | static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist, | 674 | static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist, |
594 | int nelems, enum dma_data_direction direction) | 675 | int nelems, enum dma_data_direction direction) |
595 | { | 676 | { |
596 | unsigned long flags, ctx, i, npages; | 677 | unsigned long flags, ctx; |
678 | struct scatterlist *sg; | ||
597 | struct strbuf *strbuf; | 679 | struct strbuf *strbuf; |
598 | struct iommu *iommu; | 680 | struct iommu *iommu; |
599 | iopte_t *base; | ||
600 | u32 bus_addr; | ||
601 | 681 | ||
602 | if (unlikely(direction == DMA_NONE)) { | 682 | BUG_ON(direction == DMA_NONE); |
603 | if (printk_ratelimit()) | ||
604 | WARN_ON(1); | ||
605 | } | ||
606 | 683 | ||
607 | iommu = dev->archdata.iommu; | 684 | iommu = dev->archdata.iommu; |
608 | strbuf = dev->archdata.stc; | 685 | strbuf = dev->archdata.stc; |
609 | 686 | ||
610 | bus_addr = sglist->dma_address & IO_PAGE_MASK; | 687 | ctx = fetch_sg_ctx(iommu, sglist); |
611 | 688 | ||
612 | npages = calc_npages(sglist, nelems); | 689 | spin_lock_irqsave(&iommu->lock, flags); |
613 | 690 | ||
614 | base = iommu->page_table + | 691 | sg = sglist; |
615 | ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); | 692 | while (nelems--) { |
693 | dma_addr_t dma_handle = sg->dma_address; | ||
694 | unsigned int len = sg->dma_length; | ||
695 | unsigned long npages, entry; | ||
696 | iopte_t *base; | ||
697 | int i; | ||
616 | 698 | ||
617 | spin_lock_irqsave(&iommu->lock, flags); | 699 | if (!len) |
700 | break; | ||
701 | npages = iommu_num_pages(dma_handle, len); | ||
702 | iommu_range_free(iommu, dma_handle, npages); | ||
618 | 703 | ||
619 | /* Record the context, if any. */ | 704 | entry = ((dma_handle - iommu->page_table_map_base) |
620 | ctx = 0; | 705 | >> IO_PAGE_SHIFT); |
621 | if (iommu->iommu_ctxflush) | 706 | base = iommu->page_table + entry; |
622 | ctx = (iopte_val(*base) & IOPTE_CONTEXT) >> 47UL; | ||
623 | 707 | ||
624 | /* Step 1: Kick data out of streaming buffers if necessary. */ | 708 | dma_handle &= IO_PAGE_MASK; |
625 | if (strbuf->strbuf_enabled) | 709 | if (strbuf->strbuf_enabled) |
626 | strbuf_flush(strbuf, iommu, bus_addr, ctx, npages, direction); | 710 | strbuf_flush(strbuf, iommu, dma_handle, ctx, |
711 | npages, direction); | ||
627 | 712 | ||
628 | /* Step 2: Clear out the TSB entries. */ | 713 | for (i = 0; i < npages; i++) |
629 | for (i = 0; i < npages; i++) | 714 | iopte_make_dummy(iommu, base + i); |
630 | iopte_make_dummy(iommu, base + i); | ||
631 | 715 | ||
632 | iommu_range_free(iommu, bus_addr, npages); | 716 | sg = sg_next(sg); |
717 | } | ||
633 | 718 | ||
634 | iommu_free_ctx(iommu, ctx); | 719 | iommu_free_ctx(iommu, ctx); |
635 | 720 | ||