Scatter-gather mode

A special variant of freerunning (ie memory to memory) mode is available called scatter-gather mode. This means that the data from non-contiguous memory areas can be collated together into a single buffer, or vice-versa. As with Freerunning mode, a transfer can be configured for a single location, a linear (one dimensional) array or a rectangular (two dimensional) array. At present, there is no explicit support for gathering data directly from or to a paced peripheral.

As the DMA API uses the generic Linux struct scatterlist support, a working knowledge of scatterlists and how to compile them is required to configure transfers in this mode correctly. Documentation on this system can be found in the Linux Kernel Documentation on DMA. This is available in the kernel sources at Documentation/DMA-mapping.txt and DMA-API.txt.

A scatter-gather transfer can either be from a non-contiguous memory area to a contiguous one or vice versa. It is not possible to transfer between two non-contiguous areas in this mode; although a linked list of generic memory-memory transfers could achieve this functionality. The mode parameter passed to declare_dma_parms() (for STLinux-2.2) or dma_params_init() (for STLinux-2.3) indicates the mode of operation, and can be either:

  • MODE_SRC_SCATTER - for non-contiguous to contiguous transfers
  • MODE_DST_SCATTER - for contiguous to non-contiguous transfers

As with simple freerunning transfers, the transfer descriptor must have the dimenations parameter initialised, by calling one of the dma_params_DIM_a_x_b.

The function dma_params_sg() must for called for all scatter-gather mode transfers to specify the scatter gather addresses and lengths:

void dma_params_sg(struct stm_dma_params *p,
                   struct scatterlist *sg,
                   int nents)

The parameters are described in the table below:

Parameters Description
p This is a pointer to the stm_dma_params structure for the transfer.
sg This is a pointer to a populated scatterlist structure. This structure describes the scatterlist.
nents This is the number of elements in the scatterlist.

These parameters specify the scatter-gather side of the transfer (the source or destination). The address for other side of the transfer (the destination or source) is specified using dma_params_addrs described above.

Use the function dma_parms_sg() to configure a scatter-gather mode transfer.

static inline  void dma_parms_sg(	struct stm_dma_params *p,
					struct scatterlist * sg,
					int nents)

The parameters are described in the table below:

Parameters Description
p This is a pointer to the stm_dma_params structure for the transfer.
sg This is a pointer to a populated scatterlist structure. This structure describes the scatterlist.
nents This is the number of elements in the scatterlist.

Example:

static int test_scatterlist(void)
{
 
	const char * dmac_id =STM_DMAC_ID;
	const char * hb_cap_channel = STM_DMA_CAP_HIGH_BW;
 
	int i=0;
	unsigned long xfer_bytes =2048;
	struct scatterlist sg[10];
 
	unsigned long * src_buff = (unsigned long*)kmalloc(xfer_bytes,GFP_KERNEL);
	unsigned long * dst_buff = (unsigned long*)kmalloc(xfer_bytes,GFP_KERNEL);
	unsigned long buffer=(u32)&src_buff;
	struct stm_dma_params dmap0;
 
	int ch_num = request_dma_bycap(&dmac_id,&hb_cap_channel,__FUNCTION__);	
	printk("%s IN ch%d\n",__FUNCTION__,ch_num);
 
	if(ch_num == -ENODEV){
		printk(" No DMA channel or DMAC available\n");
		return -ENODEV;
	}
 
	declare_dma_parms(
					&dmap0,
					MODE_SRC_SCATTER,
					STM_DMA_LIST_OPEN,
					STM_DMA_SETUP_CONTEXT_TASK,
					STM_DMA_NOBLOCK_MODE,
					STM_DMAC_ID);
 
	for(i;i < 2048; i++){
		src_buff[i] = 0x1234;	
	}
 
	dma_cache_wback(&src_buff[0],sizeof(u32)*xfer_bytes);	
 
	for(i=0;i < 10;i++){
		sg[i].dma_address = 	(unsigned long )virt_to_bus((void*)buffer); 
		sg[i].length = 128;		
		buffer +=sg[i].length;		
	}	
	dma_parms_addrs(&dmap0,virt_to_phys(src_buff),virt_to_phys(dst_buff),xfer_bytes);
	dma_parms_sg(&dmap0,&sg[0],10);
	dma_parms_DIM_1_x_1(&dmap0,sizeof(unsigned long));
	dma_parms_interrupts(&dmap0,STM_DMA_INTER_NODE_PAUSE);
	dma_parms_comp_cb(&dmap0,test_scatterlist_callback,(void*)ch_num,0);
	dma_compile_list(&dmap0);
	dma_xfer_list(ch_num,&dmap0);
	dma_wait_for_completion(ch_num);
	printk("%s SRC_SG TRANSFER 1 COMPLETED\n\n",__FUNCTION__);
 
	printk("%s remapping node for SRC_SG 2\n",__FUNCTION__);
 
 
	dma_parms_sg(&dmap0,&sg[0],7);
	printk(" set dmap lsitlen to %d\n",dmap0.priv.sublist_nents);	
	dma_compile_list(&dmap0);
	dma_xfer_list(ch_num,&dmap0);
	dma_wait_for_completion(ch_num);
	printk("%s SRC_SG TRANSFER 2 COMPLETED\n\n",__FUNCTION__);
 
 
	dma_free_descriptor(&dmap0);
	free_dma(ch_num);
	kfree(src_buff);
	kfree(dst_buff);
}