AnsweredAssumed Answered

SDMA from EIM linux 3.19 dmaengine API...  error.

Question asked by Joshua Clayton on May 15, 2015
Latest reply on May 21, 2015 by Joshua Clayton

I'm try to use the dma coprocessor to stream data from a set of EIM register addresses.

I'm using a 3.19 mainline kernel, so I'm using the dmaengine api.

 

dmaengine_prep_dma_cyclic() seems like the proper function to prepare since I want the dma to run in the background for a long time. I copied some of my approach from drivers/tty/serial/atmel_serial.c, which was the only non-audio driver I could find that uses dmaengine_prep_dma_cyclic().

 

I seem to set up correctly, but the callback function indicates my sdma channel is in error:

 

[   29.243776] mydevice: dma in error state!
[   29.249247] mydevice: dma residue: 4028

Any ideas what I am doing wrong?

Have I got the right idea about the ap_2_ap script?

Is my dma slave channel config ok?

 

Code below:

 

In my dts I include :

 

dmas = <&sdma 14 16 0>;

dma-names = "rx";

 

This should set sdma event source to external event 1, (14), use the ap_to_ap script (16), and set t to high priority (0)

 

MX6QDL_PAD_DISP0_DAT17__SDMA_EXT_EVENT1 0x1b0b0

 

Driver code:

 

static void my_dma_callback(void *arg)

{

        struct my_device *my = &my_device;

        enum dma_status dmastat;

        struct dma_tx_state state;

 

        dmastat = dmaengine_tx_status(my->dma.chan, my->dma.cookie, &state);

        if (dmastat == DMA_ERROR) {

                dev_err(my->dev, "dma in error state!\n");

        }

 

        dma_sync_sg_for_cpu(my->dev, &my->dma.sg, 1, DMA_DEV_TO_MEM);

        /*

         * TODO: process data here

         */

        dev_warn(my->dev, "dma residue: %d\n", state.residue);

        dma_sync_sg_for_device(my->dev, &my->dma.sg, 1, DMA_DEV_TO_MEM);

}

 

static long my_dma_probe(struct my_device *my)

{

        struct my_dma *dma = &my->dma;

        struct regmap *gpr;

        struct resource res;

        long nent, ret;

 

        gpr = syscon_regmap_lookup_by_phandle(my->dev->of_node, "gpr");

        if (IS_ERR(gpr)) {

                ret = PTR_ERR(gpr);

                dev_err(my->dev, "failed to get dma regmap: %ld\n", ret);

                return ret;

        }

 

        regmap_update_bits(gpr, IOMUXC_GPR0, IMX6Q_GPR0_DMAREQ_MUX_SEL7_MASK,

                           IMX6Q_GPR0_DMAREQ_MUX_SEL7_IOMUX);

 

        ret = of_address_to_resource(my->dev->of_node, 0, &res);

        if (ret) {

                dev_err(my->dev, "cannot get eim phy addr: %ld\n", ret);

                return ret;

        }

 

        dma->chan = dma_request_slave_channel_reason(my->dev, "rx");

        if (IS_ERR(dma->chan)) {

                ret = PTR_ERR(dma->chan);

                dev_err(my->dev, "cannot get the rx DMA channel: %ld\n", ret);

                return ret;

        }

 

        memset(&dma->cfg, 0, sizeof(dma->cfg));

        dma->cfg.direction = DMA_DEV_TO_MEM;

        dma->cfg.src_addr = res.start + FPGA_EIM_DATASRC;

        dma->cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;

        dma->cfg.src_maxburst = sizeof(struct sample) / 4;

 

        ret = dmaengine_slave_config(dma->chan, &dma->cfg);

        if (ret) {

                dev_err(my->dev, "cannot configure in RX dma: %ld\n", ret);

                return ret;

        }

 

        sg_init_table(&my->dma.sg, 1);

        sg_set_page(&my->dma.sg, virt_to_page(my->dma.ring.buf),

                        4028, (int)my->dma.ring.buf & ~PAGE_MASK);

        nent = dma_map_sg(my->dev, &my->dma.sg, 1, DMA_FROM_DEVICE);

        if (!nent) {

                dev_err(my->dev, "cannot map scatterlist\n");

                return -ENODEV;

        }

 

        return 0;

}

 

static void my_dma_release(struct my_device *my)

{

        dma_release_channel(my->dma.chan);

        my->dma.chan = NULL;

}

 

static void my_dma_start(struct my_device *my)

{

        struct dma_async_tx_descriptor *desc;

 

        desc = dmaengine_prep_dma_cyclic(my->dma.chan,

                        sg_dma_address(&my->dma.sg),

                        sg_dma_len(&my->dma.sg),

                        sg_dma_len(&my->dma.sg) >> 1,

                        DMA_DEV_TO_MEM,

                        DMA_PREP_INTERRUPT);

        if (!desc) {

                dev_err(my->dev, "failed to prepare dma\n");

                return;

        }

 

        desc->callback = my_dma_callback;

        desc->callback_param = NULL;

 

        dmaengine_submit(desc);

        dma_async_issue_pending(my->dma.chan);

}

 

static void my_dma_stop(struct my_device *my)

{

        dmaengine_terminate_all(my->dma.chan);

}

Outcomes