Demo
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); }