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);
}
已解决! 转到解答。
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.
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.