Remote eDMA users may want to prepare descriptors on the remote side while the local side only needs completion notifications (no cookie-based accounting).
Provide a lightweight per-channel notification callback infrastructure. Signed-off-by: Koichiro Den <[email protected]> --- drivers/dma/dw-edma/dw-edma-core.c | 31 ++++++++++++++++++++++++++++++ drivers/dma/dw-edma/dw-edma-core.h | 4 ++++ include/linux/dma/edma.h | 23 ++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 696b9f3ea378..0eb8fc1dcc34 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -611,6 +611,13 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) struct virt_dma_desc *vd; unsigned long flags; + if (chan->notify_only) { + if (chan->notify_cb) + chan->notify_cb(&chan->vc.chan, chan->notify_cb_param); + /* no cookie on this side, just return */ + return; + } + spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); if (vd) { @@ -815,6 +822,9 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) chan->request = EDMA_REQ_NONE; chan->status = EDMA_ST_IDLE; chan->irq_mode = DW_EDMA_CH_IRQ_DEFAULT; + chan->notify_cb = NULL; + chan->notify_cb_param = NULL; + chan->notify_only = false; if (chan->dir == EDMA_DIR_WRITE) chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ); @@ -1178,6 +1188,27 @@ bool dw_edma_chan_ignore_irq(struct dma_chan *dchan) } EXPORT_SYMBOL_GPL(dw_edma_chan_ignore_irq); +int dw_edma_chan_register_notify(struct dma_chan *dchan, + void (*cb)(struct dma_chan *chan, void *user), + void *user) +{ + struct dw_edma_chan *chan; + + if (!dchan || !dchan->device) + return -ENODEV; + + chan = dchan2dw_edma_chan(dchan); + if (!chan) + return -ENODEV; + + chan->notify_cb = cb; + chan->notify_cb_param = user; + chan->notify_only = !!cb; + + return dw_edma_chan_irq_config(dchan, DW_EDMA_CH_IRQ_LOCAL); +} +EXPORT_SYMBOL_GPL(dw_edma_chan_register_notify); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver"); MODULE_AUTHOR("Gustavo Pimentel <[email protected]>"); diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 11fe4532f0bf..f652d2e38843 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -84,6 +84,10 @@ struct dw_edma_chan { enum dw_edma_ch_irq_mode irq_mode; + void (*notify_cb)(struct dma_chan *chan, void *user); + void *notify_cb_param; + bool notify_only; + struct delayed_work poll_work; spinlock_t poll_lock; diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index 6f50165ac084..3c538246de07 100644 --- a/include/linux/dma/edma.h +++ b/include/linux/dma/edma.h @@ -138,6 +138,21 @@ int dw_edma_chan_irq_config(struct dma_chan *chan, * @chan: DMA channel obtained from dma_request_channel() */ bool dw_edma_chan_ignore_irq(struct dma_chan *chan); + +/** + * dw_edma_chan_register_notify - register local completion callback for a + * notification-only channel + * @chan: DMA channel obtained from dma_request_channel() + * @cb: callback invoked in hardirq context when LIE interrupt is raised + * @user: opaque pointer passed back to @cb + * + * Intended for channels where descriptors are prepared on the remote side and + * the local side only wants completion notifications. This forces LOCAL mode + * so that the local side receives LIE interrupts. + */ +int dw_edma_chan_register_notify(struct dma_chan *chan, + void (*cb)(struct dma_chan *chan, void *user), + void *user); #else static inline int dw_edma_probe(struct dw_edma_chip *chip) { @@ -159,6 +174,14 @@ static inline bool dw_edma_chan_ignore_irq(struct dma_chan *chan) { return false; } + +static inline int dw_edma_chan_register_notify(struct dma_chan *chan, + void (*cb)(struct dma_chan *chan, + void *user), + void *user) +{ + return -ENODEV; +} #endif /* CONFIG_DW_EDMA */ struct pci_epc; -- 2.51.0
