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

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

Jump to solution
1,502 Views
joshuaclayton
Contributor III

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

}

Labels (2)
Tags (4)
0 Kudos
1 Solution
755 Views
joshuaclayton
Contributor III

For the record, using the ap_2_ap script is not supported _at_ _all_ in the mainline kernel.

Freescale latest 3.14 has a memcpy sdma prep function, but that doesn't really do what I need either.

I need DMA_DEV_TO_MEM type behavior out of ap_2_ap.

I'm trying to see if I can beat the kernel into doing what I want reading a series of registers every time the external event pin goes high. But I am afraid that I will probably need to write my own script and load it dynamically.

View solution in original post

0 Kudos
1 Reply
756 Views
joshuaclayton
Contributor III

For the record, using the ap_2_ap script is not supported _at_ _all_ in the mainline kernel.

Freescale latest 3.14 has a memcpy sdma prep function, but that doesn't really do what I need either.

I need DMA_DEV_TO_MEM type behavior out of ap_2_ap.

I'm trying to see if I can beat the kernel into doing what I want reading a series of registers every time the external event pin goes high. But I am afraid that I will probably need to write my own script and load it dynamically.

0 Kudos