* [PATCH v3 2/2] dmaengine: Add support for multiple descriptors for imx-dma.
@ 2012-03-02 8:28 ` Javier Martin
0 siblings, 0 replies; 5+ messages in thread
From: Javier Martin @ 2012-03-02 8:28 UTC (permalink / raw)
To: linux-arm-kernel
dmaengine specifies the possibility that several descriptors
can be queued for transfer. It also indicates that tasklets
must be used for DMA callbacks.
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
---
Added comment explaining fall through in 'imxdma_xfer_desc'.
---
drivers/dma/imx-dma.c | 332 ++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 258 insertions(+), 74 deletions(-)
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 9aa6e85..c32103f 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -5,6 +5,7 @@
* found on i.MX1/21/27
*
* Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com>
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
@@ -13,6 +14,7 @@
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -30,19 +32,52 @@
#include <mach/dma-v1.h>
#include <mach/hardware.h>
+#define IMXDMA_MAX_CHAN_DESCRIPTORS 16
+
+enum imxdma_prep_type {
+ IMXDMA_DESC_MEMCPY,
+ IMXDMA_DESC_INTERLEAVED,
+ IMXDMA_DESC_SLAVE_SG,
+ IMXDMA_DESC_CYCLIC,
+};
+
+struct imxdma_desc {
+ struct list_head node;
+ struct dma_async_tx_descriptor desc;
+ enum dma_status status;
+ dma_addr_t src;
+ dma_addr_t dest;
+ size_t len;
+ unsigned int dmamode;
+ enum imxdma_prep_type type;
+ /* For memcpy and interleaved */
+ unsigned int config_port;
+ unsigned int config_mem;
+ /* For interleaved transfers */
+ unsigned int x;
+ unsigned int y;
+ unsigned int w;
+ /* For slave sg and cyclic */
+ struct scatterlist *sg;
+ unsigned int sgcount;
+};
+
struct imxdma_channel {
struct imxdma_engine *imxdma;
unsigned int channel;
unsigned int imxdma_channel;
+ struct tasklet_struct dma_tasklet;
+ struct list_head ld_free;
+ struct list_head ld_queue;
+ struct list_head ld_active;
+ int descs_allocated;
enum dma_slave_buswidth word_size;
dma_addr_t per_address;
u32 watermark_level;
struct dma_chan chan;
spinlock_t lock;
- struct dma_async_tx_descriptor desc;
dma_cookie_t last_completed;
- enum dma_status status;
int dma_request;
struct scatterlist *sg_list;
};
@@ -61,27 +96,31 @@ static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan)
return container_of(chan, struct imxdma_channel, chan);
}
-static void imxdma_handle(struct imxdma_channel *imxdmac)
+static inline bool imxdma_chan_is_doing_cyclic(struct imxdma_channel *imxdmac)
{
- if (imxdmac->desc.callback)
- imxdmac->desc.callback(imxdmac->desc.callback_param);
- imxdmac->last_completed = imxdmac->desc.cookie;
+ struct imxdma_desc *desc;
+
+ if (!list_empty(&imxdmac->ld_active)) {
+ desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc,
+ node);
+ if (desc->type == IMXDMA_DESC_CYCLIC)
+ return true;
+ }
+ return false;
}
static void imxdma_irq_handler(int channel, void *data)
{
struct imxdma_channel *imxdmac = data;
- imxdmac->status = DMA_SUCCESS;
- imxdma_handle(imxdmac);
+ tasklet_schedule(&imxdmac->dma_tasklet);
}
static void imxdma_err_handler(int channel, void *data, int error)
{
struct imxdma_channel *imxdmac = data;
- imxdmac->status = DMA_ERROR;
- imxdma_handle(imxdmac);
+ tasklet_schedule(&imxdmac->dma_tasklet);
}
static void imxdma_progression(int channel, void *data,
@@ -89,8 +128,88 @@ static void imxdma_progression(int channel, void *data,
{
struct imxdma_channel *imxdmac = data;
- imxdmac->status = DMA_SUCCESS;
- imxdma_handle(imxdmac);
+ tasklet_schedule(&imxdmac->dma_tasklet);
+}
+
+static int imxdma_xfer_desc(struct imxdma_desc *d)
+{
+ struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
+ int ret;
+
+ /* Configure and enable */
+ switch (d->type) {
+ case IMXDMA_DESC_MEMCPY:
+ ret = imx_dma_config_channel(imxdmac->imxdma_channel,
+ d->config_port, d->config_mem, 0, 0);
+ if (ret < 0)
+ return ret;
+ ret = imx_dma_setup_single(imxdmac->imxdma_channel, d->src,
+ d->len, d->dest, d->dmamode);
+ if (ret < 0)
+ return ret;
+ break;
+ case IMXDMA_DESC_CYCLIC:
+ ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel,
+ imxdma_progression);
+ if (ret < 0)
+ return ret;
+ /*
+ * We fall through here since cyclic transfer is the same as
+ * slave_sg adding a progression handler and a specific sg
+ * configuration which is done in 'imxdma_prep_dma_cyclic'.
+ */
+ case IMXDMA_DESC_SLAVE_SG:
+ if (d->dmamode == DMA_MODE_READ)
+ ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg,
+ d->sgcount, d->len, d->src, d->dmamode);
+ else
+ ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg,
+ d->sgcount, d->len, d->dest, d->dmamode);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ imx_dma_enable(imxdmac->imxdma_channel);
+ return 0;
+}
+
+static void imxdma_tasklet(unsigned long data)
+{
+ struct imxdma_channel *imxdmac = (void *)data;
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
+
+ spin_lock(&imxdmac->lock);
+
+ if (list_empty(&imxdmac->ld_active)) {
+ /* Someone might have called terminate all */
+ goto out;
+ }
+ desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node);
+
+ if (desc->desc.callback)
+ desc->desc.callback(desc->desc.callback_param);
+
+ imxdmac->last_completed = desc->desc.cookie;
+
+ /* If we are dealing with a cyclic descriptor keep it on ld_active */
+ if (imxdma_chan_is_doing_cyclic(imxdmac))
+ goto out;
+
+ list_move_tail(imxdmac->ld_active.next, &imxdmac->ld_free);
+
+ if (!list_empty(&imxdmac->ld_queue)) {
+ desc = list_first_entry(&imxdmac->ld_queue, struct imxdma_desc,
+ node);
+ list_move_tail(imxdmac->ld_queue.next, &imxdmac->ld_active);
+ if (imxdma_xfer_desc(desc) < 0)
+ dev_warn(imxdma->dev, "%s: channel: %d couldn't xfer desc\n",
+ __func__, imxdmac->channel);
+ }
+out:
+ spin_unlock(&imxdmac->lock);
}
static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -99,12 +218,17 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct dma_slave_config *dmaengine_cfg = (void *)arg;
int ret;
+ unsigned long flags;
unsigned int mode = 0;
switch (cmd) {
case DMA_TERMINATE_ALL:
- imxdmac->status = DMA_ERROR;
imx_dma_disable(imxdmac->imxdma_channel);
+
+ spin_lock_irqsave(&imxdmac->lock, flags);
+ list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+ list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
return 0;
case DMA_SLAVE_CONFIG:
if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) {
@@ -155,11 +279,14 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan,
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
dma_cookie_t last_used;
enum dma_status ret;
+ unsigned long flags;
+ spin_lock_irqsave(&imxdmac->lock, flags);
last_used = chan->cookie;
ret = dma_async_is_complete(cookie, imxdmac->last_completed, last_used);
dma_set_tx_state(txstate, imxdmac->last_completed, last_used, 0);
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
return ret;
}
@@ -172,7 +299,6 @@ static dma_cookie_t imxdma_assign_cookie(struct imxdma_channel *imxdma)
cookie = 1;
imxdma->chan.cookie = cookie;
- imxdma->desc.cookie = cookie;
return cookie;
}
@@ -181,12 +307,15 @@ static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan);
dma_cookie_t cookie;
+ unsigned long flags;
- spin_lock_irq(&imxdmac->lock);
+ spin_lock_irqsave(&imxdmac->lock, flags);
+ list_move_tail(imxdmac->ld_free.next, &imxdmac->ld_queue);
cookie = imxdma_assign_cookie(imxdmac);
+ tx->cookie = cookie;
- spin_unlock_irq(&imxdmac->lock);
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
return cookie;
}
@@ -199,21 +328,48 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan)
if (data != NULL)
imxdmac->dma_request = data->dma_request;
- dma_async_tx_descriptor_init(&imxdmac->desc, chan);
- imxdmac->desc.tx_submit = imxdma_tx_submit;
- /* txd.flags will be overwritten in prep funcs */
- imxdmac->desc.flags = DMA_CTRL_ACK;
+ while (imxdmac->descs_allocated < IMXDMA_MAX_CHAN_DESCRIPTORS) {
+ struct imxdma_desc *desc;
- imxdmac->status = DMA_SUCCESS;
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ break;
+ __memzero(&desc->desc, sizeof(struct dma_async_tx_descriptor));
+ dma_async_tx_descriptor_init(&desc->desc, chan);
+ desc->desc.tx_submit = imxdma_tx_submit;
+ /* txd.flags will be overwritten in prep funcs */
+ desc->desc.flags = DMA_CTRL_ACK;
+ desc->status = DMA_SUCCESS;
+
+ list_add_tail(&desc->node, &imxdmac->ld_free);
+ imxdmac->descs_allocated++;
+ }
- return 0;
+ if (!imxdmac->descs_allocated)
+ return -ENOMEM;
+
+ return imxdmac->descs_allocated;
}
static void imxdma_free_chan_resources(struct dma_chan *chan)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+ struct imxdma_desc *desc, *_desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdmac->lock, flags);
imx_dma_disable(imxdmac->imxdma_channel);
+ list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+ list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
+
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
+
+ list_for_each_entry_safe(desc, _desc, &imxdmac->ld_free, node) {
+ kfree(desc);
+ imxdmac->descs_allocated--;
+ }
+ INIT_LIST_HEAD(&imxdmac->ld_free);
if (imxdmac->sg_list) {
kfree(imxdmac->sg_list);
@@ -228,23 +384,19 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct scatterlist *sg;
- int i, ret, dma_length = 0;
- unsigned int dmamode;
+ int i, dma_length = 0;
+ struct imxdma_desc *desc;
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
for_each_sg(sgl, sg, sg_len, i) {
dma_length += sg->length;
}
- if (direction == DMA_DEV_TO_MEM)
- dmamode = DMA_MODE_READ;
- else
- dmamode = DMA_MODE_WRITE;
-
switch (imxdmac->word_size) {
case DMA_SLAVE_BUSWIDTH_4_BYTES:
if (sgl->length & 3 || sgl->dma_address & 3)
@@ -260,12 +412,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
return NULL;
}
- ret = imx_dma_setup_sg(imxdmac->imxdma_channel, sgl, sg_len,
- dma_length, imxdmac->per_address, dmamode);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_SLAVE_SG;
+ desc->sg = sgl;
+ desc->sgcount = sg_len;
+ desc->len = dma_length;
+ if (direction == DMA_DEV_TO_MEM) {
+ desc->dmamode = DMA_MODE_READ;
+ desc->src = imxdmac->per_address;
+ } else {
+ desc->dmamode = DMA_MODE_WRITE;
+ desc->dest = imxdmac->per_address;
+ }
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
@@ -274,23 +435,18 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
- int i, ret;
+ struct imxdma_desc *desc;
+ int i;
unsigned int periods = buf_len / period_len;
- unsigned int dmamode;
dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n",
__func__, imxdmac->channel, buf_len, period_len);
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
- ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel,
- imxdma_progression);
- if (ret) {
- dev_err(imxdma->dev, "Failed to setup the DMA handler\n");
- return NULL;
- }
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
if (imxdmac->sg_list)
kfree(imxdmac->sg_list);
@@ -316,17 +472,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
imxdmac->sg_list[periods].page_link =
((unsigned long)imxdmac->sg_list | 0x01) & ~0x02;
- if (direction == DMA_DEV_TO_MEM)
- dmamode = DMA_MODE_READ;
- else
- dmamode = DMA_MODE_WRITE;
-
- ret = imx_dma_setup_sg(imxdmac->imxdma_channel, imxdmac->sg_list, periods,
- IMX_DMA_LENGTH_LOOP, imxdmac->per_address, dmamode);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_CYCLIC;
+ desc->sg = imxdmac->sg_list;
+ desc->sgcount = periods;
+ desc->len = IMX_DMA_LENGTH_LOOP;
+ if (direction == DMA_DEV_TO_MEM) {
+ desc->dmamode = DMA_MODE_READ;
+ desc->src = imxdmac->per_address;
+ } else {
+ desc->dmamode = DMA_MODE_WRITE;
+ desc->dest = imxdmac->per_address;
+ }
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
@@ -335,36 +495,53 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
- int ret;
+ struct imxdma_desc *desc;
dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n",
__func__, imxdmac->channel, src, dest, len);
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
- ret = imx_dma_config_channel(imxdmac->imxdma_channel,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
- 0, 0);
- if (ret)
- return NULL;
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
- ret = imx_dma_setup_single(imxdmac->imxdma_channel, src, len,
- dest, DMA_MODE_WRITE);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_MEMCPY;
+ desc->src = src;
+ desc->dest = dest;
+ desc->len = len;
+ desc->dmamode = DMA_MODE_WRITE;
+ desc->config_port = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR;
+ desc->config_mem = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR;
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static void imxdma_issue_pending(struct dma_chan *chan)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
-
- if (imxdmac->status == DMA_IN_PROGRESS)
- imx_dma_enable(imxdmac->imxdma_channel);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdmac->lock, flags);
+ if (list_empty(&imxdmac->ld_active) &&
+ !list_empty(&imxdmac->ld_queue)) {
+ desc = list_first_entry(&imxdmac->ld_queue,
+ struct imxdma_desc, node);
+
+ if (imxdma_xfer_desc(desc) < 0) {
+ dev_warn(imxdma->dev,
+ "%s: channel: %d couldn't issue DMA xfer\n",
+ __func__, imxdmac->channel);
+ } else {
+ list_move_tail(imxdmac->ld_queue.next,
+ &imxdmac->ld_active);
+ }
+ }
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
}
static int __init imxdma_probe(struct platform_device *pdev)
@@ -399,11 +576,18 @@ static int __init imxdma_probe(struct platform_device *pdev)
imxdmac->imxdma = imxdma;
spin_lock_init(&imxdmac->lock);
+ INIT_LIST_HEAD(&imxdmac->ld_queue);
+ INIT_LIST_HEAD(&imxdmac->ld_free);
+ INIT_LIST_HEAD(&imxdmac->ld_active);
+
+ tasklet_init(&imxdmac->dma_tasklet, imxdma_tasklet,
+ (unsigned long)imxdmac);
imxdmac->chan.device = &imxdma->dma_device;
imxdmac->channel = i;
/* Add the channel to the DMAC list */
- list_add_tail(&imxdmac->chan.device_node, &imxdma->dma_device.channels);
+ list_add_tail(&imxdmac->chan.device_node,
+ &imxdma->dma_device.channels);
}
imxdma->dev = &pdev->dev;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/2] dmaengine: Add support for multiple descriptors for imx-dma.
@ 2012-03-02 8:28 ` Javier Martin
0 siblings, 0 replies; 5+ messages in thread
From: Javier Martin @ 2012-03-02 8:28 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux, s.hauer, vinod.koul, dan.j.williams, linux-kernel,
Javier Martin
dmaengine specifies the possibility that several descriptors
can be queued for transfer. It also indicates that tasklets
must be used for DMA callbacks.
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
---
Added comment explaining fall through in 'imxdma_xfer_desc'.
---
drivers/dma/imx-dma.c | 332 ++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 258 insertions(+), 74 deletions(-)
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 9aa6e85..c32103f 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -5,6 +5,7 @@
* found on i.MX1/21/27
*
* Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com>
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
@@ -13,6 +14,7 @@
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -30,19 +32,52 @@
#include <mach/dma-v1.h>
#include <mach/hardware.h>
+#define IMXDMA_MAX_CHAN_DESCRIPTORS 16
+
+enum imxdma_prep_type {
+ IMXDMA_DESC_MEMCPY,
+ IMXDMA_DESC_INTERLEAVED,
+ IMXDMA_DESC_SLAVE_SG,
+ IMXDMA_DESC_CYCLIC,
+};
+
+struct imxdma_desc {
+ struct list_head node;
+ struct dma_async_tx_descriptor desc;
+ enum dma_status status;
+ dma_addr_t src;
+ dma_addr_t dest;
+ size_t len;
+ unsigned int dmamode;
+ enum imxdma_prep_type type;
+ /* For memcpy and interleaved */
+ unsigned int config_port;
+ unsigned int config_mem;
+ /* For interleaved transfers */
+ unsigned int x;
+ unsigned int y;
+ unsigned int w;
+ /* For slave sg and cyclic */
+ struct scatterlist *sg;
+ unsigned int sgcount;
+};
+
struct imxdma_channel {
struct imxdma_engine *imxdma;
unsigned int channel;
unsigned int imxdma_channel;
+ struct tasklet_struct dma_tasklet;
+ struct list_head ld_free;
+ struct list_head ld_queue;
+ struct list_head ld_active;
+ int descs_allocated;
enum dma_slave_buswidth word_size;
dma_addr_t per_address;
u32 watermark_level;
struct dma_chan chan;
spinlock_t lock;
- struct dma_async_tx_descriptor desc;
dma_cookie_t last_completed;
- enum dma_status status;
int dma_request;
struct scatterlist *sg_list;
};
@@ -61,27 +96,31 @@ static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan)
return container_of(chan, struct imxdma_channel, chan);
}
-static void imxdma_handle(struct imxdma_channel *imxdmac)
+static inline bool imxdma_chan_is_doing_cyclic(struct imxdma_channel *imxdmac)
{
- if (imxdmac->desc.callback)
- imxdmac->desc.callback(imxdmac->desc.callback_param);
- imxdmac->last_completed = imxdmac->desc.cookie;
+ struct imxdma_desc *desc;
+
+ if (!list_empty(&imxdmac->ld_active)) {
+ desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc,
+ node);
+ if (desc->type == IMXDMA_DESC_CYCLIC)
+ return true;
+ }
+ return false;
}
static void imxdma_irq_handler(int channel, void *data)
{
struct imxdma_channel *imxdmac = data;
- imxdmac->status = DMA_SUCCESS;
- imxdma_handle(imxdmac);
+ tasklet_schedule(&imxdmac->dma_tasklet);
}
static void imxdma_err_handler(int channel, void *data, int error)
{
struct imxdma_channel *imxdmac = data;
- imxdmac->status = DMA_ERROR;
- imxdma_handle(imxdmac);
+ tasklet_schedule(&imxdmac->dma_tasklet);
}
static void imxdma_progression(int channel, void *data,
@@ -89,8 +128,88 @@ static void imxdma_progression(int channel, void *data,
{
struct imxdma_channel *imxdmac = data;
- imxdmac->status = DMA_SUCCESS;
- imxdma_handle(imxdmac);
+ tasklet_schedule(&imxdmac->dma_tasklet);
+}
+
+static int imxdma_xfer_desc(struct imxdma_desc *d)
+{
+ struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
+ int ret;
+
+ /* Configure and enable */
+ switch (d->type) {
+ case IMXDMA_DESC_MEMCPY:
+ ret = imx_dma_config_channel(imxdmac->imxdma_channel,
+ d->config_port, d->config_mem, 0, 0);
+ if (ret < 0)
+ return ret;
+ ret = imx_dma_setup_single(imxdmac->imxdma_channel, d->src,
+ d->len, d->dest, d->dmamode);
+ if (ret < 0)
+ return ret;
+ break;
+ case IMXDMA_DESC_CYCLIC:
+ ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel,
+ imxdma_progression);
+ if (ret < 0)
+ return ret;
+ /*
+ * We fall through here since cyclic transfer is the same as
+ * slave_sg adding a progression handler and a specific sg
+ * configuration which is done in 'imxdma_prep_dma_cyclic'.
+ */
+ case IMXDMA_DESC_SLAVE_SG:
+ if (d->dmamode == DMA_MODE_READ)
+ ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg,
+ d->sgcount, d->len, d->src, d->dmamode);
+ else
+ ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg,
+ d->sgcount, d->len, d->dest, d->dmamode);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ imx_dma_enable(imxdmac->imxdma_channel);
+ return 0;
+}
+
+static void imxdma_tasklet(unsigned long data)
+{
+ struct imxdma_channel *imxdmac = (void *)data;
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
+
+ spin_lock(&imxdmac->lock);
+
+ if (list_empty(&imxdmac->ld_active)) {
+ /* Someone might have called terminate all */
+ goto out;
+ }
+ desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node);
+
+ if (desc->desc.callback)
+ desc->desc.callback(desc->desc.callback_param);
+
+ imxdmac->last_completed = desc->desc.cookie;
+
+ /* If we are dealing with a cyclic descriptor keep it on ld_active */
+ if (imxdma_chan_is_doing_cyclic(imxdmac))
+ goto out;
+
+ list_move_tail(imxdmac->ld_active.next, &imxdmac->ld_free);
+
+ if (!list_empty(&imxdmac->ld_queue)) {
+ desc = list_first_entry(&imxdmac->ld_queue, struct imxdma_desc,
+ node);
+ list_move_tail(imxdmac->ld_queue.next, &imxdmac->ld_active);
+ if (imxdma_xfer_desc(desc) < 0)
+ dev_warn(imxdma->dev, "%s: channel: %d couldn't xfer desc\n",
+ __func__, imxdmac->channel);
+ }
+out:
+ spin_unlock(&imxdmac->lock);
}
static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -99,12 +218,17 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct dma_slave_config *dmaengine_cfg = (void *)arg;
int ret;
+ unsigned long flags;
unsigned int mode = 0;
switch (cmd) {
case DMA_TERMINATE_ALL:
- imxdmac->status = DMA_ERROR;
imx_dma_disable(imxdmac->imxdma_channel);
+
+ spin_lock_irqsave(&imxdmac->lock, flags);
+ list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+ list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
return 0;
case DMA_SLAVE_CONFIG:
if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) {
@@ -155,11 +279,14 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan,
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
dma_cookie_t last_used;
enum dma_status ret;
+ unsigned long flags;
+ spin_lock_irqsave(&imxdmac->lock, flags);
last_used = chan->cookie;
ret = dma_async_is_complete(cookie, imxdmac->last_completed, last_used);
dma_set_tx_state(txstate, imxdmac->last_completed, last_used, 0);
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
return ret;
}
@@ -172,7 +299,6 @@ static dma_cookie_t imxdma_assign_cookie(struct imxdma_channel *imxdma)
cookie = 1;
imxdma->chan.cookie = cookie;
- imxdma->desc.cookie = cookie;
return cookie;
}
@@ -181,12 +307,15 @@ static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan);
dma_cookie_t cookie;
+ unsigned long flags;
- spin_lock_irq(&imxdmac->lock);
+ spin_lock_irqsave(&imxdmac->lock, flags);
+ list_move_tail(imxdmac->ld_free.next, &imxdmac->ld_queue);
cookie = imxdma_assign_cookie(imxdmac);
+ tx->cookie = cookie;
- spin_unlock_irq(&imxdmac->lock);
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
return cookie;
}
@@ -199,21 +328,48 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan)
if (data != NULL)
imxdmac->dma_request = data->dma_request;
- dma_async_tx_descriptor_init(&imxdmac->desc, chan);
- imxdmac->desc.tx_submit = imxdma_tx_submit;
- /* txd.flags will be overwritten in prep funcs */
- imxdmac->desc.flags = DMA_CTRL_ACK;
+ while (imxdmac->descs_allocated < IMXDMA_MAX_CHAN_DESCRIPTORS) {
+ struct imxdma_desc *desc;
- imxdmac->status = DMA_SUCCESS;
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ break;
+ __memzero(&desc->desc, sizeof(struct dma_async_tx_descriptor));
+ dma_async_tx_descriptor_init(&desc->desc, chan);
+ desc->desc.tx_submit = imxdma_tx_submit;
+ /* txd.flags will be overwritten in prep funcs */
+ desc->desc.flags = DMA_CTRL_ACK;
+ desc->status = DMA_SUCCESS;
+
+ list_add_tail(&desc->node, &imxdmac->ld_free);
+ imxdmac->descs_allocated++;
+ }
- return 0;
+ if (!imxdmac->descs_allocated)
+ return -ENOMEM;
+
+ return imxdmac->descs_allocated;
}
static void imxdma_free_chan_resources(struct dma_chan *chan)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+ struct imxdma_desc *desc, *_desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdmac->lock, flags);
imx_dma_disable(imxdmac->imxdma_channel);
+ list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+ list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
+
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
+
+ list_for_each_entry_safe(desc, _desc, &imxdmac->ld_free, node) {
+ kfree(desc);
+ imxdmac->descs_allocated--;
+ }
+ INIT_LIST_HEAD(&imxdmac->ld_free);
if (imxdmac->sg_list) {
kfree(imxdmac->sg_list);
@@ -228,23 +384,19 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct scatterlist *sg;
- int i, ret, dma_length = 0;
- unsigned int dmamode;
+ int i, dma_length = 0;
+ struct imxdma_desc *desc;
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
for_each_sg(sgl, sg, sg_len, i) {
dma_length += sg->length;
}
- if (direction == DMA_DEV_TO_MEM)
- dmamode = DMA_MODE_READ;
- else
- dmamode = DMA_MODE_WRITE;
-
switch (imxdmac->word_size) {
case DMA_SLAVE_BUSWIDTH_4_BYTES:
if (sgl->length & 3 || sgl->dma_address & 3)
@@ -260,12 +412,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
return NULL;
}
- ret = imx_dma_setup_sg(imxdmac->imxdma_channel, sgl, sg_len,
- dma_length, imxdmac->per_address, dmamode);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_SLAVE_SG;
+ desc->sg = sgl;
+ desc->sgcount = sg_len;
+ desc->len = dma_length;
+ if (direction == DMA_DEV_TO_MEM) {
+ desc->dmamode = DMA_MODE_READ;
+ desc->src = imxdmac->per_address;
+ } else {
+ desc->dmamode = DMA_MODE_WRITE;
+ desc->dest = imxdmac->per_address;
+ }
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
@@ -274,23 +435,18 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
- int i, ret;
+ struct imxdma_desc *desc;
+ int i;
unsigned int periods = buf_len / period_len;
- unsigned int dmamode;
dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n",
__func__, imxdmac->channel, buf_len, period_len);
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
- ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel,
- imxdma_progression);
- if (ret) {
- dev_err(imxdma->dev, "Failed to setup the DMA handler\n");
- return NULL;
- }
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
if (imxdmac->sg_list)
kfree(imxdmac->sg_list);
@@ -316,17 +472,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
imxdmac->sg_list[periods].page_link =
((unsigned long)imxdmac->sg_list | 0x01) & ~0x02;
- if (direction == DMA_DEV_TO_MEM)
- dmamode = DMA_MODE_READ;
- else
- dmamode = DMA_MODE_WRITE;
-
- ret = imx_dma_setup_sg(imxdmac->imxdma_channel, imxdmac->sg_list, periods,
- IMX_DMA_LENGTH_LOOP, imxdmac->per_address, dmamode);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_CYCLIC;
+ desc->sg = imxdmac->sg_list;
+ desc->sgcount = periods;
+ desc->len = IMX_DMA_LENGTH_LOOP;
+ if (direction == DMA_DEV_TO_MEM) {
+ desc->dmamode = DMA_MODE_READ;
+ desc->src = imxdmac->per_address;
+ } else {
+ desc->dmamode = DMA_MODE_WRITE;
+ desc->dest = imxdmac->per_address;
+ }
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
@@ -335,36 +495,53 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
- int ret;
+ struct imxdma_desc *desc;
dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n",
__func__, imxdmac->channel, src, dest, len);
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
- ret = imx_dma_config_channel(imxdmac->imxdma_channel,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
- 0, 0);
- if (ret)
- return NULL;
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
- ret = imx_dma_setup_single(imxdmac->imxdma_channel, src, len,
- dest, DMA_MODE_WRITE);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_MEMCPY;
+ desc->src = src;
+ desc->dest = dest;
+ desc->len = len;
+ desc->dmamode = DMA_MODE_WRITE;
+ desc->config_port = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR;
+ desc->config_mem = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR;
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static void imxdma_issue_pending(struct dma_chan *chan)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
-
- if (imxdmac->status == DMA_IN_PROGRESS)
- imx_dma_enable(imxdmac->imxdma_channel);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdmac->lock, flags);
+ if (list_empty(&imxdmac->ld_active) &&
+ !list_empty(&imxdmac->ld_queue)) {
+ desc = list_first_entry(&imxdmac->ld_queue,
+ struct imxdma_desc, node);
+
+ if (imxdma_xfer_desc(desc) < 0) {
+ dev_warn(imxdma->dev,
+ "%s: channel: %d couldn't issue DMA xfer\n",
+ __func__, imxdmac->channel);
+ } else {
+ list_move_tail(imxdmac->ld_queue.next,
+ &imxdmac->ld_active);
+ }
+ }
+ spin_unlock_irqrestore(&imxdmac->lock, flags);
}
static int __init imxdma_probe(struct platform_device *pdev)
@@ -399,11 +576,18 @@ static int __init imxdma_probe(struct platform_device *pdev)
imxdmac->imxdma = imxdma;
spin_lock_init(&imxdmac->lock);
+ INIT_LIST_HEAD(&imxdmac->ld_queue);
+ INIT_LIST_HEAD(&imxdmac->ld_free);
+ INIT_LIST_HEAD(&imxdmac->ld_active);
+
+ tasklet_init(&imxdmac->dma_tasklet, imxdma_tasklet,
+ (unsigned long)imxdmac);
imxdmac->chan.device = &imxdma->dma_device;
imxdmac->channel = i;
/* Add the channel to the DMAC list */
- list_add_tail(&imxdmac->chan.device_node, &imxdma->dma_device.channels);
+ list_add_tail(&imxdmac->chan.device_node,
+ &imxdma->dma_device.channels);
}
imxdma->dev = &pdev->dev;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/2] dmaengine: Add support for multiple descriptors for imx-dma.
2012-03-02 8:28 ` Javier Martin
@ 2012-03-06 11:54 ` Vinod Koul
-1 siblings, 0 replies; 5+ messages in thread
From: Vinod Koul @ 2012-03-06 11:54 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, 2012-03-02 at 09:28 +0100, Javier Martin wrote:
> dmaengine specifies the possibility that several descriptors
> can be queued for transfer. It also indicates that tasklets
> must be used for DMA callbacks.
>
> Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
> ---
Applied this and 1/2 or previous one
--
~Vinod
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 2/2] dmaengine: Add support for multiple descriptors for imx-dma.
@ 2012-03-06 11:54 ` Vinod Koul
0 siblings, 0 replies; 5+ messages in thread
From: Vinod Koul @ 2012-03-06 11:54 UTC (permalink / raw)
To: Javier Martin
Cc: linux-arm-kernel, linux, s.hauer, dan.j.williams, linux-kernel
On Fri, 2012-03-02 at 09:28 +0100, Javier Martin wrote:
> dmaengine specifies the possibility that several descriptors
> can be queued for transfer. It also indicates that tasklets
> must be used for DMA callbacks.
>
> Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
> ---
Applied this and 1/2 or previous one
--
~Vinod
^ permalink raw reply [flat|nested] 5+ messages in thread
* [BUG ?] ARM: imx27: dmaengine: imx-dma: SD-Card: copy hangs forever
[not found] ` <1370813946.5019.254.camel@mars>
@ 2013-06-12 7:54 ` Vinod Koul
0 siblings, 0 replies; 5+ messages in thread
From: Vinod Koul @ 2013-06-12 7:54 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Jun 09, 2013 at 11:39:06PM +0200, Christoph Fritz wrote:
> Hi,
Thanks for the bug report, we are discussing this issue in [1]
--
~Vinod
[1]: http://www.spinics.net/lists/arm-kernel/msg249815.html
>
> an imx27-board (pca100) hits a DEADLOCK with current stable Kernel
> 3.4.47 while adding a SD-Card:
>
> =================================
> [ INFO: inconsistent lock state ]
> 3.4.47-00059-ga5527fe #213 Not tainted
> ---------------------------------
> inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage.
> swapper/0 [HC0[0]:SC1[1]:HE1:SE0] takes:
> (&(&imxdma->lock)->rlock){?.-...}, at: [<c01b9d18>] imxdma_tasklet+0x1c/0x138
> {IN-HARDIRQ-W} state was registered at:
> [<c004e96c>] __lock_acquire+0x97c/0x1900
> [<c004fd98>] lock_acquire+0x64/0x78
> [<c03bcd18>] _raw_spin_lock+0x40/0x50
> [<c01ba018>] dma_irq_handler+0xc8/0x358
> [<c005c750>] handle_irq_event_percpu+0x50/0x1c8
> [<c005c904>] handle_irq_event+0x3c/0x5c
> [<c005ea5c>] handle_level_irq+0x8c/0xd8
> [<c005c0c8>] generic_handle_irq+0x34/0x48
> [<c000975c>] handle_IRQ+0x30/0x84
> [<c00086e8>] avic_handle_irq+0x2c/0x4c
> [<c03bd924>] __irq_svc+0x44/0x74
> [<c00098c4>] default_idle+0x40/0x48
> [<c0009a60>] cpu_idle+0x6c/0xe8
> [<c0525700>] start_kernel+0x264/0x2a4
> irq event stamp: 20208
> hardirqs last enabled at (20208): [<c001c0fc>] tasklet_action+0x30/0xe0
> hardirqs last disabled at (20207): [<c001c0e4>] tasklet_action+0x18/0xe0
> softirqs last enabled at (20202): [<c001bc8c>] irq_enter+0x6c/0x70
> softirqs last disabled at (20203): [<c001bd2c>] irq_exit+0x9c/0xa8
>
> other info that might help us debug this:
> Possible unsafe locking scenario:
>
> CPU0
> ----
> lock(&(&imxdma->lock)->rlock);
> <Interrupt>
> lock(&(&imxdma->lock)->rlock);
>
> *** DEADLOCK ***
>
> no locks held by swapper/0.
>
> stack backtrace:
> [<c000d428>] (unwind_backtrace+0x0/0xf0) from [<c03b7724>] (print_usage_bug.part.25+0x208/0x268)
> [<c03b7724>] (print_usage_bug.part.25+0x208/0x268) from [<c004decc>] (mark_lock+0x4fc/0x620)
> [<c004decc>] (mark_lock+0x4fc/0x620) from [<c004e870>] (__lock_acquire+0x880/0x1900)
> [<c004e870>] (__lock_acquire+0x880/0x1900) from [<c004fd98>] (lock_acquire+0x64/0x78)
> [<c004fd98>] (lock_acquire+0x64/0x78) from [<c03bcd18>] (_raw_spin_lock+0x40/0x50)
> [<c03bcd18>] (_raw_spin_lock+0x40/0x50) from [<c01b9d18>] (imxdma_tasklet+0x1c/0x138)
> [<c01b9d18>] (imxdma_tasklet+0x1c/0x138) from [<c001c144>] (tasklet_action+0x78/0xe0)
> [<c001c144>] (tasklet_action+0x78/0xe0) from [<c001b76c>] (__do_softirq+0xc8/0x17c)
> [<c001b76c>] (__do_softirq+0xc8/0x17c) from [<c001bd2c>] (irq_exit+0x9c/0xa8)
> [<c001bd2c>] (irq_exit+0x9c/0xa8) from [<c0009760>] (handle_IRQ+0x34/0x84)
> [<c0009760>] (handle_IRQ+0x34/0x84) from [<c00086e8>] (avic_handle_irq+0x2c/0x4c)
> [<c00086e8>] (avic_handle_irq+0x2c/0x4c) from [<c03bd924>] (__irq_svc+0x44/0x74)
> Exception stack(0xc0549f60 to 0xc0549fa8)
> 9f60: 00000001 00000001 00000000 20000013 c0548000 c058dae8 c0554118 c0548000
> 9f80: c0b96500 41069264 a053ff1c 00000000 600000d3 c0549fa8 c0050664 c00098c4
> 9fa0: 20000013 ffffffff
> [<c03bd924>] (__irq_svc+0x44/0x74) from [<c00098c4>] (default_idle+0x40/0x48)
> [<c00098c4>] (default_idle+0x40/0x48) from [<c0009a60>] (cpu_idle+0x6c/0xe8)
> [<c0009a60>] (cpu_idle+0x6c/0xe8) from [<c0525700>] (start_kernel+0x264/0x2a4)
>
>
> This is also curious and may be related:
>
> When CONFIG_IMX_DMA is disabled, copying a file to SD-Card is relatively
> fast:
> $ dd if=/dev/zero of=/mnt/test count=102400 bs=1K
> 102400+0 records in
> 102400+0 records out
> 104857600 bytes (105 MB) copied, 33.573 s, 3.1 MB/s
>
> When CONFIG_IMX_DMA is enabled, copying a file to SD-Card is
> very slow for small files and takes forever for files roughly bigger
> than 1.0MB:
>
> $ dd if=/dev/zero of=/mnt/test count=100000 bs=10
> 100000+0 records in
> 100000+0 records out
> 1000000 bytes (1.0 MB) copied, 18.3172 s, 54.6 kB/s
>
> $ dd if=/dev/zero of=/mnt/performance_test.102400KB count=100000 bs=20
>
> INFO: task mmcqd/0:592 blocked for more than 30 seconds.
> "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> mmcqd/0 D c03bbf14 0 592 2 0x00000000
> [<c03bbf14>] (__schedule+0x220/0x590) from [<c03ba11c>] (schedule_timeout+0x154/0x1bc)
> [<c03ba11c>] (schedule_timeout+0x154/0x1bc) from [<c03bbbac>] (wait_for_common+0xac/0x150)
> [<c03bbbac>] (wait_for_common+0xac/0x150) from [<c0285504>] (mmc_wait_for_req_done+0x1c/0x74)
> [<c0285504>] (mmc_wait_for_req_done+0x1c/0x74) from [<c028612c>] (mmc_start_req+0x54/0x118)
> [<c028612c>] (mmc_start_req+0x54/0x118) from [<c02911d0>] (mmc_blk_issue_rw_rq+0x64/0x380)
> [<c02911d0>] (mmc_blk_issue_rw_rq+0x64/0x380) from [<c029169c>] (mmc_blk_issue_rq+0x1b0/0x484)
> [<c029169c>] (mmc_blk_issue_rq+0x1b0/0x484) from [<c029293c>] (mmc_queue_thread+0x58/0xf4)
> [<c029293c>] (mmc_queue_thread+0x58/0xf4) from [<c0032acc>] (kthread+0x84/0x90)
> [<c0032acc>] (kthread+0x84/0x90) from [<c0009844>] (kernel_thread_exit+0x0/0x8)
> no locks held by mmcqd/0/592.
> INFO: task kjournald:595 blocked for more than 30 seconds.
> "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> kjournald D c03bbf14 0 595 2 0x00000000
> [<c03bbf14>] (__schedule+0x220/0x590) from [<c03bc3b0>] (io_schedule+0x60/0x94)
> [<c03bc3b0>] (io_schedule+0x60/0x94) from [<c00c54a0>] (sleep_on_buffer+0x8/0x10)
> [<c00c54a0>] (sleep_on_buffer+0x8/0x10) from [<c03ba24c>] (__wait_on_bit+0x74/0xb8)
> [<c03ba24c>] (__wait_on_bit+0x74/0xb8) from [<c03ba2f4>] (out_of_line_wait_on_bit+0x64/0x70)
> [<c03ba2f4>] (out_of_line_wait_on_bit+0x64/0x70) from [<c0110ad8>] (journal_commit_transaction+0x6a0/0x14ac)
> [<c0110ad8>] (journal_commit_transaction+0x6a0/0x14ac) from [<c0113ea4>] (kjournald+0xc0/0x25c)
> [<c0113ea4>] (kjournald+0xc0/0x25c) from [<c0032acc>] (kthread+0x84/0x90)
> [<c0032acc>] (kthread+0x84/0x90) from [<c0009844>] (kernel_thread_exit+0x0/0x8)
> no locks held by kjournald/595.
> INFO: task dd:601 blocked for more than 30 seconds.
> "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> dd D c03bbf14 0 601 586 0x00000000
> [<c03bbf14>] (__schedule+0x220/0x590) from [<c03bc3b0>] (io_schedule+0x60/0x94)
> [<c03bc3b0>] (io_schedule+0x60/0x94) from [<c007104c>] (sleep_on_page+0x8/0x10)
> [<c007104c>] (sleep_on_page+0x8/0x10) from [<c03ba24c>] (__wait_on_bit+0x74/0xb8)
> [<c03ba24c>] (__wait_on_bit+0x74/0xb8) from [<c00712d4>] (wait_on_page_bit+0xbc/0xd0)
> [<c00712d4>] (wait_on_page_bit+0xbc/0xd0) from [<c0071dc4>] (grab_cache_page_write_begin+0x98/0xc4)
> [<c0071dc4>] (grab_cache_page_write_begin+0x98/0xc4) from [<c00fa3cc>] (ext3_write_begin+0x74/0x208)
> [<c00fa3cc>] (ext3_write_begin+0x74/0x208) from [<c007176c>] (generic_file_buffered_write+0x19c/0x260)
> [<c007176c>] (generic_file_buffered_write+0x19c/0x260) from [<c007283c>] (__generic_file_aio_write+0x234/0x490)
> [<c007283c>] (__generic_file_aio_write+0x234/0x490) from [<c0072af8>] (generic_file_aio_write+0x60/0xcc)
> [<c0072af8>] (generic_file_aio_write+0x60/0xcc) from [<c009b990>] (do_sync_write+0xa8/0xdc)
> [<c009b990>] (do_sync_write+0xa8/0xdc) from [<c009c2c0>] (vfs_write+0xa8/0x138)
> [<c009c2c0>] (vfs_write+0xa8/0x138) from [<c009c530>] (sys_write+0x40/0x6c)
> [<c009c530>] (sys_write+0x40/0x6c) from [<c0008ec0>] (ret_fast_syscall+0x0/0x38)
> 1 lock held by dd/601:
> #0: (&sb->s_type->i_mutex_key#12){+.+.+.}, at: [<c0072adc>] generic_file_aio_write+0x44/0xcc
> INFO: task mmcqd/0:592 blocked for more than 30 seconds.
> "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
>
>
> With git bisect this issue was boiled down to this commit:
>
> | commit 9e15db7ce949e9f2d8bb6ce32a74212a4f662370
> | Author: Javier Martin <javier.martin@vista-silicon.com>
> | Date: Fri Mar 2 09:28:47 2012 +0100
> |
> | dmaengine: Add support for multiple descriptors for imx-dma.
> |
> | dmaengine specifies the possibility that several descriptors
> | can be queued for transfer. It also indicates that tasklets
> | must be used for DMA callbacks.
>
> I'm not sure if it only triggers a side effect. Any ideas?
>
> Thanks
> -- Christoph
>
--
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2013-06-12 7:54 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-02 8:28 [PATCH v3 2/2] dmaengine: Add support for multiple descriptors for imx-dma Javier Martin
2012-03-02 8:28 ` Javier Martin
2012-03-06 11:54 ` Vinod Koul
2012-03-06 11:54 ` Vinod Koul
[not found] ` <1370813946.5019.254.camel@mars>
2013-06-12 7:54 ` [BUG ?] ARM: imx27: dmaengine: imx-dma: SD-Card: copy hangs forever Vinod Koul
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.