Edit online

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