* dmaengine: fsl-dpaa2-qdma: Add qDMA controller driver for Layerscape SoCs
From: Vinod Koul @ 2019-01-19 13:08 UTC (permalink / raw)
To: Peng Ma; +Cc: dan.j.williams, leoyang.li, jerry.huang, dmaengine, linux-kernel
On 25-12-18, 11:04, Peng Ma wrote:
> Add NXP Data Path Acceleration Architecture 2 (dpaa2) queue direct
> memory(qDMA) controller driver support.
> This module can be found on NXP LS2 SoCs.
>
> Signed-off-by: Peng Ma <peng.ma@nxp.com>
> ---
> drivers/dma/Kconfig | 2 +
> drivers/dma/Makefile | 1 +
> drivers/dma/fsl-dpaa2-qdma/Kconfig | 8 +
> drivers/dma/fsl-dpaa2-qdma/Makefile | 8 +
> drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c | 781 +++++++++++++++++++++++++++
> drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.h | 152 ++++++
> drivers/dma/fsl-dpaa2-qdma/dpdmai.c | 483 +++++++++++++++++
> drivers/dma/fsl-dpaa2-qdma/fsl_dpdmai.h | 524 ++++++++++++++++++
> drivers/dma/fsl-dpaa2-qdma/fsl_dpdmai_cmd.h | 197 +++++++
> 9 files changed, 2156 insertions(+), 0 deletions(-)
Sorry I cant review a patch that is this big. Please split and explain
why this is such a big driver...
^ permalink raw reply
* dmaengine: dmatest: wrap src & dst data into a struct
From: Vinod Koul @ 2019-01-19 13:07 UTC (permalink / raw)
To: Alexandru Ardelean; +Cc: dmaengine
Hi Alexandru,
On 26-11-18, 10:43, Alexandru Ardelean wrote:
> There's a bit too much un-wrapped & duplicated code that can be organized
> into some common logic/functions.
>
> This change wraps all the common parts between srcs & dsts into a
> `dmatest_data` struct. The purpose is to split the `dmatestfunc` into
> smaller chunks that are easier to follow & extend.
Thanks for the patch, this looks good but somehow seems to have slipped
by..
>
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> ---
> drivers/dma/dmatest.c | 257 ++++++++++++++++++++++--------------------
> 1 file changed, 133 insertions(+), 124 deletions(-)
>
> diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
> index e71aa1e3451c..c37c643e7d29 100644
> --- a/drivers/dma/dmatest.c
> +++ b/drivers/dma/dmatest.c
> @@ -166,15 +166,20 @@ struct dmatest_done {
> wait_queue_head_t *wait;
> };
>
> +struct dmatest_data {
> + u8 **raw;
> + u8 **aligned;
> + unsigned int cnt;
> + unsigned int off;
> +};
> +
> struct dmatest_thread {
> struct list_head node;
> struct dmatest_info *info;
> struct task_struct *task;
> struct dma_chan *chan;
> - u8 **srcs;
> - u8 **usrcs;
> - u8 **dsts;
> - u8 **udsts;
> + struct dmatest_data src;
> + struct dmatest_data dst;
> enum dma_transaction_type type;
> wait_queue_head_t done_wait;
> struct dmatest_done test_done;
> @@ -428,6 +433,53 @@ static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len)
> return dmatest_persec(runtime, len >> 10);
> }
>
> +static void __dmatest_free_test_data(struct dmatest_data *d, unsigned int cnt)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < cnt; i++)
> + kfree(d->raw[i]);
> +
> + kfree(d->aligned);
> + kfree(d->raw);
> +}
> +
> +static void dmatest_free_test_data(struct dmatest_data *d)
> +{
> + __dmatest_free_test_data(d, d->cnt);
> +}
why do we need a wrapper here?
> +
> +static int dmatest_alloc_test_data(struct dmatest_data *d,
> + unsigned int buf_size, u8 align)
> +{
> + unsigned int i = 0;
superfluous initialization
> + d->raw = kcalloc(d->cnt + 1, sizeof(u8 *), GFP_KERNEL);
> + if (!d->raw)
> + return -ENOMEM;
> +
> + d->aligned = kcalloc(d->cnt + 1, sizeof(u8 *), GFP_KERNEL);
> + if (!d->aligned)
> + goto err;
> +
> + for (i = 0; i < d->cnt; i++) {
> + d->raw[i] = kmalloc(buf_size + align, GFP_KERNEL);
> + if (!d->raw[i])
> + goto err;
> +
> + /* align to alignment restriction */
> + if (align)
> + d->aligned[i] = PTR_ALIGN(d->raw[i], align);
> + else
> + d->aligned[i] = d->raw[i];
> + }
> +
> + return 0;
> +err:
> + __dmatest_free_test_data(d, i);
> + return -ENOMEM;
> +}
> +
> /*
> * This function repeatedly tests DMA transfers of various lengths and
> * offsets for a given operation type until it is told to exit by
> @@ -458,8 +510,9 @@ static int dmatest_func(void *data)
> enum dma_ctrl_flags flags;
> u8 *pq_coefs = NULL;
> int ret;
> - int src_cnt;
> - int dst_cnt;
> + unsigned int buf_size;
> + struct dmatest_data *src;
> + struct dmatest_data *dst;
> int i;
> ktime_t ktime, start, diff;
> ktime_t filltime = 0;
> @@ -480,99 +533,64 @@ static int dmatest_func(void *data)
> params = &info->params;
> chan = thread->chan;
> dev = chan->device;
> + src = &thread->src;
> + dst = &thread->dst;
> if (thread->type == DMA_MEMCPY) {
> align = dev->copy_align;
> - src_cnt = dst_cnt = 1;
> + src->cnt = dst->cnt = 1;
> } else if (thread->type == DMA_MEMSET) {
> align = dev->fill_align;
> - src_cnt = dst_cnt = 1;
> + src->cnt = dst->cnt = 1;
> is_memset = true;
> } else if (thread->type == DMA_XOR) {
> /* force odd to ensure dst = src */
> - src_cnt = min_odd(params->xor_sources | 1, dev->max_xor);
> - dst_cnt = 1;
> + src->cnt = min_odd(params->xor_sources | 1, dev->max_xor);
> + dst->cnt = 1;
> align = dev->xor_align;
> } else if (thread->type == DMA_PQ) {
> /* force odd to ensure dst = src */
> - src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0));
> - dst_cnt = 2;
> + src->cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0));
> + dst->cnt = 2;
> align = dev->pq_align;
>
> pq_coefs = kmalloc(params->pq_sources + 1, GFP_KERNEL);
> if (!pq_coefs)
> goto err_thread_type;
>
> - for (i = 0; i < src_cnt; i++)
> + for (i = 0; i < src->cnt; i++)
> pq_coefs[i] = 1;
> } else
> goto err_thread_type;
>
> /* Check if buffer count fits into map count variable (u8) */
> - if ((src_cnt + dst_cnt) >= 255) {
> + if ((src->cnt + dst->cnt) >= 255) {
> pr_err("too many buffers (%d of 255 supported)\n",
> - src_cnt + dst_cnt);
> + src->cnt + dst->cnt);
> goto err_thread_type;
> }
>
> + buf_size = params->buf_size;
> if (1 << align > params->buf_size) {
> pr_err("%u-byte buffer too small for %d-byte alignment\n",
> params->buf_size, 1 << align);
> goto err_thread_type;
> }
>
> - thread->srcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL);
> - if (!thread->srcs)
> - goto err_srcs;
> -
> - thread->usrcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL);
> - if (!thread->usrcs)
> - goto err_usrcs;
> + if (dmatest_alloc_test_data(src, buf_size, align) < 0)
> + goto err_src;
>
> - for (i = 0; i < src_cnt; i++) {
> - thread->usrcs[i] = kmalloc(params->buf_size + align,
> - GFP_KERNEL);
> - if (!thread->usrcs[i])
> - goto err_srcbuf;
> -
> - /* align srcs to alignment restriction */
> - if (align)
> - thread->srcs[i] = PTR_ALIGN(thread->usrcs[i], align);
> - else
> - thread->srcs[i] = thread->usrcs[i];
> - }
> - thread->srcs[i] = NULL;
> -
> - thread->dsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL);
> - if (!thread->dsts)
> - goto err_dsts;
> -
> - thread->udsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL);
> - if (!thread->udsts)
> - goto err_udsts;
> -
> - for (i = 0; i < dst_cnt; i++) {
> - thread->udsts[i] = kmalloc(params->buf_size + align,
> - GFP_KERNEL);
> - if (!thread->udsts[i])
> - goto err_dstbuf;
> -
> - /* align dsts to alignment restriction */
> - if (align)
> - thread->dsts[i] = PTR_ALIGN(thread->udsts[i], align);
> - else
> - thread->dsts[i] = thread->udsts[i];
> - }
> - thread->dsts[i] = NULL;
> + if (dmatest_alloc_test_data(dst, buf_size, align) < 0)
> + goto err_dst;
>
> set_user_nice(current, 10);
>
> - srcs = kcalloc(src_cnt, sizeof(dma_addr_t), GFP_KERNEL);
> + srcs = kcalloc(src->cnt, sizeof(dma_addr_t), GFP_KERNEL);
> if (!srcs)
> - goto err_dstbuf;
> + goto err_srcs;
>
> - dma_pq = kcalloc(dst_cnt, sizeof(dma_addr_t), GFP_KERNEL);
> + dma_pq = kcalloc(dst->cnt, sizeof(dma_addr_t), GFP_KERNEL);
> if (!dma_pq)
> - goto err_srcs_array;
> + goto err_dma_pq;
>
> /*
> * src and dst buffers are freed by ourselves below
> @@ -585,7 +603,7 @@ static int dmatest_func(void *data)
> struct dma_async_tx_descriptor *tx = NULL;
> struct dmaengine_unmap_data *um;
> dma_addr_t *dsts;
> - unsigned int src_off, dst_off, len;
> + unsigned int len;
>
> total_tests++;
>
> @@ -601,59 +619,59 @@ static int dmatest_func(void *data)
> total_len += len;
>
> if (params->norandom) {
> - src_off = 0;
> - dst_off = 0;
> + src->off = 0;
> + dst->off = 0;
> } else {
> - src_off = dmatest_random() % (params->buf_size - len + 1);
> - dst_off = dmatest_random() % (params->buf_size - len + 1);
> + src->off = dmatest_random() % (params->buf_size - len + 1);
> + dst->off = dmatest_random() % (params->buf_size - len + 1);
>
> - src_off = (src_off >> align) << align;
> - dst_off = (dst_off >> align) << align;
> + src->off = (src->off >> align) << align;
> + dst->off = (dst->off >> align) << align;
> }
>
> if (!params->noverify) {
> start = ktime_get();
> - dmatest_init_srcs(thread->srcs, src_off, len,
> + dmatest_init_srcs(src->aligned, src->off, len,
> params->buf_size, is_memset);
> - dmatest_init_dsts(thread->dsts, dst_off, len,
> + dmatest_init_dsts(dst->aligned, dst->off, len,
> params->buf_size, is_memset);
>
> diff = ktime_sub(ktime_get(), start);
> filltime = ktime_add(filltime, diff);
> }
>
> - um = dmaengine_get_unmap_data(dev->dev, src_cnt + dst_cnt,
> + um = dmaengine_get_unmap_data(dev->dev, src->cnt + dst->cnt,
> GFP_KERNEL);
> if (!um) {
> failed_tests++;
> result("unmap data NULL", total_tests,
> - src_off, dst_off, len, ret);
> + src->off, dst->off, len, ret);
> continue;
> }
>
> um->len = params->buf_size;
> - for (i = 0; i < src_cnt; i++) {
> - void *buf = thread->srcs[i];
> + for (i = 0; i < src->cnt; i++) {
> + void *buf = src->aligned[i];
> struct page *pg = virt_to_page(buf);
> unsigned long pg_off = offset_in_page(buf);
>
> um->addr[i] = dma_map_page(dev->dev, pg, pg_off,
> um->len, DMA_TO_DEVICE);
> - srcs[i] = um->addr[i] + src_off;
> + srcs[i] = um->addr[i] + src->off;
> ret = dma_mapping_error(dev->dev, um->addr[i]);
> if (ret) {
> dmaengine_unmap_put(um);
> result("src mapping error", total_tests,
> - src_off, dst_off, len, ret);
> + src->off, dst->off, len, ret);
> failed_tests++;
> continue;
> }
> um->to_cnt++;
> }
> /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
> - dsts = &um->addr[src_cnt];
> - for (i = 0; i < dst_cnt; i++) {
> - void *buf = thread->dsts[i];
> + dsts = &um->addr[src->cnt];
> + for (i = 0; i < dst->cnt; i++) {
> + void *buf = dst->aligned[i];
> struct page *pg = virt_to_page(buf);
> unsigned long pg_off = offset_in_page(buf);
>
> @@ -663,7 +681,7 @@ static int dmatest_func(void *data)
> if (ret) {
> dmaengine_unmap_put(um);
> result("dst mapping error", total_tests,
> - src_off, dst_off, len, ret);
> + src->off, dst->off, len, ret);
> failed_tests++;
> continue;
> }
> @@ -672,30 +690,30 @@ static int dmatest_func(void *data)
>
> if (thread->type == DMA_MEMCPY)
> tx = dev->device_prep_dma_memcpy(chan,
> - dsts[0] + dst_off,
> + dsts[0] + dst->off,
> srcs[0], len, flags);
> else if (thread->type == DMA_MEMSET)
> tx = dev->device_prep_dma_memset(chan,
> - dsts[0] + dst_off,
> - *(thread->srcs[0] + src_off),
> + dsts[0] + dst->off,
> + *(src->aligned[0] + src->off),
> len, flags);
> else if (thread->type == DMA_XOR)
> tx = dev->device_prep_dma_xor(chan,
> - dsts[0] + dst_off,
> - srcs, src_cnt,
> + dsts[0] + dst->off,
> + srcs, src->cnt,
> len, flags);
> else if (thread->type == DMA_PQ) {
> - for (i = 0; i < dst_cnt; i++)
> - dma_pq[i] = dsts[i] + dst_off;
> + for (i = 0; i < dst->cnt; i++)
> + dma_pq[i] = dsts[i] + dst->off;
> tx = dev->device_prep_dma_pq(chan, dma_pq, srcs,
> - src_cnt, pq_coefs,
> + src->cnt, pq_coefs,
> len, flags);
> }
>
> if (!tx) {
> dmaengine_unmap_put(um);
> - result("prep error", total_tests, src_off,
> - dst_off, len, ret);
> + result("prep error", total_tests, src->off,
> + dst->off, len, ret);
> msleep(100);
> failed_tests++;
> continue;
> @@ -708,8 +726,8 @@ static int dmatest_func(void *data)
>
> if (dma_submit_error(cookie)) {
> dmaengine_unmap_put(um);
> - result("submit error", total_tests, src_off,
> - dst_off, len, ret);
> + result("submit error", total_tests, src->off,
> + dst->off, len, ret);
> msleep(100);
> failed_tests++;
> continue;
> @@ -724,58 +742,58 @@ static int dmatest_func(void *data)
> dmaengine_unmap_put(um);
>
> if (!done->done) {
> - result("test timed out", total_tests, src_off, dst_off,
> + result("test timed out", total_tests, src->off, dst->off,
> len, 0);
> failed_tests++;
> continue;
> } else if (status != DMA_COMPLETE) {
> result(status == DMA_ERROR ?
> "completion error status" :
> - "completion busy status", total_tests, src_off,
> - dst_off, len, ret);
> + "completion busy status", total_tests, src->off,
> + dst->off, len, ret);
> failed_tests++;
> continue;
> }
>
> if (params->noverify) {
> - verbose_result("test passed", total_tests, src_off,
> - dst_off, len, 0);
> + verbose_result("test passed", total_tests, src->off,
> + dst->off, len, 0);
> continue;
> }
>
> start = ktime_get();
> pr_debug("%s: verifying source buffer...\n", current->comm);
> - error_count = dmatest_verify(thread->srcs, 0, src_off,
> + error_count = dmatest_verify(src->aligned, 0, src->off,
> 0, PATTERN_SRC, true, is_memset);
> - error_count += dmatest_verify(thread->srcs, src_off,
> - src_off + len, src_off,
> + error_count += dmatest_verify(src->aligned, src->off,
> + src->off + len, src->off,
> PATTERN_SRC | PATTERN_COPY, true, is_memset);
> - error_count += dmatest_verify(thread->srcs, src_off + len,
> - params->buf_size, src_off + len,
> + error_count += dmatest_verify(src->aligned, src->off + len,
> + params->buf_size, src->off + len,
> PATTERN_SRC, true, is_memset);
>
> pr_debug("%s: verifying dest buffer...\n", current->comm);
> - error_count += dmatest_verify(thread->dsts, 0, dst_off,
> + error_count += dmatest_verify(dst->aligned, 0, dst->off,
> 0, PATTERN_DST, false, is_memset);
>
> - error_count += dmatest_verify(thread->dsts, dst_off,
> - dst_off + len, src_off,
> + error_count += dmatest_verify(dst->aligned, dst->off,
> + dst->off + len, src->off,
> PATTERN_SRC | PATTERN_COPY, false, is_memset);
>
> - error_count += dmatest_verify(thread->dsts, dst_off + len,
> - params->buf_size, dst_off + len,
> + error_count += dmatest_verify(dst->aligned, dst->off + len,
> + params->buf_size, dst->off + len,
> PATTERN_DST, false, is_memset);
>
> diff = ktime_sub(ktime_get(), start);
> comparetime = ktime_add(comparetime, diff);
>
> if (error_count) {
> - result("data error", total_tests, src_off, dst_off,
> + result("data error", total_tests, src->off, dst->off,
> len, error_count);
> failed_tests++;
> } else {
> - verbose_result("test passed", total_tests, src_off,
> - dst_off, len, 0);
> + verbose_result("test passed", total_tests, src->off,
> + dst->off, len, 0);
> }
> }
> ktime = ktime_sub(ktime_get(), ktime);
> @@ -785,22 +803,13 @@ static int dmatest_func(void *data)
>
> ret = 0;
> kfree(dma_pq);
> -err_srcs_array:
> +err_dma_pq:
> kfree(srcs);
> -err_dstbuf:
> - for (i = 0; thread->udsts[i]; i++)
> - kfree(thread->udsts[i]);
> - kfree(thread->udsts);
> -err_udsts:
> - kfree(thread->dsts);
> -err_dsts:
> -err_srcbuf:
> - for (i = 0; thread->usrcs[i]; i++)
> - kfree(thread->usrcs[i]);
> - kfree(thread->usrcs);
> -err_usrcs:
> - kfree(thread->srcs);
> err_srcs:
> + dmatest_free_test_data(dst);
> +err_dst:
> + dmatest_free_test_data(src);
> +err_src:
> kfree(pq_coefs);
> err_thread_type:
> pr_info("%s: summary %u tests, %u failures %llu iops %llu KB/s (%d)\n",
> --
> 2.17.1
It would also help review if things are moved in smaller chunks. I can
think of creating the common mem alloc and free routine by moving
existing code and then adding new structs..
^ permalink raw reply
* dmaengine: fsldma: Add 64-bit I/O accessors for powerpc64
From: Vinod Koul @ 2019-01-19 12:58 UTC (permalink / raw)
To: Peng Ma
Cc: Scott Wood, Leo Li, Zhang Wei, linuxppc-dev@lists.ozlabs.org,
dmaengine@vger.kernel.org, Wen He
On 24-12-18, 05:29, Peng Ma wrote:
> Hi Scott,
>
> Oh, I did not see the in_XX64/out_XX64 supported only __powerpc64__ just now.
> Thanks for your reminder.
Can you send the formal patch for this...
FWIW, fsl patches were not merged last cycle because of reported
regression...
>
> #ifdef __powerpc64__
>
> #ifdef __BIG_ENDIAN__
> DEF_MMIO_OUT_D(out_be64, 64, std);
> DEF_MMIO_IN_D(in_be64, 64, ld);
>
> /* There is no asm instructions for 64 bits reverse loads and stores */
> static inline u64 in_le64(const volatile u64 __iomem *addr)
> {
> return swab64(in_be64(addr));
> }
>
> static inline void out_le64(volatile u64 __iomem *addr, u64 val)
> {
> out_be64(addr, swab64(val));
> }
> #else
> DEF_MMIO_OUT_D(out_le64, 64, std);
> DEF_MMIO_IN_D(in_le64, 64, ld);
>
> /* There is no asm instructions for 64 bits reverse loads and stores */
> static inline u64 in_be64(const volatile u64 __iomem *addr)
> {
> return swab64(in_le64(addr));
> }
>
> static inline void out_be64(volatile u64 __iomem *addr, u64 val)
> {
> out_le64(addr, swab64(val));
> }
>
> #endif
> #endif /* __powerpc64__ */
>
> Best Regards,
> Peng
> >-----Original Message-----
> >From: Scott Wood <oss@buserror.net>
> >Sent: 2018年12月24日 12:46
> >To: Peng Ma <peng.ma@nxp.com>; Leo Li <leoyang.li@nxp.com>; Zhang Wei
> ><zw@zh-kernel.org>
> >Cc: linuxppc-dev@lists.ozlabs.org; dmaengine@vger.kernel.org; Wen He
> ><wen.he_1@nxp.com>
> >Subject: Re: [PATCH] dmaengine: fsldma: Add 64-bit I/O accessors for
> >powerpc64
> >
> >On Mon, 2018-12-24 at 03:42 +0000, Peng Ma wrote:
> >> Hi Scott,
> >>
> >> You are right, we should support powerpc64, so could I changed it as
> >> fallows:
> >>
> >> diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index
> >> 88db939..057babf 100644
> >> --- a/drivers/dma/fsldma.h
> >> +++ b/drivers/dma/fsldma.h
> >> @@ -202,35 +202,10 @@ struct fsldma_chan {
> >> #define fsl_iowrite32(v, p) out_le32(p, v)
> >> #define fsl_iowrite32be(v, p) out_be32(p, v)
> >>
> >> -#ifndef __powerpc64__
> >> -static u64 fsl_ioread64(const u64 __iomem *addr) -{
> >> - u32 fsl_addr = lower_32_bits(addr);
> >> - u64 fsl_addr_hi = (u64)in_le32((u32 *)(fsl_addr + 1)) << 32;
> >> -
> >> - return fsl_addr_hi | in_le32((u32 *)fsl_addr);
> >> -}
> >> -
> >> -static void fsl_iowrite64(u64 val, u64 __iomem *addr) -{
> >> - out_le32((u32 __iomem *)addr + 1, val >> 32);
> >> - out_le32((u32 __iomem *)addr, (u32)val);
> >> -}
> >> -
> >> -static u64 fsl_ioread64be(const u64 __iomem *addr) -{
> >> - u32 fsl_addr = lower_32_bits(addr);
> >> - u64 fsl_addr_hi = (u64)in_be32((u32 *)fsl_addr) << 32;
> >> -
> >> - return fsl_addr_hi | in_be32((u32 *)(fsl_addr + 1));
> >> -}
> >> -
> >> -static void fsl_iowrite64be(u64 val, u64 __iomem *addr) -{
> >> - out_be32((u32 __iomem *)addr, val >> 32);
> >> - out_be32((u32 __iomem *)addr + 1, (u32)val);
> >> -}
> >> -#endif
> >> +#define fsl_ioread64(p) in_le64(p)
> >> +#define fsl_ioread64be(p) in_be64(p)
> >> +#define fsl_iowrite64(v, p) out_le64(p, v)
> >> +#define fsl_iowrite64be(v, p) out_be64(p, v)
> >> #endif
> >
> >Then you'll break 32-bit, assuming those fake-it-with-two-32-bit-accesses
> >were actually needed.
> >
> >-Scott
> >
>
^ permalink raw reply
* dmaengine: fsl-edma: dma map slave device address
From: Angelo Dureghello @ 2019-01-18 21:50 UTC (permalink / raw)
To: Laurentiu Tudor
Cc: vkoul, dmaengine, linux-imx, linux-arm-kernel, linux-kernel,
iommu, robin.murphy
Hi Laurentiu,
On Fri, Jan 18, 2019 at 12:06:23PM +0200, Laurentiu Tudor wrote:
> This mapping needs to be created in order for slave dma transfers
> to work on systems with SMMU. The implementation mostly mimics the
> one in pl330 dma driver, authored by Robin Murphy.
>
> Signed-off-by: Laurentiu Tudor <laurentiu.tudor@nxp.com>
> Suggested-by: Robin Murphy <robin.murphy@arm.com>
> ---
> Original approach was to add the missing mappings in the i2c client driver,
> see here for discussion: https://patchwork.ozlabs.org/patch/1026013/
>
> drivers/dma/fsl-edma-common.c | 66 ++++++++++++++++++++++++++++++++---
> drivers/dma/fsl-edma-common.h | 4 +++
> drivers/dma/fsl-edma.c | 1 +
> drivers/dma/mcf-edma.c | 1 +
> 4 files changed, 68 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> index 8876c4c1bb2c..0e95ee24b6d4 100644
> --- a/drivers/dma/fsl-edma-common.c
> +++ b/drivers/dma/fsl-edma-common.c
> @@ -6,6 +6,7 @@
> #include <linux/dmapool.h>
> #include <linux/module.h>
> #include <linux/slab.h>
> +#include <linux/dma-mapping.h>
>
> #include "fsl-edma-common.h"
>
> @@ -173,12 +174,62 @@ int fsl_edma_resume(struct dma_chan *chan)
> }
> EXPORT_SYMBOL_GPL(fsl_edma_resume);
>
> +static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
> +{
> + if (fsl_chan->dma_dir != DMA_NONE)
> + dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
> + fsl_chan->dma_dev_addr,
> + fsl_chan->dma_dev_size,
> + fsl_chan->dma_dir, 0);
> + fsl_chan->dma_dir = DMA_NONE;
> +}
> +
> +static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
> + enum dma_transfer_direction dir)
> +{
> + struct device *dev = fsl_chan->vchan.chan.device->dev;
> + enum dma_data_direction dma_dir;
> + phys_addr_t addr = 0;
> + u32 size = 0;
> +
> + switch (dir) {
> + case DMA_MEM_TO_DEV:
> + dma_dir = DMA_FROM_DEVICE;
> + addr = fsl_chan->cfg.dst_addr;
> + size = fsl_chan->cfg.dst_maxburst;
> + break;
> + case DMA_DEV_TO_MEM:
> + dma_dir = DMA_TO_DEVICE;
> + addr = fsl_chan->cfg.src_addr;
> + size = fsl_chan->cfg.src_maxburst;
> + break;
> + default:
> + dma_dir = DMA_NONE;
> + break;
> + }
> +
> + /* Already mapped for this config? */
> + if (fsl_chan->dma_dir == dma_dir)
> + return true;
> +
> + fsl_edma_unprep_slave_dma(fsl_chan);
> +
> + fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0);
> + if (dma_mapping_error(dev, fsl_chan->dma_dev_addr))
> + return false;
> + fsl_chan->dma_dev_size = size;
> + fsl_chan->dma_dir = dma_dir;
> +
> + return true;
> +}
> +
> int fsl_edma_slave_config(struct dma_chan *chan,
> struct dma_slave_config *cfg)
> {
> struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
>
> memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg));
> + fsl_edma_unprep_slave_dma(fsl_chan);
>
> return 0;
> }
> @@ -378,6 +429,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
> if (!is_slave_direction(direction))
> return NULL;
>
> + if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
> + return NULL;
> +
> sg_len = buf_len / period_len;
> fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> if (!fsl_desc)
> @@ -409,11 +463,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
>
> if (direction == DMA_MEM_TO_DEV) {
> src_addr = dma_buf_next;
> - dst_addr = fsl_chan->cfg.dst_addr;
> + dst_addr = fsl_chan->dma_dev_addr;
> soff = fsl_chan->cfg.dst_addr_width;
> doff = 0;
> } else {
> - src_addr = fsl_chan->cfg.src_addr;
> + src_addr = fsl_chan->dma_dev_addr;
> dst_addr = dma_buf_next;
> soff = 0;
> doff = fsl_chan->cfg.src_addr_width;
> @@ -444,6 +498,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
> if (!is_slave_direction(direction))
> return NULL;
>
> + if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
> + return NULL;
> +
> fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> if (!fsl_desc)
> return NULL;
> @@ -468,11 +525,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
>
> if (direction == DMA_MEM_TO_DEV) {
> src_addr = sg_dma_address(sg);
> - dst_addr = fsl_chan->cfg.dst_addr;
> + dst_addr = fsl_chan->dma_dev_addr;
> soff = fsl_chan->cfg.dst_addr_width;
> doff = 0;
> } else {
> - src_addr = fsl_chan->cfg.src_addr;
> + src_addr = fsl_chan->dma_dev_addr;
> dst_addr = sg_dma_address(sg);
> soff = 0;
> doff = fsl_chan->cfg.src_addr_width;
> @@ -555,6 +612,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan)
> fsl_edma_chan_mux(fsl_chan, 0, false);
> fsl_chan->edesc = NULL;
> vchan_get_all_descriptors(&fsl_chan->vchan, &head);
> + fsl_edma_unprep_slave_dma(fsl_chan);
> spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
>
> vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
> diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
> index 8917e8865959..b435d8e1e3a1 100644
> --- a/drivers/dma/fsl-edma-common.h
> +++ b/drivers/dma/fsl-edma-common.h
> @@ -6,6 +6,7 @@
> #ifndef _FSL_EDMA_COMMON_H_
> #define _FSL_EDMA_COMMON_H_
>
> +#include <linux/dma-direction.h>
> #include "virt-dma.h"
>
> #define EDMA_CR_EDBG BIT(1)
> @@ -120,6 +121,9 @@ struct fsl_edma_chan {
> struct dma_slave_config cfg;
> u32 attr;
> struct dma_pool *tcd_pool;
> + dma_addr_t dma_dev_addr;
> + u32 dma_dev_size;
> + enum dma_data_direction dma_dir;
> };
>
> struct fsl_edma_desc {
> diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
> index 34d70112fcc9..75e8a7ba3a22 100644
> --- a/drivers/dma/fsl-edma.c
> +++ b/drivers/dma/fsl-edma.c
> @@ -254,6 +254,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
> fsl_chan->pm_state = RUNNING;
> fsl_chan->slave_id = 0;
> fsl_chan->idle = true;
> + fsl_chan->dma_dir = DMA_NONE;
> fsl_chan->vchan.desc_free = fsl_edma_free_desc;
> vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
>
> diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c
> index 5de1b07eddff..7de54b2fafdb 100644
> --- a/drivers/dma/mcf-edma.c
> +++ b/drivers/dma/mcf-edma.c
> @@ -214,6 +214,7 @@ static int mcf_edma_probe(struct platform_device *pdev)
> mcf_chan->edma = mcf_edma;
> mcf_chan->slave_id = i;
> mcf_chan->idle = true;
> + mcf_chan->dma_dir = DMA_NONE;
> mcf_chan->vchan.desc_free = fsl_edma_free_desc;
> vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev);
> iowrite32(0x0, ®s->tcd[i].csr);
> --
> 2.17.1
>
I tested this patch on:
- Vybrid VF50N (Toradex Colibri VF50)
- ColdFire mcf54415 (Sysam stmark2 board)
and dma still works properly.
Tested-by: Angelo Dureghello <angelo@sysam.it>
^ permalink raw reply
* dmaengine: fsl-edma: dma map slave device address
From: Laurentiu Tudor @ 2019-01-18 10:06 UTC (permalink / raw)
To: vkoul, dmaengine, linux-imx
Cc: linux-arm-kernel, linux-kernel, iommu, robin.murphy,
Laurentiu Tudor
This mapping needs to be created in order for slave dma transfers
to work on systems with SMMU. The implementation mostly mimics the
one in pl330 dma driver, authored by Robin Murphy.
Signed-off-by: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Suggested-by: Robin Murphy <robin.murphy@arm.com>
---
Original approach was to add the missing mappings in the i2c client driver,
see here for discussion: https://patchwork.ozlabs.org/patch/1026013/
drivers/dma/fsl-edma-common.c | 66 ++++++++++++++++++++++++++++++++---
drivers/dma/fsl-edma-common.h | 4 +++
drivers/dma/fsl-edma.c | 1 +
drivers/dma/mcf-edma.c | 1 +
4 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index 8876c4c1bb2c..0e95ee24b6d4 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -6,6 +6,7 @@
#include <linux/dmapool.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/dma-mapping.h>
#include "fsl-edma-common.h"
@@ -173,12 +174,62 @@ int fsl_edma_resume(struct dma_chan *chan)
}
EXPORT_SYMBOL_GPL(fsl_edma_resume);
+static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
+{
+ if (fsl_chan->dma_dir != DMA_NONE)
+ dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
+ fsl_chan->dma_dev_addr,
+ fsl_chan->dma_dev_size,
+ fsl_chan->dma_dir, 0);
+ fsl_chan->dma_dir = DMA_NONE;
+}
+
+static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
+ enum dma_transfer_direction dir)
+{
+ struct device *dev = fsl_chan->vchan.chan.device->dev;
+ enum dma_data_direction dma_dir;
+ phys_addr_t addr = 0;
+ u32 size = 0;
+
+ switch (dir) {
+ case DMA_MEM_TO_DEV:
+ dma_dir = DMA_FROM_DEVICE;
+ addr = fsl_chan->cfg.dst_addr;
+ size = fsl_chan->cfg.dst_maxburst;
+ break;
+ case DMA_DEV_TO_MEM:
+ dma_dir = DMA_TO_DEVICE;
+ addr = fsl_chan->cfg.src_addr;
+ size = fsl_chan->cfg.src_maxburst;
+ break;
+ default:
+ dma_dir = DMA_NONE;
+ break;
+ }
+
+ /* Already mapped for this config? */
+ if (fsl_chan->dma_dir == dma_dir)
+ return true;
+
+ fsl_edma_unprep_slave_dma(fsl_chan);
+
+ fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0);
+ if (dma_mapping_error(dev, fsl_chan->dma_dev_addr))
+ return false;
+ fsl_chan->dma_dev_size = size;
+ fsl_chan->dma_dir = dma_dir;
+
+ return true;
+}
+
int fsl_edma_slave_config(struct dma_chan *chan,
struct dma_slave_config *cfg)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg));
+ fsl_edma_unprep_slave_dma(fsl_chan);
return 0;
}
@@ -378,6 +429,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
if (!is_slave_direction(direction))
return NULL;
+ if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
+ return NULL;
+
sg_len = buf_len / period_len;
fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
if (!fsl_desc)
@@ -409,11 +463,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
if (direction == DMA_MEM_TO_DEV) {
src_addr = dma_buf_next;
- dst_addr = fsl_chan->cfg.dst_addr;
+ dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
- src_addr = fsl_chan->cfg.src_addr;
+ src_addr = fsl_chan->dma_dev_addr;
dst_addr = dma_buf_next;
soff = 0;
doff = fsl_chan->cfg.src_addr_width;
@@ -444,6 +498,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
if (!is_slave_direction(direction))
return NULL;
+ if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
+ return NULL;
+
fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
if (!fsl_desc)
return NULL;
@@ -468,11 +525,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
if (direction == DMA_MEM_TO_DEV) {
src_addr = sg_dma_address(sg);
- dst_addr = fsl_chan->cfg.dst_addr;
+ dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
- src_addr = fsl_chan->cfg.src_addr;
+ src_addr = fsl_chan->dma_dev_addr;
dst_addr = sg_dma_address(sg);
soff = 0;
doff = fsl_chan->cfg.src_addr_width;
@@ -555,6 +612,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan)
fsl_edma_chan_mux(fsl_chan, 0, false);
fsl_chan->edesc = NULL;
vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ fsl_edma_unprep_slave_dma(fsl_chan);
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
index 8917e8865959..b435d8e1e3a1 100644
--- a/drivers/dma/fsl-edma-common.h
+++ b/drivers/dma/fsl-edma-common.h
@@ -6,6 +6,7 @@
#ifndef _FSL_EDMA_COMMON_H_
#define _FSL_EDMA_COMMON_H_
+#include <linux/dma-direction.h>
#include "virt-dma.h"
#define EDMA_CR_EDBG BIT(1)
@@ -120,6 +121,9 @@ struct fsl_edma_chan {
struct dma_slave_config cfg;
u32 attr;
struct dma_pool *tcd_pool;
+ dma_addr_t dma_dev_addr;
+ u32 dma_dev_size;
+ enum dma_data_direction dma_dir;
};
struct fsl_edma_desc {
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 34d70112fcc9..75e8a7ba3a22 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -254,6 +254,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
fsl_chan->pm_state = RUNNING;
fsl_chan->slave_id = 0;
fsl_chan->idle = true;
+ fsl_chan->dma_dir = DMA_NONE;
fsl_chan->vchan.desc_free = fsl_edma_free_desc;
vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c
index 5de1b07eddff..7de54b2fafdb 100644
--- a/drivers/dma/mcf-edma.c
+++ b/drivers/dma/mcf-edma.c
@@ -214,6 +214,7 @@ static int mcf_edma_probe(struct platform_device *pdev)
mcf_chan->edma = mcf_edma;
mcf_chan->slave_id = i;
mcf_chan->idle = true;
+ mcf_chan->dma_dir = DMA_NONE;
mcf_chan->vchan.desc_free = fsl_edma_free_desc;
vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev);
iowrite32(0x0, ®s->tcd[i].csr);
^ permalink raw reply related
* dma: imx-sdma: pass ->dev to dma_alloc_coherent() API
From: Robin Gong @ 2019-01-18 7:41 UTC (permalink / raw)
To: Daniel Baluta, vkoul@kernel.org, dmaengine@vger.kernel.org
Cc: dl-linux-imx, kernel@pengutronix.de, dan.j.williams@intel.com,
linux-kernel@vger.kernel.org, S.j. Wang, Andy Duan
Reviewed-by: Robin Gong <yibin.gong@nxp.com>
> -----Original Message-----
> From: Daniel Baluta
> Sent: 2019年1月11日 22:30
> To: vkoul@kernel.org; dmaengine@vger.kernel.org
> Cc: dl-linux-imx <linux-imx@nxp.com>; kernel@pengutronix.de;
> dan.j.williams@intel.com; linux-kernel@vger.kernel.org; S.j. Wang
> <shengjiu.wang@nxp.com>; Andy Duan <fugang.duan@nxp.com>; Robin Gong
> <yibin.gong@nxp.com>; Daniel Baluta <daniel.baluta@nxp.com>
> Subject: [PATCH] dma: imx-sdma: pass ->dev to dma_alloc_coherent() API
>
> From: Andy Duan <fugang.duan@nxp.com>
>
> Pass ->dev to dma_alloc_coherent() API. We need this becase
> dma_alloc_coherent() makes use of dev parameter and receiving NULL will
> result in a crash.
>
> Signed-off-by: Andy Duan <fugang.duan@nxp.com>
> Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
> ---
> drivers/dma/imx-sdma.c | 15 ++++++++-------
> 1 file changed, 8 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index
> a2b0a0e71168..26133b78ced8 100644
> --- a/drivers/dma/imx-sdma.c
> +++ b/drivers/dma/imx-sdma.c
> @@ -677,7 +677,7 @@ static int sdma_load_script(struct sdma_engine
> *sdma, void *buf, int size,
> int ret;
> unsigned long flags;
>
> - buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL);
> + buf_virt = dma_alloc_coherent(sdma->dev, size, &buf_phys,
> GFP_KERNEL);
> if (!buf_virt) {
> return -ENOMEM;
> }
> @@ -696,7 +696,7 @@ static int sdma_load_script(struct sdma_engine
> *sdma, void *buf, int size,
>
> spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
>
> - dma_free_coherent(NULL, size, buf_virt, buf_phys);
> + dma_free_coherent(sdma->dev, size, buf_virt, buf_phys);
>
> return ret;
> }
> @@ -1182,7 +1182,7 @@ static int sdma_request_channel0(struct
> sdma_engine *sdma) {
> int ret = -EBUSY;
>
> - sdma->bd0 = dma_zalloc_coherent(NULL, PAGE_SIZE, &sdma->bd0_phys,
> + sdma->bd0 = dma_zalloc_coherent(sdma->dev, PAGE_SIZE,
> &sdma->bd0_phys,
> GFP_NOWAIT);
> if (!sdma->bd0) {
> ret = -ENOMEM;
> @@ -1205,8 +1205,8 @@ static int sdma_alloc_bd(struct sdma_desc *desc)
> u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor);
> int ret = 0;
>
> - desc->bd = dma_zalloc_coherent(NULL, bd_size, &desc->bd_phys,
> - GFP_NOWAIT);
> + desc->bd = dma_zalloc_coherent(desc->sdmac->sdma->dev, bd_size,
> + &desc->bd_phys, GFP_NOWAIT);
> if (!desc->bd) {
> ret = -ENOMEM;
> goto out;
> @@ -1219,7 +1219,8 @@ static void sdma_free_bd(struct sdma_desc *desc)
> {
> u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor);
>
> - dma_free_coherent(NULL, bd_size, desc->bd, desc->bd_phys);
> + dma_free_coherent(desc->sdmac->sdma->dev, bd_size, desc->bd,
> + desc->bd_phys);
> }
>
> static void sdma_desc_free(struct virt_dma_desc *vd) @@ -1842,7 +1843,7
> @@ static int sdma_init(struct sdma_engine *sdma)
> /* Be sure SDMA has not started yet */
> writel_relaxed(0, sdma->regs + SDMA_H_C0PTR);
>
> - sdma->channel_control = dma_alloc_coherent(NULL,
> + sdma->channel_control = dma_alloc_coherent(sdma->dev,
> MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) +
> sizeof(struct sdma_context_data),
> &ccb_phys, GFP_KERNEL);
> --
> 2.17.1
^ permalink raw reply
* [v10,3/3] dt-bindings: dma: uart: rename binding
From: Long Cheng @ 2019-01-18 3:10 UTC (permalink / raw)
To: Vinod Koul, Randy Dunlap, Rob Herring, Mark Rutland, Ryder Lee,
Sean Wang, Nicolas Boichat
Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
Sean Wang, dmaengine, devicetree, linux-arm-kernel,
linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
Yingjoe Chen, YT Shen, Zhenbao Liu, Long Cheng
The filename matches mtk-uart-apdma.c.
So using "mtk-uart-apdma.txt" should be better.
Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
.../devicetree/bindings/dma/8250_mtk_dma.txt | 33 --------------------
.../devicetree/bindings/dma/mtk-uart-apdma.txt | 33 ++++++++++++++++++++
2 files changed, 33 insertions(+), 33 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
create mode 100644 Documentation/devicetree/bindings/dma/mtk-uart-apdma.txt
diff --git a/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt b/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
deleted file mode 100644
index 3fe0961..0000000
--- a/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-* Mediatek UART APDMA Controller
-
-Required properties:
-- compatible should contain:
- * "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
- * "mediatek,mt6577-uart-dma" for MT6577 and all of the above
-
-- reg: The base address of the APDMA register bank.
-
-- interrupts: A single interrupt specifier.
-
-- clocks : Must contain an entry for each entry in clock-names.
- See ../clocks/clock-bindings.txt for details.
-- clock-names: The APDMA clock for register accesses
-
-Examples:
-
- apdma: dma-controller@11000380 {
- compatible = "mediatek,mt2712-uart-dma";
- reg = <0 0x11000380 0 0x400>;
- interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 65 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 66 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 67 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 68 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 69 IRQ_TYPE_LEVEL_LOW>,
- <GIC_SPI 70 IRQ_TYPE_LEVEL_LOW>;
- clocks = <&pericfg CLK_PERI_AP_DMA>;
- clock-names = "apdma";
- #dma-cells = <1>;
- };
-
diff --git a/Documentation/devicetree/bindings/dma/mtk-uart-apdma.txt b/Documentation/devicetree/bindings/dma/mtk-uart-apdma.txt
new file mode 100644
index 0000000..3fe0961
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/mtk-uart-apdma.txt
@@ -0,0 +1,33 @@
+* Mediatek UART APDMA Controller
+
+Required properties:
+- compatible should contain:
+ * "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
+ * "mediatek,mt6577-uart-dma" for MT6577 and all of the above
+
+- reg: The base address of the APDMA register bank.
+
+- interrupts: A single interrupt specifier.
+
+- clocks : Must contain an entry for each entry in clock-names.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names: The APDMA clock for register accesses
+
+Examples:
+
+ apdma: dma-controller@11000380 {
+ compatible = "mediatek,mt2712-uart-dma";
+ reg = <0 0x11000380 0 0x400>;
+ interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 65 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 66 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 67 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 68 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 69 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 70 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&pericfg CLK_PERI_AP_DMA>;
+ clock-names = "apdma";
+ #dma-cells = <1>;
+ };
+
^ permalink raw reply related
* [v10,2/3] arm: dts: mt2712: add uart APDMA to device tree
From: Long Cheng @ 2019-01-18 3:10 UTC (permalink / raw)
To: Vinod Koul, Randy Dunlap, Rob Herring, Mark Rutland, Ryder Lee,
Sean Wang, Nicolas Boichat
Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
Sean Wang, dmaengine, devicetree, linux-arm-kernel,
linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
Yingjoe Chen, YT Shen, Zhenbao Liu, Long Cheng
1. add uart APDMA controller device node
2. add uart 0/1/2/3/4/5 DMA function
Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
arch/arm64/boot/dts/mediatek/mt2712e.dtsi | 51 +++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt2712e.dtsi b/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
index ee627a7..3469a6c 100644
--- a/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
@@ -298,6 +298,9 @@
interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW>;
clocks = <&baud_clk>, <&sys_clk>;
clock-names = "baud", "bus";
+ dmas = <&apdma 10
+ &apdma 11>;
+ dma-names = "tx", "rx";
status = "disabled";
};
@@ -346,6 +349,39 @@
(GIC_CPU_MASK_RAW(0x13) | IRQ_TYPE_LEVEL_HIGH)>;
};
+ apdma: dma-controller@11000400 {
+ compatible = "mediatek,mt2712-uart-dma",
+ "mediatek,mt6577-uart-dma";
+ reg = <0 0x11000400 0 0x80>,
+ <0 0x11000480 0 0x80>,
+ <0 0x11000500 0 0x80>,
+ <0 0x11000580 0 0x80>,
+ <0 0x11000600 0 0x80>,
+ <0 0x11000680 0 0x80>,
+ <0 0x11000700 0 0x80>,
+ <0 0x11000780 0 0x80>,
+ <0 0x11000800 0 0x80>,
+ <0 0x11000880 0 0x80>,
+ <0 0x11000900 0 0x80>,
+ <0 0x11000980 0 0x80>;
+ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 105 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 106 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 107 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 108 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 109 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 110 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 112 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 114 IRQ_TYPE_LEVEL_LOW>;
+ dma-requests = <12>;
+ clocks = <&pericfg CLK_PERI_AP_DMA>;
+ clock-names = "apdma";
+ #dma-cells = <1>;
+ };
+
auxadc: adc@11001000 {
compatible = "mediatek,mt2712-auxadc";
reg = <0 0x11001000 0 0x1000>;
@@ -362,6 +398,9 @@
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
clocks = <&baud_clk>, <&sys_clk>;
clock-names = "baud", "bus";
+ dmas = <&apdma 0
+ &apdma 1>;
+ dma-names = "tx", "rx";
status = "disabled";
};
@@ -372,6 +411,9 @@
interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
clocks = <&baud_clk>, <&sys_clk>;
clock-names = "baud", "bus";
+ dmas = <&apdma 2
+ &apdma 3>;
+ dma-names = "tx", "rx";
status = "disabled";
};
@@ -382,6 +424,9 @@
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_LOW>;
clocks = <&baud_clk>, <&sys_clk>;
clock-names = "baud", "bus";
+ dmas = <&apdma 4
+ &apdma 5>;
+ dma-names = "tx", "rx";
status = "disabled";
};
@@ -392,6 +437,9 @@
interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_LOW>;
clocks = <&baud_clk>, <&sys_clk>;
clock-names = "baud", "bus";
+ dmas = <&apdma 6
+ &apdma 7>;
+ dma-names = "tx", "rx";
status = "disabled";
};
@@ -402,6 +450,9 @@
interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_LOW>;
clocks = <&baud_clk>, <&sys_clk>;
clock-names = "baud", "bus";
+ dmas = <&apdma 8
+ &apdma 9>;
+ dma-names = "tx", "rx";
status = "disabled";
};
^ permalink raw reply related
* [v10,1/3] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support
From: Long Cheng @ 2019-01-18 3:10 UTC (permalink / raw)
To: Vinod Koul, Randy Dunlap, Rob Herring, Mark Rutland, Ryder Lee,
Sean Wang, Nicolas Boichat
Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
Sean Wang, dmaengine, devicetree, linux-arm-kernel,
linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
Yingjoe Chen, YT Shen, Zhenbao Liu, Long Cheng
In DMA engine framework, add 8250 uart dma to support MediaTek uart.
If MediaTek uart enabled(SERIAL_8250_MT6577), and want to improve
the performance, can enable the function.
Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
drivers/dma/mediatek/Kconfig | 11 +
drivers/dma/mediatek/Makefile | 1 +
drivers/dma/mediatek/mtk-uart-apdma.c | 669 +++++++++++++++++++++++++++++++++
3 files changed, 681 insertions(+)
create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c
diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig
index 680fc05..ac49eb6 100644
--- a/drivers/dma/mediatek/Kconfig
+++ b/drivers/dma/mediatek/Kconfig
@@ -24,3 +24,14 @@ config MTK_CQDMA
This controller provides the channels which is dedicated to
memory-to-memory transfer to offload from CPU.
+
+config MTK_UART_APDMA
+ tristate "MediaTek SoCs APDMA support for UART"
+ depends on OF && SERIAL_8250_MT6577
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Support for the UART DMA engine found on MediaTek MTK SoCs.
+ When SERIAL_8250_MT6577 is enabled, and if you want to use DMA,
+ you can enable the config. The DMA engine can only be used
+ with MediaTek SoCs.
diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile
index 41bb381..61a6d29 100644
--- a/drivers/dma/mediatek/Makefile
+++ b/drivers/dma/mediatek/Makefile
@@ -1,2 +1,3 @@
+obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o
obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o
diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c
new file mode 100644
index 0000000..427db69
--- /dev/null
+++ b/drivers/dma/mediatek/mtk-uart-apdma.c
@@ -0,0 +1,669 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek Uart APDMA driver.
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Long Cheng <long.cheng@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "../virt-dma.h"
+
+/* The default number of virtual channel */
+#define MTK_UART_APDMA_NR_VCHANS 8
+
+#define VFF_EN_B BIT(0)
+#define VFF_STOP_B BIT(0)
+#define VFF_FLUSH_B BIT(0)
+#define VFF_4G_SUPPORT_B BIT(0)
+#define VFF_RX_INT_EN0_B BIT(0) /* rx valid size >= vff thre */
+#define VFF_RX_INT_EN1_B BIT(1)
+#define VFF_TX_INT_EN_B BIT(0) /* tx left size >= vff thre */
+#define VFF_WARM_RST_B BIT(0)
+#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1))
+#define VFF_TX_INT_CLR_B 0
+#define VFF_STOP_CLR_B 0
+#define VFF_INT_EN_CLR_B 0
+#define VFF_4G_SUPPORT_CLR_B 0
+
+/* interrupt trigger level for tx */
+#define VFF_TX_THRE(n) ((n) * 7 / 8)
+/* interrupt trigger level for rx */
+#define VFF_RX_THRE(n) ((n) * 3 / 4)
+
+#define VFF_RING_SIZE 0xffffU
+/* invert this bit when wrap ring head again */
+#define VFF_RING_WRAP 0x10000U
+
+#define VFF_INT_FLAG 0x00
+#define VFF_INT_EN 0x04
+#define VFF_EN 0x08
+#define VFF_RST 0x0c
+#define VFF_STOP 0x10
+#define VFF_FLUSH 0x14
+#define VFF_ADDR 0x1c
+#define VFF_LEN 0x24
+#define VFF_THRE 0x28
+#define VFF_WPT 0x2c
+#define VFF_RPT 0x30
+/* TX: the buffer size HW can read. RX: the buffer size SW can read. */
+#define VFF_VALID_SIZE 0x3c
+/* TX: the buffer size SW can write. RX: the buffer size HW can write. */
+#define VFF_LEFT_SIZE 0x40
+#define VFF_DEBUG_STATUS 0x50
+#define VFF_4G_SUPPORT 0x54
+
+struct mtk_uart_apdmadev {
+ struct dma_device ddev;
+ struct clk *clk;
+ bool support_33bits;
+ unsigned int dma_requests;
+ unsigned int *dma_irq;
+};
+
+struct mtk_uart_apdma_desc {
+ struct virt_dma_desc vd;
+
+ unsigned int avail_len;
+};
+
+struct mtk_chan {
+ struct virt_dma_chan vc;
+ struct dma_slave_config cfg;
+ void __iomem *base;
+ struct mtk_uart_apdma_desc *desc;
+
+ enum dma_transfer_direction dir;
+
+ bool requested;
+
+ unsigned int rx_status;
+};
+
+static inline struct mtk_uart_apdmadev *
+to_mtk_uart_apdma_dev(struct dma_device *d)
+{
+ return container_of(d, struct mtk_uart_apdmadev, ddev);
+}
+
+static inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct mtk_chan, vc.chan);
+}
+
+static inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc
+ (struct dma_async_tx_descriptor *t)
+{
+ return container_of(t, struct mtk_uart_apdma_desc, vd.tx);
+}
+
+static void mtk_uart_apdma_write(struct mtk_chan *c,
+ unsigned int reg, unsigned int val)
+{
+ writel(val, c->base + reg);
+}
+
+static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg)
+{
+ return readl(c->base + reg);
+}
+
+static void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd)
+{
+ struct dma_chan *chan = vd->tx.chan;
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ kfree(c->desc);
+}
+
+static void mtk_uart_apdma_start_tx(struct mtk_chan *c)
+{
+ unsigned int len, send, left, wpt, d_wpt, tmp;
+ int ret;
+
+ left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE);
+ if (!left) {
+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+ return;
+ }
+
+ /* Wait 1sec for flush, can't sleep */
+ ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
+ tmp != VFF_FLUSH_B, 0, 1000000);
+ if (ret)
+ dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n",
+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
+
+ send = min_t(unsigned int, left, c->desc->avail_len);
+ wpt = mtk_uart_apdma_read(c, VFF_WPT);
+ len = mtk_uart_apdma_read(c, VFF_LEN);
+
+ d_wpt = wpt + send;
+ if ((d_wpt & VFF_RING_SIZE) >= len) {
+ d_wpt = d_wpt - len;
+ d_wpt = d_wpt ^ VFF_RING_WRAP;
+ }
+ mtk_uart_apdma_write(c, VFF_WPT, d_wpt);
+
+ c->desc->avail_len -= send;
+
+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+ if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U)
+ mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
+}
+
+static void mtk_uart_apdma_start_rx(struct mtk_chan *c)
+{
+ struct mtk_uart_apdma_desc *d = c->desc;
+ unsigned int len, wg, rg;
+ int cnt;
+
+ if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) ||
+ !d || !vchan_next_desc(&c->vc))
+ return;
+
+ len = mtk_uart_apdma_read(c, VFF_LEN);
+ rg = mtk_uart_apdma_read(c, VFF_RPT);
+ wg = mtk_uart_apdma_read(c, VFF_WPT);
+ cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE);
+ /*
+ * The buffer is ring buffer. If wrap bit different,
+ * represents the start of the next cycle for WPT
+ */
+ if ((rg ^ wg) & VFF_RING_WRAP)
+ cnt += len;
+
+ c->rx_status = cnt;
+ mtk_uart_apdma_write(c, VFF_RPT, wg);
+
+ list_del(&d->vd.node);
+ vchan_cookie_complete(&d->vd);
+}
+
+static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id)
+{
+ struct dma_chan *chan = (struct dma_chan *)dev_id;
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdma_desc *d;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (c->dir == DMA_DEV_TO_MEM) {
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
+ mtk_uart_apdma_start_rx(c);
+ } else if (c->dir == DMA_MEM_TO_DEV) {
+ d = c->desc;
+
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
+
+ if (d->avail_len != 0U) {
+ mtk_uart_apdma_start_tx(c);
+ } else {
+ list_del(&d->vd.node);
+ vchan_cookie_complete(&d->vd);
+ }
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ unsigned int tmp;
+ int ret;
+
+ pm_runtime_get_sync(mtkd->ddev.dev);
+
+ mtk_uart_apdma_write(c, VFF_ADDR, 0);
+ mtk_uart_apdma_write(c, VFF_THRE, 0);
+ mtk_uart_apdma_write(c, VFF_LEN, 0);
+ mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B);
+
+ ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
+ if (ret) {
+ dev_err(chan->device->dev, "dma reset: fail, timeout\n");
+ return ret;
+ }
+
+ if (!c->requested) {
+ c->requested = true;
+ ret = request_irq(mtkd->dma_irq[chan->chan_id],
+ mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE,
+ KBUILD_MODNAME, chan);
+ if (ret < 0) {
+ dev_err(chan->device->dev, "Can't request dma IRQ\n");
+ return -EINVAL;
+ }
+ }
+
+ if (mtkd->support_33bits)
+ mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
+
+ return ret;
+}
+
+static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan)
+{
+ struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ if (c->requested) {
+ c->requested = false;
+ free_irq(mtkd->dma_irq[chan->chan_id], chan);
+ }
+
+ tasklet_kill(&c->vc.task);
+
+ vchan_free_chan_resources(&c->vc);
+
+ pm_runtime_put_sync(mtkd->ddev.dev);
+}
+
+static enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ enum dma_status ret;
+ unsigned long flags;
+
+ if (!txstate)
+ return DMA_ERROR;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (ret == DMA_IN_PROGRESS) {
+ c->rx_status = mtk_uart_apdma_read(c, VFF_RPT) & VFF_RING_SIZE;
+ dma_set_residue(txstate, c->rx_status);
+ } else if (ret == DMA_COMPLETE && c->dir == DMA_DEV_TO_MEM) {
+ dma_set_residue(txstate, c->rx_status);
+ } else {
+ dma_set_residue(txstate, 0);
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return ret;
+}
+
+static void mtk_uart_apdma_config_write(struct dma_chan *chan,
+ struct dma_slave_config *cfg,
+ enum dma_transfer_direction dir)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdmadev *mtkd =
+ to_mtk_uart_apdma_dev(c->vc.chan.device);
+ unsigned int tmp;
+
+ if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B)
+ return;
+
+ c->dir = dir;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ tmp = cfg->src_addr_width * 1024;
+
+ mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr);
+ mtk_uart_apdma_write(c, VFF_LEN, tmp);
+ mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp));
+ mtk_uart_apdma_write(c, VFF_INT_EN,
+ VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B);
+ mtk_uart_apdma_write(c, VFF_RPT, 0);
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
+ } else if (dir == DMA_MEM_TO_DEV) {
+ tmp = cfg->dst_addr_width * 1024;
+
+ mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr);
+ mtk_uart_apdma_write(c, VFF_LEN, tmp);
+ mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp));
+ mtk_uart_apdma_write(c, VFF_WPT, 0);
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
+ }
+
+ mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B);
+
+ if (mtkd->support_33bits)
+ mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
+
+ if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B)
+ dev_err(chan->device->dev, "dir[%d] fail\n", dir);
+}
+
+/*
+ * dmaengine_prep_slave_single will call the function. and sglen is 1.
+ * 8250 uart using one ring buffer, and deal with one sg.
+ */
+static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg
+ (struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sglen, enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdma_desc *d;
+
+ if (!is_slave_direction(dir))
+ return NULL;
+
+ mtk_uart_apdma_config_write(chan, &c->cfg, dir);
+
+ /* Now allocate and setup the descriptor */
+ d = kzalloc(sizeof(*d), GFP_ATOMIC);
+ if (!d)
+ return NULL;
+
+ /* sglen is 1 */
+ d->avail_len = sg_dma_len(sgl);
+
+ return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static void mtk_uart_apdma_issue_pending(struct dma_chan *chan)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct virt_dma_desc *vd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (vchan_issue_pending(&c->vc)) {
+ vd = vchan_next_desc(&c->vc);
+ c->desc = to_mtk_uart_apdma_desc(&vd->tx);
+ }
+
+ if (c->dir == DMA_DEV_TO_MEM)
+ mtk_uart_apdma_start_rx(c);
+ else if (c->dir == DMA_MEM_TO_DEV)
+ mtk_uart_apdma_start_tx(c);
+
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static int mtk_uart_apdma_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ memcpy(&c->cfg, config, sizeof(*config));
+
+ return 0;
+}
+
+static int mtk_uart_apdma_terminate_all(struct dma_chan *chan)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ unsigned long flags;
+ unsigned int tmp;
+ int ret;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+
+ mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
+ /* Wait 1sec for flush, can't sleep */
+ ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
+ tmp != VFF_FLUSH_B, 0, 1000000);
+ if (ret)
+ dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n",
+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
+
+ /* set stop as 1 -> wait until en is 0 -> set stop as 0 */
+ mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B);
+ ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
+ if (ret)
+ dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n",
+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
+
+ mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B);
+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
+
+ if (c->dir == DMA_DEV_TO_MEM)
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
+ else if (c->dir == DMA_MEM_TO_DEV)
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
+
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return 0;
+}
+
+static int mtk_uart_apdma_device_pause(struct dma_chan *chan)
+{
+ /* just for check caps pass */
+ return 0;
+}
+
+static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd)
+{
+ while (!list_empty(&mtkd->ddev.channels)) {
+ struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels,
+ struct mtk_chan, vc.chan.device_node);
+
+ list_del(&c->vc.chan.device_node);
+ tasklet_kill(&c->vc.task);
+ }
+}
+
+static const struct of_device_id mtk_uart_apdma_match[] = {
+ { .compatible = "mediatek,mt6577-uart-dma", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match);
+
+static int mtk_uart_apdma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mtk_uart_apdmadev *mtkd;
+ struct resource *res;
+ struct mtk_chan *c;
+ int bit_mask = 32, rc;
+ unsigned int i;
+
+ mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
+ if (!mtkd)
+ return -ENOMEM;
+
+ mtkd->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mtkd->clk)) {
+ dev_err(&pdev->dev, "No clock specified\n");
+ rc = PTR_ERR(mtkd->clk);
+ return rc;
+ }
+
+ if (of_property_read_bool(np, "dma-33bits"))
+ mtkd->support_33bits = true;
+
+ if (mtkd->support_33bits)
+ bit_mask = 33;
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask));
+ if (rc)
+ return rc;
+
+ dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask);
+ mtkd->ddev.device_alloc_chan_resources =
+ mtk_uart_apdma_alloc_chan_resources;
+ mtkd->ddev.device_free_chan_resources =
+ mtk_uart_apdma_free_chan_resources;
+ mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status;
+ mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending;
+ mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg;
+ mtkd->ddev.device_config = mtk_uart_apdma_slave_config;
+ mtkd->ddev.device_pause = mtk_uart_apdma_device_pause;
+ mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all;
+ mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+ mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+ mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+ mtkd->ddev.dev = &pdev->dev;
+ INIT_LIST_HEAD(&mtkd->ddev.channels);
+
+ mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS;
+ if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) {
+ dev_info(&pdev->dev,
+ "Using %u as missing dma-requests property\n",
+ MTK_UART_APDMA_NR_VCHANS);
+ }
+
+ mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests,
+ sizeof(*mtkd->dma_irq), GFP_KERNEL);
+ if (!mtkd->dma_irq)
+ return -ENOMEM;
+
+ for (i = 0; i < mtkd->dma_requests; i++) {
+ c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ rc = -ENODEV;
+ goto err_no_dma;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ rc = -ENODEV;
+ goto err_no_dma;
+ }
+
+ c->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(c->base)) {
+ rc = PTR_ERR(c->base);
+ goto err_no_dma;
+ }
+ c->requested = false;
+ c->vc.desc_free = mtk_uart_apdma_desc_free;
+ vchan_init(&c->vc, &mtkd->ddev);
+
+ mtkd->dma_irq[i] = platform_get_irq(pdev, i);
+ if ((int)mtkd->dma_irq[i] < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i);
+ rc = -EINVAL;
+ goto err_no_dma;
+ }
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+
+ rc = dma_async_device_register(&mtkd->ddev);
+ if (rc)
+ goto rpm_disable;
+
+ platform_set_drvdata(pdev, mtkd);
+
+ /* Device-tree DMA controller registration */
+ rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd);
+ if (rc)
+ goto dma_remove;
+
+ return rc;
+
+dma_remove:
+ dma_async_device_unregister(&mtkd->ddev);
+rpm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_no_dma:
+ mtk_uart_apdma_free(mtkd);
+ return rc;
+}
+
+static int mtk_uart_apdma_remove(struct platform_device *pdev)
+{
+ struct mtk_uart_apdmadev *mtkd = platform_get_drvdata(pdev);
+
+ if (pdev->dev.of_node)
+ of_dma_controller_free(pdev->dev.of_node);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ dma_async_device_unregister(&mtkd->ddev);
+ mtk_uart_apdma_free(mtkd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_uart_apdma_suspend(struct device *dev)
+{
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ clk_disable_unprepare(mtkd->clk);
+
+ return 0;
+}
+
+static int mtk_uart_apdma_resume(struct device *dev)
+{
+ int ret;
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev)) {
+ ret = clk_prepare_enable(mtkd->clk);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int mtk_uart_apdma_runtime_suspend(struct device *dev)
+{
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(mtkd->clk);
+
+ return 0;
+}
+
+static int mtk_uart_apdma_runtime_resume(struct device *dev)
+{
+ int ret;
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ ret = clk_prepare_enable(mtkd->clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops mtk_uart_apdma_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume)
+ SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend,
+ mtk_uart_apdma_runtime_resume, NULL)
+};
+
+static struct platform_driver mtk_uart_apdma_driver = {
+ .probe = mtk_uart_apdma_probe,
+ .remove = mtk_uart_apdma_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &mtk_uart_apdma_pm_ops,
+ .of_match_table = of_match_ptr(mtk_uart_apdma_match),
+ },
+};
+
+module_platform_driver(mtk_uart_apdma_driver);
+
+MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver");
+MODULE_AUTHOR("Long Cheng <long.cheng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
^ permalink raw reply related
* [2/8,v4] Documentation: bindings: dma: Add binding for dma-channel-mask
From: John Stultz @ 2019-01-17 17:43 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: lkml, Vinod Koul, Rob Herring, Mark Rutland, Tanglei Han,
Zhuangluan Su, Ryan Grachek,
open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
On Thu, Jan 17, 2019 at 9:08 AM Manivannan Sadhasivam
<manivannan.sadhasivam@linaro.org> wrote:
>
> On Wed, Jan 16, 2019 at 09:10:23AM -0800, John Stultz wrote:
> > Some dma channels can be reserved for secure mode or other
> > hardware on the SoC, so provide a binding for a bitmask
> > listing the available channels for the kernel to use.
> >
> > This follows the pre-existing bcm,dma-channel-mask binding.
> >
> > Cc: Vinod Koul <vkoul@kernel.org>
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> > Cc: Tanglei Han <hantanglei@huawei.com>
> > Cc: Zhuangluan Su <suzhuangluan@hisilicon.com>
> > Cc: Ryan Grachek <ryan@edited.us>
> > Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > Cc: dmaengine@vger.kernel.org
> > Cc: devicetree@vger.kernel.org
> > Signed-off-by: John Stultz <john.stultz@linaro.org>
> > ---
> > v3: Renamed to hisi-dma-avail-chan
> > v4: Reworked to generic dma-channel-mask
> > ---
> > Documentation/devicetree/bindings/dma/dma.txt | 4 ++++
> > 1 file changed, 4 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt
> > index 6312fb0..eeb4e4d 100644
> > --- a/Documentation/devicetree/bindings/dma/dma.txt
> > +++ b/Documentation/devicetree/bindings/dma/dma.txt
> > @@ -16,6 +16,9 @@ Optional properties:
> > - dma-channels: Number of DMA channels supported by the controller.
> > - dma-requests: Number of DMA request signals supported by the
> > controller.
> > +- dma-channel-mask: Bitmask of available DMA channels in ascending order
> > + that are not reserved by firmware and are available to
> > + the kernel. i.e. first channel corresponds to LSB.
>
> A general assumption is, "dma-channel-mask" refers to the bit fields of
> the channels which needs to be masked. But here, it refers to the channels
> which are available. Doesn't it contradict?
Hrm. So while I can sort of understand the common usage of "mask" as
to "hide", thus the desire to have a bitfield mean "the channels we
hide" or "don't use", but in my experience bitmasking is more commonly
used to keep only a portion of the the bits, so from that perspective
its more intuitive that a mask be the channels we keep to use. So I'm
not sure if your suggestion makes it more clear.
But I'm not very particular here, so I'll defer to others on this.
thanks
-john
^ permalink raw reply
* [5/8,v4] dma: k3dma: Add support for dma-channel-mask
From: Manivannan Sadhasivam @ 2019-01-17 17:14 UTC (permalink / raw)
To: John Stultz
Cc: lkml, Li Yu, Dan Williams, Vinod Koul, Tanglei Han, Zhuangluan Su,
Ryan Grachek, Guodong Xu, dmaengine
On Wed, Jan 16, 2019 at 09:10:26AM -0800, John Stultz wrote:
> From: Li Yu <liyu65@hisilicon.com>
>
> Add dma-channel-mask as a property for k3dma, it defines
> available dma channels which a non-secure mode driver can use.
>
> One sample usage of this is in Hi3660 SoC. DMA channel 0 is
> reserved to lpm3, which is a coprocessor for power management. So
> as a result, any request in kernel (which runs on main processor
> and in non-secure mode) should start from at least channel 1.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Vinod Koul <vkoul@kernel.org>
> Cc: Tanglei Han <hantanglei@huawei.com>
> Cc: Zhuangluan Su <suzhuangluan@hisilicon.com>
> Cc: Ryan Grachek <ryan@edited.us>
> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> Cc: Guodong Xu <guodong.xu@linaro.org>
> Cc: dmaengine@vger.kernel.org
> Signed-off-by: Li Yu <liyu65@hisilicon.com>
> [jstultz: Reworked to use a channel mask]
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
> v3: Rename to hisi-dma-avail-chan
> v4: Rename to dma-channel-mask
> ---
> drivers/dma/k3dma.c | 20 +++++++++++++++++++-
> 1 file changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
> index b2060bf..ed19b1f 100644
> --- a/drivers/dma/k3dma.c
> +++ b/drivers/dma/k3dma.c
> @@ -111,6 +111,7 @@ struct k3_dma_dev {
> struct dma_pool *pool;
> u32 dma_channels;
> u32 dma_requests;
> + u32 dma_channel_mask;
> unsigned int irq;
> };
>
> @@ -318,6 +319,9 @@ static void k3_dma_tasklet(unsigned long arg)
> /* check new channel request in d->chan_pending */
> spin_lock_irq(&d->lock);
> for (pch = 0; pch < d->dma_channels; pch++) {
> + if (!(d->dma_channel_mask & (1<<pch)))
> + continue;
> +
As per my comment in bindings patch, the code here gets the assumption
that we are skipping the channels which are not masked. Either the
property should be named as "dma-avail-chan" or "dma_channel_mask" should
have the bit mask of channels which needs to be masked. Then the code will
be,
/* Skip the channels which are masked */
if ((d->dma_channel_mask) & BIT(pch))
continue;
PS: Use BIT() macro where applicable.
Thanks,
Mani
> p = &d->phy[pch];
>
> if (p->vchan == NULL && !list_empty(&d->chan_pending)) {
> @@ -335,6 +339,9 @@ static void k3_dma_tasklet(unsigned long arg)
> spin_unlock_irq(&d->lock);
>
> for (pch = 0; pch < d->dma_channels; pch++) {
> + if (!(d->dma_channel_mask & (1<<pch)))
> + continue;
> +
> if (pch_alloc & (1 << pch)) {
> p = &d->phy[pch];
> c = p->vchan;
> @@ -855,6 +862,13 @@ static int k3_dma_probe(struct platform_device *op)
> "dma-channels", &d->dma_channels);
> of_property_read_u32((&op->dev)->of_node,
> "dma-requests", &d->dma_requests);
> + ret = of_property_read_u32((&op->dev)->of_node,
> + "dma-channel-mask", &d->dma_channel_mask);
> + if (ret) {
> + dev_warn(&op->dev,
> + "dma-channel-mask doesn't exist, considering all as available.\n");
> + d->dma_channel_mask = (u32)~0UL;
> + }
> }
>
> if (!(soc_data->flags & K3_FLAG_NOCLK)) {
> @@ -886,8 +900,12 @@ static int k3_dma_probe(struct platform_device *op)
> return -ENOMEM;
>
> for (i = 0; i < d->dma_channels; i++) {
> - struct k3_dma_phy *p = &d->phy[i];
> + struct k3_dma_phy *p;
> +
> + if (!(d->dma_channel_mask & (1<<i)))
> + continue;
>
> + p = &d->phy[i];
> p->idx = i;
> p->base = d->base + i * 0x40;
> }
> --
> 2.7.4
>
^ permalink raw reply
* [2/8,v4] Documentation: bindings: dma: Add binding for dma-channel-mask
From: Manivannan Sadhasivam @ 2019-01-17 17:08 UTC (permalink / raw)
To: John Stultz
Cc: lkml, Vinod Koul, Rob Herring, Mark Rutland, Tanglei Han,
Zhuangluan Su, Ryan Grachek, dmaengine, devicetree
On Wed, Jan 16, 2019 at 09:10:23AM -0800, John Stultz wrote:
> Some dma channels can be reserved for secure mode or other
> hardware on the SoC, so provide a binding for a bitmask
> listing the available channels for the kernel to use.
>
> This follows the pre-existing bcm,dma-channel-mask binding.
>
> Cc: Vinod Koul <vkoul@kernel.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Tanglei Han <hantanglei@huawei.com>
> Cc: Zhuangluan Su <suzhuangluan@hisilicon.com>
> Cc: Ryan Grachek <ryan@edited.us>
> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> Cc: dmaengine@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
> v3: Renamed to hisi-dma-avail-chan
> v4: Reworked to generic dma-channel-mask
> ---
> Documentation/devicetree/bindings/dma/dma.txt | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt
> index 6312fb0..eeb4e4d 100644
> --- a/Documentation/devicetree/bindings/dma/dma.txt
> +++ b/Documentation/devicetree/bindings/dma/dma.txt
> @@ -16,6 +16,9 @@ Optional properties:
> - dma-channels: Number of DMA channels supported by the controller.
> - dma-requests: Number of DMA request signals supported by the
> controller.
> +- dma-channel-mask: Bitmask of available DMA channels in ascending order
> + that are not reserved by firmware and are available to
> + the kernel. i.e. first channel corresponds to LSB.
A general assumption is, "dma-channel-mask" refers to the bit fields of
the channels which needs to be masked. But here, it refers to the channels
which are available. Doesn't it contradict?
Thanks,
Mani
>
> Example:
>
> @@ -29,6 +32,7 @@ Example:
> #dma-cells = <1>;
> dma-channels = <32>;
> dma-requests = <127>;
> + dma-channel-mask = <0xfffe>
> };
>
> * DMA router
> --
> 2.7.4
>
^ permalink raw reply
* dmaengine: at_xdmac: Fix wrongfull report of a channel as in use
From: Codrin Ciubotariu @ 2019-01-17 16:10 UTC (permalink / raw)
To: Ludovic.Desroches, vkoul
Cc: dmaengine, linux-arm-kernel, linux-kernel, Codrin.Ciubotariu
From: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
atchan->status is used for two things:
- pass channel interrupts status from interrupt handler to tasklet;
- channel information like whether it is cyclic or paused;
Since these operations have nothing in common, this patch adds a
different struct member to keep the interrupts status.
Fixes a bug in which a channel is wrongfully reported as in use when
trying to obtain a new descriptior for a previously used cyclic channel.
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
drivers/dma/at_xdmac.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index 4e557684f792..fe69dccfa0c0 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -203,6 +203,7 @@ struct at_xdmac_chan {
u32 save_cim;
u32 save_cnda;
u32 save_cndc;
+ u32 irq_status;
unsigned long status;
struct tasklet_struct tasklet;
struct dma_slave_config sconfig;
@@ -1580,8 +1581,8 @@ static void at_xdmac_tasklet(unsigned long data)
struct at_xdmac_desc *desc;
u32 error_mask;
- dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08lx\n",
- __func__, atchan->status);
+ dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n",
+ __func__, atchan->irq_status);
error_mask = AT_XDMAC_CIS_RBEIS
| AT_XDMAC_CIS_WBEIS
@@ -1589,15 +1590,15 @@ static void at_xdmac_tasklet(unsigned long data)
if (at_xdmac_chan_is_cyclic(atchan)) {
at_xdmac_handle_cyclic(atchan);
- } else if ((atchan->status & AT_XDMAC_CIS_LIS)
- || (atchan->status & error_mask)) {
+ } else if ((atchan->irq_status & AT_XDMAC_CIS_LIS)
+ || (atchan->irq_status & error_mask)) {
struct dma_async_tx_descriptor *txd;
- if (atchan->status & AT_XDMAC_CIS_RBEIS)
+ if (atchan->irq_status & AT_XDMAC_CIS_RBEIS)
dev_err(chan2dev(&atchan->chan), "read bus error!!!");
- if (atchan->status & AT_XDMAC_CIS_WBEIS)
+ if (atchan->irq_status & AT_XDMAC_CIS_WBEIS)
dev_err(chan2dev(&atchan->chan), "write bus error!!!");
- if (atchan->status & AT_XDMAC_CIS_ROIS)
+ if (atchan->irq_status & AT_XDMAC_CIS_ROIS)
dev_err(chan2dev(&atchan->chan), "request overflow error!!!");
spin_lock(&atchan->lock);
@@ -1652,7 +1653,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id)
atchan = &atxdmac->chan[i];
chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS);
- atchan->status = chan_status & chan_imr;
+ atchan->irq_status = chan_status & chan_imr;
dev_vdbg(atxdmac->dma.dev,
"%s: chan%d: imr=0x%x, status=0x%x\n",
__func__, i, chan_imr, chan_status);
@@ -1666,7 +1667,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id)
at_xdmac_chan_read(atchan, AT_XDMAC_CDA),
at_xdmac_chan_read(atchan, AT_XDMAC_CUBC));
- if (atchan->status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS))
+ if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS))
at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
tasklet_schedule(&atchan->tasklet);
^ permalink raw reply related
* [1/8,v3] Documentation: bindings: k3dma: Extend the k3dma driver binding to support hisi-asp
From: Rob Herring @ 2019-01-17 16:06 UTC (permalink / raw)
To: John Stultz
Cc: lkml, Vinod Koul, Mark Rutland, Zhuangluan Su, Tanglei Han,
Ryan Grachek, Manivannan Sadhasivam, dmaengine, devicetree,
Youlin Wang
On Thu, 10 Jan 2019 09:34:05 -0800, John Stultz wrote:
> From: Youlin Wang <wwx575822@notesmail.huawei.com>
>
> Extend the k3dma driver binding to support hisi-asp hardware
> variants.
>
> Cc: Vinod Koul <vkoul@kernel.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Zhuangluan Su <suzhuangluan@hisilicon.com>
> Cc: Tanglei Han <hantanglei@huawei.com>
> Cc: Ryan Grachek <ryan@edited.us>
> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> Cc: dmaengine@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Youlin Wang <wwx575822@notesmail.huawei.com>
> Signed-off-by: Tanglei Han <hantanglei@huawei.com>
> Signed-off-by: John Stultz <john.stultz@linaro.org>
> ---
> v2: Simplify patch, removing extranious examples
> ---
> Documentation/devicetree/bindings/dma/k3dma.txt | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Yoshihiro Shimoda @ 2019-01-17 10:18 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Vinod Koul, dmaengine@vger.kernel.org, Linux-Renesas, stable,
Phuong Nguyen, Ulf Hansson, Rafael J. Wysocki, Linux PM list
Hi Geert-san,
> From: Geert Uytterhoeven, Sent: Thursday, January 17, 2019 6:03 PM
>
> Hi Shimoda-san,
>
> CC linux-pm people
<snip>
>
> Thanks for your patch!
>
> This is similar in spirit to commits 1131b0a4af911de5 ("dmaengine:
> rcar-dmac: Make DMAC reinit during system resume explicit") and
> 73dcc666d6bd0db5 ("dmaengine: rcar-dmac: Fix too early/late system
> suspend/resume callbacks") for rcar-dmac.
>
> I'm only wondering if the "late" variant would be sufficient here.
I tried to use SET_LATE_... () instead of SET_NOIRQ_..., it also worked correctly.
> Does g_serial support wake-up?
I believe any USB gadgets don't support own system wake-up because they
are against USB host.
> Anyway, using the "noirq" variant, like you did, is probably the safest.
>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Thank you for your review!
Best regards,
Yoshihiro Shimoda
> > ---
> > drivers/dma/sh/usb-dmac.c | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
> > index 7f7184c..59403f6 100644
> > --- a/drivers/dma/sh/usb-dmac.c
> > +++ b/drivers/dma/sh/usb-dmac.c
> > @@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev)
> > #endif /* CONFIG_PM */
> >
> > static const struct dev_pm_ops usb_dmac_pm = {
> > + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > + pm_runtime_force_resume)
> > SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
> > NULL)
> > };
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
^ permalink raw reply
* dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Geert Uytterhoeven @ 2019-01-17 9:03 UTC (permalink / raw)
To: Yoshihiro Shimoda
Cc: Vinod Koul, dmaengine, Linux-Renesas, stable, Phuong Nguyen,
Ulf Hansson, Rafael J. Wysocki, Linux PM list
Hi Shimoda-san,
CC linux-pm people
On Thu, Jan 17, 2019 at 9:48 AM Yoshihiro Shimoda
<yoshihiro.shimoda.uh@renesas.com> wrote:
> From: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
>
> This commit fixes the issue that USB-DMAC hangs silently after system
> resumes on R-Car Gen3 hence renesas_usbhs will not work correctly
> when using USB-DMAC for bulk transfer e.g. ethernet or serial
> gadgets.
>
> The issue can be reproduced by these steps:
> 1. modprobe g_serial
> 2. Suspend and resume system.
> 3. connect a usb cable to host side
> 4. Transfer data from Host to Target
> 5. cat /dev/ttyGS0 (Target side)
> 6. echo "test" > /dev/ttyACM0 (Host side)
>
> The 'cat' will not result anything. However, system still can work
> normally.
>
> Currently, USB-DMAC driver does not have system sleep callbacks hence
> this driver relies on the PM core to force runtime suspend/resume to
> suspend and reinitialize USB-DMAC during system resume. After
> the commit 17218e0092f8 ("PM / genpd: Stop/start devices without
> pm_runtime_force_suspend/resume()"), PM core will not force
> runtime suspend/resume anymore so this issue happens.
>
> To solve this, make system suspend resume explicit by using
> pm_runtime_force_{suspend,resume}() as the system sleep callbacks.
> SET_NOIRQ_SYSTEM_SLEEP_PM_OPS()is used to make sure USB-DMAC suspended
> after and initialized before renesas_usbhs."
>
> Signed-off-by: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
> Signed-off-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
> Cc: <stable@vger.kernel.org> # v4.16+
> [shimoda: revise the commit log and add Cc tag]
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Thanks for your patch!
This is similar in spirit to commits 1131b0a4af911de5 ("dmaengine:
rcar-dmac: Make DMAC reinit during system resume explicit") and
73dcc666d6bd0db5 ("dmaengine: rcar-dmac: Fix too early/late system
suspend/resume callbacks") for rcar-dmac.
I'm only wondering if the "late" variant would be sufficient here.
Does g_serial support wake-up?
Anyway, using the "noirq" variant, like you did, is probably the safest.
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
> drivers/dma/sh/usb-dmac.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
> index 7f7184c..59403f6 100644
> --- a/drivers/dma/sh/usb-dmac.c
> +++ b/drivers/dma/sh/usb-dmac.c
> @@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev)
> #endif /* CONFIG_PM */
>
> static const struct dev_pm_ops usb_dmac_pm = {
> + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
> NULL)
> };
Gr{oetje,eeting}s,
Geert
^ permalink raw reply
* dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Yoshihiro Shimoda @ 2019-01-17 8:44 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: dmaengine@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
stable@vger.kernel.org, Phuong Nguyen, Vinod Koul
Hi Sergei,
> From: Sergei Shtylyov, Sent: Thursday, January 17, 2019 5:28 PM
>
> On 17.01.2019 5:41, Yoshihiro Shimoda wrote:
<snip>
> > To solve this, make system suspend resume explicit by using
> > pm_runtime_force_{suspend,resume}() as the system sleep callbacks.
> > SET_NOIRQ_SYSTEM_SLEEP_PM_OPS()is used to make sure USB-DMAC suspended
> ^ space missing here
Thank you for the pointed it out! I'll revise it on v2.
Best regards,
Yoshihiro Shimoda
^ permalink raw reply
* [v2] dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Yoshihiro Shimoda @ 2019-01-17 8:44 UTC (permalink / raw)
To: vkoul
Cc: dmaengine, linux-renesas-soc, stable, Phuong Nguyen,
Yoshihiro Shimoda
From: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
This commit fixes the issue that USB-DMAC hangs silently after system
resumes on R-Car Gen3 hence renesas_usbhs will not work correctly
when using USB-DMAC for bulk transfer e.g. ethernet or serial
gadgets.
The issue can be reproduced by these steps:
1. modprobe g_serial
2. Suspend and resume system.
3. connect a usb cable to host side
4. Transfer data from Host to Target
5. cat /dev/ttyGS0 (Target side)
6. echo "test" > /dev/ttyACM0 (Host side)
The 'cat' will not result anything. However, system still can work
normally.
Currently, USB-DMAC driver does not have system sleep callbacks hence
this driver relies on the PM core to force runtime suspend/resume to
suspend and reinitialize USB-DMAC during system resume. After
the commit 17218e0092f8 ("PM / genpd: Stop/start devices without
pm_runtime_force_suspend/resume()"), PM core will not force
runtime suspend/resume anymore so this issue happens.
To solve this, make system suspend resume explicit by using
pm_runtime_force_{suspend,resume}() as the system sleep callbacks.
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS() is used to make sure USB-DMAC
suspended after and initialized before renesas_usbhs."
Signed-off-by: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
Signed-off-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
Cc: <stable@vger.kernel.org> # v4.16+
[shimoda: revise the commit log and add Cc tag]
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
Changes from v1 (https://patchwork.kernel.org/patch/10767471/):
- Revise the commit log.
drivers/dma/sh/usb-dmac.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index 7f7184c..59403f6 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct dev_pm_ops usb_dmac_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
NULL)
};
^ permalink raw reply related
* dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Sergei Shtylyov @ 2019-01-17 8:27 UTC (permalink / raw)
To: Yoshihiro Shimoda, vinod.koul
Cc: dmaengine, linux-renesas-soc, stable, Phuong Nguyen
On 17.01.2019 5:41, Yoshihiro Shimoda wrote:
> From: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
>
> This commit fixes the issue that USB-DMAC hangs silently after system
> resumes on R-Car Gen3 hence renesas_usbhs will not work correctly
> when using USB-DMAC for bulk transfer e.g. ethernet or serial
> gadgets.
>
> The issue can be reproduced by these steps:
> 1. modprobe g_serial
> 2. Suspend and resume system.
> 3. connect a usb cable to host side
> 4. Transfer data from Host to Target
> 5. cat /dev/ttyGS0 (Target side)
> 6. echo "test" > /dev/ttyACM0 (Host side)
>
> The 'cat' will not result anything. However, system still can work
> normally.
>
> Currently, USB-DMAC driver does not have system sleep callbacks hence
> this driver relies on the PM core to force runtime suspend/resume to
> suspend and reinitialize USB-DMAC during system resume. After
> the commit 17218e0092f8 ("PM / genpd: Stop/start devices without
> pm_runtime_force_suspend/resume()"), PM core will not force
> runtime suspend/resume anymore so this issue happens.
>
> To solve this, make system suspend resume explicit by using
> pm_runtime_force_{suspend,resume}() as the system sleep callbacks.
> SET_NOIRQ_SYSTEM_SLEEP_PM_OPS()is used to make sure USB-DMAC suspended
^ space missing here
> after and initialized before renesas_usbhs."
>
> Signed-off-by: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
> Signed-off-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
> Cc: <stable@vger.kernel.org> # v4.16+
> [shimoda: revise the commit log and add Cc tag]
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
[...]
MBR, Sergei
^ permalink raw reply
* [1/5] dmaengine: bcm2835: Fix interrupt race on RT
From: Lukas Wunner @ 2019-01-17 8:26 UTC (permalink / raw)
To: Vinod Koul, Eric Anholt, Stefan Wahren
Cc: Frank Pavlic, Martin Sperl, Florian Meier, dmaengine,
linux-rpi-kernel, Tiejun Chen, linux-rt-users
On Sat, Dec 22, 2018 at 08:28:45AM +0100, Lukas Wunner wrote:
> That MMIO read is needed to write the same value to the CS register that
> it currently has, thereby preserving the ACTIVE flag while clearing the
> INT and END flags. Note that the transaction may finish between reading
> and writing the CS register, and in that case the write changes the
> ACTIVE flag from 0 to 1. But that's harmless, the chip will notice that
> NEXTCONBK is 0 and self-clear the ACTIVE flag again.
Further experimentation has shown that the chip does not self-clear the
ACTIVE flag if it is set on an idle channel, contrary to what I have
written above. Consequently that flag cannot be used to determine idleness
of a channel reliably. A workaround is to check whether the register
containing the current control block's address is zero. I have amended
the patch accordingly and it is currently undergoing stresstesting until
Monday. So I should be able to post an updated version of the series
next week.
I have also updated the patch to fix the following remaining race:
> @@ -477,11 +479,17 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
> spin_lock_irqsave(&c->vc.lock, flags);
>
> /* Acknowledge interrupt */
> - writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS);
> + cs = readl(c->chan_base + BCM2835_DMA_CS);
> + writel(cs, c->chan_base + BCM2835_DMA_CS);
>
> d = c->desc;
>
> - if (d) {
> + /*
> + * If this IRQ handler is threaded, clients may terminate descriptors
> + * and issue new ones before the IRQ handler runs. Avoid finalizing
> + * such a newly issued descriptor by checking the END flag.
> + */
> + if (d && cs & BCM2835_DMA_END) {
> if (d->cyclic) {
> /* call the cyclic callback */
> vchan_cyclic_callback(&d->vd);
The descriptor may be finished between the read and the write of the
CS register, and in that case the interrupt for the finished descriptor
will be acknowledged but the descriptor will not be finalized because
END was not yet set when the register was read. I haven't seen this
race in the real world but it's definitely not correct and I've fixed
it as well for v2.
Thanks,
Lukas
^ permalink raw reply
* [RFC,v3,7/7] dmaengine: Add Synopsys eDMA IP test and sample driver
From: Vinod Koul @ 2019-01-17 5:03 UTC (permalink / raw)
To: Gustavo Pimentel
Cc: Andy Shevchenko, linux-pci@vger.kernel.org,
dmaengine@vger.kernel.org, Dan Williams, Eugeniy Paltsev,
Russell King, Niklas Cassel, Joao Pinto, Jose Abreu,
Luis Oliveira, Vitor Soares, Nelson Costa, Pedro Sousa
On 15-01-19, 13:02, Gustavo Pimentel wrote:
> On 15/01/2019 05:45, Andy Shevchenko wrote:
> > On Mon, Jan 14, 2019 at 11:44:22AM +0000, Gustavo Pimentel wrote:
> >> On 11/01/2019 19:48, Andy Shevchenko wrote:
> >>> On Fri, Jan 11, 2019 at 07:33:43PM +0100, Gustavo Pimentel wrote:
> >>>> Add Synopsys eDMA IP test and sample driver to be use for testing
> >>>> purposes and also as a reference for any developer who needs to
> >>>> implement and use Synopsys eDMA.
> >>>>
> >>>> This driver can be compile as built-in or external module in kernel.
> >>>>
> >>>> To enable this driver just select DW_EDMA_TEST option in kernel
> >>>> configuration, however it requires and selects automatically DW_EDMA
> >>>> option too.
> >>>>
> >>>
> >>> Hmm... This doesn't explain what's wrong with dmatest module.
> >>
> >> There isn't anything wrong with dmatest module, that I know of. In beginning I
> >> was planning to used it, however only works with MEM_TO_MEM transfers, that's
> >> why I created a similar module but for MEM_TO_DEV and DEV_TO_MEM with
> >> scatter-gather and cyclic transfers type for my use case. I don't know if can be
> >> applied to other cases, if that is feasible, I'm glad to share it.
> >
> > What I'm trying to tell is that the dmatest driver would be nice to have such
> > capability.
> >
>
> At the beginning I thought to add those features to dmatest module, but because
> of several points such as:
> - I didn't want, by any chance, to broke dmatest module while implementing the
> support of those new features;
> - Since I'm still gaining knowledge about this subsystem I preferred to do in a
> more controlled environment;
> - Since this driver is also a reference driver aim to teach and to be use by
> someone how to use the dmaengine API, I preferred to keep it simple.
>
> Maybe in the future both modules could be merged in just one tool, but for now I
> would prefer to keep it separated specially to fix any immaturity error of this
> module.
With decent testing we should be able to iron out kinks in dmatest
module. It gets tested for memcpy controllers so we should easily catch
issues.
Said that, it would make sense to add support in generic dmatest so that
other folks can reuse and contribute as well. Future work always gets
side tracked by life, so lets do it now :)
^ permalink raw reply
* dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Yoshihiro Shimoda @ 2019-01-17 2:51 UTC (permalink / raw)
To: Vinod Koul
Cc: dmaengine@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
stable@vger.kernel.org, Phuong Nguyen
Hi Vinod (with correct email address),
I'm sorry, I should have checked your email address before I submitted...
Best regards,
Yoshihiro Shimoda
> From: Yoshihiro Shimoda, Sent: Thursday, January 17, 2019 11:42 AM
>
> From: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
>
> This commit fixes the issue that USB-DMAC hangs silently after system
> resumes on R-Car Gen3 hence renesas_usbhs will not work correctly
> when using USB-DMAC for bulk transfer e.g. ethernet or serial
> gadgets.
>
> The issue can be reproduced by these steps:
> 1. modprobe g_serial
> 2. Suspend and resume system.
> 3. connect a usb cable to host side
> 4. Transfer data from Host to Target
> 5. cat /dev/ttyGS0 (Target side)
> 6. echo "test" > /dev/ttyACM0 (Host side)
>
> The 'cat' will not result anything. However, system still can work
> normally.
>
> Currently, USB-DMAC driver does not have system sleep callbacks hence
> this driver relies on the PM core to force runtime suspend/resume to
> suspend and reinitialize USB-DMAC during system resume. After
> the commit 17218e0092f8 ("PM / genpd: Stop/start devices without
> pm_runtime_force_suspend/resume()"), PM core will not force
> runtime suspend/resume anymore so this issue happens.
>
> To solve this, make system suspend resume explicit by using
> pm_runtime_force_{suspend,resume}() as the system sleep callbacks.
> SET_NOIRQ_SYSTEM_SLEEP_PM_OPS()is used to make sure USB-DMAC suspended
> after and initialized before renesas_usbhs."
>
> Signed-off-by: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
> Signed-off-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
> Cc: <stable@vger.kernel.org> # v4.16+
> [shimoda: revise the commit log and add Cc tag]
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
> ---
> drivers/dma/sh/usb-dmac.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
> index 7f7184c..59403f6 100644
> --- a/drivers/dma/sh/usb-dmac.c
> +++ b/drivers/dma/sh/usb-dmac.c
> @@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev)
> #endif /* CONFIG_PM */
>
> static const struct dev_pm_ops usb_dmac_pm = {
> + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
> NULL)
> };
> --
> 1.9.1
^ permalink raw reply
* dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit
From: Yoshihiro Shimoda @ 2019-01-17 2:41 UTC (permalink / raw)
To: vinod.koul
Cc: dmaengine, linux-renesas-soc, stable, Phuong Nguyen,
Yoshihiro Shimoda
From: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
This commit fixes the issue that USB-DMAC hangs silently after system
resumes on R-Car Gen3 hence renesas_usbhs will not work correctly
when using USB-DMAC for bulk transfer e.g. ethernet or serial
gadgets.
The issue can be reproduced by these steps:
1. modprobe g_serial
2. Suspend and resume system.
3. connect a usb cable to host side
4. Transfer data from Host to Target
5. cat /dev/ttyGS0 (Target side)
6. echo "test" > /dev/ttyACM0 (Host side)
The 'cat' will not result anything. However, system still can work
normally.
Currently, USB-DMAC driver does not have system sleep callbacks hence
this driver relies on the PM core to force runtime suspend/resume to
suspend and reinitialize USB-DMAC during system resume. After
the commit 17218e0092f8 ("PM / genpd: Stop/start devices without
pm_runtime_force_suspend/resume()"), PM core will not force
runtime suspend/resume anymore so this issue happens.
To solve this, make system suspend resume explicit by using
pm_runtime_force_{suspend,resume}() as the system sleep callbacks.
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS()is used to make sure USB-DMAC suspended
after and initialized before renesas_usbhs."
Signed-off-by: Phuong Nguyen <phuong.nguyen.xw@renesas.com>
Signed-off-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
Cc: <stable@vger.kernel.org> # v4.16+
[shimoda: revise the commit log and add Cc tag]
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
drivers/dma/sh/usb-dmac.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index 7f7184c..59403f6 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct dev_pm_ops usb_dmac_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
NULL)
};
^ permalink raw reply related
* [5/8,v4] dma: k3dma: Add support for dma-channel-mask
From: John Stultz @ 2019-01-16 17:10 UTC (permalink / raw)
To: lkml
Cc: Li Yu, Dan Williams, Vinod Koul, Tanglei Han, Zhuangluan Su,
Ryan Grachek, Manivannan Sadhasivam, Guodong Xu, dmaengine,
John Stultz
From: Li Yu <liyu65@hisilicon.com>
Add dma-channel-mask as a property for k3dma, it defines
available dma channels which a non-secure mode driver can use.
One sample usage of this is in Hi3660 SoC. DMA channel 0 is
reserved to lpm3, which is a coprocessor for power management. So
as a result, any request in kernel (which runs on main processor
and in non-secure mode) should start from at least channel 1.
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Tanglei Han <hantanglei@huawei.com>
Cc: Zhuangluan Su <suzhuangluan@hisilicon.com>
Cc: Ryan Grachek <ryan@edited.us>
Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: Guodong Xu <guodong.xu@linaro.org>
Cc: dmaengine@vger.kernel.org
Signed-off-by: Li Yu <liyu65@hisilicon.com>
[jstultz: Reworked to use a channel mask]
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
v3: Rename to hisi-dma-avail-chan
v4: Rename to dma-channel-mask
---
drivers/dma/k3dma.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
index b2060bf..ed19b1f 100644
--- a/drivers/dma/k3dma.c
+++ b/drivers/dma/k3dma.c
@@ -111,6 +111,7 @@ struct k3_dma_dev {
struct dma_pool *pool;
u32 dma_channels;
u32 dma_requests;
+ u32 dma_channel_mask;
unsigned int irq;
};
@@ -318,6 +319,9 @@ static void k3_dma_tasklet(unsigned long arg)
/* check new channel request in d->chan_pending */
spin_lock_irq(&d->lock);
for (pch = 0; pch < d->dma_channels; pch++) {
+ if (!(d->dma_channel_mask & (1<<pch)))
+ continue;
+
p = &d->phy[pch];
if (p->vchan == NULL && !list_empty(&d->chan_pending)) {
@@ -335,6 +339,9 @@ static void k3_dma_tasklet(unsigned long arg)
spin_unlock_irq(&d->lock);
for (pch = 0; pch < d->dma_channels; pch++) {
+ if (!(d->dma_channel_mask & (1<<pch)))
+ continue;
+
if (pch_alloc & (1 << pch)) {
p = &d->phy[pch];
c = p->vchan;
@@ -855,6 +862,13 @@ static int k3_dma_probe(struct platform_device *op)
"dma-channels", &d->dma_channels);
of_property_read_u32((&op->dev)->of_node,
"dma-requests", &d->dma_requests);
+ ret = of_property_read_u32((&op->dev)->of_node,
+ "dma-channel-mask", &d->dma_channel_mask);
+ if (ret) {
+ dev_warn(&op->dev,
+ "dma-channel-mask doesn't exist, considering all as available.\n");
+ d->dma_channel_mask = (u32)~0UL;
+ }
}
if (!(soc_data->flags & K3_FLAG_NOCLK)) {
@@ -886,8 +900,12 @@ static int k3_dma_probe(struct platform_device *op)
return -ENOMEM;
for (i = 0; i < d->dma_channels; i++) {
- struct k3_dma_phy *p = &d->phy[i];
+ struct k3_dma_phy *p;
+
+ if (!(d->dma_channel_mask & (1<<i)))
+ continue;
+ p = &d->phy[i];
p->idx = i;
p->base = d->base + i * 0x40;
}
^ permalink raw reply related
* [4/8,v4] dma: k3dma: Delete axi_config
From: John Stultz @ 2019-01-16 17:10 UTC (permalink / raw)
To: lkml
Cc: Li Yu, Dan Williams, Vinod Koul, Tanglei Han, Zhuangluan Su,
Ryan Grachek, Manivannan Sadhasivam, dmaengine, Guodong Xu,
John Stultz
From: Li Yu <liyu65@hisilicon.com>
Axi_config controls whether DMA resources can be accessed in non-secure
mode, such as linux kernel. The register should be set by the bootloader
stage and depends on the device.
Thus, this patch removes axi_config from k3dma driver.
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Tanglei Han <hantanglei@huawei.com>
Cc: Zhuangluan Su <suzhuangluan@hisilicon.com>
Cc: Ryan Grachek <ryan@edited.us>
Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: dmaengine@vger.kernel.org
Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Li Yu <liyu65@hisilicon.com>
Signed-off-by: Guodong Xu <guodong.xu@linaro.org>
[jstultz: Minor tweaks to commit message]
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
drivers/dma/k3dma.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
index df61406..b2060bf 100644
--- a/drivers/dma/k3dma.c
+++ b/drivers/dma/k3dma.c
@@ -52,8 +52,6 @@
#define CX_SRC 0x814
#define CX_DST 0x818
#define CX_CFG 0x81c
-#define AXI_CFG 0x820
-#define AXI_CFG_DEFAULT 0x201201
#define CX_LLI_CHAIN_EN 0x2
#define CX_CFG_EN 0x1
@@ -168,7 +166,6 @@ static void k3_dma_set_desc(struct k3_dma_phy *phy, struct k3_desc_hw *hw)
writel_relaxed(hw->count, phy->base + CX_CNT0);
writel_relaxed(hw->saddr, phy->base + CX_SRC);
writel_relaxed(hw->daddr, phy->base + CX_DST);
- writel_relaxed(AXI_CFG_DEFAULT, phy->base + AXI_CFG);
writel_relaxed(hw->config, phy->base + CX_CFG);
}
^ 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