From e8bd84df27c5921a9ac866aef06e044590ac118f Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:54:54 +0000 Subject: fsldma: move related helper functions near each other This is a purely cosmetic cleanup. It is nice to have related functions right next to each other in the code. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 116 ++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 52 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 4de947a450fc..2e1af4555b0f 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -39,33 +39,9 @@ static const char msg_ld_oom[] = "No free memory for link descriptor\n"; -static void dma_init(struct fsldma_chan *chan) -{ - /* Reset the channel */ - DMA_OUT(chan, &chan->regs->mr, 0, 32); - - switch (chan->feature & FSL_DMA_IP_MASK) { - case FSL_DMA_IP_85XX: - /* Set the channel to below modes: - * EIE - Error interrupt enable - * EOSIE - End of segments interrupt enable (basic mode) - * EOLNIE - End of links interrupt enable - * BWC - Bandwidth sharing among channels - */ - DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC - | FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE - | FSL_DMA_MR_EOSIE, 32); - break; - case FSL_DMA_IP_83XX: - /* Set the channel to below modes: - * EOTIE - End-of-transfer interrupt enable - * PRC_RM - PCI read multiple - */ - DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EOTIE - | FSL_DMA_MR_PRC_RM, 32); - break; - } -} +/* + * Register Helpers + */ static void set_sr(struct fsldma_chan *chan, u32 val) { @@ -77,6 +53,30 @@ static u32 get_sr(struct fsldma_chan *chan) return DMA_IN(chan, &chan->regs->sr, 32); } +static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr) +{ + DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); +} + +static dma_addr_t get_cdar(struct fsldma_chan *chan) +{ + return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; +} + +static dma_addr_t get_ndar(struct fsldma_chan *chan) +{ + return DMA_IN(chan, &chan->regs->ndar, 64); +} + +static u32 get_bcr(struct fsldma_chan *chan) +{ + return DMA_IN(chan, &chan->regs->bcr, 32); +} + +/* + * Descriptor Helpers + */ + static void set_desc_cnt(struct fsldma_chan *chan, struct fsl_dma_ld_hw *hw, u32 count) { @@ -113,24 +113,49 @@ static void set_desc_next(struct fsldma_chan *chan, hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64); } -static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr) +static void set_ld_eol(struct fsldma_chan *chan, + struct fsl_desc_sw *desc) { - DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); -} + u64 snoop_bits; -static dma_addr_t get_cdar(struct fsldma_chan *chan) -{ - return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; -} + snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX) + ? FSL_DMA_SNEN : 0; -static dma_addr_t get_ndar(struct fsldma_chan *chan) -{ - return DMA_IN(chan, &chan->regs->ndar, 64); + desc->hw.next_ln_addr = CPU_TO_DMA(chan, + DMA_TO_CPU(chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL + | snoop_bits, 64); } -static u32 get_bcr(struct fsldma_chan *chan) +/* + * DMA Engine Hardware Control Helpers + */ + +static void dma_init(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->bcr, 32); + /* Reset the channel */ + DMA_OUT(chan, &chan->regs->mr, 0, 32); + + switch (chan->feature & FSL_DMA_IP_MASK) { + case FSL_DMA_IP_85XX: + /* Set the channel to below modes: + * EIE - Error interrupt enable + * EOSIE - End of segments interrupt enable (basic mode) + * EOLNIE - End of links interrupt enable + * BWC - Bandwidth sharing among channels + */ + DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC + | FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE + | FSL_DMA_MR_EOSIE, 32); + break; + case FSL_DMA_IP_83XX: + /* Set the channel to below modes: + * EOTIE - End-of-transfer interrupt enable + * PRC_RM - PCI read multiple + */ + DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EOTIE + | FSL_DMA_MR_PRC_RM, 32); + break; + } } static int dma_is_idle(struct fsldma_chan *chan) @@ -185,19 +210,6 @@ static void dma_halt(struct fsldma_chan *chan) dev_err(chan->dev, "DMA halt timeout!\n"); } -static void set_ld_eol(struct fsldma_chan *chan, - struct fsl_desc_sw *desc) -{ - u64 snoop_bits; - - snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX) - ? FSL_DMA_SNEN : 0; - - desc->hw.next_ln_addr = CPU_TO_DMA(chan, - DMA_TO_CPU(chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL - | snoop_bits, 64); -} - /** * fsl_chan_set_src_loop_size - Set source address hold transfer size * @chan : Freescale DMA channel -- cgit v1.2.3 From b158471ef63bf399165db96e945a828096502d9d Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:54:55 +0000 Subject: fsldma: use channel name in printk output This makes debugging the driver much easier when multiple channels are running concurrently. In addition, you can see how much descriptor memory each channel has allocated via the dmapool API in sysfs. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 69 ++++++++++++++++++++++++++-------------------------- drivers/dma/fsldma.h | 1 + 2 files changed, 36 insertions(+), 34 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 2e1af4555b0f..e535cd13f7cc 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -37,7 +37,12 @@ #include "fsldma.h" -static const char msg_ld_oom[] = "No free memory for link descriptor\n"; +#define chan_dbg(chan, fmt, arg...) \ + dev_dbg(chan->dev, "%s: " fmt, chan->name, ##arg) +#define chan_err(chan, fmt, arg...) \ + dev_err(chan->dev, "%s: " fmt, chan->name, ##arg) + +static const char msg_ld_oom[] = "No free memory for link descriptor"; /* * Register Helpers @@ -207,7 +212,7 @@ static void dma_halt(struct fsldma_chan *chan) } if (!dma_is_idle(chan)) - dev_err(chan->dev, "DMA halt timeout!\n"); + chan_err(chan, "DMA halt timeout!\n"); } /** @@ -405,7 +410,7 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( desc = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &pdesc); if (!desc) { - dev_dbg(chan->dev, "out of memory for link desc\n"); + chan_dbg(chan, "out of memory for link descriptor\n"); return NULL; } @@ -439,13 +444,11 @@ static int fsl_dma_alloc_chan_resources(struct dma_chan *dchan) * We need the descriptor to be aligned to 32bytes * for meeting FSL DMA specification requirement. */ - chan->desc_pool = dma_pool_create("fsl_dma_engine_desc_pool", - chan->dev, + chan->desc_pool = dma_pool_create(chan->name, chan->dev, sizeof(struct fsl_desc_sw), __alignof__(struct fsl_desc_sw), 0); if (!chan->desc_pool) { - dev_err(chan->dev, "unable to allocate channel %d " - "descriptor pool\n", chan->id); + chan_err(chan, "unable to allocate descriptor pool\n"); return -ENOMEM; } @@ -491,7 +494,7 @@ static void fsl_dma_free_chan_resources(struct dma_chan *dchan) struct fsldma_chan *chan = to_fsl_chan(dchan); unsigned long flags; - dev_dbg(chan->dev, "Free all channel resources.\n"); + chan_dbg(chan, "free all channel resources\n"); spin_lock_irqsave(&chan->desc_lock, flags); fsldma_free_desc_list(chan, &chan->ld_pending); fsldma_free_desc_list(chan, &chan->ld_running); @@ -514,7 +517,7 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags) new = fsl_dma_alloc_descriptor(chan); if (!new) { - dev_err(chan->dev, msg_ld_oom); + chan_err(chan, "%s\n", msg_ld_oom); return NULL; } @@ -551,11 +554,11 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( /* Allocate the link descriptor from DMA pool */ new = fsl_dma_alloc_descriptor(chan); if (!new) { - dev_err(chan->dev, msg_ld_oom); + chan_err(chan, "%s\n", msg_ld_oom); goto fail; } #ifdef FSL_DMA_LD_DEBUG - dev_dbg(chan->dev, "new link desc alloc %p\n", new); + chan_dbg(chan, "new link desc alloc %p\n", new); #endif copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT); @@ -639,11 +642,11 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan, /* allocate and populate the descriptor */ new = fsl_dma_alloc_descriptor(chan); if (!new) { - dev_err(chan->dev, msg_ld_oom); + chan_err(chan, "%s\n", msg_ld_oom); goto fail; } #ifdef FSL_DMA_LD_DEBUG - dev_dbg(chan->dev, "new link desc alloc %p\n", new); + chan_dbg(chan, "new link desc alloc %p\n", new); #endif set_desc_cnt(chan, &new->hw, len); @@ -815,7 +818,7 @@ static void fsl_dma_update_completed_cookie(struct fsldma_chan *chan) spin_lock_irqsave(&chan->desc_lock, flags); if (list_empty(&chan->ld_running)) { - dev_dbg(chan->dev, "no running descriptors\n"); + chan_dbg(chan, "no running descriptors\n"); goto out_unlock; } @@ -863,7 +866,7 @@ static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) spin_lock_irqsave(&chan->desc_lock, flags); - dev_dbg(chan->dev, "chan completed_cookie = %d\n", chan->completed_cookie); + chan_dbg(chan, "chan completed_cookie = %d\n", chan->completed_cookie); list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) { dma_async_tx_callback callback; void *callback_param; @@ -879,7 +882,7 @@ static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) callback_param = desc->async_tx.callback_param; if (callback) { spin_unlock_irqrestore(&chan->desc_lock, flags); - dev_dbg(chan->dev, "LD %p callback\n", desc); + chan_dbg(chan, "LD %p callback\n", desc); callback(callback_param); spin_lock_irqsave(&chan->desc_lock, flags); } @@ -913,7 +916,7 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) * don't need to do any work at all */ if (list_empty(&chan->ld_pending)) { - dev_dbg(chan->dev, "no pending LDs\n"); + chan_dbg(chan, "no pending LDs\n"); goto out_unlock; } @@ -923,7 +926,7 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) * at the end of the current transaction */ if (!dma_is_idle(chan)) { - dev_dbg(chan->dev, "DMA controller still busy\n"); + chan_dbg(chan, "DMA controller still busy\n"); goto out_unlock; } @@ -1003,14 +1006,14 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) /* save and clear the status register */ stat = get_sr(chan); set_sr(chan, stat); - dev_dbg(chan->dev, "irq: channel %d, stat = 0x%x\n", chan->id, stat); + chan_dbg(chan, "irq: stat = 0x%x\n", stat); stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH); if (!stat) return IRQ_NONE; if (stat & FSL_DMA_SR_TE) - dev_err(chan->dev, "Transfer Error!\n"); + chan_err(chan, "Transfer Error!\n"); /* * Programming Error @@ -1018,7 +1021,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) * triger a PE interrupt. */ if (stat & FSL_DMA_SR_PE) { - dev_dbg(chan->dev, "irq: Programming Error INT\n"); + chan_dbg(chan, "irq: Programming Error INT\n"); if (get_bcr(chan) == 0) { /* BCR register is 0, this is a DMA_INTERRUPT async_tx. * Now, update the completed cookie, and continue the @@ -1035,8 +1038,8 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) * we will recycle the used descriptor. */ if (stat & FSL_DMA_SR_EOSI) { - dev_dbg(chan->dev, "irq: End-of-segments INT\n"); - dev_dbg(chan->dev, "irq: clndar 0x%llx, nlndar 0x%llx\n", + chan_dbg(chan, "irq: End-of-segments INT\n"); + chan_dbg(chan, "irq: clndar 0x%llx, nlndar 0x%llx\n", (unsigned long long)get_cdar(chan), (unsigned long long)get_ndar(chan)); stat &= ~FSL_DMA_SR_EOSI; @@ -1048,7 +1051,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) * and start the next transfer if it exist. */ if (stat & FSL_DMA_SR_EOCDI) { - dev_dbg(chan->dev, "irq: End-of-Chain link INT\n"); + chan_dbg(chan, "irq: End-of-Chain link INT\n"); stat &= ~FSL_DMA_SR_EOCDI; update_cookie = 1; xfer_ld_q = 1; @@ -1060,7 +1063,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) * prepare next transfer. */ if (stat & FSL_DMA_SR_EOLNI) { - dev_dbg(chan->dev, "irq: End-of-link INT\n"); + chan_dbg(chan, "irq: End-of-link INT\n"); stat &= ~FSL_DMA_SR_EOLNI; xfer_ld_q = 1; } @@ -1070,9 +1073,9 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) if (xfer_ld_q) fsl_chan_xfer_ld_queue(chan); if (stat) - dev_dbg(chan->dev, "irq: unhandled sr 0x%02x\n", stat); + chan_dbg(chan, "irq: unhandled sr 0x%08x\n", stat); - dev_dbg(chan->dev, "irq: Exit\n"); + chan_dbg(chan, "irq: Exit\n"); tasklet_schedule(&chan->tasklet); return IRQ_HANDLED; } @@ -1128,7 +1131,7 @@ static void fsldma_free_irqs(struct fsldma_device *fdev) for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { chan = fdev->chan[i]; if (chan && chan->irq != NO_IRQ) { - dev_dbg(fdev->dev, "free channel %d IRQ\n", chan->id); + chan_dbg(chan, "free per-channel IRQ\n"); free_irq(chan->irq, chan); } } @@ -1155,19 +1158,16 @@ static int fsldma_request_irqs(struct fsldma_device *fdev) continue; if (chan->irq == NO_IRQ) { - dev_err(fdev->dev, "no interrupts property defined for " - "DMA channel %d. Please fix your " - "device tree\n", chan->id); + chan_err(chan, "interrupts property missing in device tree\n"); ret = -ENODEV; goto out_unwind; } - dev_dbg(fdev->dev, "request channel %d IRQ\n", chan->id); + chan_dbg(chan, "request per-channel IRQ\n"); ret = request_irq(chan->irq, fsldma_chan_irq, IRQF_SHARED, "fsldma-chan", chan); if (ret) { - dev_err(fdev->dev, "unable to request IRQ for DMA " - "channel %d\n", chan->id); + chan_err(chan, "unable to request per-channel IRQ\n"); goto out_unwind; } } @@ -1242,6 +1242,7 @@ static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev, fdev->chan[chan->id] = chan; tasklet_init(&chan->tasklet, dma_do_tasklet, (unsigned long)chan); + snprintf(chan->name, sizeof(chan->name), "chan%d", chan->id); /* Initialize the channel */ dma_init(chan); diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index ba9f403c0fbe..113e7134010b 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -135,6 +135,7 @@ struct fsldma_device { #define FSL_DMA_CHAN_START_EXT 0x00002000 struct fsldma_chan { + char name[8]; /* Channel name */ struct fsldma_chan_regs __iomem *regs; dma_cookie_t completed_cookie; /* The maximum cookie completed */ spinlock_t desc_lock; /* Descriptor operation lock */ -- cgit v1.2.3 From 0ab09c36818ca88f65c88f4d8c6d067fbf10578d Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:54:56 +0000 Subject: fsldma: improve link descriptor debugging This adds better tracking to link descriptor allocations, callbacks, and frees. This makes it much easier to track errors with link descriptors. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index e535cd13f7cc..82b8e9f9c7bf 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -420,6 +420,10 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( desc->async_tx.tx_submit = fsl_dma_tx_submit; desc->async_tx.phys = pdesc; +#ifdef FSL_DMA_LD_DEBUG + chan_dbg(chan, "LD %p allocated\n", desc); +#endif + return desc; } @@ -470,6 +474,9 @@ static void fsldma_free_desc_list(struct fsldma_chan *chan, list_for_each_entry_safe(desc, _desc, list, node) { list_del(&desc->node); +#ifdef FSL_DMA_LD_DEBUG + chan_dbg(chan, "LD %p free\n", desc); +#endif dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); } } @@ -481,6 +488,9 @@ static void fsldma_free_desc_list_reverse(struct fsldma_chan *chan, list_for_each_entry_safe_reverse(desc, _desc, list, node) { list_del(&desc->node); +#ifdef FSL_DMA_LD_DEBUG + chan_dbg(chan, "LD %p free\n", desc); +#endif dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); } } @@ -557,9 +567,6 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( chan_err(chan, "%s\n", msg_ld_oom); goto fail; } -#ifdef FSL_DMA_LD_DEBUG - chan_dbg(chan, "new link desc alloc %p\n", new); -#endif copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT); @@ -645,9 +652,6 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan, chan_err(chan, "%s\n", msg_ld_oom); goto fail; } -#ifdef FSL_DMA_LD_DEBUG - chan_dbg(chan, "new link desc alloc %p\n", new); -#endif set_desc_cnt(chan, &new->hw, len); set_desc_src(chan, &new->hw, src); @@ -882,13 +886,18 @@ static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) callback_param = desc->async_tx.callback_param; if (callback) { spin_unlock_irqrestore(&chan->desc_lock, flags); +#ifdef FSL_DMA_LD_DEBUG chan_dbg(chan, "LD %p callback\n", desc); +#endif callback(callback_param); spin_lock_irqsave(&chan->desc_lock, flags); } /* Run any dependencies, then free the descriptor */ dma_run_dependencies(&desc->async_tx); +#ifdef FSL_DMA_LD_DEBUG + chan_dbg(chan, "LD %p free\n", desc); +#endif dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); } -- cgit v1.2.3 From 31f4306c83a2daa3e348056b720de511bffe5a9b Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:54:57 +0000 Subject: fsldma: minor codingstyle and consistency fixes This fixes some minor violations of the coding style. It also changes the style of the device_prep_dma_*() function definitions so they are identical. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 29 +++++++++++++---------------- drivers/dma/fsldma.h | 4 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 82b8e9f9c7bf..5da1a4a817e3 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -89,7 +89,7 @@ static void set_desc_cnt(struct fsldma_chan *chan, } static void set_desc_src(struct fsldma_chan *chan, - struct fsl_dma_ld_hw *hw, dma_addr_t src) + struct fsl_dma_ld_hw *hw, dma_addr_t src) { u64 snoop_bits; @@ -99,7 +99,7 @@ static void set_desc_src(struct fsldma_chan *chan, } static void set_desc_dst(struct fsldma_chan *chan, - struct fsl_dma_ld_hw *hw, dma_addr_t dst) + struct fsl_dma_ld_hw *hw, dma_addr_t dst) { u64 snoop_bits; @@ -109,7 +109,7 @@ static void set_desc_dst(struct fsldma_chan *chan, } static void set_desc_next(struct fsldma_chan *chan, - struct fsl_dma_ld_hw *hw, dma_addr_t next) + struct fsl_dma_ld_hw *hw, dma_addr_t next) { u64 snoop_bits; @@ -118,8 +118,7 @@ static void set_desc_next(struct fsldma_chan *chan, hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64); } -static void set_ld_eol(struct fsldma_chan *chan, - struct fsl_desc_sw *desc) +static void set_ld_eol(struct fsldma_chan *chan, struct fsl_desc_sw *desc) { u64 snoop_bits; @@ -338,8 +337,7 @@ static void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable) chan->feature &= ~FSL_DMA_CHAN_START_EXT; } -static void append_ld_queue(struct fsldma_chan *chan, - struct fsl_desc_sw *desc) +static void append_ld_queue(struct fsldma_chan *chan, struct fsl_desc_sw *desc) { struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev); @@ -380,8 +378,8 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) cookie = chan->common.cookie; list_for_each_entry(child, &desc->tx_list, node) { cookie++; - if (cookie < 0) - cookie = 1; + if (cookie < DMA_MIN_COOKIE) + cookie = DMA_MIN_COOKIE; child->async_tx.cookie = cookie; } @@ -402,8 +400,7 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) * * Return - The descriptor allocated. NULL for failed. */ -static struct fsl_desc_sw *fsl_dma_alloc_descriptor( - struct fsldma_chan *chan) +static struct fsl_desc_sw *fsl_dma_alloc_descriptor(struct fsldma_chan *chan) { struct fsl_desc_sw *desc; dma_addr_t pdesc; @@ -427,7 +424,6 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( return desc; } - /** * fsl_dma_alloc_chan_resources - Allocate resources for DMA channel. * @chan : Freescale DMA channel @@ -537,14 +533,15 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags) /* Insert the link descriptor to the LD ring */ list_add_tail(&new->node, &new->tx_list); - /* Set End-of-link to the last link descriptor of new list*/ + /* Set End-of-link to the last link descriptor of new list */ set_ld_eol(chan, new); return &new->async_tx; } -static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( - struct dma_chan *dchan, dma_addr_t dma_dst, dma_addr_t dma_src, +static struct dma_async_tx_descriptor * +fsl_dma_prep_memcpy(struct dma_chan *dchan, + dma_addr_t dma_dst, dma_addr_t dma_src, size_t len, unsigned long flags) { struct fsldma_chan *chan; @@ -594,7 +591,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( new->async_tx.flags = flags; /* client is in control of this ack */ new->async_tx.cookie = -EBUSY; - /* Set End-of-link to the last link descriptor of new list*/ + /* Set End-of-link to the last link descriptor of new list */ set_ld_eol(chan, new); return &first->async_tx; diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 113e7134010b..49189dacd5f4 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -102,8 +102,8 @@ struct fsl_desc_sw { } __attribute__((aligned(32))); struct fsldma_chan_regs { - u32 mr; /* 0x00 - Mode Register */ - u32 sr; /* 0x04 - Status Register */ + u32 mr; /* 0x00 - Mode Register */ + u32 sr; /* 0x04 - Status Register */ u64 cdar; /* 0x08 - Current descriptor address register */ u64 sar; /* 0x10 - Source Address Register */ u64 dar; /* 0x18 - Destination Address Register */ -- cgit v1.2.3 From f04cd40701deace2efb9edd7120e59366bda2118 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:54:58 +0000 Subject: fsldma: fix controller lockups Enabling poisoning in the dmapool API quickly showed that the DMA controller was fetching descriptors that should not have been in use. This has caused intermittent controller lockups during testing. I have been unable to figure out the exact set of conditions which cause this to happen. However, I believe it is related to the driver using the hardware registers to track whether the controller is busy or not. The code can incorrectly decide that the hardware is idle due to lag between register writes and the hardware actually becoming busy. To fix this, the driver has been reworked to explicitly track the state of the hardware, rather than try to guess what it is doing based on the register values. This has passed dmatest with 10 threads per channel, 100000 iterations per thread several times without error. Previously, this would fail within a few seconds. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 220 +++++++++++++++++++++++---------------------------- drivers/dma/fsldma.h | 1 + 2 files changed, 99 insertions(+), 122 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 5da1a4a817e3..6e9ad6edc4af 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -68,11 +68,6 @@ static dma_addr_t get_cdar(struct fsldma_chan *chan) return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; } -static dma_addr_t get_ndar(struct fsldma_chan *chan) -{ - return DMA_IN(chan, &chan->regs->ndar, 64); -} - static u32 get_bcr(struct fsldma_chan *chan) { return DMA_IN(chan, &chan->regs->bcr, 32); @@ -143,13 +138,11 @@ static void dma_init(struct fsldma_chan *chan) case FSL_DMA_IP_85XX: /* Set the channel to below modes: * EIE - Error interrupt enable - * EOSIE - End of segments interrupt enable (basic mode) * EOLNIE - End of links interrupt enable * BWC - Bandwidth sharing among channels */ DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_BWC - | FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE - | FSL_DMA_MR_EOSIE, 32); + | FSL_DMA_MR_EIE | FSL_DMA_MR_EOLNIE, 32); break; case FSL_DMA_IP_83XX: /* Set the channel to below modes: @@ -168,25 +161,32 @@ static int dma_is_idle(struct fsldma_chan *chan) return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH); } +/* + * Start the DMA controller + * + * Preconditions: + * - the CDAR register must point to the start descriptor + * - the MRn[CS] bit must be cleared + */ static void dma_start(struct fsldma_chan *chan) { u32 mode; mode = DMA_IN(chan, &chan->regs->mr, 32); - if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) { - if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) { - DMA_OUT(chan, &chan->regs->bcr, 0, 32); - mode |= FSL_DMA_MR_EMP_EN; - } else { - mode &= ~FSL_DMA_MR_EMP_EN; - } + if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) { + DMA_OUT(chan, &chan->regs->bcr, 0, 32); + mode |= FSL_DMA_MR_EMP_EN; + } else { + mode &= ~FSL_DMA_MR_EMP_EN; } - if (chan->feature & FSL_DMA_CHAN_START_EXT) + if (chan->feature & FSL_DMA_CHAN_START_EXT) { mode |= FSL_DMA_MR_EMS_EN; - else + } else { + mode &= ~FSL_DMA_MR_EMS_EN; mode |= FSL_DMA_MR_CS; + } DMA_OUT(chan, &chan->regs->mr, mode, 32); } @@ -760,14 +760,15 @@ static int fsl_dma_device_control(struct dma_chan *dchan, switch (cmd) { case DMA_TERMINATE_ALL: + spin_lock_irqsave(&chan->desc_lock, flags); + /* Halt the DMA engine */ dma_halt(chan); - spin_lock_irqsave(&chan->desc_lock, flags); - /* Remove and free all of the descriptors in the LD queue */ fsldma_free_desc_list(chan, &chan->ld_pending); fsldma_free_desc_list(chan, &chan->ld_running); + chan->idle = true; spin_unlock_irqrestore(&chan->desc_lock, flags); return 0; @@ -805,76 +806,43 @@ static int fsl_dma_device_control(struct dma_chan *dchan, } /** - * fsl_dma_update_completed_cookie - Update the completed cookie. + * fsl_chan_ld_cleanup - Clean up link descriptors * @chan : Freescale DMA channel * - * CONTEXT: hardirq + * This function is run after the queue of running descriptors has been + * executed by the DMA engine. It will run any callbacks, and then free + * the descriptors. + * + * HARDWARE STATE: idle */ -static void fsl_dma_update_completed_cookie(struct fsldma_chan *chan) +static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) { - struct fsl_desc_sw *desc; + struct fsl_desc_sw *desc, *_desc; unsigned long flags; - dma_cookie_t cookie; spin_lock_irqsave(&chan->desc_lock, flags); + /* if the ld_running list is empty, there is nothing to do */ if (list_empty(&chan->ld_running)) { - chan_dbg(chan, "no running descriptors\n"); + chan_dbg(chan, "no descriptors to cleanup\n"); goto out_unlock; } - /* Get the last descriptor, update the cookie to that */ + /* + * Get the last descriptor, update the cookie to it + * + * This is done before callbacks run so that clients can check the + * status of their DMA transfer inside the callback. + */ desc = to_fsl_desc(chan->ld_running.prev); - if (dma_is_idle(chan)) - cookie = desc->async_tx.cookie; - else { - cookie = desc->async_tx.cookie - 1; - if (unlikely(cookie < DMA_MIN_COOKIE)) - cookie = DMA_MAX_COOKIE; - } - - chan->completed_cookie = cookie; - -out_unlock: - spin_unlock_irqrestore(&chan->desc_lock, flags); -} - -/** - * fsldma_desc_status - Check the status of a descriptor - * @chan: Freescale DMA channel - * @desc: DMA SW descriptor - * - * This function will return the status of the given descriptor - */ -static enum dma_status fsldma_desc_status(struct fsldma_chan *chan, - struct fsl_desc_sw *desc) -{ - return dma_async_is_complete(desc->async_tx.cookie, - chan->completed_cookie, - chan->common.cookie); -} - -/** - * fsl_chan_ld_cleanup - Clean up link descriptors - * @chan : Freescale DMA channel - * - * This function clean up the ld_queue of DMA channel. - */ -static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) -{ - struct fsl_desc_sw *desc, *_desc; - unsigned long flags; - - spin_lock_irqsave(&chan->desc_lock, flags); + chan->completed_cookie = desc->async_tx.cookie; + chan_dbg(chan, "completed_cookie = %d\n", chan->completed_cookie); - chan_dbg(chan, "chan completed_cookie = %d\n", chan->completed_cookie); + /* Run the callback for each descriptor, in order */ list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) { dma_async_tx_callback callback; void *callback_param; - if (fsldma_desc_status(chan, desc) == DMA_IN_PROGRESS) - break; - /* Remove from the list of running transactions */ list_del(&desc->node); @@ -898,6 +866,7 @@ static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); } +out_unlock: spin_unlock_irqrestore(&chan->desc_lock, flags); } @@ -905,10 +874,7 @@ static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) * fsl_chan_xfer_ld_queue - transfer any pending transactions * @chan : Freescale DMA channel * - * This will make sure that any pending transactions will be run. - * If the DMA controller is idle, it will be started. Otherwise, - * the DMA controller's interrupt handler will start any pending - * transactions when it becomes idle. + * HARDWARE STATE: idle */ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) { @@ -927,22 +893,15 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) } /* - * The DMA controller is not idle, which means the interrupt - * handler will start any queued transactions when it runs - * at the end of the current transaction + * The DMA controller is not idle, which means that the interrupt + * handler will start any queued transactions when it runs after + * this transaction finishes */ - if (!dma_is_idle(chan)) { + if (!chan->idle) { chan_dbg(chan, "DMA controller still busy\n"); goto out_unlock; } - /* - * TODO: - * make sure the dma_halt() function really un-wedges the - * controller as much as possible - */ - dma_halt(chan); - /* * If there are some link descriptors which have not been * transferred, we need to start the controller @@ -952,15 +911,32 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) * Move all elements from the queue of pending transactions * onto the list of running transactions */ + chan_dbg(chan, "idle, starting controller\n"); desc = list_first_entry(&chan->ld_pending, struct fsl_desc_sw, node); list_splice_tail_init(&chan->ld_pending, &chan->ld_running); + /* + * The 85xx DMA controller doesn't clear the channel start bit + * automatically at the end of a transfer. Therefore we must clear + * it in software before starting the transfer. + */ + if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) { + u32 mode; + + mode = DMA_IN(chan, &chan->regs->mr, 32); + mode &= ~FSL_DMA_MR_CS; + DMA_OUT(chan, &chan->regs->mr, mode, 32); + } + /* * Program the descriptor's address into the DMA controller, * then start the DMA transaction */ set_cdar(chan, desc->async_tx.phys); + get_cdar(chan); + dma_start(chan); + chan->idle = false; out_unlock: spin_unlock_irqrestore(&chan->desc_lock, flags); @@ -985,16 +961,18 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan, struct dma_tx_state *txstate) { struct fsldma_chan *chan = to_fsl_chan(dchan); - dma_cookie_t last_used; dma_cookie_t last_complete; + dma_cookie_t last_used; + unsigned long flags; - fsl_chan_ld_cleanup(chan); + spin_lock_irqsave(&chan->desc_lock, flags); - last_used = dchan->cookie; last_complete = chan->completed_cookie; + last_used = dchan->cookie; - dma_set_tx_state(txstate, last_complete, last_used, 0); + spin_unlock_irqrestore(&chan->desc_lock, flags); + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -1005,8 +983,6 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan, static irqreturn_t fsldma_chan_irq(int irq, void *data) { struct fsldma_chan *chan = data; - int update_cookie = 0; - int xfer_ld_q = 0; u32 stat; /* save and clear the status register */ @@ -1014,6 +990,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) set_sr(chan, stat); chan_dbg(chan, "irq: stat = 0x%x\n", stat); + /* check that this was really our device */ stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH); if (!stat) return IRQ_NONE; @@ -1028,28 +1005,9 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) */ if (stat & FSL_DMA_SR_PE) { chan_dbg(chan, "irq: Programming Error INT\n"); - if (get_bcr(chan) == 0) { - /* BCR register is 0, this is a DMA_INTERRUPT async_tx. - * Now, update the completed cookie, and continue the - * next uncompleted transfer. - */ - update_cookie = 1; - xfer_ld_q = 1; - } stat &= ~FSL_DMA_SR_PE; - } - - /* - * If the link descriptor segment transfer finishes, - * we will recycle the used descriptor. - */ - if (stat & FSL_DMA_SR_EOSI) { - chan_dbg(chan, "irq: End-of-segments INT\n"); - chan_dbg(chan, "irq: clndar 0x%llx, nlndar 0x%llx\n", - (unsigned long long)get_cdar(chan), - (unsigned long long)get_ndar(chan)); - stat &= ~FSL_DMA_SR_EOSI; - update_cookie = 1; + if (get_bcr(chan) != 0) + chan_err(chan, "Programming Error!\n"); } /* @@ -1059,8 +1017,6 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) if (stat & FSL_DMA_SR_EOCDI) { chan_dbg(chan, "irq: End-of-Chain link INT\n"); stat &= ~FSL_DMA_SR_EOCDI; - update_cookie = 1; - xfer_ld_q = 1; } /* @@ -1071,25 +1027,44 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) if (stat & FSL_DMA_SR_EOLNI) { chan_dbg(chan, "irq: End-of-link INT\n"); stat &= ~FSL_DMA_SR_EOLNI; - xfer_ld_q = 1; } - if (update_cookie) - fsl_dma_update_completed_cookie(chan); - if (xfer_ld_q) - fsl_chan_xfer_ld_queue(chan); + /* check that the DMA controller is really idle */ + if (!dma_is_idle(chan)) + chan_err(chan, "irq: controller not idle!\n"); + + /* check that we handled all of the bits */ if (stat) - chan_dbg(chan, "irq: unhandled sr 0x%08x\n", stat); + chan_err(chan, "irq: unhandled sr 0x%08x\n", stat); - chan_dbg(chan, "irq: Exit\n"); + /* + * Schedule the tasklet to handle all cleanup of the current + * transaction. It will start a new transaction if there is + * one pending. + */ tasklet_schedule(&chan->tasklet); + chan_dbg(chan, "irq: Exit\n"); return IRQ_HANDLED; } static void dma_do_tasklet(unsigned long data) { struct fsldma_chan *chan = (struct fsldma_chan *)data; + unsigned long flags; + + chan_dbg(chan, "tasklet entry\n"); + + /* run all callbacks, free all used descriptors */ fsl_chan_ld_cleanup(chan); + + /* the channel is now idle */ + spin_lock_irqsave(&chan->desc_lock, flags); + chan->idle = true; + spin_unlock_irqrestore(&chan->desc_lock, flags); + + /* start any pending transactions automatically */ + fsl_chan_xfer_ld_queue(chan); + chan_dbg(chan, "tasklet exit\n"); } static irqreturn_t fsldma_ctrl_irq(int irq, void *data) @@ -1269,6 +1244,7 @@ static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev, spin_lock_init(&chan->desc_lock); INIT_LIST_HEAD(&chan->ld_pending); INIT_LIST_HEAD(&chan->ld_running); + chan->idle = true; chan->common.device = &fdev->common; diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 49189dacd5f4..9cb5aa57c677 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -148,6 +148,7 @@ struct fsldma_chan { int id; /* Raw id of this channel */ struct tasklet_struct tasklet; u32 feature; + bool idle; /* DMA controller is idle */ void (*toggle_ext_pause)(struct fsldma_chan *fsl_chan, int enable); void (*toggle_ext_start)(struct fsldma_chan *fsl_chan, int enable); -- cgit v1.2.3 From 9c4d1e7bdeb1ed4dc0c3341d40662a6fbc5f2dc2 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:54:59 +0000 Subject: fsldma: support async_tx dependencies and automatic unmapping Previous to this patch, the dma_run_dependencies() function has been called while holding desc_lock. This function can call tx_submit() for other descriptors, which may try to re-grab the lock. Avoid this by moving the descriptors to be cleaned up to a temporary list, and dropping the lock before cleanup. At the same time, add support for automatic unmapping of src and dst buffers, as offered by the DMAEngine API. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 131 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 36 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 6e9ad6edc4af..526579df6033 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -83,6 +83,11 @@ static void set_desc_cnt(struct fsldma_chan *chan, hw->count = CPU_TO_DMA(chan, count, 32); } +static u32 get_desc_cnt(struct fsldma_chan *chan, struct fsl_desc_sw *desc) +{ + return DMA_TO_CPU(chan, desc->hw.count, 32); +} + static void set_desc_src(struct fsldma_chan *chan, struct fsl_dma_ld_hw *hw, dma_addr_t src) { @@ -93,6 +98,16 @@ static void set_desc_src(struct fsldma_chan *chan, hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64); } +static dma_addr_t get_desc_src(struct fsldma_chan *chan, + struct fsl_desc_sw *desc) +{ + u64 snoop_bits; + + snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) + ? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0; + return DMA_TO_CPU(chan, desc->hw.src_addr, 64) & ~snoop_bits; +} + static void set_desc_dst(struct fsldma_chan *chan, struct fsl_dma_ld_hw *hw, dma_addr_t dst) { @@ -103,6 +118,16 @@ static void set_desc_dst(struct fsldma_chan *chan, hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64); } +static dma_addr_t get_desc_dst(struct fsldma_chan *chan, + struct fsl_desc_sw *desc) +{ + u64 snoop_bits; + + snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) + ? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0; + return DMA_TO_CPU(chan, desc->hw.dst_addr, 64) & ~snoop_bits; +} + static void set_desc_next(struct fsldma_chan *chan, struct fsl_dma_ld_hw *hw, dma_addr_t next) { @@ -805,6 +830,57 @@ static int fsl_dma_device_control(struct dma_chan *dchan, return 0; } +/** + * fsldma_cleanup_descriptor - cleanup and free a single link descriptor + * @chan: Freescale DMA channel + * @desc: descriptor to cleanup and free + * + * This function is used on a descriptor which has been executed by the DMA + * controller. It will run any callbacks, submit any dependencies, and then + * free the descriptor. + */ +static void fsldma_cleanup_descriptor(struct fsldma_chan *chan, + struct fsl_desc_sw *desc) +{ + struct dma_async_tx_descriptor *txd = &desc->async_tx; + struct device *dev = chan->common.device->dev; + dma_addr_t src = get_desc_src(chan, desc); + dma_addr_t dst = get_desc_dst(chan, desc); + u32 len = get_desc_cnt(chan, desc); + + /* Run the link descriptor callback function */ + if (txd->callback) { +#ifdef FSL_DMA_LD_DEBUG + chan_dbg(chan, "LD %p callback\n", desc); +#endif + txd->callback(txd->callback_param); + } + + /* Run any dependencies */ + dma_run_dependencies(txd); + + /* Unmap the dst buffer, if requested */ + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(dev, dst, len, DMA_FROM_DEVICE); + else + dma_unmap_page(dev, dst, len, DMA_FROM_DEVICE); + } + + /* Unmap the src buffer, if requested */ + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(dev, src, len, DMA_TO_DEVICE); + else + dma_unmap_page(dev, src, len, DMA_TO_DEVICE); + } + +#ifdef FSL_DMA_LD_DEBUG + chan_dbg(chan, "LD %p free\n", desc); +#endif + dma_pool_free(chan->desc_pool, desc, txd->phys); +} + /** * fsl_chan_ld_cleanup - Clean up link descriptors * @chan : Freescale DMA channel @@ -818,56 +894,39 @@ static int fsl_dma_device_control(struct dma_chan *dchan, static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) { struct fsl_desc_sw *desc, *_desc; + LIST_HEAD(ld_cleanup); unsigned long flags; spin_lock_irqsave(&chan->desc_lock, flags); - /* if the ld_running list is empty, there is nothing to do */ - if (list_empty(&chan->ld_running)) { - chan_dbg(chan, "no descriptors to cleanup\n"); - goto out_unlock; + /* update the cookie if we have some descriptors to cleanup */ + if (!list_empty(&chan->ld_running)) { + dma_cookie_t cookie; + + desc = to_fsl_desc(chan->ld_running.prev); + cookie = desc->async_tx.cookie; + + chan->completed_cookie = cookie; + chan_dbg(chan, "completed cookie=%d\n", cookie); } /* - * Get the last descriptor, update the cookie to it - * - * This is done before callbacks run so that clients can check the - * status of their DMA transfer inside the callback. + * move the descriptors to a temporary list so we can drop the lock + * during the entire cleanup operation */ - desc = to_fsl_desc(chan->ld_running.prev); - chan->completed_cookie = desc->async_tx.cookie; - chan_dbg(chan, "completed_cookie = %d\n", chan->completed_cookie); + list_splice_tail_init(&chan->ld_running, &ld_cleanup); + + spin_unlock_irqrestore(&chan->desc_lock, flags); /* Run the callback for each descriptor, in order */ - list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) { - dma_async_tx_callback callback; - void *callback_param; + list_for_each_entry_safe(desc, _desc, &ld_cleanup, node) { - /* Remove from the list of running transactions */ + /* Remove from the list of transactions */ list_del(&desc->node); - /* Run the link descriptor callback function */ - callback = desc->async_tx.callback; - callback_param = desc->async_tx.callback_param; - if (callback) { - spin_unlock_irqrestore(&chan->desc_lock, flags); -#ifdef FSL_DMA_LD_DEBUG - chan_dbg(chan, "LD %p callback\n", desc); -#endif - callback(callback_param); - spin_lock_irqsave(&chan->desc_lock, flags); - } - - /* Run any dependencies, then free the descriptor */ - dma_run_dependencies(&desc->async_tx); -#ifdef FSL_DMA_LD_DEBUG - chan_dbg(chan, "LD %p free\n", desc); -#endif - dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); + /* Run all cleanup for this descriptor */ + fsldma_cleanup_descriptor(chan, desc); } - -out_unlock: - spin_unlock_irqrestore(&chan->desc_lock, flags); } /** -- cgit v1.2.3 From dc8d4091575ba81e886ebcdfd1e559c981f82f86 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:55:00 +0000 Subject: fsldma: reduce locking during descriptor cleanup This merges the fsl_chan_ld_cleanup() function into the dma_do_tasklet() function to reduce locking overhead. In the best case, we will be able to keep the DMA controller busy while we are freeing used descriptors. In all cases, the spinlock is grabbed two times fewer than before on each transaction. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 108 ++++++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 62 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 526579df6033..d300de456c90 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -881,66 +881,16 @@ static void fsldma_cleanup_descriptor(struct fsldma_chan *chan, dma_pool_free(chan->desc_pool, desc, txd->phys); } -/** - * fsl_chan_ld_cleanup - Clean up link descriptors - * @chan : Freescale DMA channel - * - * This function is run after the queue of running descriptors has been - * executed by the DMA engine. It will run any callbacks, and then free - * the descriptors. - * - * HARDWARE STATE: idle - */ -static void fsl_chan_ld_cleanup(struct fsldma_chan *chan) -{ - struct fsl_desc_sw *desc, *_desc; - LIST_HEAD(ld_cleanup); - unsigned long flags; - - spin_lock_irqsave(&chan->desc_lock, flags); - - /* update the cookie if we have some descriptors to cleanup */ - if (!list_empty(&chan->ld_running)) { - dma_cookie_t cookie; - - desc = to_fsl_desc(chan->ld_running.prev); - cookie = desc->async_tx.cookie; - - chan->completed_cookie = cookie; - chan_dbg(chan, "completed cookie=%d\n", cookie); - } - - /* - * move the descriptors to a temporary list so we can drop the lock - * during the entire cleanup operation - */ - list_splice_tail_init(&chan->ld_running, &ld_cleanup); - - spin_unlock_irqrestore(&chan->desc_lock, flags); - - /* Run the callback for each descriptor, in order */ - list_for_each_entry_safe(desc, _desc, &ld_cleanup, node) { - - /* Remove from the list of transactions */ - list_del(&desc->node); - - /* Run all cleanup for this descriptor */ - fsldma_cleanup_descriptor(chan, desc); - } -} - /** * fsl_chan_xfer_ld_queue - transfer any pending transactions * @chan : Freescale DMA channel * * HARDWARE STATE: idle + * LOCKING: must hold chan->desc_lock */ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) { struct fsl_desc_sw *desc; - unsigned long flags; - - spin_lock_irqsave(&chan->desc_lock, flags); /* * If the list of pending descriptors is empty, then we @@ -948,7 +898,7 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) */ if (list_empty(&chan->ld_pending)) { chan_dbg(chan, "no pending LDs\n"); - goto out_unlock; + return; } /* @@ -958,7 +908,7 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) */ if (!chan->idle) { chan_dbg(chan, "DMA controller still busy\n"); - goto out_unlock; + return; } /* @@ -996,9 +946,6 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) dma_start(chan); chan->idle = false; - -out_unlock: - spin_unlock_irqrestore(&chan->desc_lock, flags); } /** @@ -1008,7 +955,11 @@ out_unlock: static void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan) { struct fsldma_chan *chan = to_fsl_chan(dchan); + unsigned long flags; + + spin_lock_irqsave(&chan->desc_lock, flags); fsl_chan_xfer_ld_queue(chan); + spin_unlock_irqrestore(&chan->desc_lock, flags); } /** @@ -1109,20 +1060,53 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) static void dma_do_tasklet(unsigned long data) { struct fsldma_chan *chan = (struct fsldma_chan *)data; + struct fsl_desc_sw *desc, *_desc; + LIST_HEAD(ld_cleanup); unsigned long flags; chan_dbg(chan, "tasklet entry\n"); - /* run all callbacks, free all used descriptors */ - fsl_chan_ld_cleanup(chan); - - /* the channel is now idle */ spin_lock_irqsave(&chan->desc_lock, flags); + + /* update the cookie if we have some descriptors to cleanup */ + if (!list_empty(&chan->ld_running)) { + dma_cookie_t cookie; + + desc = to_fsl_desc(chan->ld_running.prev); + cookie = desc->async_tx.cookie; + + chan->completed_cookie = cookie; + chan_dbg(chan, "completed_cookie=%d\n", cookie); + } + + /* + * move the descriptors to a temporary list so we can drop the lock + * during the entire cleanup operation + */ + list_splice_tail_init(&chan->ld_running, &ld_cleanup); + + /* the hardware is now idle and ready for more */ chan->idle = true; - spin_unlock_irqrestore(&chan->desc_lock, flags); - /* start any pending transactions automatically */ + /* + * Start any pending transactions automatically + * + * In the ideal case, we keep the DMA controller busy while we go + * ahead and free the descriptors below. + */ fsl_chan_xfer_ld_queue(chan); + spin_unlock_irqrestore(&chan->desc_lock, flags); + + /* Run the callback for each descriptor, in order */ + list_for_each_entry_safe(desc, _desc, &ld_cleanup, node) { + + /* Remove from the list of transactions */ + list_del(&desc->node); + + /* Run all cleanup for this descriptor */ + fsldma_cleanup_descriptor(chan, desc); + } + chan_dbg(chan, "tasklet exit\n"); } -- cgit v1.2.3 From a00ae34ac8bc8a5897d9b6b9b685c39b955b14b9 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 3 Mar 2011 07:55:01 +0000 Subject: fsldma: make halt behave nicely on all supported controllers The original dma_halt() function set the CA (channel abort) bit on both the 83xx and 85xx controllers. This is incorrect on the 83xx, where this bit means TEM (transfer error mask) instead. The 83xx doesn't support channel abort, so we only do this operation on 85xx. Signed-off-by: Ira W. Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/dma/fsldma.c') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index d300de456c90..8670a5012122 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -221,13 +221,26 @@ static void dma_halt(struct fsldma_chan *chan) u32 mode; int i; + /* read the mode register */ mode = DMA_IN(chan, &chan->regs->mr, 32); - mode |= FSL_DMA_MR_CA; - DMA_OUT(chan, &chan->regs->mr, mode, 32); - mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN | FSL_DMA_MR_CA); + /* + * The 85xx controller supports channel abort, which will stop + * the current transfer. On 83xx, this bit is the transfer error + * mask bit, which should not be changed. + */ + if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) { + mode |= FSL_DMA_MR_CA; + DMA_OUT(chan, &chan->regs->mr, mode, 32); + + mode &= ~FSL_DMA_MR_CA; + } + + /* stop the DMA controller */ + mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN); DMA_OUT(chan, &chan->regs->mr, mode, 32); + /* wait for the DMA controller to become idle */ for (i = 0; i < 100; i++) { if (dma_is_idle(chan)) return; -- cgit v1.2.3