From a9ddb575d6d6c58c39e8c44a22b84445fedb0521 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:17 +0530 Subject: dmaengine: dw_dmac: Enhance device tree support dw_dmac driver already supports device tree but it used to have its platform data passed the non-DT way. This patch does following changes: - pass platform data via DT, non-DT way still takes precedence if both are used. - create generic filter routine - Earlier slave information was made available by slave specific filter routines in chan->private field. Now, this information would be passed from within dmac DT node. Slave drivers would now be required to pass bus_id (a string) as parameter to this generic filter(), which would be compared against the slave data passed from DT, by the generic filter routine. - Update binding document Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko [Fixed __devinit usage] Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac_regs.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88965597b7d..88a069f66b8 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -239,6 +239,10 @@ struct dw_dma { struct tasklet_struct tasklet; struct clk *clk; + /* slave information */ + struct dw_dma_slave *sd; + unsigned int sd_count; + u8 all_chan_mask; /* hardware configuration */ -- cgit v1.2.3-70-g09d2 From e63a47a361e03eaf79e0f2f6cdaca8e7679d1867 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:12 +0300 Subject: dw_dmac: introduce to_dw_desc() macro The to_dw_desc() macro helps to retrieve the dw_desc node from the corresponding list_head structure. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++---- drivers/dma/dw_dmac_regs.h | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9a27fb70c95..476e9c8fb6c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -93,7 +93,7 @@ static struct device *chan2parent(struct dma_chan *chan) static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) { - return list_entry(dwc->active_list.next, struct dw_desc, desc_node); + return to_dw_desc(dwc->active_list.next); } static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) @@ -600,9 +600,7 @@ static void dw_dma_tasklet(unsigned long data) if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { if (dwc->tx_node_active != dwc->tx_list) { struct dw_desc *desc = - list_entry(dwc->tx_node_active, - struct dw_desc, - desc_node); + to_dw_desc(dwc->tx_node_active); dma_writel(dw, CLEAR.XFER, dwc->mask); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88a069f66b8..8881e9b277a 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -299,6 +299,8 @@ struct dw_desc { size_t len; }; +#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) + static inline struct dw_desc * txd_to_dw_desc(struct dma_async_tx_descriptor *txd) { -- cgit v1.2.3-70-g09d2 From 0fdb567fc72da906e230ce7e2aae2feba260a6be Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:03 +0200 Subject: dw_dmac: store direction in the custom channel structure Currently the direction value comes from the generic slave configuration structure and explicitly as a preparation function parameter. The first one is kinda obsoleted. Thus, we have to store the value passed to the preparation function somewhere in our structures to be able to use it later. The best candidate to provide the storage is a custom channel structure. Until now we still keep and check the direction field of the slave config structure as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 12 ++++++++++-- drivers/dma/dw_dmac_regs.h | 14 ++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e554027849b..a2c5a60bc2e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -178,9 +178,9 @@ static void dwc_initialize(struct dw_dma_chan *dwc) cfghi = dws->cfg_hi; cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; } else { - if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV) + if (dwc->direction == DMA_MEM_TO_DEV) cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id); - else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM) + else if (dwc->direction == DMA_DEV_TO_MEM) cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id); } @@ -721,6 +721,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } + dwc->direction = DMA_MEM_TO_MEM; + data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], dwc->dw->data_width[dwc_get_dms(dws)]); @@ -805,6 +807,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (unlikely(!dws || !sg_len)) return NULL; + dwc->direction = direction; + prev = first = NULL; switch (direction) { @@ -981,6 +985,7 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->direction = sconfig->direction; convert_burst(&dwc->dma_sconfig.src_maxburst); convert_burst(&dwc->dma_sconfig.dst_maxburst); @@ -1358,6 +1363,8 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, if (unlikely(!is_slave_direction(direction))) goto out_err; + dwc->direction = direction; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1732,6 +1739,7 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); dwc->dw = dw; + dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 8881e9b277a..f9532c29b80 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #define DW_DMA_MAX_NR_CHANNELS 8 @@ -184,12 +185,13 @@ enum dw_dmac_flags { }; struct dw_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - u8 mask; - u8 priority; - bool paused; - bool initialized; + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + u8 priority; + enum dma_transfer_direction direction; + bool paused; + bool initialized; /* software emulation of the LLP transfers */ struct list_head *tx_list; -- cgit v1.2.3-70-g09d2 From 23d5f4ec9de43dbc73a42f1483d9339b907c3dff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:05 +0200 Subject: dw_dmac: backlink to dw_dma in dw_dma_chan is superfluous The same information could be extracted from the struct dma_chan. The patch introduces helper function dwc_get_data_width() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 27 ++++++++++++++++++++------- drivers/dma/dw_dmac_regs.h | 3 --- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e5378b5741b..154952abc2e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -73,6 +73,22 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + + if (master == SRC_MASTER) + return dw->data_width[dwc_get_sms(dws)]; + else if (master == DST_MASTER) + return dw->data_width[dwc_get_dms(dws)]; + + return 0; +} + /*----------------------------------------------------------------------*/ /* @@ -701,7 +717,6 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dw_desc *desc; struct dw_desc *first; struct dw_desc *prev; @@ -724,8 +739,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction = DMA_MEM_TO_MEM; - data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], - dwc->dw->data_width[dwc_get_dms(dws)]); + data_width = min_t(unsigned int, dwc_get_data_width(chan, SRC_MASTER), + dwc_get_data_width(chan, DST_MASTER)); src_width = dst_width = min_t(unsigned int, data_width, dwc_fast_fls(src | dest | len)); @@ -790,7 +805,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags, void *context) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; @@ -824,7 +838,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); - data_width = dwc->dw->data_width[dwc_get_sms(dws)]; + data_width = dwc_get_data_width(chan, SRC_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -887,7 +901,7 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); - data_width = dwc->dw->data_width[dwc_get_dms(dws)]; + data_width = dwc_get_data_width(chan, DST_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -1739,7 +1753,6 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); - dwc->dw = dw; dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index f9532c29b80..577f2dd3588 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -214,9 +214,6 @@ struct dw_dma_chan { /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; - - /* backlink to dw_dma */ - struct dw_dma *dw; }; static inline struct dw_dma_chan_regs __iomem * -- cgit v1.2.3-70-g09d2 From f8122a82d2eae8ef42de48829deed0ca9d9e1f17 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Jan 2013 15:48:50 +0200 Subject: dw_dmac: allocate dma descriptors from DMA_COHERENT memory Currently descriptors are allocated from normal cacheable memory and that slows down filling the descriptors, as we need to call cache_coherency routines afterwards. It would be better to allocate memory for these descriptors from DMA_COHERENT memory. This would make code much cleaner too. Signed-off-by: Andy Shevchenko Tested-by: Mika Westerberg Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 81 ++++++++++------------------------------------ drivers/dma/dw_dmac_regs.h | 1 + 2 files changed, 18 insertions(+), 64 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 28d5f01c350..76301c4ba1a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -91,14 +92,6 @@ static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) /*----------------------------------------------------------------------*/ -/* - * Because we're not relying on writeback from the controller (it may not - * even be configured into the core!) we don't need to use dma_pool. These - * descriptors -- and associated data -- are cacheable. We do need to make - * sure their dcache entries are written back before handing them off to - * the controller, though. - */ - static struct device *chan2dev(struct dma_chan *chan) { return &chan->dev->device; @@ -137,19 +130,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) return ret; } -static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - struct dw_desc *child; - - list_for_each_entry(child, &desc->tx_list, desc_node) - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - child->txd.phys, sizeof(child->lli), - DMA_TO_DEVICE); - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - desc->txd.phys, sizeof(desc->lli), - DMA_TO_DEVICE); -} - /* * Move a descriptor, including any children, to the free list. * `desc' must not be on any lists. @@ -161,8 +141,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) if (desc) { struct dw_desc *child; - dwc_sync_desc_for_cpu(dwc, desc); - spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dwc->chan), @@ -335,8 +313,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, param = txd->callback_param; } - dwc_sync_desc_for_cpu(dwc, desc); - /* async_tx_ack */ list_for_each_entry(child, &desc->tx_list, desc_node) async_tx_ack(&child->txd); @@ -770,25 +746,17 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } - if (flags & DMA_PREP_INTERRUPT) /* Trigger interrupt after last block */ prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->txd.flags = flags; first->len = len; @@ -876,10 +844,6 @@ slave_sg_todev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -938,10 +902,6 @@ slave_sg_fromdev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -961,10 +921,6 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->len = total_len; return &first->txd; @@ -1118,7 +1074,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; - int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1139,21 +1094,21 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); i = dwc->descs_allocated; while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + spin_unlock_irqrestore(&dwc->lock, flags); - desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); if (!desc) goto err_desc_alloc; + memset(desc, 0, sizeof(struct dw_desc)); + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, - sizeof(desc->lli), DMA_TO_DEVICE); - ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); - if (ret) - goto err_desc_alloc; + desc->txd.phys = phys; dwc_desc_put(dwc, desc); @@ -1168,8 +1123,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) return i; err_desc_alloc: - kfree(desc); - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); return i; @@ -1204,9 +1157,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) list_for_each_entry_safe(desc, _desc, &list, desc_node) { dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_unmap_single(chan2parent(chan), desc->txd.phys, - sizeof(desc->lli), DMA_TO_DEVICE); - kfree(desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); } dev_vdbg(chan2dev(chan), "%s: done\n", __func__); @@ -1451,20 +1402,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, desc->lli.ctlhi = (period_len >> reg_width); cdesc->desc[i] = desc; - if (last) { + if (last) last->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - last->txd.phys, sizeof(last->lli), - DMA_TO_DEVICE); - } last = desc; } /* lets make a cyclic list */ last->lli.llp = cdesc->desc[0]->txd.phys; - dma_sync_single_for_device(chan2parent(chan), last->txd.phys, - sizeof(last->lli), DMA_TO_DEVICE); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " "period %zu periods %d\n", (unsigned long long)buf_addr, @@ -1722,6 +1667,14 @@ static int dw_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw); + /* create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); INIT_LIST_HEAD(&dw->dma.channels); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 577f2dd3588..fef296de4af 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -235,6 +235,7 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) struct dw_dma { struct dma_device dma; void __iomem *regs; + struct dma_pool *desc_pool; struct tasklet_struct tasklet; struct clk *clk; -- cgit v1.2.3-70-g09d2 From fdf475fa40f1468cf43a72b270f74dc6a4a5c905 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:00 +0200 Subject: dw_dmac: remove unnecessary tx_list field in dw_dma_chan The soft LLP mode is working for active descriptor only. So, we do not need to have a copy of its pointer. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 20 +++++++++++++++----- drivers/dma/dw_dmac_regs.h | 1 - 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index ddd3ad2a96a..721beafe8f7 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -282,9 +282,9 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); - dwc->tx_list = &first->tx_list; dwc->tx_node_active = &first->tx_list; + /* Submit first block */ dwc_do_single_block(dwc, first); return; @@ -402,15 +402,25 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) dma_writel(dw, CLEAR.XFER, dwc->mask); if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - if (dwc->tx_node_active != dwc->tx_list) { - desc = to_dw_desc(dwc->tx_node_active); + struct list_head *head, *active = dwc->tx_node_active; + + /* + * We are inside first active descriptor. + * Otherwise something is really wrong. + */ + desc = dwc_first_active(dwc); + + head = &desc->tx_list; + if (active != head) { + child = to_dw_desc(active); /* Submit next block */ - dwc_do_single_block(dwc, desc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc_do_single_block(dwc, child); + spin_unlock_irqrestore(&dwc->lock, flags); return; } + /* We are done here */ clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index fef296de4af..13000d2b335 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -194,7 +194,6 @@ struct dw_dma_chan { bool initialized; /* software emulation of the LLP transfers */ - struct list_head *tx_list; struct list_head *tx_node_active; spinlock_t lock; -- cgit v1.2.3-70-g09d2 From 30d38a3286b140ae8cea84a93cde1f112e352aaf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:01 +0200 Subject: dw_dmac: introduce total_len field in struct dw_desc By this new field we distinguish a total length of the chain and the individual length of each descriptor in the chain. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 12 ++++++------ drivers/dma/dw_dmac_regs.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 721beafe8f7..7f9f3324de1 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -333,18 +333,18 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) dma_unmap_single(parent, desc->lli.dar, - desc->len, DMA_FROM_DEVICE); + desc->total_len, DMA_FROM_DEVICE); else dma_unmap_page(parent, desc->lli.dar, - desc->len, DMA_FROM_DEVICE); + desc->total_len, DMA_FROM_DEVICE); } if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) dma_unmap_single(parent, desc->lli.sar, - desc->len, DMA_TO_DEVICE); + desc->total_len, DMA_TO_DEVICE); else dma_unmap_page(parent, desc->lli.sar, - desc->len, DMA_TO_DEVICE); + desc->total_len, DMA_TO_DEVICE); } } @@ -774,7 +774,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, prev->lli.llp = 0; first->txd.flags = flags; - first->len = len; + first->total_len = len; return &first->txd; @@ -937,7 +937,7 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - first->len = total_len; + first->total_len = total_len; return &first->txd; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 13000d2b335..833b4cf9843 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -296,6 +296,7 @@ struct dw_desc { struct list_head tx_list; struct dma_async_tx_descriptor txd; size_t len; + size_t total_len; }; #define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) -- cgit v1.2.3-70-g09d2 From 4702d5244ca947263e8b7eb2ba6d8721e80c46e2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2013 11:48:03 +0200 Subject: dw_dmac: return proper residue value Currently the driver returns full length of the active descriptor which is wrong. We have to go throught the active descriptor and substract the length of each sent children in the chain from the total length along with the actual data in the DMA channel registers. The cyclic case is not handled by this patch due to len field in the descriptor structure is left untouched by the original code. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 47 ++++++++++++++++++++++++++++++++++++++++++++-- drivers/dma/dw_dmac_regs.h | 1 + 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers/dma/dw_dmac_regs.h') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 5816da34129..35c8dd5f101 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -282,6 +282,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); + dwc->residue = first->total_len; dwc->tx_node_active = &first->tx_list; /* Submit first block */ @@ -385,6 +386,15 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_descriptor_complete(dwc, desc, true); } +/* Returns how many bytes were already received from source */ +static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) +{ + u32 ctlhi = channel_readl(dwc, CTL_HI); + u32 ctllo = channel_readl(dwc, CTL_LO); + + return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7)); +} + static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) { dma_addr_t llp; @@ -412,6 +422,12 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) head = &desc->tx_list; if (active != head) { + /* Update desc to reflect last sent one */ + if (active != head->next) + desc = to_dw_desc(active->prev); + + dwc->residue -= desc->len; + child = to_dw_desc(active); /* Submit next block */ @@ -424,6 +440,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) /* We are done here */ clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } + + dwc->residue = 0; + spin_unlock_irqrestore(&dwc->lock, flags); dwc_complete_all(dw, dwc); @@ -431,6 +450,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) } if (list_empty(&dwc->active_list)) { + dwc->residue = 0; spin_unlock_irqrestore(&dwc->lock, flags); return; } @@ -445,6 +465,9 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) (unsigned long long)llp); list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + /* initial residue value */ + dwc->residue = desc->total_len; + /* check first descriptors addr */ if (desc->txd.phys == llp) { spin_unlock_irqrestore(&dwc->lock, flags); @@ -454,16 +477,21 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) /* check first descriptors llp */ if (desc->lli.llp == llp) { /* This one is currently in progress */ + dwc->residue -= dwc_get_sent(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return; } - list_for_each_entry(child, &desc->tx_list, desc_node) + dwc->residue -= desc->len; + list_for_each_entry(child, &desc->tx_list, desc_node) { if (child->lli.llp == llp) { /* Currently in progress */ + dwc->residue -= dwc_get_sent(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return; } + dwc->residue -= child->len; + } /* * No descriptors so far seem to be in progress, i.e. @@ -1054,6 +1082,21 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, return 0; } +static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) +{ + unsigned long flags; + u32 residue; + + spin_lock_irqsave(&dwc->lock, flags); + + residue = dwc->residue; + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) + residue -= dwc_get_sent(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + return residue; +} + static enum dma_status dwc_tx_status(struct dma_chan *chan, dma_cookie_t cookie, @@ -1070,7 +1113,7 @@ dwc_tx_status(struct dma_chan *chan, } if (ret != DMA_SUCCESS) - dma_set_residue(txstate, dwc_first_active(dwc)->len); + dma_set_residue(txstate, dwc_get_residue(dwc)); if (dwc->paused) return DMA_PAUSED; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 833b4cf9843..88dd8eb3195 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -203,6 +203,7 @@ struct dw_dma_chan { struct list_head active_list; struct list_head queue; struct list_head free_list; + u32 residue; struct dw_cyclic_desc *cdesc; unsigned int descs_allocated; -- cgit v1.2.3-70-g09d2