Now, submission from callbacks are permitted as per dmaengine framework. So we
shouldn't hold any spinlock nor disable IRQs while calling callbacks.
As locks were taken by parent routines, spin_lock_irqsave() has to be called
inside all routines, wherever they are required.

The little used atc_issue_pending() function is made void.

Signed-off-by: Nicolas Ferre <nicolas.fe...@atmel.com>
---
 drivers/dma/at_hdmac.c | 121 +++++++++++++++++++++++++++++--------------------
 1 file changed, 71 insertions(+), 50 deletions(-)

diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index b28759b6d1ca..f7bf4065636c 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -268,10 +268,14 @@ static struct at_desc *atc_get_current_descriptors(struct 
at_dma_chan *atchan,
 static int atc_get_bytes_left(struct dma_chan *chan)
 {
        struct at_dma_chan      *atchan = to_at_dma_chan(chan);
-       struct at_desc *desc_first = atc_first_active(atchan);
+       struct at_desc *desc_first;
        struct at_desc *desc_cur;
+       unsigned long flags;
        int ret = 0, count = 0;
 
+       spin_lock_irqsave(&atchan->lock, flags);
+       desc_first = atc_first_active(atchan);
+
        /*
         * Initialize necessary values in the first time.
         * remain_desc record remain desc length.
@@ -311,6 +315,7 @@ static int atc_get_bytes_left(struct dma_chan *chan)
        }
 
 out:
+       spin_unlock_irqrestore(&atchan->lock, flags);
        return ret;
 }
 
@@ -318,12 +323,14 @@ out:
  * atc_chain_complete - finish work for one transaction chain
  * @atchan: channel we work on
  * @desc: descriptor at the head of the chain we want do complete
- *
- * Called with atchan->lock held and bh disabled */
+ */
 static void
 atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
 {
        struct dma_async_tx_descriptor  *txd = &desc->txd;
+       unsigned long                   flags;
+
+       spin_lock_irqsave(&atchan->lock, flags);
 
        dev_vdbg(chan2dev(&atchan->chan_common),
                "descriptor %u complete\n", txd->cookie);
@@ -337,6 +344,8 @@ atc_chain_complete(struct at_dma_chan *atchan, struct 
at_desc *desc)
        /* move myself to free_list */
        list_move(&desc->desc_node, &atchan->free_list);
 
+       spin_unlock_irqrestore(&atchan->lock, flags);
+
        dma_descriptor_unmap(txd);
        /* for cyclic transfers,
         * no need to replay callback function while stopping */
@@ -344,10 +353,6 @@ atc_chain_complete(struct at_dma_chan *atchan, struct 
at_desc *desc)
                dma_async_tx_callback   callback = txd->callback;
                void                    *param = txd->callback_param;
 
-               /*
-                * The API requires that no submissions are done from a
-                * callback, so we don't need to drop the lock here
-                */
                if (callback)
                        callback(param);
        }
@@ -362,15 +367,17 @@ atc_chain_complete(struct at_dma_chan *atchan, struct 
at_desc *desc)
  * Eventually submit queued descriptors if any
  *
  * Assume channel is idle while calling this function
- * Called with atchan->lock held and bh disabled
  */
 static void atc_complete_all(struct at_dma_chan *atchan)
 {
        struct at_desc *desc, *_desc;
        LIST_HEAD(list);
+       unsigned long flags;
 
        dev_vdbg(chan2dev(&atchan->chan_common), "complete all\n");
 
+       spin_lock_irqsave(&atchan->lock, flags);
+
        /*
         * Submit queued descriptors ASAP, i.e. before we go through
         * the completed ones.
@@ -382,6 +389,8 @@ static void atc_complete_all(struct at_dma_chan *atchan)
        /* empty queue list by moving descriptors (if any) to active_list */
        list_splice_init(&atchan->queue, &atchan->active_list);
 
+       spin_unlock_irqrestore(&atchan->lock, flags);
+
        list_for_each_entry_safe(desc, _desc, &list, desc_node)
                atc_chain_complete(atchan, desc);
 }
@@ -389,23 +398,35 @@ static void atc_complete_all(struct at_dma_chan *atchan)
 /**
  * atc_advance_work - at the end of a transaction, move forward
  * @atchan: channel where the transaction ended
- *
- * Called with atchan->lock held and bh disabled
  */
 static void atc_advance_work(struct at_dma_chan *atchan)
 {
+       unsigned long   flags;
+
        dev_vdbg(chan2dev(&atchan->chan_common), "advance_work\n");
 
-       if (atc_chan_is_enabled(atchan))
+       spin_lock_irqsave(&atchan->lock, flags);
+
+       if (atc_chan_is_enabled(atchan)) {
+               spin_unlock_irqrestore(&atchan->lock, flags);
                return;
+       }
 
        if (list_empty(&atchan->active_list) ||
            list_is_singular(&atchan->active_list)) {
+               spin_unlock_irqrestore(&atchan->lock, flags);
                atc_complete_all(atchan);
        } else {
-               atc_chain_complete(atchan, atc_first_active(atchan));
+               struct at_desc *desc_first = atc_first_active(atchan);
+
+               spin_unlock_irqrestore(&atchan->lock, flags);
+               atc_chain_complete(atchan, desc_first);
+               barrier();
                /* advance work */
-               atc_dostart(atchan, atc_first_active(atchan));
+               spin_lock_irqsave(&atchan->lock, flags);
+               desc_first = atc_first_active(atchan);
+               atc_dostart(atchan, desc_first);
+               spin_unlock_irqrestore(&atchan->lock, flags);
        }
 }
 
@@ -413,13 +434,14 @@ static void atc_advance_work(struct at_dma_chan *atchan)
 /**
  * atc_handle_error - handle errors reported by DMA controller
  * @atchan: channel where error occurs
- *
- * Called with atchan->lock held and bh disabled
  */
 static void atc_handle_error(struct at_dma_chan *atchan)
 {
        struct at_desc *bad_desc;
        struct at_desc *child;
+       unsigned long flags;
+
+       spin_lock_irqsave(&atchan->lock, flags);
 
        /*
         * The descriptor currently at the head of the active list is
@@ -452,6 +474,8 @@ static void atc_handle_error(struct at_dma_chan *atchan)
        list_for_each_entry(child, &bad_desc->tx_list, desc_node)
                atc_dump_lli(atchan, &child->lli);
 
+       spin_unlock_irqrestore(&atchan->lock, flags);
+
        /* Pretend the descriptor completed successfully */
        atc_chain_complete(atchan, bad_desc);
 }
@@ -459,19 +483,27 @@ static void atc_handle_error(struct at_dma_chan *atchan)
 /**
  * atc_handle_cyclic - at the end of a period, run callback function
  * @atchan: channel used for cyclic operations
- *
- * Called with atchan->lock held and bh disabled
  */
 static void atc_handle_cyclic(struct at_dma_chan *atchan)
 {
-       struct at_desc                  *first = atc_first_active(atchan);
-       struct dma_async_tx_descriptor  *txd = &first->txd;
-       dma_async_tx_callback           callback = txd->callback;
-       void                            *param = txd->callback_param;
+       struct at_desc                  *first;
+       struct dma_async_tx_descriptor  *txd;
+       dma_async_tx_callback           callback;
+       void                            *param;
+       u32                             dscr;
+       unsigned long                   flags;
+
+       spin_lock_irqsave(&atchan->lock, flags);
+       first = atc_first_active(atchan);
+       dscr = channel_readl(atchan, DSCR);
+       spin_unlock_irqrestore(&atchan->lock, flags);
+
+       txd = &first->txd;
+       callback = txd->callback;
+       param = txd->callback_param;
 
        dev_vdbg(chan2dev(&atchan->chan_common),
-                       "new cyclic period llp 0x%08x\n",
-                       channel_readl(atchan, DSCR));
+                       "new cyclic period llp 0x%08x\n", dscr);
 
        if (callback)
                callback(param);
@@ -482,17 +514,13 @@ static void atc_handle_cyclic(struct at_dma_chan *atchan)
 static void atc_tasklet(unsigned long data)
 {
        struct at_dma_chan *atchan = (struct at_dma_chan *)data;
-       unsigned long flags;
 
-       spin_lock_irqsave(&atchan->lock, flags);
        if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status))
                atc_handle_error(atchan);
        else if (atc_chan_is_cyclic(atchan))
                atc_handle_cyclic(atchan);
        else
                atc_advance_work(atchan);
-
-       spin_unlock_irqrestore(&atchan->lock, flags);
 }
 
 static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
@@ -1013,6 +1041,11 @@ static int atc_control(struct dma_chan *chan, enum 
dma_ctrl_cmd cmd,
                spin_unlock_irqrestore(&atchan->lock, flags);
        } else if (cmd == DMA_TERMINATE_ALL) {
                struct at_desc  *desc, *_desc;
+
+               /* Disable interrupts */
+               atc_disable_chan_irq(atdma, chan->chan_id);
+               tasklet_disable(&atchan->tasklet);
+
                /*
                 * This is only called when something went wrong elsewhere, so
                 * we don't really care about the data. Just disable the
@@ -1033,14 +1066,22 @@ static int atc_control(struct dma_chan *chan, enum 
dma_ctrl_cmd cmd,
                list_splice_init(&atchan->active_list, &list);
 
                /* Flush all pending and queued descriptors */
-               list_for_each_entry_safe(desc, _desc, &list, desc_node)
+               list_for_each_entry_safe(desc, _desc, &list, desc_node) {
+                       spin_unlock_irqrestore(&atchan->lock, flags);
                        atc_chain_complete(atchan, desc);
+                       spin_lock_irqsave(&atchan->lock, flags);
+               }
 
                clear_bit(ATC_IS_PAUSED, &atchan->status);
                /* if channel dedicated to cyclic operations, free it */
                clear_bit(ATC_IS_CYCLIC, &atchan->status);
 
                spin_unlock_irqrestore(&atchan->lock, flags);
+
+               /* Re-enable channel for future operations */
+               tasklet_enable(&atchan->tasklet);
+               atc_enable_chan_irq(atdma, chan->chan_id);
+
        } else if (cmd == DMA_SLAVE_CONFIG) {
                return set_runtime_config(chan, (struct dma_slave_config *)arg);
        } else {
@@ -1065,8 +1106,6 @@ atc_tx_status(struct dma_chan *chan,
                dma_cookie_t cookie,
                struct dma_tx_state *txstate)
 {
-       struct at_dma_chan      *atchan = to_at_dma_chan(chan);
-       unsigned long           flags;
        enum dma_status         ret;
        int bytes = 0;
 
@@ -1080,13 +1119,9 @@ atc_tx_status(struct dma_chan *chan,
        if (!txstate)
                return DMA_ERROR;
 
-       spin_lock_irqsave(&atchan->lock, flags);
-
        /*  Get number of bytes left in the active transactions */
        bytes = atc_get_bytes_left(chan);
 
-       spin_unlock_irqrestore(&atchan->lock, flags);
-
        if (unlikely(bytes < 0)) {
                dev_vdbg(chan2dev(chan), "get residual bytes error\n");
                return DMA_ERROR;
@@ -1101,24 +1136,10 @@ atc_tx_status(struct dma_chan *chan,
 }
 
 /**
- * atc_issue_pending - try to finish work
+ * atc_issue_pending - void function
  * @chan: target DMA channel
  */
-static void atc_issue_pending(struct dma_chan *chan)
-{
-       struct at_dma_chan      *atchan = to_at_dma_chan(chan);
-       unsigned long           flags;
-
-       dev_vdbg(chan2dev(chan), "issue_pending\n");
-
-       /* Not needed for cyclic transfers */
-       if (atc_chan_is_cyclic(atchan))
-               return;
-
-       spin_lock_irqsave(&atchan->lock, flags);
-       atc_advance_work(atchan);
-       spin_unlock_irqrestore(&atchan->lock, flags);
-}
+static void atc_issue_pending(struct dma_chan *chan) {}
 
 /**
  * atc_alloc_chan_resources - allocate resources for DMA channel
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to