From b36f09c3c441a6e59eab9315032e7d546571de3f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Oct 2015 11:46:28 +0200 Subject: dmaengine: Add transfer termination synchronization support The DMAengine API has a long standing race condition that is inherent to the API itself. Calling dmaengine_terminate_all() is supposed to stop and abort any pending or active transfers that have previously been submitted. Unfortunately it is possible that this operation races against a currently running (or with some drivers also scheduled) completion callback. Since the API allows dmaengine_terminate_all() to be called from atomic context as well as from within a completion callback it is not possible to synchronize to the execution of the completion callback from within dmaengine_terminate_all() itself. This means that a user of the DMAengine API does not know when it is safe to free resources used in the completion callback, which can result in a use-after-free race condition. This patch addresses the issue by introducing an explicit synchronization primitive to the DMAengine API called dmaengine_synchronize(). The existing dmaengine_terminate_all() is deprecated in favor of dmaengine_terminate_sync() and dmaengine_terminate_async(). The former aborts all pending and active transfers and synchronizes to the current context, meaning it will wait until all running completion callbacks have finished. This means it is only possible to call this function from non-atomic context. The later function does not synchronize, but can still be used in atomic context or from within a complete callback. It has to be followed up by dmaengine_synchronize() before a client can free the resources used in a completion callback. In addition to this the semantics of the device_terminate_all() callback are slightly relaxed by this patch. It is now OK for a driver to only schedule the termination of the active transfer, but does not necessarily have to wait until the DMA controller has completely stopped. The driver must ensure though that the controller has stopped and no longer accesses any memory when the device_synchronize() callback returns. This was in part done since most drivers do not pay attention to this anyway at the moment and to emphasize that this needs to be done when the device_synchronize() callback is implemented. But it also helps with implementing support for devices where stopping the controller can require operations that may sleep. Signed-off-by: Lars-Peter Clausen Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c47c68e535e8..4662d9aa6d5a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -654,6 +654,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 @@ -737,6 +739,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, @@ -828,6 +831,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) @@ -836,6 +846,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) -- cgit v1.2.3-58-ga151 From 9eeacd3a2f17438d9d286ff2f78c4709a4148be7 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 13 Oct 2015 21:54:29 +0200 Subject: dmaengine: enable DMA_CTRL_REUSE In the current state, the capability of transfer reuse can neither be set by a slave dmaengine driver, nor used by a client driver, because the capability is not available to dma_get_slave_caps(). Fix this by adding a way to declare the capability. Fixes: 272420214d26 ("dmaengine: Add DMA_CTRL_REUSE") Signed-off-by: Robert Jarzmik Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 1 + include/linux/dmaengine.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 3ecec1445adf..4aced6689734 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -493,6 +493,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) caps->dst_addr_widths = device->dst_addr_widths; caps->directions = device->directions; caps->residue_granularity = device->residue_granularity; + caps->descriptor_reuse = device->descriptor_reuse; /* * Some devices implement only pause (e.g. to get residuum) but no diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c47c68e535e8..6f94b5cbd97c 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -659,6 +659,7 @@ enum dmaengine_alignment { * struct with auxiliary transfer status information, otherwise the call * will just return a simple status code * @device_issue_pending: push pending transactions to hardware + * @descriptor_reuse: a submitted transfer can be resubmitted after completion */ struct dma_device { @@ -681,6 +682,7 @@ struct dma_device { u32 src_addr_widths; u32 dst_addr_widths; u32 directions; + bool descriptor_reuse; enum dma_residue_granularity residue_granularity; int (*device_alloc_chan_resources)(struct dma_chan *chan); -- cgit v1.2.3-58-ga151 From 2bb129ebb23d2dfec3cd9c22dc7defd681cfcd58 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 13 Nov 2015 12:46:00 +0100 Subject: dmaengine: ioatdma: constify dca_ops structures The dca_ops structure is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Dan Williams Signed-off-by: Vinod Koul --- drivers/dca/dca-core.c | 3 ++- drivers/dma/ioat/dca.c | 2 +- include/linux/dca.h | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index 819dfda88236..7afbb28d6a0f 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -321,7 +321,8 @@ EXPORT_SYMBOL_GPL(dca_get_tag); * @ops - pointer to struct of dca operation function pointers * @priv_size - size of extra mem to be added for provider's needs */ -struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size) +struct dca_provider *alloc_dca_provider(const struct dca_ops *ops, + int priv_size) { struct dca_provider *dca; int alloc_size; diff --git a/drivers/dma/ioat/dca.c b/drivers/dma/ioat/dca.c index 2cb7c308d5c7..0b9b6b07db9e 100644 --- a/drivers/dma/ioat/dca.c +++ b/drivers/dma/ioat/dca.c @@ -224,7 +224,7 @@ static u8 ioat_dca_get_tag(struct dca_provider *dca, return tag; } -static struct dca_ops ioat_dca_ops = { +static const struct dca_ops ioat_dca_ops = { .add_requester = ioat_dca_add_requester, .remove_requester = ioat_dca_remove_requester, .get_tag = ioat_dca_get_tag, diff --git a/include/linux/dca.h b/include/linux/dca.h index d27a7a05718d..ad956c2e07a8 100644 --- a/include/linux/dca.h +++ b/include/linux/dca.h @@ -34,7 +34,7 @@ void dca_unregister_notify(struct notifier_block *nb); struct dca_provider { struct list_head node; - struct dca_ops *ops; + const struct dca_ops *ops; struct device *cd; int id; }; @@ -53,7 +53,8 @@ struct dca_ops { int (*dev_managed) (struct dca_provider *, struct device *); }; -struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size); +struct dca_provider *alloc_dca_provider(const struct dca_ops *ops, + int priv_size); void free_dca_provider(struct dca_provider *dca); int register_dca_provider(struct dca_provider *dca, struct device *dev); void unregister_dca_provider(struct dca_provider *dca, struct device *dev); -- cgit v1.2.3-58-ga151 From b1d6ab1aa8cdc23b89bcd578ea8d5e3c501a13d9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 23 Nov 2015 11:06:43 +0100 Subject: dmaengine: Add might_sleep() to dmaengine_synchronize() Implementations of dmaengine_synchronize() are allowed to sleep, hence the function must not be called to from atomic context. Add might_sleep() to dmaengine_synchronize() to make it easier to detect non-compliant callers. Suggested-by: Andy Shevchenko Signed-off-by: Lars-Peter Clausen Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4662d9aa6d5a..2f69e1d93f92 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -895,6 +895,8 @@ static inline int dmaengine_terminate_async(struct dma_chan *chan) */ static inline void dmaengine_synchronize(struct dma_chan *chan) { + might_sleep(); + if (chan->device->device_synchronize) chan->device->device_synchronize(chan); } -- cgit v1.2.3-58-ga151 From 4d42e95fc789393d267bbab8b4684936c1529378 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 4 Dec 2015 16:56:29 +0100 Subject: dmaengine: sh: Remove unused R-Car HPB-DMAC driver As of commit 4baadb9e05c68962 ("ARM: shmobile: r8a7778: remove obsolete setup code"), the Renesas R-Car HPB-DMAC driver is no longer used. In theory it could still be used on R-Car Gen1 SoCs, but that requires adding DT support to the driver, which is not planned. Remove the driver, it can be resurrected from git history when needed. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Vinod Koul --- drivers/dma/sh/Kconfig | 6 - drivers/dma/sh/Makefile | 1 - drivers/dma/sh/rcar-hpbdma.c | 669 -------------------------- include/linux/platform_data/dma-rcar-hpbdma.h | 103 ---- 4 files changed, 779 deletions(-) delete mode 100644 drivers/dma/sh/rcar-hpbdma.c delete mode 100644 include/linux/platform_data/dma-rcar-hpbdma.h (limited to 'include/linux') diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index 9fda65af841e..f32c430eb16c 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -47,12 +47,6 @@ config RCAR_DMAC This driver supports the general purpose DMA controller found in the Renesas R-Car second generation SoCs. -config RCAR_HPB_DMAE - tristate "Renesas R-Car HPB DMAC support" - depends on SH_DMAE_BASE - help - Enable support for the Renesas R-Car series DMA controllers. - config RENESAS_USB_DMAC tristate "Renesas USB-DMA Controller" depends on ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index 0133e4658196..f1e2fd64f279 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -14,6 +14,5 @@ shdma-objs := $(shdma-y) obj-$(CONFIG_SH_DMAE) += shdma.o obj-$(CONFIG_RCAR_DMAC) += rcar-dmac.o -obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o obj-$(CONFIG_RENESAS_USB_DMAC) += usb-dmac.o obj-$(CONFIG_SUDMAC) += sudmac.o diff --git a/drivers/dma/sh/rcar-hpbdma.c b/drivers/dma/sh/rcar-hpbdma.c deleted file mode 100644 index 749f26ecd3b3..000000000000 --- a/drivers/dma/sh/rcar-hpbdma.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * Copyright (C) 2011-2013 Renesas Electronics Corporation - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This file is based on the drivers/dma/sh/shdma.c - * - * Renesas SuperH DMA Engine support - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * - DMA of SuperH does not have Hardware DMA chain mode. - * - max DMA size is 16MB. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* DMA channel registers */ -#define HPB_DMAE_DSAR0 0x00 -#define HPB_DMAE_DDAR0 0x04 -#define HPB_DMAE_DTCR0 0x08 -#define HPB_DMAE_DSAR1 0x0C -#define HPB_DMAE_DDAR1 0x10 -#define HPB_DMAE_DTCR1 0x14 -#define HPB_DMAE_DSASR 0x18 -#define HPB_DMAE_DDASR 0x1C -#define HPB_DMAE_DTCSR 0x20 -#define HPB_DMAE_DPTR 0x24 -#define HPB_DMAE_DCR 0x28 -#define HPB_DMAE_DCMDR 0x2C -#define HPB_DMAE_DSTPR 0x30 -#define HPB_DMAE_DSTSR 0x34 -#define HPB_DMAE_DDBGR 0x38 -#define HPB_DMAE_DDBGR2 0x3C -#define HPB_DMAE_CHAN(n) (0x40 * (n)) - -/* DMA command register (DCMDR) bits */ -#define HPB_DMAE_DCMDR_BDOUT BIT(7) -#define HPB_DMAE_DCMDR_DQSPD BIT(6) -#define HPB_DMAE_DCMDR_DQSPC BIT(5) -#define HPB_DMAE_DCMDR_DMSPD BIT(4) -#define HPB_DMAE_DCMDR_DMSPC BIT(3) -#define HPB_DMAE_DCMDR_DQEND BIT(2) -#define HPB_DMAE_DCMDR_DNXT BIT(1) -#define HPB_DMAE_DCMDR_DMEN BIT(0) - -/* DMA forced stop register (DSTPR) bits */ -#define HPB_DMAE_DSTPR_DMSTP BIT(0) - -/* DMA status register (DSTSR) bits */ -#define HPB_DMAE_DSTSR_DQSTS BIT(2) -#define HPB_DMAE_DSTSR_DMSTS BIT(0) - -/* DMA common registers */ -#define HPB_DMAE_DTIMR 0x00 -#define HPB_DMAE_DINTSR0 0x0C -#define HPB_DMAE_DINTSR1 0x10 -#define HPB_DMAE_DINTCR0 0x14 -#define HPB_DMAE_DINTCR1 0x18 -#define HPB_DMAE_DINTMR0 0x1C -#define HPB_DMAE_DINTMR1 0x20 -#define HPB_DMAE_DACTSR0 0x24 -#define HPB_DMAE_DACTSR1 0x28 -#define HPB_DMAE_HSRSTR(n) (0x40 + (n) * 4) -#define HPB_DMAE_HPB_DMASPR(n) (0x140 + (n) * 4) -#define HPB_DMAE_HPB_DMLVLR0 0x160 -#define HPB_DMAE_HPB_DMLVLR1 0x164 -#define HPB_DMAE_HPB_DMSHPT0 0x168 -#define HPB_DMAE_HPB_DMSHPT1 0x16C - -#define HPB_DMA_SLAVE_NUMBER 256 -#define HPB_DMA_TCR_MAX 0x01000000 /* 16 MiB */ - -struct hpb_dmae_chan { - struct shdma_chan shdma_chan; - int xfer_mode; /* DMA transfer mode */ -#define XFER_SINGLE 1 -#define XFER_DOUBLE 2 - unsigned plane_idx; /* current DMA information set */ - bool first_desc; /* first/next transfer */ - int xmit_shift; /* log_2(bytes_per_xfer) */ - void __iomem *base; - const struct hpb_dmae_slave_config *cfg; - char dev_id[16]; /* unique name per DMAC of channel */ - dma_addr_t slave_addr; -}; - -struct hpb_dmae_device { - struct shdma_dev shdma_dev; - spinlock_t reg_lock; /* comm_reg operation lock */ - struct hpb_dmae_pdata *pdata; - void __iomem *chan_reg; - void __iomem *comm_reg; - void __iomem *reset_reg; - void __iomem *mode_reg; -}; - -struct hpb_dmae_regs { - u32 sar; /* SAR / source address */ - u32 dar; /* DAR / destination address */ - u32 tcr; /* TCR / transfer count */ -}; - -struct hpb_desc { - struct shdma_desc shdma_desc; - struct hpb_dmae_regs hw; - unsigned plane_idx; -}; - -#define to_chan(schan) container_of(schan, struct hpb_dmae_chan, shdma_chan) -#define to_desc(sdesc) container_of(sdesc, struct hpb_desc, shdma_desc) -#define to_dev(sc) container_of(sc->shdma_chan.dma_chan.device, \ - struct hpb_dmae_device, shdma_dev.dma_dev) - -static void ch_reg_write(struct hpb_dmae_chan *hpb_dc, u32 data, u32 reg) -{ - iowrite32(data, hpb_dc->base + reg); -} - -static u32 ch_reg_read(struct hpb_dmae_chan *hpb_dc, u32 reg) -{ - return ioread32(hpb_dc->base + reg); -} - -static void dcmdr_write(struct hpb_dmae_device *hpbdev, u32 data) -{ - iowrite32(data, hpbdev->chan_reg + HPB_DMAE_DCMDR); -} - -static void hsrstr_write(struct hpb_dmae_device *hpbdev, u32 ch) -{ - iowrite32(0x1, hpbdev->comm_reg + HPB_DMAE_HSRSTR(ch)); -} - -static u32 dintsr_read(struct hpb_dmae_device *hpbdev, u32 ch) -{ - u32 v; - - if (ch < 32) - v = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTSR0) >> ch; - else - v = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTSR1) >> (ch - 32); - return v & 0x1; -} - -static void dintcr_write(struct hpb_dmae_device *hpbdev, u32 ch) -{ - if (ch < 32) - iowrite32((0x1 << ch), hpbdev->comm_reg + HPB_DMAE_DINTCR0); - else - iowrite32((0x1 << (ch - 32)), - hpbdev->comm_reg + HPB_DMAE_DINTCR1); -} - -static void asyncmdr_write(struct hpb_dmae_device *hpbdev, u32 data) -{ - iowrite32(data, hpbdev->mode_reg); -} - -static u32 asyncmdr_read(struct hpb_dmae_device *hpbdev) -{ - return ioread32(hpbdev->mode_reg); -} - -static void hpb_dmae_enable_int(struct hpb_dmae_device *hpbdev, u32 ch) -{ - u32 intreg; - - spin_lock_irq(&hpbdev->reg_lock); - if (ch < 32) { - intreg = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTMR0); - iowrite32(BIT(ch) | intreg, - hpbdev->comm_reg + HPB_DMAE_DINTMR0); - } else { - intreg = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTMR1); - iowrite32(BIT(ch - 32) | intreg, - hpbdev->comm_reg + HPB_DMAE_DINTMR1); - } - spin_unlock_irq(&hpbdev->reg_lock); -} - -static void hpb_dmae_async_reset(struct hpb_dmae_device *hpbdev, u32 data) -{ - u32 rstr; - int timeout = 10000; /* 100 ms */ - - spin_lock(&hpbdev->reg_lock); - rstr = ioread32(hpbdev->reset_reg); - rstr |= data; - iowrite32(rstr, hpbdev->reset_reg); - do { - rstr = ioread32(hpbdev->reset_reg); - if ((rstr & data) == data) - break; - udelay(10); - } while (timeout--); - - if (timeout < 0) - dev_err(hpbdev->shdma_dev.dma_dev.dev, - "%s timeout\n", __func__); - - rstr &= ~data; - iowrite32(rstr, hpbdev->reset_reg); - spin_unlock(&hpbdev->reg_lock); -} - -static void hpb_dmae_set_async_mode(struct hpb_dmae_device *hpbdev, - u32 mask, u32 data) -{ - u32 mode; - - spin_lock_irq(&hpbdev->reg_lock); - mode = asyncmdr_read(hpbdev); - mode &= ~mask; - mode |= data; - asyncmdr_write(hpbdev, mode); - spin_unlock_irq(&hpbdev->reg_lock); -} - -static void hpb_dmae_ctl_stop(struct hpb_dmae_device *hpbdev) -{ - dcmdr_write(hpbdev, HPB_DMAE_DCMDR_DQSPD); -} - -static void hpb_dmae_reset(struct hpb_dmae_device *hpbdev) -{ - u32 ch; - - for (ch = 0; ch < hpbdev->pdata->num_hw_channels; ch++) - hsrstr_write(hpbdev, ch); -} - -static unsigned int calc_xmit_shift(struct hpb_dmae_chan *hpb_chan) -{ - struct hpb_dmae_device *hpbdev = to_dev(hpb_chan); - struct hpb_dmae_pdata *pdata = hpbdev->pdata; - int width = ch_reg_read(hpb_chan, HPB_DMAE_DCR); - int i; - - switch (width & (HPB_DMAE_DCR_SPDS_MASK | HPB_DMAE_DCR_DPDS_MASK)) { - case HPB_DMAE_DCR_SPDS_8BIT | HPB_DMAE_DCR_DPDS_8BIT: - default: - i = XMIT_SZ_8BIT; - break; - case HPB_DMAE_DCR_SPDS_16BIT | HPB_DMAE_DCR_DPDS_16BIT: - i = XMIT_SZ_16BIT; - break; - case HPB_DMAE_DCR_SPDS_32BIT | HPB_DMAE_DCR_DPDS_32BIT: - i = XMIT_SZ_32BIT; - break; - } - return pdata->ts_shift[i]; -} - -static void hpb_dmae_set_reg(struct hpb_dmae_chan *hpb_chan, - struct hpb_dmae_regs *hw, unsigned plane) -{ - ch_reg_write(hpb_chan, hw->sar, - plane ? HPB_DMAE_DSAR1 : HPB_DMAE_DSAR0); - ch_reg_write(hpb_chan, hw->dar, - plane ? HPB_DMAE_DDAR1 : HPB_DMAE_DDAR0); - ch_reg_write(hpb_chan, hw->tcr >> hpb_chan->xmit_shift, - plane ? HPB_DMAE_DTCR1 : HPB_DMAE_DTCR0); -} - -static void hpb_dmae_start(struct hpb_dmae_chan *hpb_chan, bool next) -{ - ch_reg_write(hpb_chan, (next ? HPB_DMAE_DCMDR_DNXT : 0) | - HPB_DMAE_DCMDR_DMEN, HPB_DMAE_DCMDR); -} - -static void hpb_dmae_halt(struct shdma_chan *schan) -{ - struct hpb_dmae_chan *chan = to_chan(schan); - - ch_reg_write(chan, HPB_DMAE_DCMDR_DQEND, HPB_DMAE_DCMDR); - ch_reg_write(chan, HPB_DMAE_DSTPR_DMSTP, HPB_DMAE_DSTPR); - - chan->plane_idx = 0; - chan->first_desc = true; -} - -static const struct hpb_dmae_slave_config * -hpb_dmae_find_slave(struct hpb_dmae_chan *hpb_chan, int slave_id) -{ - struct hpb_dmae_device *hpbdev = to_dev(hpb_chan); - struct hpb_dmae_pdata *pdata = hpbdev->pdata; - int i; - - if (slave_id >= HPB_DMA_SLAVE_NUMBER) - return NULL; - - for (i = 0; i < pdata->num_slaves; i++) - if (pdata->slaves[i].id == slave_id) - return pdata->slaves + i; - - return NULL; -} - -static void hpb_dmae_start_xfer(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - struct hpb_dmae_chan *chan = to_chan(schan); - struct hpb_dmae_device *hpbdev = to_dev(chan); - struct hpb_desc *desc = to_desc(sdesc); - - if (chan->cfg->flags & HPB_DMAE_SET_ASYNC_RESET) - hpb_dmae_async_reset(hpbdev, chan->cfg->rstr); - - desc->plane_idx = chan->plane_idx; - hpb_dmae_set_reg(chan, &desc->hw, chan->plane_idx); - hpb_dmae_start(chan, !chan->first_desc); - - if (chan->xfer_mode == XFER_DOUBLE) { - chan->plane_idx ^= 1; - chan->first_desc = false; - } -} - -static bool hpb_dmae_desc_completed(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - /* - * This is correct since we always have at most single - * outstanding DMA transfer per channel, and by the time - * we get completion interrupt the transfer is completed. - * This will change if we ever use alternating DMA - * information sets and submit two descriptors at once. - */ - return true; -} - -static bool hpb_dmae_chan_irq(struct shdma_chan *schan, int irq) -{ - struct hpb_dmae_chan *chan = to_chan(schan); - struct hpb_dmae_device *hpbdev = to_dev(chan); - int ch = chan->cfg->dma_ch; - - /* Check Complete DMA Transfer */ - if (dintsr_read(hpbdev, ch)) { - /* Clear Interrupt status */ - dintcr_write(hpbdev, ch); - return true; - } - return false; -} - -static int hpb_dmae_desc_setup(struct shdma_chan *schan, - struct shdma_desc *sdesc, - dma_addr_t src, dma_addr_t dst, size_t *len) -{ - struct hpb_desc *desc = to_desc(sdesc); - - if (*len > (size_t)HPB_DMA_TCR_MAX) - *len = (size_t)HPB_DMA_TCR_MAX; - - desc->hw.sar = src; - desc->hw.dar = dst; - desc->hw.tcr = *len; - - return 0; -} - -static size_t hpb_dmae_get_partial(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - struct hpb_desc *desc = to_desc(sdesc); - struct hpb_dmae_chan *chan = to_chan(schan); - u32 tcr = ch_reg_read(chan, desc->plane_idx ? - HPB_DMAE_DTCR1 : HPB_DMAE_DTCR0); - - return (desc->hw.tcr - tcr) << chan->xmit_shift; -} - -static bool hpb_dmae_channel_busy(struct shdma_chan *schan) -{ - struct hpb_dmae_chan *chan = to_chan(schan); - u32 dstsr = ch_reg_read(chan, HPB_DMAE_DSTSR); - - if (chan->xfer_mode == XFER_DOUBLE) - return dstsr & HPB_DMAE_DSTSR_DQSTS; - else - return dstsr & HPB_DMAE_DSTSR_DMSTS; -} - -static int -hpb_dmae_alloc_chan_resources(struct hpb_dmae_chan *hpb_chan, - const struct hpb_dmae_slave_config *cfg) -{ - struct hpb_dmae_device *hpbdev = to_dev(hpb_chan); - struct hpb_dmae_pdata *pdata = hpbdev->pdata; - const struct hpb_dmae_channel *channel = pdata->channels; - int slave_id = cfg->id; - int i, err; - - for (i = 0; i < pdata->num_channels; i++, channel++) { - if (channel->s_id == slave_id) { - struct device *dev = hpb_chan->shdma_chan.dev; - - hpb_chan->base = hpbdev->chan_reg + - HPB_DMAE_CHAN(cfg->dma_ch); - - dev_dbg(dev, "Detected Slave device\n"); - dev_dbg(dev, " -- slave_id : 0x%x\n", slave_id); - dev_dbg(dev, " -- cfg->dma_ch : %d\n", cfg->dma_ch); - dev_dbg(dev, " -- channel->ch_irq: %d\n", - channel->ch_irq); - break; - } - } - - err = shdma_request_irq(&hpb_chan->shdma_chan, channel->ch_irq, - IRQF_SHARED, hpb_chan->dev_id); - if (err) { - dev_err(hpb_chan->shdma_chan.dev, - "DMA channel request_irq %d failed with error %d\n", - channel->ch_irq, err); - return err; - } - - hpb_chan->plane_idx = 0; - hpb_chan->first_desc = true; - - if ((cfg->dcr & (HPB_DMAE_DCR_CT | HPB_DMAE_DCR_DIP)) == 0) { - hpb_chan->xfer_mode = XFER_SINGLE; - } else if ((cfg->dcr & (HPB_DMAE_DCR_CT | HPB_DMAE_DCR_DIP)) == - (HPB_DMAE_DCR_CT | HPB_DMAE_DCR_DIP)) { - hpb_chan->xfer_mode = XFER_DOUBLE; - } else { - dev_err(hpb_chan->shdma_chan.dev, "DCR setting error"); - return -EINVAL; - } - - if (cfg->flags & HPB_DMAE_SET_ASYNC_MODE) - hpb_dmae_set_async_mode(hpbdev, cfg->mdm, cfg->mdr); - ch_reg_write(hpb_chan, cfg->dcr, HPB_DMAE_DCR); - ch_reg_write(hpb_chan, cfg->port, HPB_DMAE_DPTR); - hpb_chan->xmit_shift = calc_xmit_shift(hpb_chan); - hpb_dmae_enable_int(hpbdev, cfg->dma_ch); - - return 0; -} - -static int hpb_dmae_set_slave(struct shdma_chan *schan, int slave_id, - dma_addr_t slave_addr, bool try) -{ - struct hpb_dmae_chan *chan = to_chan(schan); - const struct hpb_dmae_slave_config *sc = - hpb_dmae_find_slave(chan, slave_id); - - if (!sc) - return -ENODEV; - if (try) - return 0; - chan->cfg = sc; - chan->slave_addr = slave_addr ? : sc->addr; - return hpb_dmae_alloc_chan_resources(chan, sc); -} - -static void hpb_dmae_setup_xfer(struct shdma_chan *schan, int slave_id) -{ -} - -static dma_addr_t hpb_dmae_slave_addr(struct shdma_chan *schan) -{ - struct hpb_dmae_chan *chan = to_chan(schan); - - return chan->slave_addr; -} - -static struct shdma_desc *hpb_dmae_embedded_desc(void *buf, int i) -{ - return &((struct hpb_desc *)buf)[i].shdma_desc; -} - -static const struct shdma_ops hpb_dmae_ops = { - .desc_completed = hpb_dmae_desc_completed, - .halt_channel = hpb_dmae_halt, - .channel_busy = hpb_dmae_channel_busy, - .slave_addr = hpb_dmae_slave_addr, - .desc_setup = hpb_dmae_desc_setup, - .set_slave = hpb_dmae_set_slave, - .setup_xfer = hpb_dmae_setup_xfer, - .start_xfer = hpb_dmae_start_xfer, - .embedded_desc = hpb_dmae_embedded_desc, - .chan_irq = hpb_dmae_chan_irq, - .get_partial = hpb_dmae_get_partial, -}; - -static int hpb_dmae_chan_probe(struct hpb_dmae_device *hpbdev, int id) -{ - struct shdma_dev *sdev = &hpbdev->shdma_dev; - struct platform_device *pdev = - to_platform_device(hpbdev->shdma_dev.dma_dev.dev); - struct hpb_dmae_chan *new_hpb_chan; - struct shdma_chan *schan; - - /* Alloc channel */ - new_hpb_chan = devm_kzalloc(&pdev->dev, - sizeof(struct hpb_dmae_chan), GFP_KERNEL); - if (!new_hpb_chan) { - dev_err(hpbdev->shdma_dev.dma_dev.dev, - "No free memory for allocating DMA channels!\n"); - return -ENOMEM; - } - - schan = &new_hpb_chan->shdma_chan; - schan->max_xfer_len = HPB_DMA_TCR_MAX; - - shdma_chan_probe(sdev, schan, id); - - if (pdev->id >= 0) - snprintf(new_hpb_chan->dev_id, sizeof(new_hpb_chan->dev_id), - "hpb-dmae%d.%d", pdev->id, id); - else - snprintf(new_hpb_chan->dev_id, sizeof(new_hpb_chan->dev_id), - "hpb-dma.%d", id); - - return 0; -} - -static int hpb_dmae_probe(struct platform_device *pdev) -{ - const enum dma_slave_buswidth widths = DMA_SLAVE_BUSWIDTH_1_BYTE | - DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES; - struct hpb_dmae_pdata *pdata = pdev->dev.platform_data; - struct hpb_dmae_device *hpbdev; - struct dma_device *dma_dev; - struct resource *chan, *comm, *rest, *mode, *irq_res; - int err, i; - - /* Get platform data */ - if (!pdata || !pdata->num_channels) - return -ENODEV; - - chan = platform_get_resource(pdev, IORESOURCE_MEM, 0); - comm = platform_get_resource(pdev, IORESOURCE_MEM, 1); - rest = platform_get_resource(pdev, IORESOURCE_MEM, 2); - mode = platform_get_resource(pdev, IORESOURCE_MEM, 3); - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq_res) - return -ENODEV; - - hpbdev = devm_kzalloc(&pdev->dev, sizeof(struct hpb_dmae_device), - GFP_KERNEL); - if (!hpbdev) { - dev_err(&pdev->dev, "Not enough memory\n"); - return -ENOMEM; - } - - hpbdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan); - if (IS_ERR(hpbdev->chan_reg)) - return PTR_ERR(hpbdev->chan_reg); - - hpbdev->comm_reg = devm_ioremap_resource(&pdev->dev, comm); - if (IS_ERR(hpbdev->comm_reg)) - return PTR_ERR(hpbdev->comm_reg); - - hpbdev->reset_reg = devm_ioremap_resource(&pdev->dev, rest); - if (IS_ERR(hpbdev->reset_reg)) - return PTR_ERR(hpbdev->reset_reg); - - hpbdev->mode_reg = devm_ioremap_resource(&pdev->dev, mode); - if (IS_ERR(hpbdev->mode_reg)) - return PTR_ERR(hpbdev->mode_reg); - - dma_dev = &hpbdev->shdma_dev.dma_dev; - - spin_lock_init(&hpbdev->reg_lock); - - /* Platform data */ - hpbdev->pdata = pdata; - - pm_runtime_enable(&pdev->dev); - err = pm_runtime_get_sync(&pdev->dev); - if (err < 0) - dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err); - - /* Reset DMA controller */ - hpb_dmae_reset(hpbdev); - - pm_runtime_put(&pdev->dev); - - dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); - dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); - dma_dev->src_addr_widths = widths; - dma_dev->dst_addr_widths = widths; - dma_dev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); - dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; - - hpbdev->shdma_dev.ops = &hpb_dmae_ops; - hpbdev->shdma_dev.desc_size = sizeof(struct hpb_desc); - err = shdma_init(&pdev->dev, &hpbdev->shdma_dev, pdata->num_channels); - if (err < 0) - goto error; - - /* Create DMA channels */ - for (i = 0; i < pdata->num_channels; i++) - hpb_dmae_chan_probe(hpbdev, i); - - platform_set_drvdata(pdev, hpbdev); - err = dma_async_device_register(dma_dev); - if (!err) - return 0; - - shdma_cleanup(&hpbdev->shdma_dev); -error: - pm_runtime_disable(&pdev->dev); - return err; -} - -static void hpb_dmae_chan_remove(struct hpb_dmae_device *hpbdev) -{ - struct shdma_chan *schan; - int i; - - shdma_for_each_chan(schan, &hpbdev->shdma_dev, i) { - BUG_ON(!schan); - - shdma_chan_remove(schan); - } -} - -static int hpb_dmae_remove(struct platform_device *pdev) -{ - struct hpb_dmae_device *hpbdev = platform_get_drvdata(pdev); - - dma_async_device_unregister(&hpbdev->shdma_dev.dma_dev); - - pm_runtime_disable(&pdev->dev); - - hpb_dmae_chan_remove(hpbdev); - - return 0; -} - -static void hpb_dmae_shutdown(struct platform_device *pdev) -{ - struct hpb_dmae_device *hpbdev = platform_get_drvdata(pdev); - hpb_dmae_ctl_stop(hpbdev); -} - -static struct platform_driver hpb_dmae_driver = { - .probe = hpb_dmae_probe, - .remove = hpb_dmae_remove, - .shutdown = hpb_dmae_shutdown, - .driver = { - .name = "hpb-dma-engine", - }, -}; -module_platform_driver(hpb_dmae_driver); - -MODULE_AUTHOR("Max Filippov "); -MODULE_DESCRIPTION("Renesas HPB DMA Engine driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/dma-rcar-hpbdma.h b/include/linux/platform_data/dma-rcar-hpbdma.h deleted file mode 100644 index 648b8ea61a22..000000000000 --- a/include/linux/platform_data/dma-rcar-hpbdma.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2011-2013 Renesas Electronics Corporation - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - */ - -#ifndef __DMA_RCAR_HPBDMA_H -#define __DMA_RCAR_HPBDMA_H - -#include -#include - -/* Transmit sizes and respective register values */ -enum { - XMIT_SZ_8BIT = 0, - XMIT_SZ_16BIT = 1, - XMIT_SZ_32BIT = 2, - XMIT_SZ_MAX -}; - -/* DMA control register (DCR) bits */ -#define HPB_DMAE_DCR_DTAMD (1u << 26) -#define HPB_DMAE_DCR_DTAC (1u << 25) -#define HPB_DMAE_DCR_DTAU (1u << 24) -#define HPB_DMAE_DCR_DTAU1 (1u << 23) -#define HPB_DMAE_DCR_SWMD (1u << 22) -#define HPB_DMAE_DCR_BTMD (1u << 21) -#define HPB_DMAE_DCR_PKMD (1u << 20) -#define HPB_DMAE_DCR_CT (1u << 18) -#define HPB_DMAE_DCR_ACMD (1u << 17) -#define HPB_DMAE_DCR_DIP (1u << 16) -#define HPB_DMAE_DCR_SMDL (1u << 13) -#define HPB_DMAE_DCR_SPDAM (1u << 12) -#define HPB_DMAE_DCR_SDRMD_MASK (3u << 10) -#define HPB_DMAE_DCR_SDRMD_MOD (0u << 10) -#define HPB_DMAE_DCR_SDRMD_AUTO (1u << 10) -#define HPB_DMAE_DCR_SDRMD_TIMER (2u << 10) -#define HPB_DMAE_DCR_SPDS_MASK (3u << 8) -#define HPB_DMAE_DCR_SPDS_8BIT (0u << 8) -#define HPB_DMAE_DCR_SPDS_16BIT (1u << 8) -#define HPB_DMAE_DCR_SPDS_32BIT (2u << 8) -#define HPB_DMAE_DCR_DMDL (1u << 5) -#define HPB_DMAE_DCR_DPDAM (1u << 4) -#define HPB_DMAE_DCR_DDRMD_MASK (3u << 2) -#define HPB_DMAE_DCR_DDRMD_MOD (0u << 2) -#define HPB_DMAE_DCR_DDRMD_AUTO (1u << 2) -#define HPB_DMAE_DCR_DDRMD_TIMER (2u << 2) -#define HPB_DMAE_DCR_DPDS_MASK (3u << 0) -#define HPB_DMAE_DCR_DPDS_8BIT (0u << 0) -#define HPB_DMAE_DCR_DPDS_16BIT (1u << 0) -#define HPB_DMAE_DCR_DPDS_32BIT (2u << 0) - -/* Asynchronous reset register (ASYNCRSTR) bits */ -#define HPB_DMAE_ASYNCRSTR_ASRST41 BIT(10) -#define HPB_DMAE_ASYNCRSTR_ASRST40 BIT(9) -#define HPB_DMAE_ASYNCRSTR_ASRST39 BIT(8) -#define HPB_DMAE_ASYNCRSTR_ASRST27 BIT(7) -#define HPB_DMAE_ASYNCRSTR_ASRST26 BIT(6) -#define HPB_DMAE_ASYNCRSTR_ASRST25 BIT(5) -#define HPB_DMAE_ASYNCRSTR_ASRST24 BIT(4) -#define HPB_DMAE_ASYNCRSTR_ASRST23 BIT(3) -#define HPB_DMAE_ASYNCRSTR_ASRST22 BIT(2) -#define HPB_DMAE_ASYNCRSTR_ASRST21 BIT(1) -#define HPB_DMAE_ASYNCRSTR_ASRST20 BIT(0) - -struct hpb_dmae_slave_config { - unsigned int id; - dma_addr_t addr; - u32 dcr; - u32 port; - u32 rstr; - u32 mdr; - u32 mdm; - u32 flags; -#define HPB_DMAE_SET_ASYNC_RESET BIT(0) -#define HPB_DMAE_SET_ASYNC_MODE BIT(1) - u32 dma_ch; -}; - -#define HPB_DMAE_CHANNEL(_irq, _s_id) \ -{ \ - .ch_irq = _irq, \ - .s_id = _s_id, \ -} - -struct hpb_dmae_channel { - unsigned int ch_irq; - unsigned int s_id; -}; - -struct hpb_dmae_pdata { - const struct hpb_dmae_slave_config *slaves; - int num_slaves; - const struct hpb_dmae_channel *channels; - int num_channels; - const unsigned int ts_shift[XMIT_SZ_MAX]; - int num_hw_channels; -}; - -#endif -- cgit v1.2.3-58-ga151 From a8135d0d79e9d0ad3a4ff494fceeaae838becf38 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Dec 2015 22:47:40 +0200 Subject: dmaengine: core: Introduce new, universal API to request a channel The two API function can cover most, if not all current APIs used to request a channel. With minimal effort dmaengine drivers, platforms and dmaengine user drivers can be converted to use the two function. struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); To request any channel matching with the requested capabilities, can be used to request channel for memcpy, memset, xor, etc where no hardware synchronization is needed. struct dma_chan *dma_request_chan(struct device *dev, const char *name); To request a slave channel. The dma_request_chan() will try to find the channel via DT, ACPI or in case if the kernel booted in non DT/ACPI mode it will use a filter lookup table and retrieves the needed information from the dma_slave_map provided by the DMA drivers. This legacy mode needs changes in platform code, in dmaengine drivers and finally the dmaengine user drivers can be converted: For each dmaengine driver an array of DMA device, slave and the parameter for the filter function needs to be added: static const struct dma_slave_map da830_edma_map[] = { { "davinci-mcasp.0", "rx", EDMA_FILTER_PARAM(0, 0) }, { "davinci-mcasp.0", "tx", EDMA_FILTER_PARAM(0, 1) }, { "davinci-mcasp.1", "rx", EDMA_FILTER_PARAM(0, 2) }, { "davinci-mcasp.1", "tx", EDMA_FILTER_PARAM(0, 3) }, { "davinci-mcasp.2", "rx", EDMA_FILTER_PARAM(0, 4) }, { "davinci-mcasp.2", "tx", EDMA_FILTER_PARAM(0, 5) }, { "spi_davinci.0", "rx", EDMA_FILTER_PARAM(0, 14) }, { "spi_davinci.0", "tx", EDMA_FILTER_PARAM(0, 15) }, { "da830-mmc.0", "rx", EDMA_FILTER_PARAM(0, 16) }, { "da830-mmc.0", "tx", EDMA_FILTER_PARAM(0, 17) }, { "spi_davinci.1", "rx", EDMA_FILTER_PARAM(0, 18) }, { "spi_davinci.1", "tx", EDMA_FILTER_PARAM(0, 19) }, }; This information is going to be needed by the dmaengine driver, so modification to the platform_data is needed, and the driver map should be added to the pdata of the DMA driver: da8xx_edma0_pdata.slave_map = da830_edma_map; da8xx_edma0_pdata.slavecnt = ARRAY_SIZE(da830_edma_map); The DMA driver then needs to configure the needed device -> filter_fn mapping before it registers with dma_async_device_register() : ecc->dma_slave.filter_map.map = info->slave_map; ecc->dma_slave.filter_map.mapcnt = info->slavecnt; ecc->dma_slave.filter_map.fn = edma_filter_fn; When neither DT or ACPI lookup is available the dma_request_chan() will try to match the requester's device name with the filter_map's list of device names, when a match found it will use the information from the dma_slave_map to get the channel with the dma_get_channel() internal function. Signed-off-by: Peter Ujfalusi Reviewed-by: Arnd Bergmann Signed-off-by: Vinod Koul --- Documentation/dmaengine/client.txt | 23 +++------- drivers/dma/dmaengine.c | 89 +++++++++++++++++++++++++++++++++----- include/linux/dmaengine.h | 51 +++++++++++++++++++--- 3 files changed, 127 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/Documentation/dmaengine/client.txt b/Documentation/dmaengine/client.txt index 11fb87ff6cd0..4b04d8988708 100644 --- a/Documentation/dmaengine/client.txt +++ b/Documentation/dmaengine/client.txt @@ -22,25 +22,14 @@ The slave DMA usage consists of following steps: Channel allocation is slightly different in the slave DMA context, client drivers typically need a channel from a particular DMA controller only and even in some cases a specific channel is desired. - To request a channel dma_request_channel() API is used. + To request a channel dma_request_chan() API is used. Interface: - struct dma_chan *dma_request_channel(dma_cap_mask_t mask, - dma_filter_fn filter_fn, - void *filter_param); - where dma_filter_fn is defined as: - typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param); - - The 'filter_fn' parameter is optional, but highly recommended for - slave and cyclic channels as they typically need to obtain a specific - DMA channel. - - When the optional 'filter_fn' parameter is NULL, dma_request_channel() - simply returns the first channel that satisfies the capability mask. - - Otherwise, the 'filter_fn' routine will be called once for each free - channel which has a capability in 'mask'. 'filter_fn' is expected to - return 'true' when the desired DMA channel is found. + struct dma_chan *dma_request_chan(struct device *dev, const char *name); + + Which will find and return the 'name' DMA channel associated with the 'dev' + device. The association is done via DT, ACPI or board file based + dma_slave_map matching table. A channel allocated via this interface is exclusive to the caller, until dma_release_channel() is called. diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 81a36fc445a7..a094dbb54f46 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -43,6 +43,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -665,27 +666,73 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, } EXPORT_SYMBOL_GPL(__dma_request_channel); +static const struct dma_slave_map *dma_filter_match(struct dma_device *device, + const char *name, + struct device *dev) +{ + int i; + + if (!device->filter.mapcnt) + return NULL; + + for (i = 0; i < device->filter.mapcnt; i++) { + const struct dma_slave_map *map = &device->filter.map[i]; + + if (!strcmp(map->devname, dev_name(dev)) && + !strcmp(map->slave, name)) + return map; + } + + return NULL; +} + /** - * dma_request_slave_channel_reason - try to allocate an exclusive slave channel + * dma_request_chan - try to allocate an exclusive slave channel * @dev: pointer to client device structure * @name: slave channel name * * Returns pointer to appropriate DMA channel on success or an error pointer. */ -struct dma_chan *dma_request_slave_channel_reason(struct device *dev, - const char *name) +struct dma_chan *dma_request_chan(struct device *dev, const char *name) { + struct dma_device *d, *_d; + struct dma_chan *chan = NULL; + /* If device-tree is present get slave info from here */ if (dev->of_node) - return of_dma_request_slave_channel(dev->of_node, name); + chan = of_dma_request_slave_channel(dev->of_node, name); /* If device was enumerated by ACPI get slave info from here */ - if (ACPI_HANDLE(dev)) - return acpi_dma_request_slave_chan_by_name(dev, name); + if (has_acpi_companion(dev) && !chan) + chan = acpi_dma_request_slave_chan_by_name(dev, name); + + if (chan) { + /* Valid channel found or requester need to be deferred */ + if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) + return chan; + } + + /* Try to find the channel via the DMA filter map(s) */ + mutex_lock(&dma_list_mutex); + list_for_each_entry_safe(d, _d, &dma_device_list, global_node) { + dma_cap_mask_t mask; + const struct dma_slave_map *map = dma_filter_match(d, name, dev); + + if (!map) + continue; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); - return ERR_PTR(-ENODEV); + chan = find_candidate(d, &mask, d->filter.fn, map->param); + if (!IS_ERR(chan)) + break; + } + mutex_unlock(&dma_list_mutex); + + return chan ? chan : ERR_PTR(-EPROBE_DEFER); } -EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason); +EXPORT_SYMBOL_GPL(dma_request_chan); /** * dma_request_slave_channel - try to allocate an exclusive slave channel @@ -697,17 +744,35 @@ EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason); struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name) { - struct dma_chan *ch = dma_request_slave_channel_reason(dev, name); + struct dma_chan *ch = dma_request_chan(dev, name); if (IS_ERR(ch)) return NULL; - dma_cap_set(DMA_PRIVATE, ch->device->cap_mask); - ch->device->privatecnt++; - return ch; } EXPORT_SYMBOL_GPL(dma_request_slave_channel); +/** + * dma_request_chan_by_mask - allocate a channel satisfying certain capabilities + * @mask: capabilities that the channel must satisfy + * + * Returns pointer to appropriate DMA channel on success or an error pointer. + */ +struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask) +{ + struct dma_chan *chan; + + if (!mask) + return ERR_PTR(-ENODEV); + + chan = __dma_request_channel(mask, NULL, NULL); + if (!chan) + chan = ERR_PTR(-ENODEV); + + return chan; +} +EXPORT_SYMBOL_GPL(dma_request_chan_by_mask); + void dma_release_channel(struct dma_chan *chan) { mutex_lock(&dma_list_mutex); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c47c68e535e8..d50a6b51a73d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -606,12 +606,39 @@ enum dmaengine_alignment { DMAENGINE_ALIGN_64_BYTES = 6, }; +/** + * struct dma_slave_map - associates slave device and it's slave channel with + * parameter to be used by a filter function + * @devname: name of the device + * @slave: slave channel name + * @param: opaque parameter to pass to struct dma_filter.fn + */ +struct dma_slave_map { + const char *devname; + const char *slave; + void *param; +}; + +/** + * struct dma_filter - information for slave device/channel to filter_fn/param + * mapping + * @fn: filter function callback + * @mapcnt: number of slave device/channel in the map + * @map: array of channel to filter mapping data + */ +struct dma_filter { + dma_filter_fn fn; + int mapcnt; + const struct dma_slave_map *map; +}; + /** * struct dma_device - info on the entity supplying DMA services * @chancnt: how many DMA channels are supported * @privatecnt: how many DMA channels are requested by dma_request_channel * @channels: the list of struct dma_chan * @global_node: list_head for global dma_device_list + * @filter: information for device/slave to filter function/param mapping * @cap_mask: one or more dma_capability flags * @max_xor: maximum number of xor sources, 0 if no capability * @max_pq: maximum number of PQ sources and PQ-continue capability @@ -666,6 +693,7 @@ struct dma_device { unsigned int privatecnt; struct list_head channels; struct list_head global_node; + struct dma_filter filter; dma_cap_mask_t cap_mask; unsigned short max_xor; unsigned short max_pq; @@ -1140,9 +1168,11 @@ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); void dma_issue_pending_all(void); struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param); -struct dma_chan *dma_request_slave_channel_reason(struct device *dev, - const char *name); struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); + +struct dma_chan *dma_request_chan(struct device *dev, const char *name); +struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); + void dma_release_channel(struct dma_chan *chan); int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); #else @@ -1166,16 +1196,21 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, { return NULL; } -static inline struct dma_chan *dma_request_slave_channel_reason( - struct device *dev, const char *name) -{ - return ERR_PTR(-ENODEV); -} static inline struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name) { return NULL; } +static inline struct dma_chan *dma_request_chan(struct device *dev, + const char *name) +{ + return ERR_PTR(-ENODEV); +} +static inline struct dma_chan *dma_request_chan_by_mask( + const dma_cap_mask_t *mask) +{ + return ERR_PTR(-ENODEV); +} static inline void dma_release_channel(struct dma_chan *chan) { } @@ -1186,6 +1221,8 @@ static inline int dma_get_slave_caps(struct dma_chan *chan, } #endif +#define dma_request_slave_channel_reason(dev, name) dma_request_chan(dev, name) + static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx) { struct dma_slave_caps caps; -- cgit v1.2.3-58-ga151 From 23e6723c060faf5a0fc8d7bfbec440d29943fa99 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Dec 2015 22:47:41 +0200 Subject: dmaengine: edma: Add support for DMA filter mapping to slave devices Add support for providing device to filter_fn mapping so client drivers can switch to use the dma_request_chan() API. Signed-off-by: Peter Ujfalusi Reviewed-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++++ include/linux/platform_data/edma.h | 7 +++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 6b03e4e84e6b..c7a011f4b860 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -2297,6 +2297,10 @@ static int edma_probe(struct platform_device *pdev) edma_set_chmap(&ecc->slave_chans[i], ecc->dummy_slot); } + ecc->dma_slave.filter.map = info->slave_map; + ecc->dma_slave.filter.mapcnt = info->slavecnt; + ecc->dma_slave.filter.fn = edma_filter_fn; + ret = dma_async_device_register(&ecc->dma_slave); if (ret) { dev_err(dev, "slave ddev registration failed (%d)\n", ret); diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index e2878baeb90e..105700e62ea1 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -53,12 +53,16 @@ enum dma_event_q { #define EDMA_CTLR(i) ((i) >> 16) #define EDMA_CHAN_SLOT(i) ((i) & 0xffff) +#define EDMA_FILTER_PARAM(ctlr, chan) ((int[]) { EDMA_CTLR_CHAN(ctlr, chan) }) + struct edma_rsv_info { const s16 (*rsv_chans)[2]; const s16 (*rsv_slots)[2]; }; +struct dma_slave_map; + /* platform_data for EDMA driver */ struct edma_soc_info { /* @@ -76,6 +80,9 @@ struct edma_soc_info { s8 (*queue_priority_mapping)[2]; const s16 (*xbar_chans)[2]; + + const struct dma_slave_map *slave_map; + int slavecnt; }; #endif -- cgit v1.2.3-58-ga151 From 020c62ae38946cae01571a0b4e6f445dfdb7ec1c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Dec 2015 22:47:42 +0200 Subject: dmaengine: omap-dma: Add support for DMA filter mapping to slave devices Add support for providing device to filter_fn mapping so client drivers can switch to use the dma_request_chan() API. Signed-off-by: Peter Ujfalusi Reviewed-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/omap-dma.c | 4 ++++ include/linux/omap-dma.h | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 1dfc71c90123..48f77c289cd3 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -1203,6 +1203,10 @@ static int omap_dma_probe(struct platform_device *pdev) return rc; } + od->ddev.filter.map = od->plat->slave_map; + od->ddev.filter.mapcnt = od->plat->slavecnt; + od->ddev.filter.fn = omap_dma_filter_fn; + rc = dma_async_device_register(&od->ddev); if (rc) { pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n", diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h index 88fa8af2b937..1d99b61adc65 100644 --- a/include/linux/omap-dma.h +++ b/include/linux/omap-dma.h @@ -267,6 +267,9 @@ struct omap_dma_reg { u8 type; }; +#define SDMA_FILTER_PARAM(hw_req) ((int[]) { (hw_req) }) +struct dma_slave_map; + /* System DMA platform data structure */ struct omap_system_dma_plat_info { const struct omap_dma_reg *reg_map; @@ -278,6 +281,9 @@ struct omap_system_dma_plat_info { void (*clear_dma)(int lch); void (*dma_write)(u32 val, int reg, int lch); u32 (*dma_read)(int reg, int lch); + + const struct dma_slave_map *slave_map; + int slavecnt; }; #ifdef CONFIG_ARCH_OMAP2PLUS -- cgit v1.2.3-58-ga151