3
votes

I am writing a driver for Altera Soc Developement Kit and need to support two modes of data transfer to/from a FPGA:

  1. FIFO transfers: When writing to (or reading from) an FPGA FIFO, the destination (or source) address must not be incremented by the DMA controller.
  2. non-FIFO transfers: These are normal (RAM-like) transfers where both the source and destination addresses require an increment for each word transferred.

The particular DMA controller I am using is the CoreLink DMA-330 DMA Controller and its Linux driver is pl330.c (drivers/dma/pl330.c). This DMA controller does provide a mechanism to switch between "Fixed-address burst" and "Incrementing-address burst" (these are synonymous with my "FIFO transfers" and "non-FIFO transfers"). The pl330 driver specifies which behavior it wants by setting the appropriate bits in the CCRn register

#define CC_SRCINC       (1 << 0)
#define CC_DSTINC       (1 << 14)

My question: it is not at all clear to me how clients of the pl330 (my driver, for example) should specify the address-incrementing behavior.

The DMA engine client API says nothing about how to specify this while the DMA engine provider API simply states:

Addresses pointing to RAM are typically incremented (or decremented) after each transfer. In case of a ring buffer, they may loop (DMA_CYCLIC). Addresses pointing to a device's register (e.g. a FIFO) are typically fixed.

without giving any detail as to how the address types are communicated to providers (in my case the pl300 driver).

The in the pl330_prep_slave_sg method it does:

 if (direction == DMA_MEM_TO_DEV) {                  
     desc->rqcfg.src_inc = 1;                        
     desc->rqcfg.dst_inc = 0;                        
     desc->req.rqtype = MEMTODEV;                    
     fill_px(&desc->px,                              
         addr, sg_dma_address(sg), sg_dma_len(sg));  
 } else {                                            
     desc->rqcfg.src_inc = 0;                        
     desc->rqcfg.dst_inc = 1;                        
     desc->req.rqtype = DEVTOMEM;                    
     fill_px(&desc->px,                              
         sg_dma_address(sg), addr, sg_dma_len(sg));  
 }    

where later, the desc->rqcfg.src_inc, and desc->rqcfg.dst_inc are used by the driver to specify the address-increment behavior.

This implies the following:

  • Specifying a direction = DMA_MEM_TO_DEV means the client wishes to pull data from a FIFO into RAM. And presumably DMA_DEV_TO_MEM means the client wishes to push data from RAM into a FIFO.
  • Scatter-gather DMA operations (for the pl300 at least) is restricted to cases where either the source or destination end point is a FIFO. What if I wanted to do a scatter-gather operation from system RAM into FPGA (non-FIFO) memory?

Am I misunderstanding and/or overlooking something? Does the DMA engine already provide a (undocumented) mechanism to specify address-increment behavior?

2

2 Answers

2
votes

Look at this

pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_prep_slave_sg = pl330_prep_slave_sg;

It means you have different approaches like you have read in documentation. RAM-like transfers could be done, I suspect, via device_prep_dma_memcpy().

0
votes

It appears to me (after looking to various drivers in a kernel) the only DMA transfer styles which allows you (indirectly) control auto-increment behavior is the ones which have enum dma_transfer_direction in its corresponding device_prep_... function.

And this parameter declared only for device_prep_slave_sg and device_prep_dma_cyclic, according to include/linux/dmaengine.h

Another option should be to use and struct dma_interleaved_template which allows you to specify increment behaviour directly. But support for this method is limited (only i.MX DMA driver does support it in 3.8 kernel, for example. And even this support seems to be limited)

So I think, we are stuck with device_prep_slave_sg case with all sg-related complexities for a some while.

That is that I am doing at the moment (although it is for accessing of some EBI-connected device on Atmel SAM9 SOC)

Another thing to consider is a device's bus width. memcopy-variant can perform different bus-width transfers, depending on a source and target addresses and sizes. And this may not match size of FIFO element.