/** * sg_next - return the next scatterlist entry in a list * @sg: The current sg entry * * Description: * Usually the next entry will be @sg@ + 1, but if this sg element is part * of a chained scatterlist, it could jump to the start of a new * scatterlist array. * **/ struct scatterlist *sg_next(struct scatterlist *sg) { #ifdef CONFIG_DEBUG_SG BUG_ON(sg->sg_magic != SG_MAGIC); #endif if (sg_is_last(sg)) return NULL; sg++; if (unlikely(sg_is_chain(sg))) sg = sg_chain_ptr(sg); return sg; }
static int sg_count_ents(struct scatterlist *sg_list, int nbytes, int *lbytes) { int nents = 0; while (nbytes > 0) { if (sg_is_chain(sg_list)) { DX_LOG_ERR("Unexpected chanined entry " "in sg (entry =0x%X) \n", nents); BUG(); } nents++; /* get the number of bytes in the last entry */ *lbytes = nbytes; nbytes -= sg_list->length; sg_list = sg_next(sg_list); } DX_LOG_DEBUG("nents %d last bytes %d\n",nents, *lbytes); return nents; }
int coh901318_lli_fill_sg(struct coh901318_pool *pool, struct coh901318_lli *lli, struct scatterlist *sgl, unsigned int nents, dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl, u32 ctrl_last, enum dma_data_direction dir, u32 ctrl_irq_mask) { int i; struct scatterlist *sg; u32 ctrl_sg; dma_addr_t src = 0; dma_addr_t dst = 0; u32 bytes_to_transfer; u32 elem_size; if (lli == NULL) goto err; spin_lock(&pool->lock); if (dir == DMA_TO_DEVICE) dst = dev_addr; else if (dir == DMA_FROM_DEVICE) src = dev_addr; else goto err; for_each_sg(sgl, sg, nents, i) { if (sg_is_chain(sg)) { /* sg continues to the next sg-element don't * send ctrl_finish until the last * sg-element in the chain */ ctrl_sg = ctrl_chained; } else if (i == nents - 1) ctrl_sg = ctrl_last; else ctrl_sg = ctrl ? ctrl : ctrl_last; if (dir == DMA_TO_DEVICE) /* increment source address */ src = sg_phys(sg); else /* increment destination address */ dst = sg_phys(sg); bytes_to_transfer = sg_dma_len(sg); while (bytes_to_transfer) { u32 val; if (bytes_to_transfer > MAX_DMA_PACKET_SIZE) { elem_size = MAX_DMA_PACKET_SIZE; val = ctrl_chained; } else { elem_size = bytes_to_transfer; val = ctrl_sg; } lli->control = val | elem_size; lli->src_addr = src; lli->dst_addr = dst; if (dir == DMA_FROM_DEVICE) dst += elem_size; else src += elem_size; BUG_ON(lli->link_addr & 3); bytes_to_transfer -= elem_size; lli = coh901318_lli_next(lli); } } spin_unlock(&pool->lock); return 0; err: spin_unlock(&pool->lock); return -EINVAL; }