UPSTREAM: dmaengine: Add transfer termination synchronization support
[firefly-linux-kernel-4.4.55.git] / include / linux / dmaengine.h
index 965492391b6a565126cc38fbf2dc0880a4acd39a..3080b4e22a49c85ec5913e7f88775914fea91474 100644 (file)
@@ -657,6 +657,8 @@ enum dmaengine_alignment {
  *     paused. Returns 0 or an error code
  * @device_terminate_all: Aborts all transfers on a channel. Returns 0
  *     or an error code
+ * @device_synchronize: Synchronizes the termination of a transfers to the
+ *  current context.
  * @device_tx_status: poll for transaction completion, the optional
  *     txstate parameter can be supplied with a pointer to get a
  *     struct with auxiliary transfer status information, otherwise the call
@@ -741,6 +743,7 @@ struct dma_device {
        int (*device_pause)(struct dma_chan *chan);
        int (*device_resume)(struct dma_chan *chan);
        int (*device_terminate_all)(struct dma_chan *chan);
+       void (*device_synchronize)(struct dma_chan *chan);
 
        enum dma_status (*device_tx_status)(struct dma_chan *chan,
                                            dma_cookie_t cookie,
@@ -832,6 +835,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg(
                        src_sg, src_nents, flags);
 }
 
+/**
+ * dmaengine_terminate_all() - Terminate all active DMA transfers
+ * @chan: The channel for which to terminate the transfers
+ *
+ * This function is DEPRECATED use either dmaengine_terminate_sync() or
+ * dmaengine_terminate_async() instead.
+ */
 static inline int dmaengine_terminate_all(struct dma_chan *chan)
 {
        if (chan->device->device_terminate_all)
@@ -840,6 +850,86 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan)
        return -ENOSYS;
 }
 
+/**
+ * dmaengine_terminate_async() - Terminate all active DMA transfers
+ * @chan: The channel for which to terminate the transfers
+ *
+ * Calling this function will terminate all active and pending descriptors
+ * that have previously been submitted to the channel. It is not guaranteed
+ * though that the transfer for the active descriptor has stopped when the
+ * function returns. Furthermore it is possible the complete callback of a
+ * submitted transfer is still running when this function returns.
+ *
+ * dmaengine_synchronize() needs to be called before it is safe to free
+ * any memory that is accessed by previously submitted descriptors or before
+ * freeing any resources accessed from within the completion callback of any
+ * perviously submitted descriptors.
+ *
+ * This function can be called from atomic context as well as from within a
+ * complete callback of a descriptor submitted on the same channel.
+ *
+ * If none of the two conditions above apply consider using
+ * dmaengine_terminate_sync() instead.
+ */
+static inline int dmaengine_terminate_async(struct dma_chan *chan)
+{
+       if (chan->device->device_terminate_all)
+               return chan->device->device_terminate_all(chan);
+
+       return -EINVAL;
+}
+
+/**
+ * dmaengine_synchronize() - Synchronize DMA channel termination
+ * @chan: The channel to synchronize
+ *
+ * Synchronizes to the DMA channel termination to the current context. When this
+ * function returns it is guaranteed that all transfers for previously issued
+ * descriptors have stopped and and it is safe to free the memory assoicated
+ * with them. Furthermore it is guaranteed that all complete callback functions
+ * for a previously submitted descriptor have finished running and it is safe to
+ * free resources accessed from within the complete callbacks.
+ *
+ * The behavior of this function is undefined if dma_async_issue_pending() has
+ * been called between dmaengine_terminate_async() and this function.
+ *
+ * This function must only be called from non-atomic context and must not be
+ * called from within a complete callback of a descriptor submitted on the same
+ * channel.
+ */
+static inline void dmaengine_synchronize(struct dma_chan *chan)
+{
+       if (chan->device->device_synchronize)
+               chan->device->device_synchronize(chan);
+}
+
+/**
+ * dmaengine_terminate_sync() - Terminate all active DMA transfers
+ * @chan: The channel for which to terminate the transfers
+ *
+ * Calling this function will terminate all active and pending transfers
+ * that have previously been submitted to the channel. It is similar to
+ * dmaengine_terminate_async() but guarantees that the DMA transfer has actually
+ * stopped and that all complete callbacks have finished running when the
+ * function returns.
+ *
+ * This function must only be called from non-atomic context and must not be
+ * called from within a complete callback of a descriptor submitted on the same
+ * channel.
+ */
+static inline int dmaengine_terminate_sync(struct dma_chan *chan)
+{
+       int ret;
+
+       ret = dmaengine_terminate_async(chan);
+       if (ret)
+               return ret;
+
+       dmaengine_synchronize(chan);
+
+       return 0;
+}
+
 static inline int dmaengine_pause(struct dma_chan *chan)
 {
        if (chan->device->device_pause)