* [v1,3/7] dt-bindings: stm32-mdma: Add DMA/MDMA chaining support bindings
From: Pierre Yves MORDRET @ 2018-09-11 7:26 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Alexandre Torgue,
Maxime Coquelin, Dan Williams, devicetree, dmaengine,
linux-arm-kernel, linux-kernel
Cc: Pierre-Yves MORDRET
This patch adds the description of the 2 properties needed to support M2M
transfer triggered by STM32 DMA when his transfer is complete.
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
Version history:
v1:
* Initial
---
---
.../devicetree/bindings/dma/stm32-mdma.txt | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/Documentation/devicetree/bindings/dma/stm32-mdma.txt b/Documentation/devicetree/bindings/dma/stm32-mdma.txt
index d18772d..1810f87 100644
--- a/Documentation/devicetree/bindings/dma/stm32-mdma.txt
+++ b/Documentation/devicetree/bindings/dma/stm32-mdma.txt
@@ -10,7 +10,7 @@ Required properties:
- interrupts: Should contain the MDMA interrupt.
- clocks: Should contain the input clock of the DMA instance.
- resets: Reference to a reset controller asserting the DMA controller.
-- #dma-cells : Must be <5>. See DMA client paragraph for more details.
+- #dma-cells : Must be <6>. See DMA client paragraph for more details.
Optional properties:
- dma-channels: Number of DMA channels supported by the controller.
@@ -26,7 +26,7 @@ Example:
interrupts = <122>;
clocks = <&timer_clk>;
resets = <&rcc 992>;
- #dma-cells = <5>;
+ #dma-cells = <6>;
dma-channels = <16>;
dma-requests = <32>;
st,ahb-addr-masks = <0x20000000>, <0x00000000>;
@@ -35,8 +35,8 @@ Example:
* DMA client
DMA clients connected to the STM32 MDMA controller must use the format
-described in the dma.txt file, using a five-cell specifier for each channel:
-a phandle to the MDMA controller plus the following five integer cells:
+described in the dma.txt file, using a six-cell specifier for each channel:
+a phandle to the MDMA controller plus the following six integer cells:
1. The request line number
2. The priority level
@@ -76,19 +76,23 @@ a phandle to the MDMA controller plus the following five integer cells:
if no HW ack signal is used by the MDMA client
5. A 32bit mask specifying the value to be written to acknowledge the request
if no HW ack signal is used by the MDMA client
+6. A bitfield value specifying if the MDMA client wants to generate M2M
+ transfer with HW trigger (1) or not (0). This bitfield should be only
+ enabled for M2M transfer triggered by STM32 DMA client. The memory devices
+ involved in this kind of transfer are SRAM and DDR.
Example:
i2c4: i2c@5c002000 {
compatible = "st,stm32f7-i2c";
reg = <0x5c002000 0x400>;
- interrupts = <95>,
- <96>;
- clocks = <&timer_clk>;
+ interrupts = <GIC_SPI 95 IRQ_TYPE_NONE>,
+ <GIC_SPI 96 IRQ_TYPE_NONE>;
+ clocks = <&clk_hsi>;
#address-cells = <1>;
#size-cells = <0>;
- dmas = <&mdma1 36 0x0 0x40008 0x0 0x0>,
- <&mdma1 37 0x0 0x40002 0x0 0x0>;
+ dmas = <&mdma1 36 0x0 0x40008 0x0 0x0 0>,
+ <&mdma1 37 0x0 0x40002 0x0 0x0 0>;
dma-names = "rx", "tx";
status = "disabled";
};
^ permalink raw reply related
* [v1,4/7] dmaengine: stm32-dma: Add DMA/MDMA chaining support
From: Pierre Yves MORDRET @ 2018-09-11 7:26 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Alexandre Torgue,
Maxime Coquelin, Dan Williams, devicetree, dmaengine,
linux-arm-kernel, linux-kernel
Cc: Pierre-Yves MORDRET
This patch adds support of DMA/MDMA chaining support.
It introduces an intermediate transfer between peripherals and STM32 DMA.
This intermediate transfer is triggered by SW for single M2D transfer and
by STM32 DMA IP for all other modes (sg, cyclic) and direction (D2M).
A generic SRAM allocator is used for this intermediate buffer
Each DMA channel will be able to define its SRAM needs to achieve chaining
feature : (2 ^ order) * PAGE_SIZE.
For cyclic, SRAM buffer is derived from period length (rounded on
PAGE_SIZE).
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
Version history:
v1:
* Initial
---
---
drivers/dma/stm32-dma.c | 879 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 772 insertions(+), 107 deletions(-)
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 379e8d5..1571f2f 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -15,11 +15,14 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
+#include <linux/genalloc.h>
#include <linux/init.h>
+#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
@@ -118,6 +121,7 @@
#define STM32_DMA_FIFO_THRESHOLD_FULL 0x03
#define STM32_DMA_MAX_DATA_ITEMS 0xffff
+#define STM32_DMA_SRAM_GRANULARITY PAGE_SIZE
/*
* Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter
* gather at boundary. Thus it's safer to round down this value on FIFO
@@ -135,6 +139,12 @@
/* DMA Features */
#define STM32_DMA_THRESHOLD_FTR_MASK GENMASK(1, 0)
#define STM32_DMA_THRESHOLD_FTR_GET(n) ((n) & STM32_DMA_THRESHOLD_FTR_MASK)
+#define STM32_DMA_MDMA_CHAIN_FTR_MASK BIT(2)
+#define STM32_DMA_MDMA_CHAIN_FTR_GET(n) (((n) & STM32_DMA_MDMA_CHAIN_FTR_MASK) \
+ >> 2)
+#define STM32_DMA_MDMA_SRAM_SIZE_MASK GENMASK(4, 3)
+#define STM32_DMA_MDMA_SRAM_SIZE_GET(n) (((n) & STM32_DMA_MDMA_SRAM_SIZE_MASK) \
+ >> 3)
enum stm32_dma_width {
STM32_DMA_BYTE,
@@ -176,15 +186,31 @@ struct stm32_dma_chan_reg {
u32 dma_sfcr;
};
+struct stm32_dma_mdma_desc {
+ struct sg_table sgt;
+ struct dma_async_tx_descriptor *desc;
+};
+
+struct stm32_dma_mdma {
+ struct dma_chan *chan;
+ enum dma_transfer_direction dir;
+ dma_addr_t sram_buf;
+ u32 sram_period;
+ u32 num_sgs;
+};
+
struct stm32_dma_sg_req {
- u32 len;
+ struct scatterlist stm32_sgl_req;
struct stm32_dma_chan_reg chan_reg;
+ struct stm32_dma_mdma_desc m_desc;
};
struct stm32_dma_desc {
struct virt_dma_desc vdesc;
bool cyclic;
u32 num_sgs;
+ dma_addr_t dma_buf;
+ void *dma_buf_cpu;
struct stm32_dma_sg_req sg_req[];
};
@@ -201,6 +227,10 @@ struct stm32_dma_chan {
u32 threshold;
u32 mem_burst;
u32 mem_width;
+ struct stm32_dma_mdma mchan;
+ u32 use_mdma;
+ u32 sram_size;
+ u32 residue_after_drain;
};
struct stm32_dma_device {
@@ -210,6 +240,7 @@ struct stm32_dma_device {
struct reset_control *rst;
bool mem2mem;
struct stm32_dma_chan chan[STM32_DMA_MAX_CHANNELS];
+ struct gen_pool *sram_pool;
};
static struct stm32_dma_device *stm32_dma_get_dev(struct stm32_dma_chan *chan)
@@ -497,11 +528,15 @@ static void stm32_dma_stop(struct stm32_dma_chan *chan)
static int stm32_dma_terminate_all(struct dma_chan *c)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_mdma *mchan = &chan->mchan;
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (chan->use_mdma)
+ dmaengine_terminate_async(mchan->chan);
+
if (chan->busy) {
stm32_dma_stop(chan);
chan->desc = NULL;
@@ -514,9 +549,96 @@ static int stm32_dma_terminate_all(struct dma_chan *c)
return 0;
}
+static u32 stm32_dma_get_remaining_bytes(struct stm32_dma_chan *chan)
+{
+ u32 dma_scr, width, ndtr;
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+
+ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
+ width = STM32_DMA_SCR_PSIZE_GET(dma_scr);
+ ndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id));
+
+ return ndtr << width;
+}
+
+static int stm32_dma_mdma_drain(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_mdma *mchan = &chan->mchan;
+ struct stm32_dma_sg_req *sg_req;
+ struct dma_device *ddev = mchan->chan->device;
+ struct dma_async_tx_descriptor *desc = NULL;
+ enum dma_status status;
+ dma_addr_t src_buf, dst_buf;
+ u32 mdma_residue, mdma_wrote, dma_to_write, len;
+ struct dma_tx_state state;
+ int ret;
+
+ /* DMA/MDMA chain: drain remaining data in SRAM */
+
+ /* Get the residue on MDMA side */
+ status = dmaengine_tx_status(mchan->chan, mchan->chan->cookie, &state);
+ if (status == DMA_COMPLETE)
+ return status;
+
+ mdma_residue = state.residue;
+ sg_req = &chan->desc->sg_req[chan->next_sg - 1];
+ len = sg_dma_len(&sg_req->stm32_sgl_req);
+
+ /*
+ * Total = mdma blocks * sram_period + rest (< sram_period)
+ * so mdma blocks * sram_period = len - mdma residue - rest
+ */
+ mdma_wrote = len - mdma_residue - (len % mchan->sram_period);
+
+ /* Remaining data stuck in SRAM */
+ dma_to_write = mchan->sram_period - stm32_dma_get_remaining_bytes(chan);
+ if (dma_to_write > 0) {
+ /* Stop DMA current operation */
+ stm32_dma_disable_chan(chan);
+
+ /* Terminate current MDMA to initiate a new one */
+ dmaengine_terminate_all(mchan->chan);
+
+ /* Double buffer management */
+ src_buf = mchan->sram_buf +
+ ((mdma_wrote / mchan->sram_period) & 0x1) *
+ mchan->sram_period;
+ dst_buf = sg_dma_address(&sg_req->stm32_sgl_req) + mdma_wrote;
+
+ desc = ddev->device_prep_dma_memcpy(mchan->chan,
+ dst_buf, src_buf,
+ dma_to_write,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EINVAL;
+
+ ret = dma_submit_error(dmaengine_submit(desc));
+ if (ret < 0)
+ return ret;
+
+ status = dma_wait_for_async_tx(desc);
+ if (status != DMA_COMPLETE) {
+ dev_err(chan2dev(chan), "flush() dma_wait_for_async_tx error\n");
+ dmaengine_terminate_async(mchan->chan);
+ return -EBUSY;
+ }
+
+ /* We need to store residue for tx_status() */
+ chan->residue_after_drain = len - (mdma_wrote + dma_to_write);
+ }
+
+ return 0;
+}
+
static void stm32_dma_synchronize(struct dma_chan *c)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_mdma *mchan = &chan->mchan;
+
+ if (chan->desc && chan->use_mdma && mchan->dir == DMA_DEV_TO_MEM)
+ if (stm32_dma_mdma_drain(chan))
+ dev_err(chan2dev(chan), "%s: can't drain DMA\n",
+ __func__);
vchan_synchronize(&chan->vchan);
}
@@ -539,62 +661,232 @@ static void stm32_dma_dump_reg(struct stm32_dma_chan *chan)
dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr);
}
-static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan);
-
-static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
+static int stm32_dma_dummy_memcpy_xfer(struct stm32_dma_chan *chan)
{
struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
- struct virt_dma_desc *vdesc;
+ struct dma_device *ddev = &dmadev->ddev;
+ struct stm32_dma_chan_reg reg;
+ u8 src_buf, dst_buf;
+ dma_addr_t dma_src_buf, dma_dst_buf;
+ u32 ndtr, status;
+ int len, ret;
+
+ ret = 0;
+ src_buf = 0;
+ len = 1;
+
+ dma_src_buf = dma_map_single(ddev->dev, &src_buf, len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(ddev->dev, dma_src_buf);
+ if (ret < 0) {
+ dev_err(chan2dev(chan), "Source buffer map failed\n");
+ return ret;
+ }
+
+ dma_dst_buf = dma_map_single(ddev->dev, &dst_buf, len, DMA_FROM_DEVICE);
+ ret = dma_mapping_error(ddev->dev, dma_dst_buf);
+ if (ret < 0) {
+ dev_err(chan2dev(chan), "Destination buffer map failed\n");
+ dma_unmap_single(ddev->dev, dma_src_buf, len, DMA_TO_DEVICE);
+ return ret;
+ }
+
+ reg.dma_scr = STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) |
+ STM32_DMA_SCR_PBURST(STM32_DMA_BURST_SINGLE) |
+ STM32_DMA_SCR_MBURST(STM32_DMA_BURST_SINGLE) |
+ STM32_DMA_SCR_MINC |
+ STM32_DMA_SCR_PINC |
+ STM32_DMA_SCR_TEIE;
+ reg.dma_spar = dma_src_buf;
+ reg.dma_sm0ar = dma_dst_buf;
+ reg.dma_sfcr = STM32_DMA_SFCR_MASK |
+ STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL);
+ reg.dma_sm1ar = dma_dst_buf;
+ reg.dma_sndtr = 1;
+
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg.dma_scr);
+ stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg.dma_spar);
+ stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg.dma_sm0ar);
+ stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg.dma_sfcr);
+ stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg.dma_sm1ar);
+ stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg.dma_sndtr);
+
+ /* Clear interrupt status if it is there */
+ status = stm32_dma_irq_status(chan);
+ if (status)
+ stm32_dma_irq_clear(chan, status);
+
+ stm32_dma_dump_reg(chan);
+
+ chan->busy = true;
+ /* Start DMA */
+ reg.dma_scr |= STM32_DMA_SCR_EN;
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg.dma_scr);
+
+ ret = readl_relaxed_poll_timeout_atomic(dmadev->base +
+ STM32_DMA_SNDTR(chan->id),
+ ndtr, !ndtr, 10, 1000);
+ if (ret) {
+ dev_err(chan2dev(chan), "%s: timeout!\n", __func__);
+ ret = -EBUSY;
+ }
+
+ chan->busy = false;
+
+ ret = stm32_dma_disable_chan(chan);
+ status = stm32_dma_irq_status(chan);
+ if (status)
+ stm32_dma_irq_clear(chan, status);
+
+ dma_unmap_single(ddev->dev, dma_src_buf, len, DMA_TO_DEVICE);
+ dma_unmap_single(ddev->dev, dma_dst_buf, len, DMA_FROM_DEVICE);
+
+ return ret;
+}
+
+static int stm32_dma_mdma_flush_remaining(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_mdma *mchan = &chan->mchan;
struct stm32_dma_sg_req *sg_req;
- struct stm32_dma_chan_reg *reg;
- u32 status;
+ struct dma_device *ddev = mchan->chan->device;
+ struct dma_async_tx_descriptor *desc = NULL;
+ enum dma_status status;
+ dma_addr_t src_buf, dst_buf;
+ u32 residue, remain, len;
int ret;
- ret = stm32_dma_disable_chan(chan);
- if (ret < 0)
- return;
+ sg_req = &chan->desc->sg_req[chan->next_sg - 1];
- if (!chan->desc) {
- vdesc = vchan_next_desc(&chan->vchan);
- if (!vdesc)
- return;
+ residue = stm32_dma_get_remaining_bytes(chan);
+ len = sg_dma_len(&sg_req->stm32_sgl_req);
+ remain = len % mchan->sram_period;
- chan->desc = to_stm32_dma_desc(vdesc);
- chan->next_sg = 0;
+ if (residue > 0 && len > mchan->sram_period &&
+ ((len % mchan->sram_period) != 0)) {
+ unsigned long dma_sync_wait_timeout =
+ jiffies + msecs_to_jiffies(5000);
+
+ while (residue > 0 &&
+ residue > (mchan->sram_period - remain)) {
+ if (time_after_eq(jiffies, dma_sync_wait_timeout)) {
+ dev_err(chan2dev(chan),
+ "%s timeout waiting for last bytes\n",
+ __func__);
+ break;
+ }
+ cpu_relax();
+ residue = stm32_dma_get_remaining_bytes(chan);
+ }
+ stm32_dma_disable_chan(chan);
+
+ src_buf = mchan->sram_buf + ((len / mchan->sram_period) & 0x1)
+ * mchan->sram_period;
+ dst_buf = sg_dma_address(&sg_req->stm32_sgl_req) + len -
+ (len % mchan->sram_period);
+
+ desc = ddev->device_prep_dma_memcpy(mchan->chan,
+ dst_buf, src_buf,
+ len % mchan->sram_period,
+ DMA_PREP_INTERRUPT);
+
+ if (!desc)
+ return -EINVAL;
+
+ ret = dma_submit_error(dmaengine_submit(desc));
+ if (ret < 0)
+ return ret;
+
+ status = dma_wait_for_async_tx(desc);
+ if (status != DMA_COMPLETE) {
+ dmaengine_terminate_async(mchan->chan);
+ return -EBUSY;
+ }
}
- if (chan->next_sg == chan->desc->num_sgs)
- chan->next_sg = 0;
+ return 0;
+}
- sg_req = &chan->desc->sg_req[chan->next_sg];
- reg = &sg_req->chan_reg;
+static void stm32_dma_start_transfer(struct stm32_dma_chan *chan);
- stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
- stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg->dma_spar);
- stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg->dma_sm0ar);
- stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg->dma_sfcr);
- stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg->dma_sm1ar);
- stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg->dma_sndtr);
+static void stm32_mdma_chan_complete(void *param,
+ const struct dmaengine_result *result)
+{
+ struct stm32_dma_chan *chan = param;
- chan->next_sg++;
+ chan->busy = false;
+ if (result->result == DMA_TRANS_NOERROR) {
+ if (stm32_dma_mdma_flush_remaining(chan)) {
+ dev_err(chan2dev(chan), "Can't flush DMA\n");
+ return;
+ }
- /* Clear interrupt status if it is there */
- status = stm32_dma_irq_status(chan);
- if (status)
- stm32_dma_irq_clear(chan, status);
+ if (chan->next_sg == chan->desc->num_sgs) {
+ list_del(&chan->desc->vdesc.node);
+ vchan_cookie_complete(&chan->desc->vdesc);
+ chan->desc = NULL;
+ }
+ stm32_dma_start_transfer(chan);
+ } else {
+ dev_err(chan2dev(chan), "MDMA transfer error: %d\n",
+ result->result);
+ }
+}
- if (chan->desc->cyclic)
- stm32_dma_configure_next_sg(chan);
+static int stm32_dma_mdma_start(struct stm32_dma_chan *chan,
+ struct stm32_dma_sg_req *sg_req)
+{
+ struct stm32_dma_mdma *mchan = &chan->mchan;
+ struct stm32_dma_mdma_desc *m_desc = &sg_req->m_desc;
+ struct dma_slave_config config;
+ int ret;
- stm32_dma_dump_reg(chan);
+ /* Configure MDMA channel */
+ memset(&config, 0, sizeof(config));
+ if (mchan->dir == DMA_MEM_TO_DEV)
+ config.dst_addr = mchan->sram_buf;
+ else
+ config.src_addr = mchan->sram_buf;
- /* Start DMA */
- reg->dma_scr |= STM32_DMA_SCR_EN;
- stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
+ ret = dmaengine_slave_config(mchan->chan, &config);
+ if (ret < 0)
+ goto error;
+
+ /* Prepare MDMA descriptor */
+ m_desc->desc = dmaengine_prep_slave_sg(mchan->chan, m_desc->sgt.sgl,
+ m_desc->sgt.nents, mchan->dir,
+ DMA_PREP_INTERRUPT);
+ if (!m_desc->desc) {
+ ret = -EINVAL;
+ goto error;
+ }
- chan->busy = true;
+ if (mchan->dir != DMA_MEM_TO_DEV) {
+ m_desc->desc->callback_result = stm32_mdma_chan_complete;
+ m_desc->desc->callback_param = chan;
+ }
- dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan);
+ ret = dma_submit_error(dmaengine_submit(m_desc->desc));
+ if (ret < 0) {
+ dev_err(chan2dev(chan), "MDMA submit failed\n");
+ goto error;
+ }
+
+ dma_async_issue_pending(mchan->chan);
+
+ /*
+ * In case of M2D transfer, we have to generate dummy DMA transfer to
+ * copy 1st sg data into SRAM
+ */
+ if (mchan->dir == DMA_MEM_TO_DEV) {
+ ret = stm32_dma_dummy_memcpy_xfer(chan);
+ if (ret < 0) {
+ dmaengine_terminate_async(mchan->chan);
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ return ret;
}
static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan)
@@ -626,23 +918,132 @@ static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan)
}
}
-static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan)
+static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
{
- if (chan->desc) {
- if (chan->desc->cyclic) {
- vchan_cyclic_callback(&chan->desc->vdesc);
- chan->next_sg++;
- stm32_dma_configure_next_sg(chan);
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ struct virt_dma_desc *vdesc;
+ struct stm32_dma_sg_req *sg_req;
+ struct stm32_dma_chan_reg *reg;
+ u32 status;
+ int ret;
+
+ ret = stm32_dma_disable_chan(chan);
+ if (ret < 0)
+ return;
+
+ if (!chan->desc) {
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc)
+ return;
+
+ chan->desc = to_stm32_dma_desc(vdesc);
+ chan->next_sg = 0;
+ } else {
+ vdesc = &chan->desc->vdesc;
+ }
+
+ if (chan->next_sg == chan->desc->num_sgs)
+ chan->next_sg = 0;
+
+ sg_req = &chan->desc->sg_req[chan->next_sg];
+ reg = &sg_req->chan_reg;
+
+ /* Clear interrupt status if it is there */
+ status = stm32_dma_irq_status(chan);
+ if (status)
+ stm32_dma_irq_clear(chan, status);
+
+ if (chan->use_mdma) {
+ if (chan->next_sg == 0) {
+ struct stm32_dma_mdma_desc *m_desc;
+
+ m_desc = &sg_req->m_desc;
+ if (chan->desc->cyclic) {
+ /*
+ * If one callback is set, it will be called by
+ * MDMA driver.
+ */
+ if (vdesc->tx.callback) {
+ m_desc->desc->callback =
+ vdesc->tx.callback;
+ m_desc->desc->callback_param =
+ vdesc->tx.callback_param;
+ vdesc->tx.callback = NULL;
+ vdesc->tx.callback_param = NULL;
+ }
+ }
+ }
+
+ if (chan->mchan.dir == DMA_MEM_TO_DEV) {
+ ret = stm32_dma_dummy_memcpy_xfer(chan);
+ if (ret < 0) {
+ dmaengine_terminate_async(chan->mchan.chan);
+ chan->desc = NULL;
+ return;
+ }
} else {
- chan->busy = false;
- if (chan->next_sg == chan->desc->num_sgs) {
- list_del(&chan->desc->vdesc.node);
- vchan_cookie_complete(&chan->desc->vdesc);
+ reg->dma_scr &= ~STM32_DMA_SCR_TCIE;
+ }
+
+ if (!chan->desc->cyclic) {
+ /* MDMA already started */
+ if (chan->mchan.dir != DMA_MEM_TO_DEV &&
+ sg_dma_len(&sg_req->stm32_sgl_req) >
+ chan->mchan.sram_period)
+ reg->dma_scr |= STM32_DMA_SCR_DBM;
+ ret = stm32_dma_mdma_start(chan, sg_req);
+ if (ret < 0) {
chan->desc = NULL;
+ return;
}
- stm32_dma_start_transfer(chan);
}
}
+
+ chan->next_sg++;
+
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
+ stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg->dma_spar);
+ stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg->dma_sm0ar);
+ stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg->dma_sfcr);
+ stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg->dma_sm1ar);
+ stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg->dma_sndtr);
+
+ if (chan->desc->cyclic)
+ stm32_dma_configure_next_sg(chan);
+
+ stm32_dma_dump_reg(chan);
+
+ /* Start DMA */
+ chan->busy = true;
+ reg->dma_scr |= STM32_DMA_SCR_EN;
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
+
+ dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan);
+}
+
+static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan)
+{
+ if (!chan->desc)
+ return;
+
+ if (chan->desc->cyclic) {
+ vchan_cyclic_callback(&chan->desc->vdesc);
+ if (chan->use_mdma)
+ return;
+ chan->next_sg++;
+ stm32_dma_configure_next_sg(chan);
+ } else {
+ chan->busy = false;
+ if (chan->use_mdma && chan->mchan.dir != DMA_MEM_TO_DEV)
+ return;
+ if (chan->next_sg == chan->desc->num_sgs) {
+ list_del(&chan->desc->vdesc.node);
+ vchan_cookie_complete(&chan->desc->vdesc);
+ chan->desc = NULL;
+ }
+
+ stm32_dma_start_transfer(chan);
+ }
}
static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
@@ -695,7 +1096,6 @@ static void stm32_dma_issue_pending(struct dma_chan *c)
if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) {
dev_dbg(chan2dev(chan), "vchan %pK: issued\n", &chan->vchan);
stm32_dma_start_transfer(chan);
-
}
spin_unlock_irqrestore(&chan->vchan.lock, flags);
}
@@ -836,16 +1236,128 @@ static void stm32_dma_clear_reg(struct stm32_dma_chan_reg *regs)
memset(regs, 0, sizeof(struct stm32_dma_chan_reg));
}
+static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan,
+ struct scatterlist *sgl, u32 sg_len,
+ struct stm32_dma_desc *desc)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ struct scatterlist *sg, *m_sg;
+ dma_addr_t dma_buf;
+ u32 len, num_sgs, sram_period;
+ int i, j, ret;
+
+ desc->dma_buf_cpu = gen_pool_dma_alloc(dmadev->sram_pool,
+ chan->sram_size,
+ &desc->dma_buf);
+ if (!desc->dma_buf_cpu)
+ return -ENOMEM;
+
+ sram_period = chan->sram_size / 2;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[i].m_desc;
+
+ len = sg_dma_len(sg);
+ desc->sg_req[i].stm32_sgl_req = *sg;
+ num_sgs = 1;
+
+ if (chan->mchan.dir == DMA_MEM_TO_DEV) {
+ if (len > chan->sram_size) {
+ dev_err(chan2dev(chan),
+ "max buf size = %d bytes\n",
+ chan->sram_size);
+ goto free_alloc;
+ }
+ } else {
+ /*
+ * Build new sg for MDMA transfer
+ * Scatter DMA Req into several SDRAM transfer
+ */
+ if (len > sram_period)
+ num_sgs = len / sram_period;
+ }
+
+ ret = sg_alloc_table(&m_desc->sgt, num_sgs, GFP_ATOMIC);
+ if (ret) {
+ dev_err(chan2dev(chan), "MDMA sg table alloc failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dma_buf = sg_dma_address(sg);
+ for_each_sg(m_desc->sgt.sgl, m_sg, num_sgs, j) {
+ size_t bytes = min_t(size_t, len, sram_period);
+
+ sg_dma_address(m_sg) = dma_buf;
+ sg_dma_len(m_sg) = bytes;
+ dma_buf += bytes;
+ len -= bytes;
+ }
+ }
+
+ chan->mchan.sram_buf = desc->dma_buf;
+ chan->mchan.sram_period = sram_period;
+ chan->mchan.num_sgs = num_sgs;
+
+ return 0;
+
+err:
+ for (j = 0; j < i; j++)
+ sg_free_table(&desc->sg_req[j].m_desc.sgt);
+free_alloc:
+ gen_pool_free(dmadev->sram_pool, (unsigned long)desc->dma_buf_cpu,
+ chan->sram_size);
+ return ret;
+}
+
+static int stm32_dma_setup_sg_requests(struct stm32_dma_chan *chan,
+ struct scatterlist *sgl,
+ unsigned int sg_len,
+ enum dma_transfer_direction direction,
+ struct stm32_dma_desc *desc)
+{
+ struct scatterlist *sg;
+ u32 nb_data_items;
+ int i, ret;
+ enum dma_slave_buswidth buswidth;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth,
+ sg_dma_len(sg));
+ if (ret < 0)
+ return ret;
+
+ nb_data_items = sg_dma_len(sg) / buswidth;
+ if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
+ dev_err(chan2dev(chan), "nb items not supported\n");
+ return -EINVAL;
+ }
+
+ stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
+ desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr;
+ desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr;
+ desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar;
+ desc->sg_req[i].chan_reg.dma_sm0ar = sg_dma_address(sg);
+ desc->sg_req[i].chan_reg.dma_sm1ar = sg_dma_address(sg);
+ if (chan->use_mdma)
+ desc->sg_req[i].chan_reg.dma_sm1ar +=
+ chan->mchan.sram_period;
+ desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items;
+ }
+
+ desc->num_sgs = sg_len;
+
+ return 0;
+}
+
static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
struct dma_chan *c, struct scatterlist *sgl,
u32 sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+
struct stm32_dma_desc *desc;
- struct scatterlist *sg;
- enum dma_slave_buswidth buswidth;
- u32 nb_data_items;
int i, ret;
if (!chan->config_init) {
@@ -868,48 +1380,141 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
else
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;
- for_each_sg(sgl, sg, sg_len, i) {
- ret = stm32_dma_set_xfer_param(chan, direction, &buswidth,
- sg_dma_len(sg));
- if (ret < 0)
- goto err;
-
- desc->sg_req[i].len = sg_dma_len(sg);
+ if (chan->use_mdma) {
+ struct sg_table new_sgt;
+ struct scatterlist *s, *_sgl;
- nb_data_items = desc->sg_req[i].len / buswidth;
- if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
- dev_err(chan2dev(chan), "nb items not supported\n");
- goto err;
+ chan->mchan.dir = direction;
+ ret = stm32_dma_mdma_prep_slave_sg(chan, sgl, sg_len, desc);
+ if (ret < 0)
+ return NULL;
+
+ ret = sg_alloc_table(&new_sgt, sg_len, GFP_ATOMIC);
+ if (ret)
+ dev_err(chan2dev(chan), "DMA sg table alloc failed\n");
+
+ for_each_sg(new_sgt.sgl, s, sg_len, i) {
+ _sgl = sgl;
+ sg_dma_len(s) =
+ min(sg_dma_len(_sgl), chan->mchan.sram_period);
+ s->dma_address = chan->mchan.sram_buf;
+ _sgl = sg_next(_sgl);
}
- stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
- desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr;
- desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr;
- desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar;
- desc->sg_req[i].chan_reg.dma_sm0ar = sg_dma_address(sg);
- desc->sg_req[i].chan_reg.dma_sm1ar = sg_dma_address(sg);
- desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items;
+ ret = stm32_dma_setup_sg_requests(chan, new_sgt.sgl, sg_len,
+ direction, desc);
+ sg_free_table(&new_sgt);
+ if (ret < 0)
+ goto err;
+ } else {
+ /* Prepare a normal DMA transfer */
+ ret = stm32_dma_setup_sg_requests(chan, sgl, sg_len, direction,
+ desc);
+ if (ret < 0)
+ goto err;
}
- desc->num_sgs = sg_len;
desc->cyclic = false;
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
-
err:
+ if (chan->use_mdma) {
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+
+ for (i = 0; i < sg_len; i++)
+ sg_free_table(&desc->sg_req[i].m_desc.sgt);
+
+ gen_pool_free(dmadev->sram_pool,
+ (unsigned long)desc->dma_buf_cpu,
+ chan->sram_size);
+ }
kfree(desc);
+
return NULL;
}
+static int stm32_dma_mdma_prep_dma_cyclic(struct stm32_dma_chan *chan,
+ dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len,
+ struct stm32_dma_desc *desc)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ struct stm32_dma_mdma *mchan = &chan->mchan;
+ struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[0].m_desc;
+ struct dma_slave_config config;
+ dma_addr_t mem;
+ int ret;
+
+ chan->sram_size = ALIGN(period_len, STM32_DMA_SRAM_GRANULARITY);
+ desc->dma_buf_cpu = gen_pool_dma_alloc(dmadev->sram_pool,
+ 2 * chan->sram_size,
+ &desc->dma_buf);
+ if (!desc->dma_buf_cpu)
+ return -ENOMEM;
+
+ memset(&config, 0, sizeof(config));
+ mem = buf_addr;
+
+ /* Configure MDMA channel */
+ if (chan->mchan.dir == DMA_MEM_TO_DEV)
+ config.dst_addr = desc->dma_buf;
+ else
+ config.src_addr = desc->dma_buf;
+ ret = dmaengine_slave_config(mchan->chan, &config);
+ if (ret < 0)
+ goto err;
+
+ /* Prepare MDMA descriptor */
+ m_desc->desc = dmaengine_prep_dma_cyclic(mchan->chan, buf_addr, buf_len,
+ period_len, chan->mchan.dir,
+ DMA_PREP_INTERRUPT);
+
+ if (!m_desc->desc) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = dma_submit_error(dmaengine_submit(m_desc->desc));
+ if (ret < 0) {
+ dev_err(chan2dev(chan), "MDMA submit failed\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(mchan->chan);
+
+ /*
+ * In case of M2D transfer, we have to generate dummy DMA transfer to
+ * copy 1 period of data into SRAM
+ */
+ if (chan->mchan.dir == DMA_MEM_TO_DEV) {
+ ret = stm32_dma_dummy_memcpy_xfer(chan);
+ if (ret < 0) {
+ dev_err(chan2dev(chan),
+ "stm32_dma_dummy_memcpy_xfer failed\n");
+ dmaengine_terminate_async(mchan->chan);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ gen_pool_free(dmadev->sram_pool,
+ (unsigned long)desc->dma_buf_cpu,
+ chan->sram_size);
+ return ret;
+}
+
static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
unsigned long flags)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_chan_reg *chan_reg = &chan->chan_reg;
struct stm32_dma_desc *desc;
enum dma_slave_buswidth buswidth;
u32 num_periods, nb_data_items;
+ dma_addr_t dma_buf = 0;
int i, ret;
if (!buf_len || !period_len) {
@@ -957,28 +1562,49 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
/* Clear periph ctrl if client set it */
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;
- num_periods = buf_len / period_len;
+ if (chan->use_mdma)
+ num_periods = 1;
+ else
+ num_periods = buf_len / period_len;
desc = stm32_dma_alloc_desc(num_periods);
if (!desc)
return NULL;
- for (i = 0; i < num_periods; i++) {
- desc->sg_req[i].len = period_len;
+ desc->num_sgs = num_periods;
+ desc->cyclic = true;
+ if (chan->use_mdma) {
+ chan->mchan.dir = direction;
+
+ ret = stm32_dma_mdma_prep_dma_cyclic(chan, buf_addr, buf_len,
+ period_len, desc);
+ if (ret < 0)
+ return NULL;
+ dma_buf = desc->dma_buf;
+ } else {
+ dma_buf = buf_addr;
+ }
+
+ for (i = 0; i < num_periods; i++) {
+ sg_dma_len(&desc->sg_req[i].stm32_sgl_req) = period_len;
+ sg_dma_address(&desc->sg_req[i].stm32_sgl_req) = dma_buf;
stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
- desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr;
- desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr;
- desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar;
- desc->sg_req[i].chan_reg.dma_sm0ar = buf_addr;
- desc->sg_req[i].chan_reg.dma_sm1ar = buf_addr;
+ desc->sg_req[i].chan_reg.dma_scr = chan_reg->dma_scr;
+ desc->sg_req[i].chan_reg.dma_sfcr = chan_reg->dma_sfcr;
+ desc->sg_req[i].chan_reg.dma_spar = chan_reg->dma_spar;
+ if (chan->use_mdma) {
+ desc->sg_req[i].chan_reg.dma_sm0ar = desc->dma_buf;
+ desc->sg_req[i].chan_reg.dma_sm1ar = desc->dma_buf +
+ chan->sram_size;
+ } else {
+ desc->sg_req[i].chan_reg.dma_sm0ar = dma_buf;
+ desc->sg_req[i].chan_reg.dma_sm1ar = dma_buf;
+ dma_buf += period_len;
+ }
desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items;
- buf_addr += period_len;
}
- desc->num_sgs = num_periods;
- desc->cyclic = true;
-
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
}
@@ -1019,13 +1645,13 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
STM32_DMA_SCR_PINC |
STM32_DMA_SCR_TCIE |
STM32_DMA_SCR_TEIE;
- desc->sg_req[i].chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK;
+ desc->sg_req[i].chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK;
desc->sg_req[i].chan_reg.dma_sfcr |=
STM32_DMA_SFCR_FTH(threshold);
desc->sg_req[i].chan_reg.dma_spar = src + offset;
desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset;
desc->sg_req[i].chan_reg.dma_sndtr = xfer_count;
- desc->sg_req[i].len = xfer_count;
+ sg_dma_len(&desc->sg_req[i].stm32_sgl_req) = xfer_count;
}
desc->num_sgs = num_sgs;
@@ -1034,18 +1660,6 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
}
-static u32 stm32_dma_get_remaining_bytes(struct stm32_dma_chan *chan)
-{
- u32 dma_scr, width, ndtr;
- struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
-
- dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
- width = STM32_DMA_SCR_PSIZE_GET(dma_scr);
- ndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id));
-
- return ndtr << width;
-}
-
static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
struct stm32_dma_desc *desc,
u32 next_sg)
@@ -1054,6 +1668,10 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
u32 residue = 0;
int i;
+ /* Drain case */
+ if (chan->residue_after_drain)
+ return chan->residue_after_drain;
+
/*
* In cyclic mode, for the last period, residue = remaining bytes from
* NDTR
@@ -1069,7 +1687,7 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
* transferred
*/
for (i = next_sg; i < desc->num_sgs; i++)
- residue += desc->sg_req[i].len;
+ residue += sg_dma_len(&desc->sg_req[i].stm32_sgl_req);
residue += stm32_dma_get_remaining_bytes(chan);
end:
@@ -1089,11 +1707,23 @@ static enum dma_status stm32_dma_tx_status(struct dma_chan *c,
struct dma_tx_state *state)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_mdma *mchan = &chan->mchan;
struct virt_dma_desc *vdesc;
enum dma_status status;
unsigned long flags;
u32 residue = 0;
+ /*
+ * When DMA/MDMA chain is used, we return the status of MDMA in cyclic
+ * mode and for D2M transfer in sg mode in order to return the correct
+ * residue if any
+ */
+ if (chan->desc && chan->use_mdma &&
+ (mchan->dir != DMA_MEM_TO_DEV || chan->desc->cyclic) &&
+ !chan->residue_after_drain)
+ return dmaengine_tx_status(mchan->chan, mchan->chan->cookie,
+ state);
+
status = dma_cookie_status(c, cookie, state);
if (status == DMA_COMPLETE || !state)
return status;
@@ -1155,21 +1785,34 @@ static void stm32_dma_free_chan_resources(struct dma_chan *c)
static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
{
- kfree(container_of(vdesc, struct stm32_dma_desc, vdesc));
+ struct stm32_dma_desc *desc = to_stm32_dma_desc(vdesc);
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(vdesc->tx.chan);
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ int i;
+
+ if (chan->use_mdma) {
+ for (i = 0; i < desc->num_sgs; i++)
+ sg_free_table(&desc->sg_req[i].m_desc.sgt);
+
+ gen_pool_free(dmadev->sram_pool,
+ (unsigned long)desc->dma_buf_cpu,
+ chan->sram_size);
+ }
+
+ kfree(desc);
}
static void stm32_dma_set_config(struct stm32_dma_chan *chan,
struct stm32_dma_cfg *cfg)
{
stm32_dma_clear_reg(&chan->chan_reg);
-
chan->chan_reg.dma_scr = cfg->stream_config & STM32_DMA_SCR_CFG_MASK;
chan->chan_reg.dma_scr |= STM32_DMA_SCR_REQ(cfg->request_line);
-
- /* Enable Interrupts */
chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE;
-
chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features);
+ chan->use_mdma = STM32_DMA_MDMA_CHAIN_FTR_GET(cfg->features);
+ chan->sram_size = (1 << STM32_DMA_MDMA_SRAM_SIZE_GET(cfg->features)) *
+ STM32_DMA_SRAM_GRANULARITY;
}
static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
@@ -1207,6 +1850,9 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
stm32_dma_set_config(chan, &cfg);
+ if (!dmadev->sram_pool || !chan->mchan.chan)
+ chan->use_mdma = 0;
+
return c;
}
@@ -1219,10 +1865,12 @@ MODULE_DEVICE_TABLE(of, stm32_dma_of_match);
static int stm32_dma_probe(struct platform_device *pdev)
{
struct stm32_dma_chan *chan;
+ struct stm32_dma_mdma *mchan;
struct stm32_dma_device *dmadev;
struct dma_device *dd;
const struct of_device_id *match;
struct resource *res;
+ char name[4];
int i, ret;
match = of_match_device(stm32_dma_of_match, &pdev->dev);
@@ -1258,6 +1906,13 @@ static int stm32_dma_probe(struct platform_device *pdev)
reset_control_deassert(dmadev->rst);
}
+ dmadev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (!dmadev->sram_pool)
+ dev_info(&pdev->dev, "no dma pool: can't use MDMA: %d\n", ret);
+ else
+ dev_dbg(&pdev->dev, "SRAM pool: %zu KiB\n",
+ gen_pool_size(dmadev->sram_pool) / 1024);
+
dma_cap_set(DMA_SLAVE, dd->cap_mask);
dma_cap_set(DMA_PRIVATE, dd->cap_mask);
dma_cap_set(DMA_CYCLIC, dd->cap_mask);
@@ -1293,6 +1948,16 @@ static int stm32_dma_probe(struct platform_device *pdev)
chan->id = i;
chan->vchan.desc_free = stm32_dma_desc_free;
vchan_init(&chan->vchan, dd);
+
+ mchan = &chan->mchan;
+ if (dmadev->sram_pool) {
+ snprintf(name, sizeof(name), "ch%d", chan->id);
+ mchan->chan = dma_request_slave_channel(dd->dev, name);
+ if (!mchan->chan)
+ dev_info(&pdev->dev,
+ "can't request MDMA chan for %s\n",
+ name);
+ }
}
ret = dma_async_device_register(dd);
@@ -1350,4 +2015,4 @@ static int __init stm32_dma_init(void)
{
return platform_driver_probe(&stm32_dma_driver, stm32_dma_probe);
}
-subsys_initcall(stm32_dma_init);
+device_initcall(stm32_dma_init);
^ permalink raw reply related
* [v1,5/7] dmaengine: stm32-mdma: Add DMA/MDMA chaining support
From: Pierre Yves MORDRET @ 2018-09-11 7:26 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Alexandre Torgue,
Maxime Coquelin, Dan Williams, devicetree, dmaengine,
linux-arm-kernel, linux-kernel
Cc: Pierre-Yves MORDRET
This patch adds support for M2M transfer triggered by STM32 DMA in order to
transfer data from/to SRAM to/from DDR.
Normally, this mode should not be needed as transferring data from/to DDR
is supported by the STM32 DMA.
However, the STM32 DMA don't have the ability to generate burst transfer
on the DDR as it only embeds only a 4-word FIFO although the minimal burst
length on the DDR is 8 words.
Due to this constraint, the STM32 DMA transfers data from/to DDR in a
single way and could lead to pollute the DDR.
To avoid this, we have to use SRAM for all transfers where STM32 DMA is
involved.
So, we need to add an intermediate M2M transfer handled by the MDMA, which
has the ability to generate burst transfer on the DDR, to copy data
from/to SRAM to/from DDR as described below:
For M2D: DDR --> MDMA --> SRAM --> DMA --> IP
For D2M: IP --> DMA --> SRAM --> MDMA --> DDR
This intermediate transfer is triggered by the STM32 DMA when his transfer
complete flag is set. In that way, we are able to build a DMA/MDMA
chaining transfer completely handled by HW.
This patch clearly adds support for M2M transfer triggered by HW.
This mode is not really available in dmaengine framework as normally M2M
transfers are triggered by SW.
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
Version history:
v1:
* Initial
---
---
drivers/dma/stm32-mdma.c | 131 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 114 insertions(+), 17 deletions(-)
diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c
index 06dd172..6b6e63b 100644
--- a/drivers/dma/stm32-mdma.c
+++ b/drivers/dma/stm32-mdma.c
@@ -211,6 +211,8 @@
#define STM32_MDMA_MAX_BURST 128
#define STM32_MDMA_VERY_HIGH_PRIORITY 0x11
+#define STM32_DMA_SRAM_GRANULARITY PAGE_SIZE
+
enum stm32_mdma_trigger_mode {
STM32_MDMA_BUFFER,
STM32_MDMA_BLOCK,
@@ -237,6 +239,7 @@ struct stm32_mdma_chan_config {
u32 transfer_config;
u32 mask_addr;
u32 mask_data;
+ bool m2m_hw;
};
struct stm32_mdma_hwdesc {
@@ -262,6 +265,7 @@ struct stm32_mdma_desc {
u32 ccr;
bool cyclic;
u32 count;
+ enum dma_transfer_direction dir;
struct stm32_mdma_desc_node node[];
};
@@ -577,13 +581,25 @@ static int stm32_mdma_set_xfer_param(struct stm32_mdma_chan *chan,
dst_addr = chan->dma_config.dst_addr;
/* Set device data size */
+ if (chan_config->m2m_hw)
+ dst_addr_width =
+ stm32_mdma_get_max_width(dst_addr, buf_len,
+ STM32_MDMA_MAX_BUF_LEN);
+
dst_bus_width = stm32_mdma_get_width(chan, dst_addr_width);
if (dst_bus_width < 0)
return dst_bus_width;
ctcr &= ~STM32_MDMA_CTCR_DSIZE_MASK;
ctcr |= STM32_MDMA_CTCR_DSIZE(dst_bus_width);
+ if (chan_config->m2m_hw) {
+ ctcr &= ~STM32_MDMA_CTCR_DINCOS_MASK;
+ ctcr |= STM32_MDMA_CTCR_DINCOS(dst_bus_width);
+ }
/* Set device burst value */
+ if (chan_config->m2m_hw)
+ dst_maxburst = STM32_MDMA_MAX_BUF_LEN / dst_addr_width;
+
dst_best_burst = stm32_mdma_get_best_burst(buf_len, tlen,
dst_maxburst,
dst_addr_width);
@@ -626,13 +642,25 @@ static int stm32_mdma_set_xfer_param(struct stm32_mdma_chan *chan,
src_addr = chan->dma_config.src_addr;
/* Set device data size */
+ if (chan_config->m2m_hw)
+ src_addr_width =
+ stm32_mdma_get_max_width(src_addr, buf_len,
+ STM32_MDMA_MAX_BUF_LEN);
+
src_bus_width = stm32_mdma_get_width(chan, src_addr_width);
if (src_bus_width < 0)
return src_bus_width;
ctcr &= ~STM32_MDMA_CTCR_SSIZE_MASK;
ctcr |= STM32_MDMA_CTCR_SSIZE(src_bus_width);
+ if (chan_config->m2m_hw) {
+ ctcr &= ~STM32_MDMA_CTCR_SINCOS_MASK;
+ ctcr |= STM32_MDMA_CTCR_SINCOS(src_bus_width);
+ }
/* Set device burst value */
+ if (chan_config->m2m_hw)
+ src_maxburst = STM32_MDMA_MAX_BUF_LEN / src_addr_width;
+
src_best_burst = stm32_mdma_get_best_burst(buf_len, tlen,
src_maxburst,
src_addr_width);
@@ -740,6 +768,7 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan,
{
struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan);
struct dma_slave_config *dma_config = &chan->dma_config;
+ struct stm32_mdma_chan_config *chan_config = &chan->chan_config;
struct scatterlist *sg;
dma_addr_t src_addr, dst_addr;
u32 ccr, ctcr, ctbr;
@@ -762,6 +791,8 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan,
} else {
src_addr = dma_config->src_addr;
dst_addr = sg_dma_address(sg);
+ if (chan_config->m2m_hw)
+ src_addr += ((i & 1) ? sg_dma_len(sg) : 0);
ret = stm32_mdma_set_xfer_param(chan, direction, &ccr,
&ctcr, &ctbr, dst_addr,
sg_dma_len(sg));
@@ -780,8 +811,6 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan,
/* Enable interrupts */
ccr &= ~STM32_MDMA_CCR_IRQ_MASK;
ccr |= STM32_MDMA_CCR_TEIE | STM32_MDMA_CCR_CTCIE;
- if (sg_len > 1)
- ccr |= STM32_MDMA_CCR_BTIE;
desc->ccr = ccr;
return 0;
@@ -793,7 +822,9 @@ stm32_mdma_prep_slave_sg(struct dma_chan *c, struct scatterlist *sgl,
unsigned long flags, void *context)
{
struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c);
+ struct stm32_mdma_chan_config *chan_config = &chan->chan_config;
struct stm32_mdma_desc *desc;
+ struct stm32_mdma_hwdesc *hwdesc;
int i, ret;
/*
@@ -815,6 +846,20 @@ stm32_mdma_prep_slave_sg(struct dma_chan *c, struct scatterlist *sgl,
if (ret < 0)
goto xfer_setup_err;
+ /*
+ * In case of M2M HW transfer triggered by STM32 DMA, we do not have to
+ * clear the transfer complete flag by hardware in order to let the
+ * CPU rearm the DMA with the next sg element and update some data in
+ * dmaengine framework
+ */
+ if (chan_config->m2m_hw && direction == DMA_MEM_TO_DEV) {
+ for (i = 0; i < sg_len; i++) {
+ hwdesc = desc->node[i].hwdesc;
+ hwdesc->cmar = 0;
+ hwdesc->cmdr = 0;
+ }
+ }
+
desc->cyclic = false;
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
@@ -836,9 +881,10 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr,
struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c);
struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan);
struct dma_slave_config *dma_config = &chan->dma_config;
+ struct stm32_mdma_chan_config *chan_config = &chan->chan_config;
struct stm32_mdma_desc *desc;
dma_addr_t src_addr, dst_addr;
- u32 ccr, ctcr, ctbr, count;
+ u32 ccr, ctcr, ctbr, count, offset;
int i, ret;
/*
@@ -892,12 +938,29 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr,
desc->ccr = ccr;
/* Configure hwdesc list */
+ offset = ALIGN(period_len, STM32_DMA_SRAM_GRANULARITY);
for (i = 0; i < count; i++) {
if (direction == DMA_MEM_TO_DEV) {
+ /*
+ * When the DMA is configured in double buffer mode,
+ * the MDMA has to use 2 destination buffers to be
+ * compliant with this mode.
+ */
+ if (chan_config->m2m_hw && count > 1 && i % 2)
+ dst_addr = dma_config->dst_addr + offset;
+ else
+ dst_addr = dma_config->dst_addr;
src_addr = buf_addr + i * period_len;
- dst_addr = dma_config->dst_addr;
} else {
- src_addr = dma_config->src_addr;
+ /*
+ * When the DMA is configured in double buffer mode,
+ * the MDMA has to use 2 destination buffers to be
+ * compliant with this mode.
+ */
+ if (chan_config->m2m_hw && count > 1 && i % 2)
+ src_addr = dma_config->src_addr + offset;
+ else
+ src_addr = dma_config->src_addr;
dst_addr = buf_addr + i * period_len;
}
@@ -907,6 +970,7 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr,
}
desc->cyclic = true;
+ desc->dir = direction;
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
@@ -1287,14 +1351,28 @@ static size_t stm32_mdma_desc_residue(struct stm32_mdma_chan *chan,
{
struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan);
struct stm32_mdma_hwdesc *hwdesc = desc->node[0].hwdesc;
- u32 cbndtr, residue, modulo, burst_size;
+ u32 residue = 0;
+ u32 modulo, burst_size;
+ dma_addr_t next_clar;
+ u32 cbndtr;
int i;
- residue = 0;
- for (i = curr_hwdesc + 1; i < desc->count; i++) {
+ /*
+ * Get the residue of pending descriptors
+ */
+ /* Get the next hw descriptor to process from current transfer */
+ next_clar = stm32_mdma_read(dmadev, STM32_MDMA_CLAR(chan->id));
+ for (i = desc->count - 1; i >= 0; i--) {
hwdesc = desc->node[i].hwdesc;
+
+ if (hwdesc->clar == next_clar)
+ break;/* Current transfer found, stop cumulating */
+
+ /* Cumulate residue of unprocessed hw descriptors */
residue += STM32_MDMA_CBNDTR_BNDT(hwdesc->cbndtr);
}
+
+ /* Read & cumulate the residue of the current transfer */
cbndtr = stm32_mdma_read(dmadev, STM32_MDMA_CBNDTR(chan->id));
residue += cbndtr & STM32_MDMA_CBNDTR_BNDT_MASK;
@@ -1314,24 +1392,39 @@ static enum dma_status stm32_mdma_tx_status(struct dma_chan *c,
struct dma_tx_state *state)
{
struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c);
+ struct stm32_mdma_chan_config *chan_config = &chan->chan_config;
struct virt_dma_desc *vdesc;
enum dma_status status;
unsigned long flags;
u32 residue = 0;
status = dma_cookie_status(c, cookie, state);
- if ((status == DMA_COMPLETE) || (!state))
+ if (status == DMA_COMPLETE || !state)
return status;
spin_lock_irqsave(&chan->vchan.lock, flags);
vdesc = vchan_find_desc(&chan->vchan, cookie);
- if (chan->desc && cookie == chan->desc->vdesc.tx.cookie)
- residue = stm32_mdma_desc_residue(chan, chan->desc,
- chan->curr_hwdesc);
- else if (vdesc)
+ if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) {
+ /*
+ * In case of M2D transfer triggered by STM32 DMA, the MDMA has
+ * always one period in advance in cyclic mode. So, we have to
+ * add 1 period of data to return the good residue to the
+ * client
+ */
+ if (chan_config->m2m_hw && chan->desc->dir == DMA_MEM_TO_DEV &&
+ chan->curr_hwdesc > 1)
+ residue =
+ stm32_mdma_desc_residue(chan, chan->desc,
+ chan->curr_hwdesc - 1);
+ else
+ residue = stm32_mdma_desc_residue(chan, chan->desc,
+ chan->curr_hwdesc);
+ } else if (vdesc) {
residue = stm32_mdma_desc_residue(chan,
to_stm32_mdma_desc(vdesc), 0);
+ }
+
dma_set_residue(state, residue);
spin_unlock_irqrestore(&chan->vchan.lock, flags);
@@ -1498,7 +1591,7 @@ static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec,
struct dma_chan *c;
struct stm32_mdma_chan_config config;
- if (dma_spec->args_count < 5) {
+ if (dma_spec->args_count < 6) {
dev_err(mdma2dev(dmadev), "Bad number of args\n");
return NULL;
}
@@ -1508,6 +1601,7 @@ static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec,
config.transfer_config = dma_spec->args[2];
config.mask_addr = dma_spec->args[3];
config.mask_data = dma_spec->args[4];
+ config.m2m_hw = dma_spec->args[5];
if (config.request >= dmadev->nr_requests) {
dev_err(mdma2dev(dmadev), "Bad request line\n");
@@ -1646,19 +1740,20 @@ static int stm32_mdma_probe(struct platform_device *pdev)
dmadev->irq = platform_get_irq(pdev, 0);
if (dmadev->irq < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
- return dmadev->irq;
+ ret = dmadev->irq;
+ goto clk_free;
}
ret = devm_request_irq(&pdev->dev, dmadev->irq, stm32_mdma_irq_handler,
0, dev_name(&pdev->dev), dmadev);
if (ret) {
dev_err(&pdev->dev, "failed to request IRQ\n");
- return ret;
+ goto clk_free;
}
ret = dma_async_device_register(dd);
if (ret)
- return ret;
+ goto clk_free;
ret = of_dma_controller_register(of_node, stm32_mdma_of_xlate, dmadev);
if (ret < 0) {
@@ -1675,6 +1770,8 @@ static int stm32_mdma_probe(struct platform_device *pdev)
err_unregister:
dma_async_device_unregister(dd);
+clk_free:
+ clk_disable_unprepare(dmadev->clk);
return ret;
}
^ permalink raw reply related
* [v1,6/7] dmaengine: stm32-dma: enable descriptor_reuse
From: Pierre Yves MORDRET @ 2018-09-11 7:26 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Alexandre Torgue,
Maxime Coquelin, Dan Williams, devicetree, dmaengine,
linux-arm-kernel, linux-kernel
Cc: Pierre-Yves MORDRET
Enable client to resubmit already processed descriptors
in order to save descriptor creation time.
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
Version history:
v1:
* Initial
---
---
drivers/dma/stm32-dma.c | 84 +++++++++++++++++++++++++++++++------------------
1 file changed, 54 insertions(+), 30 deletions(-)
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 1571f2f..fac44ed 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -836,34 +836,8 @@ static int stm32_dma_mdma_start(struct stm32_dma_chan *chan,
{
struct stm32_dma_mdma *mchan = &chan->mchan;
struct stm32_dma_mdma_desc *m_desc = &sg_req->m_desc;
- struct dma_slave_config config;
int ret;
- /* Configure MDMA channel */
- memset(&config, 0, sizeof(config));
- if (mchan->dir == DMA_MEM_TO_DEV)
- config.dst_addr = mchan->sram_buf;
- else
- config.src_addr = mchan->sram_buf;
-
- ret = dmaengine_slave_config(mchan->chan, &config);
- if (ret < 0)
- goto error;
-
- /* Prepare MDMA descriptor */
- m_desc->desc = dmaengine_prep_slave_sg(mchan->chan, m_desc->sgt.sgl,
- m_desc->sgt.nents, mchan->dir,
- DMA_PREP_INTERRUPT);
- if (!m_desc->desc) {
- ret = -EINVAL;
- goto error;
- }
-
- if (mchan->dir != DMA_MEM_TO_DEV) {
- m_desc->desc->callback_result = stm32_mdma_chan_complete;
- m_desc->desc->callback_param = chan;
- }
-
ret = dma_submit_error(dmaengine_submit(m_desc->desc));
if (ret < 0) {
dev_err(chan2dev(chan), "MDMA submit failed\n");
@@ -1001,6 +975,7 @@ static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
chan->next_sg++;
+ reg->dma_scr &= ~STM32_DMA_SCR_EN;
stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg->dma_spar);
stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg->dma_sm0ar);
@@ -1238,9 +1213,11 @@ static void stm32_dma_clear_reg(struct stm32_dma_chan_reg *regs)
static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan,
struct scatterlist *sgl, u32 sg_len,
- struct stm32_dma_desc *desc)
+ struct stm32_dma_desc *desc,
+ unsigned long flags)
{
struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ struct stm32_dma_mdma *mchan = &chan->mchan;
struct scatterlist *sg, *m_sg;
dma_addr_t dma_buf;
u32 len, num_sgs, sram_period;
@@ -1256,12 +1233,13 @@ static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan,
for_each_sg(sgl, sg, sg_len, i) {
struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[i].m_desc;
+ struct dma_slave_config config;
len = sg_dma_len(sg);
desc->sg_req[i].stm32_sgl_req = *sg;
num_sgs = 1;
- if (chan->mchan.dir == DMA_MEM_TO_DEV) {
+ if (mchan->dir == DMA_MEM_TO_DEV) {
if (len > chan->sram_size) {
dev_err(chan2dev(chan),
"max buf size = %d bytes\n",
@@ -1293,6 +1271,38 @@ static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan,
dma_buf += bytes;
len -= bytes;
}
+
+ /* Configure MDMA channel */
+ memset(&config, 0, sizeof(config));
+ if (mchan->dir == DMA_MEM_TO_DEV)
+ config.dst_addr = desc->dma_buf;
+ else
+ config.src_addr = desc->dma_buf;
+
+ ret = dmaengine_slave_config(mchan->chan, &config);
+ if (ret < 0)
+ goto err;
+
+ /* Prepare MDMA descriptor */
+ m_desc->desc = dmaengine_prep_slave_sg(mchan->chan,
+ m_desc->sgt.sgl,
+ m_desc->sgt.nents,
+ mchan->dir,
+ DMA_PREP_INTERRUPT);
+
+ if (!m_desc->desc) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (flags & DMA_CTRL_REUSE)
+ dmaengine_desc_set_reuse(m_desc->desc);
+
+ if (mchan->dir != DMA_MEM_TO_DEV) {
+ m_desc->desc->callback_result =
+ stm32_mdma_chan_complete;
+ m_desc->desc->callback_param = chan;
+ }
}
chan->mchan.sram_buf = desc->dma_buf;
@@ -1302,8 +1312,12 @@ static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan,
return 0;
err:
- for (j = 0; j < i; j++)
+ for (j = 0; j < i; j++) {
+ struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[j].m_desc;
+
+ m_desc->desc = NULL;
sg_free_table(&desc->sg_req[j].m_desc.sgt);
+ }
free_alloc:
gen_pool_free(dmadev->sram_pool, (unsigned long)desc->dma_buf_cpu,
chan->sram_size);
@@ -1385,7 +1399,8 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
struct scatterlist *s, *_sgl;
chan->mchan.dir = direction;
- ret = stm32_dma_mdma_prep_slave_sg(chan, sgl, sg_len, desc);
+ ret = stm32_dma_mdma_prep_slave_sg(chan, sgl, sg_len, desc,
+ flags);
if (ret < 0)
return NULL;
@@ -1791,6 +1806,14 @@ static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
int i;
if (chan->use_mdma) {
+ struct stm32_dma_mdma_desc *m_desc;
+
+ for (i = 0; i < desc->num_sgs; i++) {
+ m_desc = &desc->sg_req[i].m_desc;
+ dmaengine_desc_free(m_desc->desc);
+ m_desc->desc = NULL;
+ }
+
for (i = 0; i < desc->num_sgs; i++)
sg_free_table(&desc->sg_req[i].m_desc.sgt);
@@ -1934,6 +1957,7 @@ static int stm32_dma_probe(struct platform_device *pdev)
dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
dd->max_burst = STM32_DMA_MAX_BURST;
+ dd->descriptor_reuse = true;
dd->dev = &pdev->dev;
INIT_LIST_HEAD(&dd->channels);
^ permalink raw reply related
* [v1,7/7] dmaengine: stm32-mdma: enable descriptor_reuse
From: Pierre Yves MORDRET @ 2018-09-11 7:27 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Alexandre Torgue,
Maxime Coquelin, Dan Williams, devicetree, dmaengine,
linux-arm-kernel, linux-kernel
Cc: Pierre-Yves MORDRET
enable reuse to spare descriptors creation on critical UC.
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
Version history:
v1:
* Initial
---
---
drivers/dma/stm32-mdma.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c
index 6b6e63b..80a17dd 100644
--- a/drivers/dma/stm32-mdma.c
+++ b/drivers/dma/stm32-mdma.c
@@ -1715,6 +1715,8 @@ static int stm32_mdma_probe(struct platform_device *pdev)
dd->device_resume = stm32_mdma_resume;
dd->device_terminate_all = stm32_mdma_terminate_all;
dd->device_synchronize = stm32_mdma_synchronize;
+ dd->descriptor_reuse = true;
+
dd->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
^ permalink raw reply related
* [v1,1/1] dmaengine: stm32-dma: check whether length is aligned on FIFO threshold
From: Pierre Yves MORDRET @ 2018-09-11 7:31 UTC (permalink / raw)
To: Dan Williams, Vinod Koul, Maxime Coquelin, Alexandre Torgue,
dmaengine, linux-arm-kernel, linux-kernel
Cc: Pierre-Yves MORDRET
When a period length is not multiple of FIFO some data may be stuck
within FIFO.
Burst/FIFO Threshold/Period or buffer length check has to be hardened
In any case DMA will grant any request from client but will degraded
any parameters whether awkward.
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
Version history:
v1:
* Initial
---
---
drivers/dma/stm32-dma.c | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 379e8d5..4903a40 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -308,20 +308,12 @@ static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold,
static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold)
{
- switch (threshold) {
- case STM32_DMA_FIFO_THRESHOLD_FULL:
- if (buf_len >= STM32_DMA_MAX_BURST)
- return true;
- else
- return false;
- case STM32_DMA_FIFO_THRESHOLD_HALFFULL:
- if (buf_len >= STM32_DMA_MAX_BURST / 2)
- return true;
- else
- return false;
- default:
- return false;
- }
+ /*
+ * Buffer or period length has to be aligned on FIFO depth.
+ * Otherwise bytes may be stuck within FIFO at buffer or period
+ * length.
+ */
+ return ((buf_len % ((threshold + 1) * 4)) == 0);
}
static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold,
^ permalink raw reply related
* [v2,1/2] dmaengine: Add metadata_ops for dma_async_tx_descriptor
From: Vinod Koul @ 2018-09-11 7:33 UTC (permalink / raw)
To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, linux-kernel, lars, radheys
On 30-08-18, 15:19, Peter Ujfalusi wrote:
> The metadata is best described as side band data or parameters traveling
> alongside the data DMAd by the DMA engine. It is data
> which is understood by the peripheral and the peripheral driver only, the
> DMA engine see it only as data block and it is not interpreting it in any
> way.
>
> The metadata can be different per descriptor as it is a parameter for the
> data being transferred.
>
> If the DMA supports per descriptor metadata it can implement the attach,
> get_ptr/set_len callbacks.
>
> Client drivers must only use either attach or get_ptr/set_len to avoid
> misconfiguration.
>
> Client driver can check if a given metadata mode is supported by the
> channel during probe time with
> dmaengine_is_metadata_mode_supported(chan, DESC_METADATA_CLIENT);
> dmaengine_is_metadata_mode_supported(chan, DESC_METADATA_ENGINE);
>
> and based on this information can use either mode.
>
> Wrappers are also added for the metadata_ops.
>
> To be used in DESC_METADATA_CLIENT mode:
> dmaengine_desc_attach_metadata()
>
> To be used in DESC_METADATA_ENGINE mode:
> dmaengine_desc_get_metadata_ptr()
> dmaengine_desc_set_metadata_len()
>
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
> drivers/dma/dmaengine.c | 73 ++++++++++++++++++++++++++
> include/linux/dmaengine.h | 108 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 181 insertions(+)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index f1a441ab395d..53bd1eae23f2 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -1306,6 +1306,79 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
> }
> EXPORT_SYMBOL(dma_async_tx_descriptor_init);
>
> +static inline int desc_check_and_set_metadata_mode(
> + struct dma_async_tx_descriptor *desc, enum dma_desc_metadata_mode mode)
> +{
> + /* Make sure that the metadata mode is not mixed */
> + if (!desc->desc_metadata_mode) {
> + if (dmaengine_is_metadata_mode_supported(desc->chan, mode))
> + desc->desc_metadata_mode = mode;
> + else
> + return -ENOTSUPP;
> + } else if (desc->desc_metadata_mode != mode) {
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int dmaengine_desc_attach_metadata(struct dma_async_tx_descriptor *desc,
> + void *data, size_t len)
> +{
> + int ret;
> +
> + if (!desc)
> + return -EINVAL;
> +
> + ret = desc_check_and_set_metadata_mode(desc, DESC_METADATA_CLIENT);
> + if (ret)
> + return ret;
> +
> + if (!desc->metadata_ops || !desc->metadata_ops->attach)
> + return -ENOTSUPP;
> +
> + return desc->metadata_ops->attach(desc, data, len);
> +}
> +EXPORT_SYMBOL(dmaengine_desc_attach_metadata);
EXPORT_SYMBOL_GPL ?
^ permalink raw reply
* [v2,2/2] dmaengine: doc: Add sections for per descriptor metadata support
From: Vinod Koul @ 2018-09-11 7:36 UTC (permalink / raw)
To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, linux-kernel, lars, radheys
On 30-08-18, 15:19, Peter Ujfalusi wrote:
> Update the provider and client documentation with details about the
> metadata support.
It would make sense to make this patch 1 :)
>
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
> Documentation/driver-api/dmaengine/client.rst | 70 +++++++++++++++++++
> .../driver-api/dmaengine/provider.rst | 46 ++++++++++++
> 2 files changed, 116 insertions(+)
>
> diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst
> index fbbb2831f29f..584a39347cfe 100644
> --- a/Documentation/driver-api/dmaengine/client.rst
> +++ b/Documentation/driver-api/dmaengine/client.rst
> @@ -151,6 +151,76 @@ The details of these operations are:
> Note that callbacks will always be invoked from the DMA
> engines tasklet, never from interrupt context.
>
> + Optional: per descriptor metadata
> + ---------------------------------
> + DMAengine provides two ways for metadata support.
> +
> + DESC_METADATA_CLIENT
> +
> + The metadata buffer is allocated/provided by the client driver and it is
> + attached to the descriptor.
> +
> + .. code-block:: c
> +
> + int dmaengine_desc_attach_metadata(struct dma_async_tx_descriptor *desc,
> + void *data, size_t len);
> +
> + DESC_METADATA_ENGINE
> +
> + The metadata buffer is allocated/managed by the DMA driver. The client
> + driver can ask for the pointer, maximum size and the currently used size of
> + the metadata and can directly update or read it.
> +
> + .. code-block:: c
> +
> + void *dmaengine_desc_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
> + size_t *payload_len, size_t *max_len);
> +
> + int dmaengine_desc_set_metadata_len(struct dma_async_tx_descriptor *desc,
> + size_t payload_len);
> +
> + Client drivers can query if a given mode is supported with:
> +
> + .. code-block:: c
> +
> + bool dmaengine_is_metadata_mode_supported(struct dma_chan *chan,
> + enum dma_desc_metadata_mode mode);
> +
> + Depending on the used mode client drivers must follow different flow.
> +
> + DESC_METADATA_CLIENT
> +
> + - DMA_MEM_TO_DEV / DEV_MEM_TO_MEM:
> + 1. prepare the descriptor (dmaengine_prep_*)
> + construct the metadata in the client's buffer
> + 2. use dmaengine_desc_attach_metadata() to attach the buffer to the
> + descriptor
> + 3. submit the transfer
> + - DMA_DEV_TO_MEM:
> + 1. prepare the descriptor (dmaengine_prep_*)
> + 2. use dmaengine_desc_attach_metadata() to attach the buffer to the
> + descriptor
> + 3. submit the transfer
> + 4. when the transfer is completed, the metadata should be available in the
> + attached buffer
> +
> + DESC_METADATA_ENGINE
> +
> + - DMA_MEM_TO_DEV / DEV_MEM_TO_MEM:
> + 1. prepare the descriptor (dmaengine_prep_*)
> + 2. use dmaengine_desc_get_metadata_ptr() to get the pointer to the
> + engine's metadata area
> + 3. update the metadata at the pointer
> + 4. use dmaengine_desc_set_metadata_len() to tell the DMA engine the
> + amount of data the client has placed into the metadata buffer
> + 5. submit the transfer
> + - DMA_DEV_TO_MEM:
> + 1. prepare the descriptor (dmaengine_prep_*)
> + 2. submit the transfer
> + 3. on transfer completion, use dmaengine_desc_get_metadata_ptr() to get the
> + pointer to the engine's metadata are
> + 4. Read out the metadate from the pointer
Lets put a note here that only one method should be used
> +
> 4. Submit the transaction
>
> Once the descriptor has been prepared and the callback information
> diff --git a/Documentation/driver-api/dmaengine/provider.rst b/Documentation/driver-api/dmaengine/provider.rst
> index dfc4486b5743..502c59f75302 100644
> --- a/Documentation/driver-api/dmaengine/provider.rst
> +++ b/Documentation/driver-api/dmaengine/provider.rst
> @@ -247,6 +247,52 @@ after each transfer. In case of a ring buffer, they may loop
> (DMA_CYCLIC). Addresses pointing to a device's register (e.g. a FIFO)
> are typically fixed.
>
> +Per descriptor metadata support
> +-------------------------------
> +Some data movement architecure (DMA controller and peripherals) uses metadata
typo architecure
> +associated with a transaction. The DMA controller role is to transfer the
> +payload and the metadata alongside.
> +The metadata itself is not used by the DMA engine itself, but it contains
> +parameters, keys, vectors, etc for peripheral or from the peripheral.
> +
> +The DMAengine framework provides a generic ways to facilitate the metadata for
> +descriptors. Depending on the architecture the DMA driver can implment either
implment
^ permalink raw reply
* [3/3] dmaengine: xilinx_dma: Fix 64-bit simple CDMA transfer
From: Vinod Koul @ 2018-09-11 7:41 UTC (permalink / raw)
To: Radhey Shyam Pandey
Cc: dan.j.williams@intel.com, Michal Simek,
Appana Durga Kedareswara Rao, lars@metafoo.de,
dmaengine@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
On 07-09-18, 12:08, Radhey Shyam Pandey wrote:
> > > Yeah that part was clear but the implementation can be better..
> I thought over it and it seems having a new interface dma_ctrl_write_64
> taking lsb and msb bits input looks better and scalable. It will be similar
> to existing vdma_desc_write_64 impl. I will send v2 if it looks ok.
Yes that is much better, btw why not reuse same routine as common xilinx
lib functions :)
^ permalink raw reply
* dma: idma64: replace spin_lock_irqsave with spin_lock
From: Vinod Koul @ 2018-09-11 7:44 UTC (permalink / raw)
To: Zhaoxiong Yuan; +Cc: dan.j.williams, dmaengine, linux-kernel
On 08-09-18, 06:02, Zhaoxiong Yuan wrote:
> idma64_chan_irq() is invoked in hardirq handle function, it is unnecessary
> to call spin_lock_irqsave.
Please add a line after log and s-o-b
I have applied after doing above and fixing subsystem tag (dmaengine)
^ permalink raw reply
* dma: sh: convert to SPDX identifiers
From: Vinod Koul @ 2018-09-11 7:48 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Linux-Renesas, Dan Williams, Geert Uytterhoeven, Simon Horman,
dmaengine
On 07-09-18, 01:58, Kuninori Morimoto wrote:
>
> From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
>
> This patch updates license to use SPDX-License-Identifier
> instead of verbose license text.
Thanks but the style is not consistent in files :(
Can we use one only?
> diff --git a/drivers/dma/sh/shdma-arm.h b/drivers/dma/sh/shdma-arm.h
> index a1b0ef4..30bcfe3 100644
> --- a/drivers/dma/sh/shdma-arm.h
> +++ b/drivers/dma/sh/shdma-arm.h
> @@ -1,11 +1,8 @@
> -/*
> +/* SPDX-License-Identifier: GPL-2.0
this is one
> #ifndef SHDMA_ARM_H
> diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c
> index 6b5626e..c51de49 100644
> --- a/drivers/dma/sh/shdma-base.c
> +++ b/drivers/dma/sh/shdma-base.c
> @@ -1,3 +1,4 @@
> +// SPDX-License-Identifier: GPL-2.0
different
> diff --git a/drivers/dma/sh/shdma.h b/drivers/dma/sh/shdma.h
> index 2c0a969..73aec72 100644
> --- a/drivers/dma/sh/shdma.h
> +++ b/drivers/dma/sh/shdma.h
> @@ -1,14 +1,9 @@
> -/*
> +/* SPDX-License-Identifier: GPL-2.0+
this and so on..
^ permalink raw reply
* dma: sh: convert to SPDX identifiers
From: Geert Uytterhoeven @ 2018-09-11 8:17 UTC (permalink / raw)
To: vkoul
Cc: Kuninori Morimoto, Linux-Renesas, Dan Williams,
Geert Uytterhoeven, Simon Horman, dmaengine
Hi Vinod,
On Tue, Sep 11, 2018 at 9:48 AM Vinod <vkoul@kernel.org> wrote:
> On 07-09-18, 01:58, Kuninori Morimoto wrote:
> > From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> >
> > This patch updates license to use SPDX-License-Identifier
> > instead of verbose license text.
>
> Thanks but the style is not consistent in files :(
>
> Can we use one only?
Please read
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/license-rules.rst#n69
2. Style:
The SPDX license identifier is added in form of a comment. The comment
style depends on the file type::
C source: // SPDX-License-Identifier: <SPDX License Expression>
C header: /* SPDX-License-Identifier: <SPDX License Expression> */
Gr{oetje,eeting}s,
Geert
^ permalink raw reply
* [3/4] dmaengine: imx-sdma: implement channel termination via worker
From: Robin Gong @ 2018-09-11 8:18 UTC (permalink / raw)
To: Lucas Stach, Vinod Koul
Cc: dmaengine@vger.kernel.org, dl-linux-imx, kernel@pengutronix.de,
patchwork-lst@pengutronix.de
Hi Lucas,
I have quick test for UART, seems this patch set broke uart function on i.mx7d,
I believe it should be same on other i.mx6 family. Could you double check it?
> -----Original Message-----
> From: Lucas Stach <l.stach@pengutronix.de>
> Sent: 2018年9月10日 17:59
> To: Robin Gong <yibin.gong@nxp.com>; Vinod Koul <vkoul@kernel.org>
> Cc: dmaengine@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com>;
> kernel@pengutronix.de; patchwork-lst@pengutronix.de
> Subject: Re: [PATCH 3/4] dmaengine: imx-sdma: implement channel
> termination via worker
>
> Am Dienstag, den 04.09.2018, 02:36 +0000 schrieb Robin Gong:
> > > -----Original Message-----
> > > From: Lucas Stach <l.stach@pengutronix.de>
> > > Sent: 2018年9月3日 21:12
> > > To: Robin Gong <yibin.gong@nxp.com>; Vinod Koul <vkoul@kernel.org>
> > > Cc: dmaengine@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com>;
> > > kernel@pengutronix.de; patchwork-lst@pengutronix.de
> > > Subject: Re: [PATCH 3/4] dmaengine: imx-sdma: implement channel
> > > termination via worker
> > >
> > > Am Montag, den 03.09.2018, 08:59 +0000 schrieb Robin Gong:
> > > > > -----Original Message-----
> > > > > From: Lucas Stach <l.stach@pengutronix.de>
> > > > > Sent: 2018年9月3日 16:41
> > > > > To: Robin Gong <yibin.gong@nxp.com>; Vinod Koul <vkoul@kernel.o
> > > > > rg>
> > > > > Cc: dmaengine@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com>
> > > > > ; kernel@pengutronix.de; patchwork-lst@pengutronix.de
> > > > > Subject: Re: [PATCH 3/4] dmaengine: imx-sdma: implement channel
> > > > > termination via worker
> > > > >
> > > > > Hi Robin,
> > > > >
> > > > > Am Freitag, den 31.08.2018, 09:49 +0000 schrieb Robin Gong:
> > > > > > Hi Lucas,
> > > > > > Seems I miss your previous mail. Thanks for your patch, but
> > > > > > if move most jobs of sdma_disable_channel_with_delay() into
> > > > > > worker, that will bring another race condition that upper
> > > > > > driver such as Audio terminate channel and free resource of
> > > > > > dma channel without really channel stop, if dma transfer done
> > > > > > interrupt come after that, oops or kernel cash may be caught.
> > > > > > Leave 'sdmac->desc = NULL' in the
> > > > >
> > > > > sdma_disable_channel_with_delay() may fix such potential issue.
> > > > >
> > > > > No, there is no such issue. The audio channel terminate will
> > > > > call dmaengine_terminate_sync(), which internally calls
> > > > > dmaengine_terminate_async() and then does a
> > > > > dmaengine_synchronize().
> > > > > As this patchset implements the device_synchronize function in
> > > > > the sdma driver, this will wait for the worker to finish its
> > > > > execution, so there is no race condition to worry about here.
> > > > >
> > > > > Regards,
> > > > > Lucas
> > > >
> > > > Yes, but how about other drivers which not call
> > > > dmaengine_terminate_sync()?
> > >
> > > Please read the dmaengine documentation. device_terminate_all has no
> > > requirement that the transfer is actually canceled when the call
> > > returns. If the caller needs a guarantee that the channel is stopped
> > > it _must_ call device_synchronize.
> >
> > I know that, but the fact is some driver still use
> > dmaengine_terminate_all() such as
> > Spi/uart driver. My concern is how to avoid to break their function.
>
> They should simply be fixed to not use a deprecated function. Both of those are
> only using device_terminate_all in error or shutdown paths, so the risk of races
> is pretty minimal even with the current code. And I think the SPI driver is trivial
> to fix, as we can just use the terminate_sync variant there. The UART driver
> is a bit more tricky.
>
> Regards,
> Lucas
^ permalink raw reply
* dma: sh: convert to SPDX identifiers
From: Kuninori Morimoto @ 2018-09-11 8:30 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: vkoul, Linux-Renesas, Dan Williams, Geert Uytterhoeven,
Simon Horman, dmaengine
Hi Vinod
> > > From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> > >
> > > This patch updates license to use SPDX-License-Identifier
> > > instead of verbose license text.
> >
> > Thanks but the style is not consistent in files :(
> >
> > Can we use one only?
>
> Please read
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/license-rules.rst#n69
>
> 2. Style:
>
> The SPDX license identifier is added in form of a comment. The comment
> style depends on the file type::
>
> C source: // SPDX-License-Identifier: <SPDX License Expression>
> C header: /* SPDX-License-Identifier: <SPDX License Expression> */
>
> Gr{oetje,eeting}s,
And this, too
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/license-rules.rst#n15
GPL-1.0+ : GNU General Public License v1.0 or later
GPL-2.0+ : GNU General Public License v2.0 or later
LGPL-2.0 : GNU Library General Public License v2 only
LGPL-2.0+ : GNU Library General Public License v2 or later
LGPL-2.1 : GNU Lesser General Public License v2.1 only
LGPL-2.1+ : GNU Lesser General Public License v2.1 or later
Best regards
---
Kuninori Morimoto
^ permalink raw reply
* [01/12] dmaengine: at_hdmac: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Ludovic Desroches
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Ludovic Desroches <ludovic.desroches@microchip.com>
drivers/dma/at_hdmac.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 75f38d19fcbe..7cbac6e8c113 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1320,7 +1320,7 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
if (unlikely(!is_slave_direction(direction)))
goto err_out;
- if (sconfig->direction == DMA_MEM_TO_DEV)
+ if (direction == DMA_MEM_TO_DEV)
reg_width = convert_buswidth(sconfig->dst_addr_width);
else
reg_width = convert_buswidth(sconfig->src_addr_width);
^ permalink raw reply related
* [02/12] dmaengine: bcm2835: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Ray Jui, Scott Branden
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Ray Jui <rjui@broadcom.com>
CC: Scott Branden <sbranden@broadcom.com>
drivers/dma/bcm2835-dma.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 847f84a41a69..cad55ab80d41 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -778,14 +778,6 @@ static int bcm2835_dma_slave_config(struct dma_chan *chan,
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
- if ((cfg->direction == DMA_DEV_TO_MEM &&
- cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) ||
- (cfg->direction == DMA_MEM_TO_DEV &&
- cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) ||
- !is_slave_direction(cfg->direction)) {
- return -EINVAL;
- }
-
c->cfg = *cfg;
return 0;
^ permalink raw reply related
* [03/12] dmaengine: coh901318: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Eric Anholt, Linus Walleij
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Eric Anholt <eric@anholt.net>
CC: Linus Walleij <linus.walleij@linaro.org>
drivers/dma/coh901318.c | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index da74fd74636b..eebaba3d9e78 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -1306,6 +1306,7 @@ struct coh901318_chan {
unsigned long nbr_active_done;
unsigned long busy;
+ struct dma_slave_config config;
u32 addr;
u32 ctrl;
@@ -1402,6 +1403,10 @@ static inline struct coh901318_chan *to_coh901318_chan(struct dma_chan *chan)
return container_of(chan, struct coh901318_chan, chan);
}
+static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan,
+ struct dma_slave_config *config,
+ enum dma_transfer_direction direction);
+
static inline const struct coh901318_params *
cohc_chan_param(struct coh901318_chan *cohc)
{
@@ -2360,6 +2365,8 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (lli == NULL)
goto err_dma_alloc;
+ coh901318_dma_set_runtimeconfig(chan, &cohc->config, direction);
+
/* initiate allocated lli list */
ret = coh901318_lli_fill_sg(&cohc->base->pool, lli, sgl, sg_len,
cohc->addr,
@@ -2499,7 +2506,8 @@ static const struct burst_table burst_sizes[] = {
};
static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan,
- struct dma_slave_config *config)
+ struct dma_slave_config *config,
+ enum dma_transfer_direction direction)
{
struct coh901318_chan *cohc = to_coh901318_chan(chan);
dma_addr_t addr;
@@ -2509,11 +2517,11 @@ static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan,
int i = 0;
/* We only support mem to per or per to mem transfers */
- if (config->direction == DMA_DEV_TO_MEM) {
+ if (direction == DMA_DEV_TO_MEM) {
addr = config->src_addr;
addr_width = config->src_addr_width;
maxburst = config->src_maxburst;
- } else if (config->direction == DMA_MEM_TO_DEV) {
+ } else if (direction == DMA_MEM_TO_DEV) {
addr = config->dst_addr;
addr_width = config->dst_addr_width;
maxburst = config->dst_maxburst;
@@ -2579,6 +2587,16 @@ static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan,
return 0;
}
+static int coh901318_dma_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct coh901318_chan *cohc = to_coh901318_chan(chan);
+
+ memcpy(&cohc->config, config, sizeof(*config));
+
+ return 0;
+}
+
static void coh901318_base_init(struct dma_device *dma, const int *pick_chans,
struct coh901318_base *base)
{
@@ -2684,7 +2702,7 @@ static int __init coh901318_probe(struct platform_device *pdev)
base->dma_slave.device_prep_slave_sg = coh901318_prep_slave_sg;
base->dma_slave.device_tx_status = coh901318_tx_status;
base->dma_slave.device_issue_pending = coh901318_issue_pending;
- base->dma_slave.device_config = coh901318_dma_set_runtimeconfig;
+ base->dma_slave.device_config = coh901318_dma_slave_config;
base->dma_slave.device_pause = coh901318_pause;
base->dma_slave.device_resume = coh901318_resume;
base->dma_slave.device_terminate_all = coh901318_terminate_all;
@@ -2707,7 +2725,7 @@ static int __init coh901318_probe(struct platform_device *pdev)
base->dma_memcpy.device_prep_dma_memcpy = coh901318_prep_memcpy;
base->dma_memcpy.device_tx_status = coh901318_tx_status;
base->dma_memcpy.device_issue_pending = coh901318_issue_pending;
- base->dma_memcpy.device_config = coh901318_dma_set_runtimeconfig;
+ base->dma_memcpy.device_config = coh901318_dma_slave_config;
base->dma_memcpy.device_pause = coh901318_pause;
base->dma_memcpy.device_resume = coh901318_resume;
base->dma_memcpy.device_terminate_all = coh901318_terminate_all;
^ permalink raw reply related
* [04/12] dmaengine: jz4740: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/dma/dma-jz4740.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c
index afd5e10f8927..5253e3c0dc04 100644
--- a/drivers/dma/dma-jz4740.c
+++ b/drivers/dma/dma-jz4740.c
@@ -113,6 +113,7 @@ struct jz4740_dma_desc {
struct jz4740_dmaengine_chan {
struct virt_dma_chan vchan;
unsigned int id;
+ struct dma_slave_config config;
dma_addr_t fifo_addr;
unsigned int transfer_shift;
@@ -203,8 +204,9 @@ static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst)
return JZ4740_DMA_TRANSFER_SIZE_32BYTE;
}
-static int jz4740_dma_slave_config(struct dma_chan *c,
- struct dma_slave_config *config)
+static int jz4740_dma_slave_config_write(struct dma_chan *c,
+ struct dma_slave_config *config,
+ enum dma_transfer_direction direction)
{
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
@@ -214,7 +216,7 @@ static int jz4740_dma_slave_config(struct dma_chan *c,
enum jz4740_dma_flags flags;
uint32_t cmd;
- switch (config->direction) {
+ switch (direction) {
case DMA_MEM_TO_DEV:
flags = JZ4740_DMA_SRC_AUTOINC;
transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
@@ -265,6 +267,15 @@ static int jz4740_dma_slave_config(struct dma_chan *c,
return 0;
}
+static int jz4740_dma_slave_config(struct dma_chan *c,
+ struct dma_slave_config *config)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+
+ memcpy(&chan->config, config, sizeof(*config));
+ return 0;
+}
+
static int jz4740_dma_terminate_all(struct dma_chan *c)
{
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
@@ -407,6 +418,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg(
desc->direction = direction;
desc->cyclic = false;
+ jz4740_dma_slave_config_write(c, &chan->config, direction);
+
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
}
@@ -438,6 +451,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic(
desc->direction = direction;
desc->cyclic = true;
+ jz4740_dma_slave_config_write(c, &chan->config, direction);
+
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
}
^ permalink raw reply related
* [05/12] dmaengine: dw: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Viresh Kumar, Andy Shevchenko
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Viresh Kumar <vireshk@kernel.org>
CC: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
drivers/dma/dw/core.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index f43e6dafe446..d0c3e50b39fb 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -886,12 +886,7 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
*/
u32 s = dw->pdata->is_idma32 ? 1 : 2;
- /* Check if chan will be configured for slave transfers */
- if (!is_slave_direction(sconfig->direction))
- return -EINVAL;
-
memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
- dwc->direction = sconfig->direction;
sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0;
sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0;
^ permalink raw reply related
* [06/12] dmaengine: fsl-edma: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/dma/fsl-edma.c | 87 ++++++++++++++++++++++++++------------------------
1 file changed, 46 insertions(+), 41 deletions(-)
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index c7568869284e..f8b4408aedb8 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -140,14 +140,6 @@ struct fsl_edma_sw_tcd {
struct fsl_edma_hw_tcd *vtcd;
};
-struct fsl_edma_slave_config {
- enum dma_transfer_direction dir;
- enum dma_slave_buswidth addr_width;
- u32 dev_addr;
- u32 burst;
- u32 attr;
-};
-
struct fsl_edma_chan {
struct virt_dma_chan vchan;
enum dma_status status;
@@ -156,7 +148,8 @@ struct fsl_edma_chan {
u32 slave_id;
struct fsl_edma_engine *edma;
struct fsl_edma_desc *edesc;
- struct fsl_edma_slave_config fsc;
+ struct dma_slave_config cfg;
+ u32 attr;
struct dma_pool *tcd_pool;
};
@@ -164,6 +157,7 @@ struct fsl_edma_desc {
struct virt_dma_desc vdesc;
struct fsl_edma_chan *echan;
bool iscyclic;
+ enum dma_transfer_direction dirn;
unsigned int n_tcds;
struct fsl_edma_sw_tcd tcd[];
};
@@ -347,20 +341,8 @@ static int fsl_edma_slave_config(struct dma_chan *chan,
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
- fsl_chan->fsc.dir = cfg->direction;
- if (cfg->direction == DMA_DEV_TO_MEM) {
- fsl_chan->fsc.dev_addr = cfg->src_addr;
- fsl_chan->fsc.addr_width = cfg->src_addr_width;
- fsl_chan->fsc.burst = cfg->src_maxburst;
- fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width);
- } else if (cfg->direction == DMA_MEM_TO_DEV) {
- fsl_chan->fsc.dev_addr = cfg->dst_addr;
- fsl_chan->fsc.addr_width = cfg->dst_addr_width;
- fsl_chan->fsc.burst = cfg->dst_maxburst;
- fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width);
- } else {
- return -EINVAL;
- }
+ memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg));
+
return 0;
}
@@ -370,7 +352,7 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
struct fsl_edma_desc *edesc = fsl_chan->edesc;
void __iomem *addr = fsl_chan->edma->membase;
u32 ch = fsl_chan->vchan.chan.chan_id;
- enum dma_transfer_direction dir = fsl_chan->fsc.dir;
+ enum dma_transfer_direction dir = edesc->dirn;
dma_addr_t cur_addr, dma_addr;
size_t len, size;
int i;
@@ -550,7 +532,7 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
u32 src_addr, dst_addr, last_sg, nbytes;
u16 soff, doff, iter;
- if (!is_slave_direction(fsl_chan->fsc.dir))
+ if (!is_slave_direction(direction))
return NULL;
sg_len = buf_len / period_len;
@@ -558,9 +540,21 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
if (!fsl_desc)
return NULL;
fsl_desc->iscyclic = true;
+ fsl_desc->dirn = direction;
dma_buf_next = dma_addr;
- nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
+ if (direction == DMA_MEM_TO_DEV) {
+ fsl_chan->attr =
+ fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width);
+ nbytes = fsl_chan->cfg.dst_addr_width *
+ fsl_chan->cfg.dst_maxburst;
+ } else {
+ fsl_chan->attr =
+ fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width);
+ nbytes = fsl_chan->cfg.src_addr_width *
+ fsl_chan->cfg.src_maxburst;
+ }
+
iter = period_len / nbytes;
for (i = 0; i < sg_len; i++) {
@@ -570,20 +564,20 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
/* get next sg's physical address */
last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
- if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
+ if (direction == DMA_MEM_TO_DEV) {
src_addr = dma_buf_next;
- dst_addr = fsl_chan->fsc.dev_addr;
- soff = fsl_chan->fsc.addr_width;
+ dst_addr = fsl_chan->cfg.dst_addr;
+ soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
- src_addr = fsl_chan->fsc.dev_addr;
+ src_addr = fsl_chan->cfg.src_addr;
dst_addr = dma_buf_next;
soff = 0;
- doff = fsl_chan->fsc.addr_width;
+ doff = fsl_chan->cfg.src_addr_width;
}
fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
- fsl_chan->fsc.attr, soff, nbytes, 0, iter,
+ fsl_chan->attr, soff, nbytes, 0, iter,
iter, doff, last_sg, true, false, true);
dma_buf_next += period_len;
}
@@ -603,42 +597,53 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
u16 soff, doff, iter;
int i;
- if (!is_slave_direction(fsl_chan->fsc.dir))
+ if (!is_slave_direction(direction))
return NULL;
fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
if (!fsl_desc)
return NULL;
fsl_desc->iscyclic = false;
+ fsl_desc->dirn = direction;
- nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
+ if (direction == DMA_MEM_TO_DEV) {
+ fsl_chan->attr =
+ fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width);
+ nbytes = fsl_chan->cfg.dst_addr_width *
+ fsl_chan->cfg.dst_maxburst;
+ } else {
+ fsl_chan->attr =
+ fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width);
+ nbytes = fsl_chan->cfg.src_addr_width *
+ fsl_chan->cfg.src_maxburst;
+ }
for_each_sg(sgl, sg, sg_len, i) {
/* get next sg's physical address */
last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
- if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
+ if (direction == DMA_MEM_TO_DEV) {
src_addr = sg_dma_address(sg);
- dst_addr = fsl_chan->fsc.dev_addr;
- soff = fsl_chan->fsc.addr_width;
+ dst_addr = fsl_chan->cfg.dst_addr;
+ soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
- src_addr = fsl_chan->fsc.dev_addr;
+ src_addr = fsl_chan->cfg.src_addr;
dst_addr = sg_dma_address(sg);
soff = 0;
- doff = fsl_chan->fsc.addr_width;
+ doff = fsl_chan->cfg.src_addr_width;
}
iter = sg_dma_len(sg) / nbytes;
if (i < sg_len - 1) {
last_sg = fsl_desc->tcd[(i + 1)].ptcd;
fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
- dst_addr, fsl_chan->fsc.attr, soff,
+ dst_addr, fsl_chan->attr, soff,
nbytes, 0, iter, iter, doff, last_sg,
false, false, true);
} else {
last_sg = 0;
fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
- dst_addr, fsl_chan->fsc.attr, soff,
+ dst_addr, fsl_chan->attr, soff,
nbytes, 0, iter, iter, doff, last_sg,
true, true, false);
}
^ permalink raw reply related
* [07/12] dmaengine: hsu: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Andy Shevchenko
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
drivers/dma/hsu/hsu.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c
index 202ffa9f7611..e06f20272fd7 100644
--- a/drivers/dma/hsu/hsu.c
+++ b/drivers/dma/hsu/hsu.c
@@ -348,10 +348,6 @@ static int hsu_dma_slave_config(struct dma_chan *chan,
{
struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan);
- /* Check if chan will be configured for slave transfers */
- if (!is_slave_direction(config->direction))
- return -EINVAL;
-
memcpy(&hsuc->config, config, sizeof(hsuc->config));
return 0;
^ permalink raw reply related
* [08/12] dmaengine: idma: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Andy Shevchenko
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
drivers/dma/idma64.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c
index 1fbf9cb9b742..40dc5d2ddf3d 100644
--- a/drivers/dma/idma64.c
+++ b/drivers/dma/idma64.c
@@ -408,10 +408,6 @@ static int idma64_slave_config(struct dma_chan *chan,
{
struct idma64_chan *idma64c = to_idma64_chan(chan);
- /* Check if chan will be configured for slave transfers */
- if (!is_slave_direction(config->direction))
- return -EINVAL;
-
memcpy(&idma64c->config, config, sizeof(idma64c->config));
convert_burst(&idma64c->config.src_maxburst);
^ permalink raw reply related
* [09/12] dmaengine: imx-dma: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Fabio Estevam
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Fabio Estevam <fabio.estevam@nxp.com>
drivers/dma/imx-dma.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 75b6ff0415ee..c2fff3f6c9ca 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -162,6 +162,7 @@ struct imxdma_channel {
bool enabled_2d;
int slot_2d;
unsigned int irq;
+ struct dma_slave_config config;
};
enum imx_dma_type {
@@ -675,14 +676,15 @@ static int imxdma_terminate_all(struct dma_chan *chan)
return 0;
}
-static int imxdma_config(struct dma_chan *chan,
- struct dma_slave_config *dmaengine_cfg)
+static int imxdma_config_write(struct dma_chan *chan,
+ struct dma_slave_config *dmaengine_cfg,
+ enum dma_transfer_direction direction)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
unsigned int mode = 0;
- if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) {
+ if (direction == DMA_DEV_TO_MEM) {
imxdmac->per_address = dmaengine_cfg->src_addr;
imxdmac->watermark_level = dmaengine_cfg->src_maxburst;
imxdmac->word_size = dmaengine_cfg->src_addr_width;
@@ -723,6 +725,16 @@ static int imxdma_config(struct dma_chan *chan,
return 0;
}
+static int imxdma_config(struct dma_chan *chan,
+ struct dma_slave_config *dmaengine_cfg)
+{
+ struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+
+ memcpy(&imxdmac->config, dmaengine_cfg, sizeof(*dmaengine_cfg));
+
+ return 0;
+}
+
static enum dma_status imxdma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate)
@@ -905,6 +917,8 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
desc->desc.callback = NULL;
desc->desc.callback_param = NULL;
+ imxdma_config_write(chan, &imxdmac->config, direction);
+
return &desc->desc;
}
^ permalink raw reply related
* [10/12] dmaengine: k3dma: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul, Zhangfei Gao
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
CC: Zhangfei Gao <zhangfei.gao@linaro.org>
drivers/dma/k3dma.c | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
index 6bfa217ed6d0..10e2e0a0735b 100644
--- a/drivers/dma/k3dma.c
+++ b/drivers/dma/k3dma.c
@@ -91,6 +91,7 @@ struct k3_dma_chan {
dma_addr_t dev_addr;
enum dma_status status;
bool cyclic;
+ struct dma_slave_config slave_config;
};
struct k3_dma_phy {
@@ -118,6 +119,10 @@ struct k3_dma_dev {
#define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave)
+static int k3_dma_config_write(struct dma_chan *chan,
+ enum dma_transfer_direction dir,
+ struct dma_slave_config *cfg);
+
static struct k3_dma_chan *to_k3_chan(struct dma_chan *chan)
{
return container_of(chan, struct k3_dma_chan, vc.chan);
@@ -542,6 +547,7 @@ static struct dma_async_tx_descriptor *k3_dma_prep_slave_sg(
if (!ds)
return NULL;
num = 0;
+ k3_dma_config_write(chan, dir, &c->slave_config);
for_each_sg(sgl, sg, sglen, i) {
addr = sg_dma_address(sg);
@@ -602,6 +608,7 @@ k3_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
avail = buf_len;
total = avail;
num = 0;
+ k3_dma_config_write(chan, dir, &c->slave_config);
if (period_len < modulo)
modulo = period_len;
@@ -642,13 +649,21 @@ static int k3_dma_config(struct dma_chan *chan,
struct dma_slave_config *cfg)
{
struct k3_dma_chan *c = to_k3_chan(chan);
+
+ memcpy(&c->slave_config, cfg, sizeof(*cfg));
+
+ return 0;
+}
+
+static int k3_dma_config_write(struct dma_chan *chan,
+ enum dma_transfer_direction dir,
+ struct dma_slave_config *cfg)
+{
+ struct k3_dma_chan *c = to_k3_chan(chan);
u32 maxburst = 0, val = 0;
enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
- if (cfg == NULL)
- return -EINVAL;
- c->dir = cfg->direction;
- if (c->dir == DMA_DEV_TO_MEM) {
+ if (dir == DMA_DEV_TO_MEM) {
c->ccfg = CX_CFG_DSTINCR;
c->dev_addr = cfg->src_addr;
maxburst = cfg->src_maxburst;
^ permalink raw reply related
* [11/12] dmaengine: ep93xx_dma: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-09-11 8:35 UTC (permalink / raw)
To: dmaengine; +Cc: Vinod Koul
dma_slave_config direction was marked as deprecated quite some
time back, remove the usage from this driver so that the field
can be removed
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/dma/ep93xx_dma.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index a15592383d4e..f674eb5fbbef 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -109,6 +109,9 @@
#define DMA_MAX_CHAN_DESCRIPTORS 32
struct ep93xx_dma_engine;
+static int ep93xx_dma_slave_config_write(struct dma_chan *chan,
+ enum dma_transfer_direction dir,
+ struct dma_slave_config *config);
/**
* struct ep93xx_dma_desc - EP93xx specific transaction descriptor
@@ -180,6 +183,7 @@ struct ep93xx_dma_chan {
struct list_head free_list;
u32 runtime_addr;
u32 runtime_ctrl;
+ struct dma_slave_config slave_config;
};
/**
@@ -1051,6 +1055,8 @@ ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL;
}
+ ep93xx_dma_slave_config_write(chan, dir, &edmac->slave_config);
+
first = NULL;
for_each_sg(sgl, sg, sg_len, i) {
size_t len = sg_dma_len(sg);
@@ -1136,6 +1142,8 @@ ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
return NULL;
}
+ ep93xx_dma_slave_config_write(chan, dir, &edmac->slave_config);
+
/* Split the buffer into period size chunks */
first = NULL;
for (offset = 0; offset < buf_len; offset += period_len) {
@@ -1227,6 +1235,17 @@ static int ep93xx_dma_slave_config(struct dma_chan *chan,
struct dma_slave_config *config)
{
struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+
+ memcpy(&edmac->slave_config, config, sizeof(*config));
+
+ return 0;
+}
+
+static int ep93xx_dma_slave_config_write(struct dma_chan *chan,
+ enum dma_transfer_direction dir,
+ struct dma_slave_config *config)
+{
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
enum dma_slave_buswidth width;
unsigned long flags;
u32 addr, ctrl;
@@ -1234,7 +1253,7 @@ static int ep93xx_dma_slave_config(struct dma_chan *chan,
if (!edmac->edma->m2m)
return -EINVAL;
- switch (config->direction) {
+ switch (dir) {
case DMA_DEV_TO_MEM:
width = config->src_addr_width;
addr = config->src_addr;
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox