11da177e4SLinus Torvalds /** 21da177e4SLinus Torvalds * iommu_fill_pdir - Insert coalesced scatter/gather chunks into the I/O Pdir. 31da177e4SLinus Torvalds * @ioc: The I/O Controller. 41da177e4SLinus Torvalds * @startsg: The scatter/gather list of coalesced chunks. 51da177e4SLinus Torvalds * @nents: The number of entries in the scatter/gather list. 61da177e4SLinus Torvalds * @hint: The DMA Hint. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This function inserts the coalesced scatter/gather list chunks into the 91da177e4SLinus Torvalds * I/O Controller's I/O Pdir. 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds static inline unsigned int 121da177e4SLinus Torvalds iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, 131da177e4SLinus Torvalds unsigned long hint, 141da177e4SLinus Torvalds void (*iommu_io_pdir_entry)(u64 *, space_t, unsigned long, 151da177e4SLinus Torvalds unsigned long)) 161da177e4SLinus Torvalds { 171da177e4SLinus Torvalds struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ 181da177e4SLinus Torvalds unsigned int n_mappings = 0; 191da177e4SLinus Torvalds unsigned long dma_offset = 0, dma_len = 0; 201da177e4SLinus Torvalds u64 *pdirp = NULL; 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds /* Horrible hack. For efficiency's sake, dma_sg starts one 231da177e4SLinus Torvalds * entry below the true start (it is immediately incremented 241da177e4SLinus Torvalds * in the loop) */ 251da177e4SLinus Torvalds dma_sg--; 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds while (nents-- > 0) { 281da177e4SLinus Torvalds unsigned long vaddr; 291da177e4SLinus Torvalds long size; 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds DBG_RUN_SG(" %d : %08lx/%05x %08lx/%05x\n", nents, 321da177e4SLinus Torvalds (unsigned long)sg_dma_address(startsg), cnt, 331da177e4SLinus Torvalds sg_virt_addr(startsg), startsg->length 341da177e4SLinus Torvalds ); 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds /* 381da177e4SLinus Torvalds ** Look for the start of a new DMA stream 391da177e4SLinus Torvalds */ 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds if (sg_dma_address(startsg) & PIDE_FLAG) { 421da177e4SLinus Torvalds u32 pide = sg_dma_address(startsg) & ~PIDE_FLAG; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds BUG_ON(pdirp && (dma_len != sg_dma_len(dma_sg))); 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds dma_sg++; 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds dma_len = sg_dma_len(startsg); 491da177e4SLinus Torvalds sg_dma_len(startsg) = 0; 501da177e4SLinus Torvalds dma_offset = (unsigned long) pide & ~IOVP_MASK; 511da177e4SLinus Torvalds n_mappings++; 521da177e4SLinus Torvalds #if defined(ZX1_SUPPORT) 531da177e4SLinus Torvalds /* Pluto IOMMU IO Virt Address is not zero based */ 541da177e4SLinus Torvalds sg_dma_address(dma_sg) = pide | ioc->ibase; 551da177e4SLinus Torvalds #else 561da177e4SLinus Torvalds /* SBA, ccio, and dino are zero based. 571da177e4SLinus Torvalds * Trying to save a few CPU cycles for most users. 581da177e4SLinus Torvalds */ 591da177e4SLinus Torvalds sg_dma_address(dma_sg) = pide; 601da177e4SLinus Torvalds #endif 611da177e4SLinus Torvalds pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]); 621da177e4SLinus Torvalds prefetchw(pdirp); 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds BUG_ON(pdirp == NULL); 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds vaddr = sg_virt_addr(startsg); 681da177e4SLinus Torvalds sg_dma_len(dma_sg) += startsg->length; 691da177e4SLinus Torvalds size = startsg->length + dma_offset; 701da177e4SLinus Torvalds dma_offset = 0; 711da177e4SLinus Torvalds #ifdef IOMMU_MAP_STATS 721da177e4SLinus Torvalds ioc->msg_pages += startsg->length >> IOVP_SHIFT; 731da177e4SLinus Torvalds #endif 741da177e4SLinus Torvalds do { 751da177e4SLinus Torvalds iommu_io_pdir_entry(pdirp, KERNEL_SPACE, 761da177e4SLinus Torvalds vaddr, hint); 771da177e4SLinus Torvalds vaddr += IOVP_SIZE; 781da177e4SLinus Torvalds size -= IOVP_SIZE; 791da177e4SLinus Torvalds pdirp++; 801da177e4SLinus Torvalds } while(unlikely(size > 0)); 811da177e4SLinus Torvalds startsg++; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds return(n_mappings); 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds /* 881da177e4SLinus Torvalds ** First pass is to walk the SG list and determine where the breaks are 891da177e4SLinus Torvalds ** in the DMA stream. Allocates PDIR entries but does not fill them. 901da177e4SLinus Torvalds ** Returns the number of DMA chunks. 911da177e4SLinus Torvalds ** 921da177e4SLinus Torvalds ** Doing the fill separate from the coalescing/allocation keeps the 931da177e4SLinus Torvalds ** code simpler. Future enhancement could make one pass through 941da177e4SLinus Torvalds ** the sglist do both. 951da177e4SLinus Torvalds */ 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds static inline unsigned int 98*d1b51632SFUJITA Tomonori iommu_coalesce_chunks(struct ioc *ioc, struct device *dev, 99*d1b51632SFUJITA Tomonori struct scatterlist *startsg, int nents, 1001da177e4SLinus Torvalds int (*iommu_alloc_range)(struct ioc *, size_t)) 1011da177e4SLinus Torvalds { 1021da177e4SLinus Torvalds struct scatterlist *contig_sg; /* contig chunk head */ 1031da177e4SLinus Torvalds unsigned long dma_offset, dma_len; /* start/len of DMA stream */ 1041da177e4SLinus Torvalds unsigned int n_mappings = 0; 105*d1b51632SFUJITA Tomonori unsigned int max_seg_size = dma_get_max_seg_size(dev); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds while (nents > 0) { 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds /* 1101da177e4SLinus Torvalds ** Prepare for first/next DMA stream 1111da177e4SLinus Torvalds */ 1121da177e4SLinus Torvalds contig_sg = startsg; 1131da177e4SLinus Torvalds dma_len = startsg->length; 1141da177e4SLinus Torvalds dma_offset = sg_virt_addr(startsg) & ~IOVP_MASK; 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds /* PARANOID: clear entries */ 1171da177e4SLinus Torvalds sg_dma_address(startsg) = 0; 1181da177e4SLinus Torvalds sg_dma_len(startsg) = 0; 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds /* 1211da177e4SLinus Torvalds ** This loop terminates one iteration "early" since 1221da177e4SLinus Torvalds ** it's always looking one "ahead". 1231da177e4SLinus Torvalds */ 1241da177e4SLinus Torvalds while(--nents > 0) { 1251da177e4SLinus Torvalds unsigned long prevstartsg_end, startsg_end; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds prevstartsg_end = sg_virt_addr(startsg) + 1281da177e4SLinus Torvalds startsg->length; 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds startsg++; 1311da177e4SLinus Torvalds startsg_end = sg_virt_addr(startsg) + 1321da177e4SLinus Torvalds startsg->length; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* PARANOID: clear entries */ 1351da177e4SLinus Torvalds sg_dma_address(startsg) = 0; 1361da177e4SLinus Torvalds sg_dma_len(startsg) = 0; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds /* 1391da177e4SLinus Torvalds ** First make sure current dma stream won't 1401da177e4SLinus Torvalds ** exceed DMA_CHUNK_SIZE if we coalesce the 1411da177e4SLinus Torvalds ** next entry. 1421da177e4SLinus Torvalds */ 1433cb1d958SMilind Arun Choudhary if(unlikely(ALIGN(dma_len + dma_offset + startsg->length, 1441da177e4SLinus Torvalds IOVP_SIZE) > DMA_CHUNK_SIZE)) 1451da177e4SLinus Torvalds break; 1461da177e4SLinus Torvalds 147*d1b51632SFUJITA Tomonori if (startsg->length + dma_len > max_seg_size) 148*d1b51632SFUJITA Tomonori break; 149*d1b51632SFUJITA Tomonori 1501da177e4SLinus Torvalds /* 1511da177e4SLinus Torvalds ** Next see if we can append the next chunk (i.e. 1521da177e4SLinus Torvalds ** it must end on one page and begin on another 1531da177e4SLinus Torvalds */ 1541da177e4SLinus Torvalds if (unlikely(((prevstartsg_end | sg_virt_addr(startsg)) & ~PAGE_MASK) != 0)) 1551da177e4SLinus Torvalds break; 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds dma_len += startsg->length; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds /* 1611da177e4SLinus Torvalds ** End of DMA Stream 1621da177e4SLinus Torvalds ** Terminate last VCONTIG block. 1631da177e4SLinus Torvalds ** Allocate space for DMA stream. 1641da177e4SLinus Torvalds */ 1651da177e4SLinus Torvalds sg_dma_len(contig_sg) = dma_len; 1663cb1d958SMilind Arun Choudhary dma_len = ALIGN(dma_len + dma_offset, IOVP_SIZE); 1671da177e4SLinus Torvalds sg_dma_address(contig_sg) = 1681da177e4SLinus Torvalds PIDE_FLAG 1691da177e4SLinus Torvalds | (iommu_alloc_range(ioc, dma_len) << IOVP_SHIFT) 1701da177e4SLinus Torvalds | dma_offset; 1711da177e4SLinus Torvalds n_mappings++; 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds return n_mappings; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 177