This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DMA to and from GPMC using dmaengine

I'm trying to do DMA to and from GPMC in linux. This will work if I use a MEM_TO_MEM transaction through device_prep_dma_memcpy(). The caveat is that the dma driver will set the transaction to 32-bit bursts, which limits the GPMC bursts to 2x16bit. I've been trying to use dmaengine_prep_slave_single(), but I can't figure out how to get the transfer working. The transfer never starts after I issue dma_async_issue_pending();

I've tried quite a number of combinations, but I just can't figure out what it is that I'm missing. How can I get this running properly?

Code looks something like:

static void dma_callback(void *param)
{
	complete(&dma_comp);
}

	int ch_num = 52;
	dma_buf = dma_alloc_coherent(NULL, MAX_DMA_TRANSFER_IN_BYTES,
					      &dma_phys_addr, GFP_KERNEL);
	dma_cap_zero(mask);
	dma_cap_set(DMA_SLAVE, mask);
	dma_chan = dma_request_channel(mask, edma_filter_fn, &ch_num);

int my_dma_copy(struct my_dev_class* my_dev, dma_addr_t dest_addr,
		  dma_addr_t src_addr, int count, bool is_write)
{
	unsigned int ch_num = 52;

	dir = is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
	dma_addr_t mem_dma_addr = is_write ? src_addr : dest_addr;

	struct dma_slave_config	conf = {
		.src_addr = src_addr,
		.dst_addr = dest_addr,
		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
		.src_maxburst = 16,
		.dst_maxburst = 16,
		.device_fc = 0,
		.slave_id = 0,
	};
	
	result = dmaengine_slave_config(chan, &conf);

	/* This works, but does not do GPMC bursts */
	tx = dma_chan->device->device_prep_dma_memcpy(chan, trgt_addr, src_addr,
							count, 
							DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
	/* This doesn't trigger any data over GPMC */
	//tx = dmaengine_prep_slave_single(dma_chan, mem_dma_addr, count, dir, 
	//					DMA_CTRL_ACK | DMA_PREP_INTERRUPT); 
	reinit_competion(&dma_comp);

	tx->callback = dma_callback;
	cookie = dmaengine_submit(tx);

	dma_async_issue_pending(chan);
	wait_for_completion(&dma_comp);
}

	dma_release_channel(dma_chan);
	dma_free_coherent(NULL, MAX_DMA_TRANSFER_IN_BYTES, dma_buf, dma_phys_addr);

  • Hi Alexandru,

    Which TI device you are using? Which TI SDK you are using?

    Regards,
    Pavel
  • Hi Pavel,

    We're seeing this with either a AM3354ZCZ80, or AM3352BZCZD80. I have no knowledge which TI SDK the linux sources I've inherited come from. They're based on linux 3.18.

  • Alexandru,

    There is no TI SDK which comes with linux kernel 3.18. Is your linux based on Arago or other linux distribution (like Debian, Ubuntu)? Can you provide the full boot up log? Do you use TI board or BeagleBone board or custom board?

    Regards,
    Pavel
  • I would also suggest you to try with the latest TI SDK (am335x 03.01.00.06) which is based on kernel 4.4.19. For example of dmaengine_prep_slave_single() usage, see the below files:

    linux-kernel/drivers/tty/serial/8250/8250_dma.c
    linux-kernel/drivers/tty/serial/8250/8250_omap.c
    linux-kernel/drivers/crypto/omap-sham.c

    Regards,
    Pavel
  • WOW! That's 2300+ patches on top of 4.4.19. I rebased our driver on top of the TI linux, and the DMA_MEMCPY case is working beautifully. I was suspecting that the edma driver in 3.18 setting acnt to 4 restricted the GPMC burst.

    As far as the DMA_SLAVE and dmaengine_prep_slave_single() go, they still hang without doing any IO over GPMC. Is GPMC (non-NAND) restricted to dma_memcpy transactions?

  • Alexandru,

    From what I understand you need to transfer data (with EDMA) from GPMC (in non-NAND mode) to memory. Can you confirm my understanding is right?

    For GPMC in NOR/SRAM mode (not NAND) I think you can not use the GPMCEVT (EDMA event #52), as this EDMA event is specific for NAND pre-fetch and write posting engine. For GPMC connected to FPGA in NOR/SRAM mode, you might use the xdma_event_intr[2:0] pins. With one of these pins you can send DMA request from the external FPGA to EDMA controller and make the data transfer from GPMC memory mapped area (512MB, 0x0 to 0x1FFFFFFF) to memory (OCMC RAM or DDR3 RAM).

    See also if the below pointers will be in help:

    e2e.ti.com/.../516225
    e2e.ti.com/.../542894
    e2e.ti.com/.../431021
    e2e.ti.com/.../552280
    e2e.ti.com/.../421591
    e2e.ti.com/.../1938446

    Regarding DMA_SLAVE usage, refer to the below pointers:

    processors.wiki.ti.com/.../MCSDK_UG_Chapter_Exploring
    linux-kernel/drivers/dma/edma.c
    linux-kernel/drivers/tty/serial/8250/8250_edma.c
    linux-kernel/drivers/mtd/nand/omap.c

    Regards,
    Pavel
  • Hi Pavel. Thank you very much for all the information provided.

    Yes, we need to transfer data to/from GPMC in non-nand mode.

    I now have the bits I need in order to formulate a plan. Because the board design did not plan for using xdma_event_intr pins, we'll have to stick with DMA_MEMCPY. This is sufficiently efficient with the dmaengine driver in linux 4.4.

    Since the solution is to be based on OpenWRT, I have made the following design decisions in order to do efficient DMA to and from GPMC:

    • Use the openwrt kernel, which is based on linux 4.4.14
    • Use DMA_MEMCPY transfers
    • Cherry-pick "dmaengine: edma: Fix paRAM slot allocation for entry channel 0" patch from ti-linux

    This is currently working very well in our test setup. I am very grateful that TI is actively involved in upstreaming their linux support.