Demo
26 Nov 2024
Read time: 6 minute(s)
Mem to Device
SPI 驱动(详见
bsp/artinchip/hal/qspi/aic_hal_qspi.c)中调用了 DMA 进行数据传输,其使用过程可以当作 Demo 参考:
static s32 qspi_tx_rx_dma(u32 base, u8 *tx, u32 txlen, u8 *rx, u32 rxlen)
{
u32 poll_time, single_len;
s32 ret = 0;
single_len = 0;
struct aic_dma_chan *rx_dma, *tx_dma;
struct dma_slave_config dmacfg;
qspi_reset_fifo(base);
tx_dma = NULL;
rx_dma = NULL;
if (tx) {
spi_setbits(FCR_BIT_TX_DMA_EN, SPI_REG_FCR(base));
if (qspi_in_single_mode(base))
single_len = txlen;
qspi_set_xfer_cnt(base, txlen, 0, single_len, 0);
tx_dma = hal_request_dma_chan();
if (!tx_dma)
goto out;
dmacfg.direction = DMA_MEM_TO_DEV;
dmacfg.src_addr = (unsigned long)tx;
dmacfg.dst_addr = (unsigned long)SPI_REG_TXD(base);
dmacfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dmacfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dmacfg.src_maxburst = 1;
dmacfg.dst_maxburst = 1;
dmacfg.slave_id = 10; // FIXME: should set id according spi id
ret = hal_dma_chan_config(tx_dma, &dmacfg);
if (ret)
goto out;
ret = hal_dma_chan_prep_device(
tx_dma, (uint32_t)(unsigned long)SPI_REG_TXD(base),
(uint32_t)(unsigned long)tx, txlen, DMA_MEM_TO_DEV);
if (ret)
goto out;
ret = hal_dma_chan_start(tx_dma);
if (ret)
goto out;
/* Start transfer */
spi_setbits(TCR_BIT_XCH, SPI_REG_TCR(base));
poll_time = 0x7FFFFFFF;
while (!(readl(SPI_REG_ISR(base)) & ISR_BIT_TC)) {
poll_time--;
if (poll_time == 0) {
ret = -1;
spi_clrbits(FCR_BIT_TX_DMA_EN, SPI_REG_FCR(base));
hal_log_err("TX Transfer complete timeout at the end.\n");
goto out;
}
}
spi_setbits(ISR_BIT_TX_EMP, SPI_REG_ISR(base));
spi_setbits(ISR_BIT_TX_FULL, SPI_REG_ISR(base));
spi_setbits(ISR_BIT_TX_RDY, SPI_REG_ISR(base));
spi_setbits(ISR_BIT_TC, SPI_REG_ISR(base));
spi_clrbits(FCR_BIT_TX_DMA_EN, SPI_REG_FCR(base));
}
if (rx) {
spi_setbits(FCR_BIT_RX_DMA_EN, SPI_REG_FCR(base));
qspi_set_xfer_cnt(base, 0, rxlen, 0, 0);
rx_dma = hal_request_dma_chan();
if (!rx_dma)
goto out;
dmacfg.direction = DMA_DEV_TO_MEM;
dmacfg.src_addr = (unsigned long)SPI_REG_RXD(base);
dmacfg.dst_addr = (unsigned long)rx;
dmacfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dmacfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dmacfg.src_maxburst = 1;
dmacfg.dst_maxburst = 1;
dmacfg.slave_id = 10; // FIXME: should set id according spi id
ret = hal_dma_chan_config(rx_dma, &dmacfg);
if (ret)
goto out;
ret =
hal_dma_chan_prep_device(rx_dma, (uint32_t)(unsigned long)rx,
(uint32_t)(unsigned long)SPI_REG_RXD(base),
rxlen, DMA_DEV_TO_MEM);
if (ret)
goto out;
ret = hal_dma_chan_start(rx_dma);
if (ret)
goto out;
/* Start transfer */
spi_setbits(TCR_BIT_XCH, SPI_REG_TCR(base));
poll_time = 0x7FFFFFFF;
while (!(readl(SPI_REG_ISR(base)) & ISR_BIT_TC)) {
poll_time--;
if (poll_time == 0) {
ret = -1;
spi_clrbits(FCR_BIT_TX_DMA_EN, SPI_REG_FCR(base));
hal_log_err("RX Transfer complete timeout at the end.\n");
goto out;
}
}
spi_setbits(ISR_BIT_TC, SPI_REG_ISR(base));
spi_setbits(ISR_BIT_RX_EMP, SPI_REG_ISR(base));
spi_setbits(ISR_BIT_RX_FULL, SPI_REG_ISR(base));
spi_setbits(ISR_BIT_RX_RDY, SPI_REG_ISR(base));
spi_clrbits(FCR_BIT_TX_DMA_EN, SPI_REG_FCR(base));
}
out:
if (tx_dma) {
hal_dma_chan_stop(tx_dma);
hal_release_dma_chan(tx_dma);
}
if (rx_dma) {
hal_dma_chan_stop(rx_dma);
hal_release_dma_chan(rx_dma);
}
return ret;
}
Mem to Mem
本 Demo 是 test_dma_memcpy
的源码(bsp/examples/test-dma/test_dma.c),完成一次 Mem
的拷贝操作:
static void dma_test_cb(void *param)
{
printf("DMA complete, callback....\n");
}
static void cmd_test_dma_memcpy(int argc, char **argv)
{
struct dma_chan *chan = NULL;
uint32_t test_len = 0, align_len = 0;
char *src = NULL, *dest = NULL;
int ret, i;
uint32_t size = 0;
#ifdef RT_USING_POSIX_CLOCK
struct timespec start, end;
#endif
if (argc != 2) {
pr_err("Invalid parameter\n");
return;
}
sscanf((char *)argv[1], "%u", &test_len);
test_len = roundup(test_len, 8);
align_len = roundup(test_len, CACHE_LINE_SIZE);
src = aicos_malloc_align(0, align_len, CACHE_LINE_SIZE);
dest = aicos_malloc_align(0, align_len, CACHE_LINE_SIZE);
if ((src == NULL) || (dest == NULL)){
pr_err("Alloc %d mem fail!\n ", align_len);
goto free_mem;
}
printf("DMA memcpy test: src = 0x%lx, dest = 0x%lx, len = 0x%x\n",
(unsigned long)src, (unsigned long)dest, test_len);
for (i = 0;i < test_len; i++)
src[i] = i & 0xff;
#ifdef RT_USING_POSIX_CLOCK
clock_gettime(CLOCK_REALTIME, &start);
#endif
chan = dma_request_channel();
if (chan == NULL){
pr_err("Alloc dma chan fail!\n ");
goto free_mem;
}
ret = dmaengine_prep_dma_memcpy(chan, (unsigned long)dest, (unsigned long)src, test_len);
if (ret){
pr_err("dmaengine_prep_dma_memcpy fail! ret = %d\n ", ret);
goto free_chan;
}
ret = dmaengine_submit(chan, dma_test_cb, chan);
if (ret){
pr_err("dmaengine_submit fail! ret = %d\n ", ret);
goto free_chan;
}
dma_async_issue_pending(chan);
while (dmaengine_tx_status(chan, &size) != DMA_COMPLETE);
aicos_dcache_invalid_range((unsigned long *)src, align_len);
aicos_dcache_invalid_range((unsigned long *)dest, align_len);
#ifdef RT_USING_POSIX_CLOCK
clock_gettime(CLOCK_REALTIME, &end);
#endif
for (i = 0;i < test_len; i++){
if (dest[i] != src[i]){
printf("addr 0x%x err: src - 0x%x, dest - 0x%x\n",
i, src[i], dest[i]);
ret = -1;
}
}
if (ret)
printf("DMA test fail!\n");
else
printf("DMA test succeed!\n");
#ifdef RT_USING_POSIX_CLOCK
printf("DMA memcpy %u bytes, speed %.2f MB/s\n", align_len,
(float)align_len / 1024 / 1024 / time_diff(&start, &end));
#endif
free_chan:
if (chan)
dma_release_channel(chan);
free_mem:
if (src)
aicos_free_align(0, src);
if (dest)
aicos_free_align(0, dest);
}