FSL DMA engine transfer to PCI memory
David Laight
David.Laight at ACULAB.COM
Tue Jan 25 19:56:09 EST 2011
> I'm trying to use FSL DMA engine to perform DMA transfer from
> memory buffer obtained by kmalloc() to PCI memory. This is on
> custom board based on P2020 running linux-2.6.35. The PCI
> device is Altera FPGA, connected directly to SoC PCI-E controller.
You'll need to use the dma engine that is part of the PCIe
interface in order to get large PCIe transfers.
I think everything else will still generate single 32bit PCIe
transfers - which are (if your measurements match mine)
exceptionally lethargic - the ISA bus is faster!
That does work provided you remember to give the dma controller
physical addresses and byteswap absolutely everything.
(Oh, and I didn't get single word transfers to work - they locked
the dma controller - not a problem since they are faster by PIO.)
Note that the PPC Linux (Linux in general??) doesn't have a
'virtual to physical' function that works for all addresses,
you'll need to remember the physical address of the PCIe slave
and use malloc'ed memory for the descriptors (on which
virt_to_phys() actually works).
I don't think there is a standard device driver for the PCIe dma,
I couldn't even find any header files that were vaugely relevent
except in the uboot sources.
I certainly wrote some code that just assumes it is on the right
hardware!
These are the relevant bits of code ....
Global initialisation:
/* Enable the read/write dma controllers */
csb_ctrl = in_le32(&pex->pex_csb_ctrl);
csb_ctrl |= PEX_CSB_CTRL_WDMAE | PEX_CSB_CTRL_RDMAE;
out_le32(&pex->pex_csb_ctrl, csb_ctrl);
/* We don't rely on the dma polling the descriptor, I have NFI
* whether the default of 0 means 'never poll' or 'poll very
quickly'.
* Set a large slow value for sanity. */
out_le32(&pex->pex_dms_dstmr, ~0u);
Transfer setup:
/* We only support aligned writes - caller must verify */
dma_ctrl = PDMAD_CTRL_VALID;
dma_ctrl |= PDMAD_CTRL_SNOOP_CSB;
dma_ctrl |= PDMAD_CTRL_1ST_BYTES | PDMAD_CTRL_LAST_BYTES;
dma_ctrl |= PDMAD_CTRL_NEXT_VALID;
dma_ctrl |= len << (PDMAD_CTRL_LEN_SHIFT - 2);
/* Fill in DMA descriptor */
st_le32(&desc->pdmad_ctrl, dma_ctrl);
/* We MUST clear the status - otherwise the xfer will be skipped */
st_le32(&desc->pdmad_stat, 0);
st_le32(&desc->pdmad_src_address, src_phys);
st_le32(&desc->pdmad_dst_address, dst_phys);
st_le32(&desc->pdmad_next_desc, 0);
/* Clear old status */
st_le32(&pex_dma->pex_dma_stat, in_le32(&pex_dma->pex_dma_stat));
/* Give descriptor address to dma engine */
st_le32(&pex_dma->pex_dma_addr, virt_to_phys(desc));
/* Wait for all above memory cycles, then start xfer */
iosync();
st_le32(&pex_dma->pex_dma_ctrl, PEX_DMA_CTRL_START |
PEX_DMA_CTRL_SNOOP);
Poll for completion:
/* Wait for transfer to complete/fail */
do {
desc_stat = ld_le32(&desc->pdmad_stat);
} while (!(desc_stat & PDMAD_STAT_DONE));
status = ld_le32(&pex_dma->pex_dma_stat);
if (status == (PEX_DMA_STAT_DSCPL | PEX_DMA_STAT_CHCPL)
&& desc_stat == PDMAD_STAT_DONE)
/* Transfer ok */
return 0;
/* Transfer failed */
Oh, since I couldn't find it in the documentation, the first
word of the dma descriptor is 'ctrl' and the last 'next_desc'.
David
More information about the Linuxppc-dev
mailing list