* [v8,2/3] dmaengine: fsl-edma: add edma version and configurable registers
From: Angelo Dureghello @ 2018-08-06 20:31 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: vinod.koul, dmaengine, linux-arm-kernel, linux-m68k, Stefan Agner
Hi Krzysztof,
many thanks for testing.
On Mon, Aug 06, 2018 at 09:59:35AM +0200, Krzysztof Kozlowski wrote:
> On 3 August 2018 at 21:32, Angelo Dureghello <angelo@sysam.it> wrote:
> > This patch adds configurable registers (using __iomem addresses)
> > to allow the use of fsl-edma-common code with slightly different
> > edma module versions, as Vybrid (v1) and ColdFire (v2) are.
> >
> > Removal of old membase-referenced registers, amd some fixes on
> > macroes are included.
> >
> > Signed-off-by: Angelo Dureghello <angelo@sysam.it>
> > ---
> > Changes from v7:
> > - patch rewritten from scratch, this patch (2/3) has just been added.
> > ---
> > drivers/dma/fsl-edma-common.c | 138 ++++++++++++++++++++++++++--------
> > drivers/dma/fsl-edma-common.h | 115 ++++++++++++++--------------
> > drivers/dma/fsl-edma.c | 32 ++++----
> > 3 files changed, 182 insertions(+), 103 deletions(-)
> >
> > diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> > index 0ae7094f477a..948a3ee51bbb 100644
> > --- a/drivers/dma/fsl-edma-common.c
> > +++ b/drivers/dma/fsl-edma-common.c
> > @@ -9,6 +9,38 @@
> >
> > #include "fsl-edma-common.h"
> >
> > +#define EDMA_CR 0x00
> > +#define EDMA_ES 0x04
> > +#define EDMA_ERQ 0x0C
> > +#define EDMA_EEI 0x14
> > +#define EDMA_SERQ 0x1B
> > +#define EDMA_CERQ 0x1A
> > +#define EDMA_SEEI 0x19
> > +#define EDMA_CEEI 0x18
> > +#define EDMA_CINT 0x1F
> > +#define EDMA_CERR 0x1E
> > +#define EDMA_SSRT 0x1D
> > +#define EDMA_CDNE 0x1C
> > +#define EDMA_INTR 0x24
> > +#define EDMA_ERR 0x2C
> > +
> > +#define EDMA64_ERQH 0x08
> > +#define EDMA64_EEIH 0x10
> > +#define EDMA64_SERQ 0x18
> > +#define EDMA64_CERQ 0x19
> > +#define EDMA64_SEEI 0x1a
> > +#define EDMA64_CEEI 0x1b
> > +#define EDMA64_CINT 0x1c
> > +#define EDMA64_CERR 0x1d
> > +#define EDMA64_SSRT 0x1e
> > +#define EDMA64_CDNE 0x1f
> > +#define EDMA64_INTH 0x20
> > +#define EDMA64_INTL 0x24
> > +#define EDMA64_ERRH 0x28
> > +#define EDMA64_ERRL 0x2c
> > +
> > +#define EDMA_TCD 0x1000
> > +
> > /*
> > * R/W functions for big- or little-endian registers:
> > * The eDMA controller's endian is independent of the CPU core's endian.
> > @@ -67,20 +99,20 @@ EXPORT_SYMBOL_GPL(to_fsl_edma_desc);
> >
> > static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
> > {
> > - void __iomem *addr = fsl_chan->edma->membase;
> > + struct edma_regs *regs = &fsl_chan->edma->regs;
> > u32 ch = fsl_chan->vchan.chan.chan_id;
> >
> > - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI);
> > - edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ);
> > + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
> > + edma_writeb(fsl_chan->edma, ch, regs->serq);
> > }
> >
> > void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
> > {
> > - void __iomem *addr = fsl_chan->edma->membase;
> > + struct edma_regs *regs = &fsl_chan->edma->regs;
> > u32 ch = fsl_chan->vchan.chan.chan_id;
> >
> > - edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ);
> > - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI);
> > + edma_writeb(fsl_chan->edma, ch, regs->cerq);
> > + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
> > }
> > EXPORT_SYMBOL_GPL(fsl_edma_disable_request);
> >
> > @@ -208,7 +240,7 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
> > struct virt_dma_desc *vdesc, bool in_progress)
> > {
> > struct fsl_edma_desc *edesc = fsl_chan->edesc;
> > - void __iomem *addr = fsl_chan->edma->membase;
> > + struct edma_regs *regs = &fsl_chan->edma->regs;
> > u32 ch = fsl_chan->vchan.chan.chan_id;
> > enum dma_transfer_direction dir = fsl_chan->fsc.dir;
> > dma_addr_t cur_addr, dma_addr;
> > @@ -224,11 +256,9 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
> > return len;
> >
> > if (dir == DMA_MEM_TO_DEV)
> > - cur_addr = edma_readl(
> > - fsl_chan->edma, addr + EDMA_TCD_SADDR(ch));
> > + cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].saddr);
> > else
> > - cur_addr = edma_readl(
> > - fsl_chan->edma, addr + EDMA_TCD_DADDR(ch));
> > + cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].daddr);
> >
> > /* figure out the finished and calculate the residue */
> > for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
> > @@ -285,7 +315,7 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
> > struct fsl_edma_hw_tcd *tcd)
> > {
> > struct fsl_edma_engine *edma = fsl_chan->edma;
> > - void __iomem *addr = fsl_chan->edma->membase;
> > + struct edma_regs *regs = &fsl_chan->edma->regs;
> > u32 ch = fsl_chan->vchan.chan.chan_id;
> >
> > /*
> > @@ -293,24 +323,24 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
> > * endian format. However, we need to load the TCD registers in
> > * big- or little-endian obeying the eDMA engine model endian.
> > */
> > - edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch));
> > - edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch));
> > - edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch));
> > + edma_writew(edma, 0, ®s->tcd[ch].csr);
> > + edma_writel(edma, le32_to_cpu(tcd->saddr), ®s->tcd[ch].saddr);
> > + edma_writel(edma, le32_to_cpu(tcd->daddr), ®s->tcd[ch].daddr);
> >
> > - edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch));
> > - edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch));
> > + edma_writew(edma, le16_to_cpu(tcd->attr), ®s->tcd[ch].attr);
> > + edma_writew(edma, le16_to_cpu(tcd->soff), ®s->tcd[ch].soff);
> >
> > - edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch));
> > - edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch));
> > + edma_writel(edma, le32_to_cpu(tcd->nbytes), ®s->tcd[ch].nbytes);
> > + edma_writel(edma, le32_to_cpu(tcd->slast), ®s->tcd[ch].slast);
> >
> > - edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch));
> > - edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch));
> > - edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch));
> > + edma_writew(edma, le16_to_cpu(tcd->citer), ®s->tcd[ch].citer);
> > + edma_writew(edma, le16_to_cpu(tcd->biter), ®s->tcd[ch].biter);
> > + edma_writew(edma, le16_to_cpu(tcd->doff), ®s->tcd[ch].doff);
> >
> > - edma_writel(edma,
> > - le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch));
> > + edma_writel(edma, le32_to_cpu(tcd->dlast_sga),
> > + ®s->tcd[ch].dlast_sga);
> >
> > - edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch));
> > + edma_writew(edma, le16_to_cpu(tcd->csr), ®s->tcd[ch].csr);
> > }
> >
> > static inline
> > @@ -332,15 +362,15 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
> >
> > tcd->attr = cpu_to_le16(attr);
> >
> > - tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff));
> > + tcd->soff = cpu_to_le16(soff);
> >
> > - tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes));
> > - tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast));
> > + tcd->nbytes = cpu_to_le32(nbytes);
> > + tcd->slast = cpu_to_le32(slast);
> >
> > tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer));
> > - tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff));
> > + tcd->doff = cpu_to_le16(doff);
> >
> > - tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga));
> > + tcd->dlast_sga = cpu_to_le32(dlast_sga);
> >
> > tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter));
> > if (major_int)
> > @@ -573,4 +603,52 @@ void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
> > }
> > EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan);
> >
> > +/*
> > + * On the 32 channels Vybrid/mpc577x edma version (here called "v1"),
> > + * register offsets are different compared to ColdFire mcf5441x 64 channels
> > + * edma (here called "v2").
> > + *
> > + * This function sets up register offsets as per proper declared version
> > + * so must be called in xxx_edma_probe() just after setting the
> > + * edma "version" and "membase" appropriately.
> > + */
> > +void fsl_edma_setup_regs(struct fsl_edma_engine *edma)
> > +{
> > + edma->regs.cr = edma->membase + EDMA_CR;
> > + edma->regs.es = edma->membase + EDMA_ES;
> > + edma->regs.erql = edma->membase + EDMA_ERQ;
> > + edma->regs.eeil = edma->membase + EDMA_EEI;
> > +
> > + edma->regs.serq = edma->membase + ((edma->version == v1) ?
> > + EDMA_SERQ : EDMA64_SERQ);
> > + edma->regs.cerq = edma->membase + ((edma->version == v1) ?
> > + EDMA_CERQ : EDMA64_CERQ);
> > + edma->regs.seei = edma->membase + ((edma->version == v1) ?
> > + EDMA_SEEI : EDMA64_SEEI);
> > + edma->regs.ceei = edma->membase + ((edma->version == v1) ?
> > + EDMA_CEEI : EDMA64_CEEI);
> > + edma->regs.cint = edma->membase + ((edma->version == v1) ?
> > + EDMA_CINT : EDMA64_CINT);
> > + edma->regs.cerr = edma->membase + ((edma->version == v1) ?
> > + EDMA_CERR : EDMA64_CERR);
> > + edma->regs.ssrt = edma->membase + ((edma->version == v1) ?
> > + EDMA_SSRT : EDMA64_SSRT);
> > + edma->regs.cdne = edma->membase + ((edma->version == v1) ?
> > + EDMA_CDNE : EDMA64_CDNE);
> > + edma->regs.intl = edma->membase + ((edma->version == v1) ?
> > + EDMA_INTR : EDMA64_INTL);
> > + edma->regs.errl = edma->membase + ((edma->version == v1) ?
> > + EDMA_ERR : EDMA64_ERRL);
> > +
> > + if (edma->version == v2) {
> > + edma->regs.erqh = edma->membase + EDMA64_ERQH;
> > + edma->regs.eeih = edma->membase + EDMA64_EEIH;
> > + edma->regs.errh = edma->membase + EDMA64_ERRH;
> > + edma->regs.inth = edma->membase + EDMA64_INTH;
> > + }
> > +
> > + edma->regs.tcd = edma->membase + EDMA_TCD;
> > +}
> > +EXPORT_SYMBOL_GPL(fsl_edma_setup_regs);
> > +
> > MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
> > index f3ea68e15b23..7a9231e7639f 100644
> > --- a/drivers/dma/fsl-edma-common.h
> > +++ b/drivers/dma/fsl-edma-common.h
> > @@ -8,35 +8,6 @@
> >
> > #include "virt-dma.h"
> >
> > -#define EDMA_CR 0x00
> > -#define EDMA_ES 0x04
> > -#define EDMA_ERQ 0x0C
> > -#define EDMA_EEI 0x14
> > -#define EDMA_SERQ 0x1B
> > -#define EDMA_CERQ 0x1A
> > -#define EDMA_SEEI 0x19
> > -#define EDMA_CEEI 0x18
> > -#define EDMA_CINT 0x1F
> > -#define EDMA_CERR 0x1E
> > -#define EDMA_SSRT 0x1D
> > -#define EDMA_CDNE 0x1C
> > -#define EDMA_INTR 0x24
> > -#define EDMA_ERR 0x2C
> > -
> > -#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
> > -#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
> > -#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
> > -#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
> > -#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
> > -#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
> > -#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
> > -#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
> > -#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
> > -#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
> > -#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
> > -#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
> > -#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
> > -
> > #define EDMA_CR_EDBG BIT(1)
> > #define EDMA_CR_ERCA BIT(2)
> > #define EDMA_CR_ERGA BIT(3)
> > @@ -47,34 +18,29 @@
> > #define EDMA_CR_ECX BIT(16)
> > #define EDMA_CR_CX BIT(17)
> >
> > -#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
> > -#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
> > -#define EDMA_CINT_CINT(x) ((x) & 0x1F)
> > -#define EDMA_CERR_CERR(x) ((x) & 0x1F)
> > -
> > -#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
> > -#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
> > -#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
> > -#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
> > -#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
> > -#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
> > -#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
> > -#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
> > -#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
> > -#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
> > -#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
> > -#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
> > -#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
> > -#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
> > -
> > -#define EDMA_TCD_SOFF_SOFF(x) (x)
> > -#define EDMA_TCD_NBYTES_NBYTES(x) (x)
> > -#define EDMA_TCD_SLAST_SLAST(x) (x)
> > -#define EDMA_TCD_DADDR_DADDR(x) (x)
> > -#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
> > -#define EDMA_TCD_DOFF_DOFF(x) (x)
> > -#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
> > -#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
> > +#define EDMA_SEEI_SEEI(x) ((x) & GENMASK(4, 0))
> > +#define EDMA_CEEI_CEEI(x) ((x) & GENMASK(4, 0))
> > +#define EDMA_CINT_CINT(x) ((x) & GENMASK(4, 0))
> > +#define EDMA_CERR_CERR(x) ((x) & GENMASK(4, 0))
> > +
> > +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & GENMASK(2, 0)))
> > +#define EDMA_TCD_ATTR_DMOD(x) (((x) & GENMASK(4, 0)) << 3)
> > +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & GENMASK(2, 0)) << 8)
> > +#define EDMA_TCD_ATTR_SMOD(x) (((x) & GENMASK(4, 0)) << 11)
>
> This looks like a change for its own commit.
>
Yes, was previously required the usage of BIT() and GENMASK().
> > +
> > +#define EDMA_TCD_ATTR_DSIZE_8BIT 0
> > +#define EDMA_TCD_ATTR_DSIZE_16BIT BIT(0)
> > +#define EDMA_TCD_ATTR_DSIZE_32BIT BIT(1)
> > +#define EDMA_TCD_ATTR_DSIZE_64BIT (BIT(0) | BIT(1))
> > +#define EDMA_TCD_ATTR_DSIZE_32BYTE (BIT(3) | BIT(0))
> > +#define EDMA_TCD_ATTR_SSIZE_8BIT 0
> > +#define EDMA_TCD_ATTR_SSIZE_16BIT (EDMA_TCD_ATTR_DSIZE_16BIT << 8)
> > +#define EDMA_TCD_ATTR_SSIZE_32BIT (EDMA_TCD_ATTR_DSIZE_32BIT << 8)
> > +#define EDMA_TCD_ATTR_SSIZE_64BIT (EDMA_TCD_ATTR_DSIZE_64BIT << 8)
> > +#define EDMA_TCD_ATTR_SSIZE_32BYTE (EDMA_TCD_ATTR_DSIZE_32BYTE << 8)
> > +
> > +#define EDMA_TCD_CITER_CITER(x) ((x) & GENMASK(14, 0))
> > +#define EDMA_TCD_BITER_BITER(x) ((x) & GENMASK(14, 0))
> >
> > #define EDMA_TCD_CSR_START BIT(0)
> > #define EDMA_TCD_CSR_INT_MAJOR BIT(1)
> > @@ -87,7 +53,7 @@
> >
> > #define EDMAMUX_CHCFG_DIS 0x0
> > #define EDMAMUX_CHCFG_ENBL 0x80
> > -#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
> > +#define EDMAMUX_CHCFG_SOURCE(n) ((n) & GENMASK(6, 0))
> >
> > #define DMAMUX_NR 2
> >
> > @@ -114,6 +80,31 @@ struct fsl_edma_hw_tcd {
> > __le16 biter;
> > };
> >
> > +/*
> > + * This are iomem pointers, for both v32 and v64.
>
> s/This/These/
>
Ack. Will eventually fix all in a v9. Collecting feedbacks.
> Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
>
> Best regards,
> Krzysztof
> --
> To unsubscribe from this list: send the line "unsubscribe linux-m68k" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Best Regards,
Angelo
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v8,1/3] dmaengine: fsl-edma: extract common fsl-edma code (no changes in behavior intended)
From: Angelo Dureghello @ 2018-08-06 20:40 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: vinod.koul, dmaengine, linux-arm-kernel, linux-m68k, Stefan Agner
Hi Krzysztof,
On Mon, Aug 06, 2018 at 09:43:42AM +0200, Krzysztof Kozlowski wrote:
> On 3 August 2018 at 21:32, Angelo Dureghello <angelo@sysam.it> wrote:
> > This patch adds a new fsl-edma-common module to allow new
> > mcf-edma module code to use most of the fsl-edma code.
> >
> > Signed-off-by: Angelo Dureghello <angelo@sysam.it>
> > ---
> > Changes for v2:
> > - patch splitted into 4
> > - add mcf-edma as minimal different parts from fsl-edma
> >
> > Changes for v3:
> > none
> >
> > Changes for v4:
> > - patch simplified from 4/4 into 2/2
> > - collecting all the mcf-edma-related changes
> >
> > Changes for v5:
> > none
> >
> > Changes for v6:
> > - adjusted comment header
> > - fixed bit shift with BIT()
> > - we need to free the interrupts at remove(), so removed all devm_
> > interrupt related calls
> >
> > Changes for v7:
> > none
> >
> > Changes for v8:
> > - patch rewritten from scratch, splitted into 3, common code isolated,
> > minimal changes from the original Freescale code have been done.
> > The patch has been tested with both Iris + Colibri Vybrid VF50 and
> > stmark2/mcf54415 Coldfire boards.
> > ---
> > drivers/dma/Makefile | 2 +-
> > drivers/dma/fsl-edma-common.c | 576 ++++++++++++++++++++++++++++
> > drivers/dma/fsl-edma-common.h | 196 ++++++++++
> > drivers/dma/fsl-edma.c | 697 +---------------------------------
> > 4 files changed, 774 insertions(+), 697 deletions(-)
> > create mode 100644 drivers/dma/fsl-edma-common.c
> > create mode 100644 drivers/dma/fsl-edma-common.h
> >
> > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > index 203a99d68315..66022f59fca4 100644
> > --- a/drivers/dma/Makefile
> > +++ b/drivers/dma/Makefile
> > @@ -31,7 +31,7 @@ obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/
> > obj-$(CONFIG_DW_DMAC_CORE) += dw/
> > obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
> > obj-$(CONFIG_FSL_DMA) += fsldma.o
> > -obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
> > +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
> > obj-$(CONFIG_FSL_RAID) += fsl_raid.o
> > obj-$(CONFIG_HSU_DMA) += hsu/
> > obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
> > diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> > new file mode 100644
> > index 000000000000..0ae7094f477a
> > --- /dev/null
> > +++ b/drivers/dma/fsl-edma-common.c
> > @@ -0,0 +1,576 @@
> > +// SPDX-License-Identifier: GPL-2.0
>
> You moved code from fsl-edma.c which is licensed on GPL-2.0+. I think
> that's not allowed as you effectively relicense the work on new
> license.
>
Ok, so if i undestrand, i should change that comment to
// SPDX-License-Identifier: GPL-2.0+
Right ?
> Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
>
> Best regards,
> Krzysztof
>
Best regards,
Angelo
> > +//
> > +// Copyright (c) 2013-2014 Freescale Semiconductor, Inc
> > +// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it>
> --
> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v8,3/3] dmaengine: fsl-edma: add ColdFire mcf5441x edma support
From: Angelo Dureghello @ 2018-08-06 21:01 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: vinod.koul, dmaengine, linux-arm-kernel, linux-m68k, Stefan Agner
Hi Krzysztof,
On Mon, Aug 06, 2018 at 10:09:08AM +0200, Krzysztof Kozlowski wrote:
> On 3 August 2018 at 21:32, Angelo Dureghello <angelo@sysam.it> wrote:
> > This patch adds support for ColdFire mcf5441x-family edma
> > module.
> >
> > The ColdFire edma module is slightly different from fsl-edma,
> > so a new driver is added. But most of the code is common
> > between fsl-edma and mcf-edma so it has been collected into a
> > separate common module fsl-edma-common (patch 1/3).
> >
> > Signed-off-by: Angelo Dureghello <angelo@sysam.it>
> > ---
> > Changes for v7:
> > - patch rewritten from scratch, this patch (3/3) has just been added.
> > ---
> > drivers/dma/Kconfig | 11 +
> > drivers/dma/Makefile | 1 +
> > drivers/dma/fsl-edma-common.c | 24 +-
> > drivers/dma/mcf-edma.c | 315 +++++++++++++++++++++
> > include/linux/platform_data/dma-mcf-edma.h | 38 +++
> > 5 files changed, 385 insertions(+), 4 deletions(-)
> > create mode 100644 drivers/dma/mcf-edma.c
> > create mode 100644 include/linux/platform_data/dma-mcf-edma.h
> >
> > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> > index ca1680afa20a..b45008e9c7e9 100644
> > --- a/drivers/dma/Kconfig
> > +++ b/drivers/dma/Kconfig
> > @@ -320,6 +320,17 @@ config LPC18XX_DMAMUX
> > Enable support for DMA on NXP LPC18xx/43xx platforms
> > with PL080 and multiplexed DMA request lines.
> >
> > +config MCF_EDMA
> > + tristate "Freescale eDMA engine support, ColdFire mcf5441x SoCs"
> > + depends on M5441x
> > + select DMA_ENGINE
> > + select DMA_VIRTUAL_CHANNELS
> > + help
> > + Support the Freescale ColdFire eDMA engine, 64-channel
> > + implementation that performs complex data transfers with
> > + minimal intervention from a host processor.
> > + This module can be found on Freescale ColdFire mcf5441x SoCs.
> > +
> > config MMP_PDMA
> > bool "MMP PDMA support"
> > depends on ARCH_MMP || ARCH_PXA || COMPILE_TEST
> > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > index 66022f59fca4..d97f317f4b34 100644
> > --- a/drivers/dma/Makefile
> > +++ b/drivers/dma/Makefile
> > @@ -32,6 +32,7 @@ obj-$(CONFIG_DW_DMAC_CORE) += dw/
> > obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
> > obj-$(CONFIG_FSL_DMA) += fsldma.o
> > obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
> > +obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o
> > obj-$(CONFIG_FSL_RAID) += fsl_raid.o
> > obj-$(CONFIG_HSU_DMA) += hsu/
> > obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
> > diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> > index 948a3ee51bbb..5a830a238a0c 100644
> > --- a/drivers/dma/fsl-edma-common.c
> > +++ b/drivers/dma/fsl-edma-common.c
> > @@ -102,8 +102,16 @@ static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
> > struct edma_regs *regs = &fsl_chan->edma->regs;
> > u32 ch = fsl_chan->vchan.chan.chan_id;
> >
> > - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
> > - edma_writeb(fsl_chan->edma, ch, regs->serq);
> > + if (fsl_chan->edma->version == v1) {
> > + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
> > + edma_writeb(fsl_chan->edma, ch, regs->serq);
> > + } else {
> > + /* ColdFire is big endian, and accesses natively
> > + * big endian I/O peripherals
> > + */
> > + iowrite8(EDMA_SEEI_SEEI(ch), regs->seei);
> > + iowrite8(ch, regs->serq);
> > + }
> > }
> >
> > void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
> > @@ -111,8 +119,16 @@ void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
> > struct edma_regs *regs = &fsl_chan->edma->regs;
> > u32 ch = fsl_chan->vchan.chan.chan_id;
> >
> > - edma_writeb(fsl_chan->edma, ch, regs->cerq);
> > - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
> > + if (fsl_chan->edma->version == v1) {
> > + edma_writeb(fsl_chan->edma, ch, regs->cerq);
> > + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
> > + } else {
> > + /* ColdFire is big endian, and accesses natively
> > + * big endian I/O peripherals
> > + */
> > + iowrite8(ch, regs->cerq);
> > + iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei);
> > + }
> > }
> > EXPORT_SYMBOL_GPL(fsl_edma_disable_request);
> >
> > diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c
> > new file mode 100644
> > index 000000000000..31e5317a8f90
> > --- /dev/null
> > +++ b/drivers/dma/mcf-edma.c
> > @@ -0,0 +1,315 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +//
> > +// Copyright (c) 2013-2014 Freescale Semiconductor, Inc
>
> Same comment as to 1/3 - if this is derivative work...
>
Well, in this case the driver is brand new, i maintained the
Freescale Copyright too, since i copied some code from fsl-edma.c.
> > +// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it>
> > +
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/dmaengine.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/dma-mcf-edma.h>
> > +
> > +#include "fsl-edma-common.h"
> > +
> > +#define EDMA_CHANNELS 64
> > +#define EDMA_MASK_CH(x) ((x) & GENMASK(5, 0))
> > +
> > +static irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id)
> > +{
> > + struct fsl_edma_engine *mcf_edma = dev_id;
> > + struct edma_regs *regs = &mcf_edma->regs;
> > + unsigned int ch;
> > + struct fsl_edma_chan *mcf_chan;
> > + u64 intmap;
> > +
> > + intmap = ioread32(regs->inth);
> > + intmap <<= 32;
> > + intmap |= ioread32(regs->intl);
> > + if (!intmap)
> > + return IRQ_NONE;
> > +
> > + for (ch = 0; ch < mcf_edma->n_chans; ch++) {
> > + if (intmap & BIT(ch)) {
> > + iowrite8(EDMA_MASK_CH(ch), regs->cint);
> > +
> > + mcf_chan = &mcf_edma->chans[ch];
> > +
> > + spin_lock(&mcf_chan->vchan.lock);
> > + if (!mcf_chan->edesc->iscyclic) {
> > + list_del(&mcf_chan->edesc->vdesc.node);
> > + vchan_cookie_complete(&mcf_chan->edesc->vdesc);
> > + mcf_chan->edesc = NULL;
> > + mcf_chan->status = DMA_COMPLETE;
> > + mcf_chan->idle = true;
> > + } else {
> > + vchan_cyclic_callback(&mcf_chan->edesc->vdesc);
> > + }
> > +
> > + if (!mcf_chan->edesc)
> > + fsl_edma_xfer_desc(mcf_chan);
> > +
> > + spin_unlock(&mcf_chan->vchan.lock);
> > + }
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t mcf_edma_err_handler(int irq, void *dev_id)
> > +{
> > + struct fsl_edma_engine *mcf_edma = dev_id;
> > + struct edma_regs *regs = &mcf_edma->regs;
> > + unsigned int err, ch;
> > +
> > + err = ioread32(regs->errl);
> > + if (!err)
> > + return IRQ_NONE;
> > +
> > + for (ch = 0; ch < (EDMA_CHANNELS / 2); ch++) {
> > + if (err & BIT(ch)) {
> > + fsl_edma_disable_request(&mcf_edma->chans[ch]);
> > + iowrite8(EDMA_CERR_CERR(ch), regs->cerr);
> > + mcf_edma->chans[ch].status = DMA_ERROR;
> > + mcf_edma->chans[ch].idle = true;
> > + }
> > + }
> > +
> > + err = ioread32(regs->errh);
> > + if (!err)
> > + return IRQ_NONE;
> > +
> > + for (ch = (EDMA_CHANNELS / 2); ch < EDMA_CHANNELS; ch++) {
> > + if (err & (BIT(ch - (EDMA_CHANNELS / 2)))) {
> > + fsl_edma_disable_request(&mcf_edma->chans[ch]);
> > + iowrite8(EDMA_CERR_CERR(ch), regs->cerr);
> > + mcf_edma->chans[ch].status = DMA_ERROR;
> > + mcf_edma->chans[ch].idle = true;
> > + }
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int mcf_edma_irq_init(struct platform_device *pdev,
> > + struct fsl_edma_engine *mcf_edma)
> > +{
> > + int ret = 0, i;
> > + struct resource *res;
> > +
> > + res = platform_get_resource_byname(pdev,
> > + IORESOURCE_IRQ, "edma-tx-00-15");
> > + if (!res)
> > + return -1;
> > +
> > + for (ret = 0, i = res->start; i <= res->end; ++i)
> > + ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma);
> > + if (ret)
> > + return ret;
> > +
> > + res = platform_get_resource_byname(pdev,
> > + IORESOURCE_IRQ, "edma-tx-16-55");
> > + if (!res)
> > + return -1;
> > +
> > + for (ret = 0, i = res->start; i <= res->end; ++i)
> > + ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma);
> > + if (ret)
> > + return ret;
> > +
> > + ret = platform_get_irq_byname(pdev, "edma-tx-56-63");
> > + if (ret != -ENXIO) {
> > + ret = request_irq(ret, mcf_edma_tx_handler,
> > + 0, "eDMA", mcf_edma);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + ret = platform_get_irq_byname(pdev, "edma-err");
> > + if (ret != -ENXIO) {
> > + ret = request_irq(ret, mcf_edma_err_handler,
> > + 0, "eDMA", mcf_edma);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void mcf_edma_irq_free(struct platform_device *pdev,
> > + struct fsl_edma_engine *mcf_edma)
> > +{
> > + int irq;
> > + struct resource *res;
> > +
> > + res = platform_get_resource_byname(pdev,
> > + IORESOURCE_IRQ, "edma-tx-00-15");
> > + if (res) {
> > + for (irq = res->start; irq <= res->end; irq++)
> > + free_irq(irq, mcf_edma);
> > + }
> > +
> > + res = platform_get_resource_byname(pdev,
> > + IORESOURCE_IRQ, "edma-tx-16-55");
> > + if (res) {
> > + for (irq = res->start; irq <= res->end; irq++)
> > + free_irq(irq, mcf_edma);
> > + }
> > +
> > + irq = platform_get_irq_byname(pdev, "edma-tx-56-63");
> > + if (irq != -ENXIO)
> > + free_irq(irq, mcf_edma);
> > +
> > + irq = platform_get_irq_byname(pdev, "edma-err");
> > + if (irq != -ENXIO)
> > + free_irq(irq, mcf_edma);
> > +}
> > +
> > +static int mcf_edma_probe(struct platform_device *pdev)
> > +{
> > + struct mcf_edma_platform_data *pdata;
> > + struct fsl_edma_engine *mcf_edma;
> > + struct fsl_edma_chan *mcf_chan;
> > + struct edma_regs *regs;
> > + struct resource *res;
> > + int ret, i, len, chans;
> > +
> > + pdata = dev_get_platdata(&pdev->dev);
> > + if (!pdata)
> > + return PTR_ERR(pdata);
> > +
> > + chans = pdata->dma_channels;
> > + len = sizeof(*mcf_edma) + sizeof(*mcf_chan) * chans;
> > + mcf_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
> > + if (!mcf_edma)
> > + return -ENOMEM;
> > +
> > + mcf_edma->n_chans = chans;
> > +
> > + /* Set up version for ColdFire edma */
> > + mcf_edma->version = v2;
> > + mcf_edma->big_endian = 1;
> > +
> > + if (!mcf_edma->n_chans) {
> > + dev_info(&pdev->dev, "setting default channel number to 64");
> > + mcf_edma->n_chans = 64;
> > + }
> > +
> > + mutex_init(&mcf_edma->fsl_edma_mutex);
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +
> > + mcf_edma->membase = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(mcf_edma->membase))
> > + return PTR_ERR(mcf_edma->membase);
> > +
> > + fsl_edma_setup_regs(mcf_edma);
> > + regs = &mcf_edma->regs;
> > +
> > + INIT_LIST_HEAD(&mcf_edma->dma_dev.channels);
> > + for (i = 0; i < mcf_edma->n_chans; i++) {
> > + struct fsl_edma_chan *mcf_chan = &mcf_edma->chans[i];
> > +
> > + mcf_chan->edma = mcf_edma;
> > + mcf_chan->slave_id = i;
> > + mcf_chan->idle = true;
> > + mcf_chan->vchan.desc_free = fsl_edma_free_desc;
> > + vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev);
> > + iowrite32(0x0, ®s->tcd[i].csr);
> > + }
> > +
> > + iowrite32(~0, regs->inth);
> > + iowrite32(~0, regs->intl);
> > +
> > + ret = mcf_edma_irq_init(pdev, mcf_edma);
> > + if (ret)
> > + return ret;
> > +
> > + dma_cap_set(DMA_PRIVATE, mcf_edma->dma_dev.cap_mask);
> > + dma_cap_set(DMA_SLAVE, mcf_edma->dma_dev.cap_mask);
> > + dma_cap_set(DMA_CYCLIC, mcf_edma->dma_dev.cap_mask);
> > +
> > + mcf_edma->dma_dev.dev = &pdev->dev;
> > + mcf_edma->dma_dev.device_alloc_chan_resources =
> > + fsl_edma_alloc_chan_resources;
> > + mcf_edma->dma_dev.device_free_chan_resources =
> > + fsl_edma_free_chan_resources;
> > + mcf_edma->dma_dev.device_config = fsl_edma_slave_config;
> > + mcf_edma->dma_dev.device_prep_dma_cyclic =
> > + fsl_edma_prep_dma_cyclic;
> > + mcf_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
> > + mcf_edma->dma_dev.device_tx_status = fsl_edma_tx_status;
> > + mcf_edma->dma_dev.device_pause = fsl_edma_pause;
> > + mcf_edma->dma_dev.device_resume = fsl_edma_resume;
> > + mcf_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all;
> > + mcf_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending;
> > +
> > + mcf_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS;
> > + mcf_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS;
> > + mcf_edma->dma_dev.directions =
> > + BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
> > +
> > + mcf_edma->dma_dev.filter.fn = mcf_edma_filter_fn;
> > + mcf_edma->dma_dev.filter.map = pdata->slave_map;
> > + mcf_edma->dma_dev.filter.mapcnt = pdata->slavecnt;
> > +
> > + platform_set_drvdata(pdev, mcf_edma);
> > +
> > + ret = dma_async_device_register(&mcf_edma->dma_dev);
> > + if (ret) {
> > + dev_err(&pdev->dev,
> > + "Can't register Freescale eDMA engine. (%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + /* Enable round robin arbitration */
> > + iowrite32(EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr);
> > +
> > + return 0;
> > +}
> > +
> > +static int mcf_edma_remove(struct platform_device *pdev)
> > +{
> > + struct fsl_edma_engine *mcf_edma = platform_get_drvdata(pdev);
> > +
> > + mcf_edma_irq_free(pdev, mcf_edma);
> > + fsl_edma_cleanup_vchan(&mcf_edma->dma_dev);
> > + dma_async_device_unregister(&mcf_edma->dma_dev);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver mcf_edma_driver = {
> > + .driver = {
> > + .name = "mcf-edma",
> > + },
> > + .probe = mcf_edma_probe,
> > + .remove = mcf_edma_remove,
> > +};
> > +
> > +bool mcf_edma_filter_fn(struct dma_chan *chan, void *param)
> > +{
> > + if (chan->device->dev->driver == &mcf_edma_driver.driver) {
> > + struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan);
> > +
> > + return (mcf_chan->slave_id == (int)param);
> > + }
> > +
> > + return false;
> > +}
> > +EXPORT_SYMBOL(mcf_edma_filter_fn);
> > +
> > +static int __init mcf_edma_init(void)
> > +{
> > + return platform_driver_register(&mcf_edma_driver);
> > +}
> > +subsys_initcall(mcf_edma_init);
> > +
> > +static void __exit mcf_edma_exit(void)
> > +{
> > + platform_driver_unregister(&mcf_edma_driver);
> > +}
> > +module_exit(mcf_edma_exit);
> > +
> > +MODULE_ALIAS("platform:mcf-edma");
> > +MODULE_DESCRIPTION("Freescale eDMA engine driver, ColdFire family");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/linux/platform_data/dma-mcf-edma.h b/include/linux/platform_data/dma-mcf-edma.h
> > new file mode 100644
> > index 000000000000..9a1819acb28f
> > --- /dev/null
> > +++ b/include/linux/platform_data/dma-mcf-edma.h
> > @@ -0,0 +1,38 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Freescale eDMA platform data, ColdFire SoC's family.
> > + *
> > + * Copyright (c) 2017 Angelo Dureghello <angelo@sysam.it>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#ifndef __MACH_MCF_EDMA_H__
> > +#define __MACH_MCF_EDMA_H__
>
> The guard should rather match current location, e.g.
> __LINUX_PLATFORM_DATA_MCF_EDMA_H__
>
Ack.
> Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
>
> Best regards,
> Krzysztof
> --
> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Regards,
Angelo
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v8,1/3] dmaengine: fsl-edma: extract common fsl-edma code (no changes in behavior intended)
From: Krzysztof Kozlowski @ 2018-08-07 6:02 UTC (permalink / raw)
To: Angelo Dureghello
Cc: vinod.koul, dmaengine, linux-arm-kernel, linux-m68k, Stefan Agner
On 6 August 2018 at 22:40, Angelo Dureghello <angelo@sysam.it> wrote:
> Hi Krzysztof,
>
> On Mon, Aug 06, 2018 at 09:43:42AM +0200, Krzysztof Kozlowski wrote:
>> On 3 August 2018 at 21:32, Angelo Dureghello <angelo@sysam.it> wrote:
>> > This patch adds a new fsl-edma-common module to allow new
>> > mcf-edma module code to use most of the fsl-edma code.
>> >
>> > Signed-off-by: Angelo Dureghello <angelo@sysam.it>
>> > ---
>> > Changes for v2:
>> > - patch splitted into 4
>> > - add mcf-edma as minimal different parts from fsl-edma
>> >
>> > Changes for v3:
>> > none
>> >
>> > Changes for v4:
>> > - patch simplified from 4/4 into 2/2
>> > - collecting all the mcf-edma-related changes
>> >
>> > Changes for v5:
>> > none
>> >
>> > Changes for v6:
>> > - adjusted comment header
>> > - fixed bit shift with BIT()
>> > - we need to free the interrupts at remove(), so removed all devm_
>> > interrupt related calls
>> >
>> > Changes for v7:
>> > none
>> >
>> > Changes for v8:
>> > - patch rewritten from scratch, splitted into 3, common code isolated,
>> > minimal changes from the original Freescale code have been done.
>> > The patch has been tested with both Iris + Colibri Vybrid VF50 and
>> > stmark2/mcf54415 Coldfire boards.
>> > ---
>> > drivers/dma/Makefile | 2 +-
>> > drivers/dma/fsl-edma-common.c | 576 ++++++++++++++++++++++++++++
>> > drivers/dma/fsl-edma-common.h | 196 ++++++++++
>> > drivers/dma/fsl-edma.c | 697 +---------------------------------
>> > 4 files changed, 774 insertions(+), 697 deletions(-)
>> > create mode 100644 drivers/dma/fsl-edma-common.c
>> > create mode 100644 drivers/dma/fsl-edma-common.h
>> >
>> > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
>> > index 203a99d68315..66022f59fca4 100644
>> > --- a/drivers/dma/Makefile
>> > +++ b/drivers/dma/Makefile
>> > @@ -31,7 +31,7 @@ obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/
>> > obj-$(CONFIG_DW_DMAC_CORE) += dw/
>> > obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
>> > obj-$(CONFIG_FSL_DMA) += fsldma.o
>> > -obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
>> > +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
>> > obj-$(CONFIG_FSL_RAID) += fsl_raid.o
>> > obj-$(CONFIG_HSU_DMA) += hsu/
>> > obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
>> > diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
>> > new file mode 100644
>> > index 000000000000..0ae7094f477a
>> > --- /dev/null
>> > +++ b/drivers/dma/fsl-edma-common.c
>> > @@ -0,0 +1,576 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>>
>> You moved code from fsl-edma.c which is licensed on GPL-2.0+. I think
>> that's not allowed as you effectively relicense the work on new
>> license.
>>
> Ok, so if i undestrand, i should change that comment to
> // SPDX-License-Identifier: GPL-2.0+
> Right ?
Yes, GPL-2.0+.
Best regards,
Krzysztof
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v2,9/9] dmaengine: add COMPILE_TEST for the drivers
From: Robert Jarzmik @ 2018-08-07 6:32 UTC (permalink / raw)
To: Huang Shijie
Cc: vkoul, dan.j.williams, Zubair.Kakakhel, daniel, haojian.zhuang,
patrice.chotard, linus.walleij, mcoquelin.stm32, alexandre.torgue,
Eugeniy.Paltsev, colin.king, dmaengine, linux-arm-kernel
Huang Shijie <sjhuang@iluvatar.ai> writes:
> We can do the compiling test with COMPILE_TEST.
> This patch adds the COMPILE_TEST for the drivers.
>
> Signed-off-by: Huang Shijie <sjhuang@iluvatar.ai>
For pxa :
Acked-by: Robert Jarzmik <robert.jarzmik@free.fr>
Cheers.
---
Robert
--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v2,7/9] dmaengine: pxa_dma: use dmaenginem_async_device_register to simplify the code
From: Robert Jarzmik @ 2018-08-07 6:37 UTC (permalink / raw)
To: Huang Shijie
Cc: vkoul, dan.j.williams, Zubair.Kakakhel, daniel, haojian.zhuang,
patrice.chotard, linus.walleij, mcoquelin.stm32, alexandre.torgue,
Eugeniy.Paltsev, colin.king, dmaengine, linux-arm-kernel
Huang Shijie <sjhuang@iluvatar.ai> writes:
> Use dmaenginem_async_device_register to simplify the code:
> remove dma_async_device_unregister.
>
> Signed-off-by: Huang Shijie <sjhuang@iluvatar.ai>
Acked-by: Robert Jarzmik <robert.jarzmik@free.fr>
^ permalink raw reply
* [09/46] dmaengine: cppi41: use dmaenginem_async_device_register to simplify the code
From: Peter Ujfalusi @ 2018-08-07 7:01 UTC (permalink / raw)
To: Huang Shijie, Alexandre Bailon, Tony Lindgren
Cc: vkoul, dmaengine, linux-kernel, dave.jiang, radhey.shyam.pandey,
appana.durga.rao, jmkrzyszt, gomonovych, keescook, horms+renesas,
geert+renesas, shawnguo, baoyou.xie, michal.simek, baohua,
ludovic.desroches, linus.walleij, david.brown
Hi,
On 2018-08-06 06:28, Huang Shijie wrote:
> On Fri, Aug 03, 2018 at 10:55:25AM +0300, Peter Ujfalusi wrote:
>>
>>
>> On 2018-08-03 10:19, Huang Shijie wrote:
>>> Use dmaenginem_async_device_register to simplify the code:
>>> remove dma_async_device_unregister
>>>
>>> Signed-off-by: Huang Shijie <sjhuang@iluvatar.ai>
>>> ---
>>> drivers/dma/ti/cppi41.c | 7 ++-----
>>> 1 file changed, 2 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/dma/ti/cppi41.c b/drivers/dma/ti/cppi41.c
>>> index 1497da367710..d2998a19ed2e 100644
>>> --- a/drivers/dma/ti/cppi41.c
>>> +++ b/drivers/dma/ti/cppi41.c
>>> @@ -1096,21 +1096,19 @@ static int cppi41_dma_probe(struct platform_device *pdev)
>>> goto err_chans;
>>> cdd->irq = irq;
>>>
>>> - ret = dma_async_device_register(&cdd->ddev);
>>> + ret = dmaenginem_async_device_register(&cdd->ddev);
>>> if (ret)
>>> goto err_chans;
>>>
>>> ret = of_dma_controller_register(dev->of_node,
>>> cppi41_dma_xlate, &cpp41_dma_info);
>>> if (ret)
>>> - goto err_of;
>>> + goto err_chans;
>>>
>>> pm_runtime_mark_last_busy(dev);
>>> pm_runtime_put_autosuspend(dev);
>>>
>>> return 0;
>>> -err_of:
>>> - dma_async_device_unregister(&cdd->ddev);
>>> err_chans:
>>> deinit_cppi41(dev, cdd);
>>> err_init_cppi:
>>> @@ -1132,7 +1130,6 @@ static int cppi41_dma_remove(struct platform_device *pdev)
>>> dev_err(&pdev->dev, "%s could not pm_runtime_get: %i\n",
>>> __func__, error);
>>> of_dma_controller_free(pdev->dev.of_node);
>>> - dma_async_device_unregister(&cdd->ddev);
>>
>> If I read the code right then this is not safe.
> I read the code again, and find it is okay.
>
>> We would have deinitalized cppi41 driver which is not functional, but we
>> will still have the dma device registered and if a channel is requested
>> we will have kernel crash.
> We cannot succeed to request a channel when the drv->remove() is called.
>
> Please see __device_release_driver:
> ---------------------------------------------------------------------
> if (dev->bus && dev->bus->remove)
> dev->bus->remove(dev);
> else if (drv->remove)
> drv->remove(dev);
>
> device_links_driver_cleanup(dev);
> dma_deconfigure(dev);
>
> devres_release_all(dev); ============> Devres release
> ---------------------------------------------------------------------
>
> For the DMA engine driver, there is only one case which will calls drv->remove():
> Use the rmmod(or modprobe -r).
>
> We do not use the device_link_add API for DMA engines.
> And we not manually call the device_release_driver() for DMA engines.
>
> But when we use the rmmod, the module state will be MODULE_STATE_GOING.
> In the find_candidate(), dma_chan_get() will fail.
> And we cannot get a channel.
>
> Please correct me if I am wrong :)
You are perfectly right.
It might be only me, but I like to keep the resource teardown in a
reverse order of their creation. If everything is devm then it is granted.
In case of cppi4 it looks safe after reading in to the DMAengine core,
module core and platform core code.
But does the removed three lines worth over the clarity of how the
module removal is proceeding?
Alexandre and Tony put lots of effort to the cppi4 driver, I let them
decide.
- Péter
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [09/46] dmaengine: cppi41: use dmaenginem_async_device_register to simplify the code
From: Huang Shijie @ 2018-08-07 7:16 UTC (permalink / raw)
To: Peter Ujfalusi
Cc: Alexandre Bailon, Tony Lindgren, vkoul, dmaengine, linux-kernel,
dave.jiang, radhey.shyam.pandey, appana.durga.rao, jmkrzyszt,
gomonovych, keescook, horms+renesas, geert+renesas, shawnguo,
michal.simek, baohua, ludovic.desroches, linus.walleij,
david.brown
On Tue, Aug 07, 2018 at 10:01:47AM +0300, Peter Ujfalusi wrote:
> Hi,
>
> On 2018-08-06 06:28, Huang Shijie wrote:
> It might be only me, but I like to keep the resource teardown in a
> reverse order of their creation. If everything is devm then it is granted.
Yes.
If everything is devm then it is granted..
>
> In case of cppi4 it looks safe after reading in to the DMAengine core,
> module core and platform core code.
>
> But does the removed three lines worth over the clarity of how the
> module removal is proceeding?
Please keep the driver as it is if you like the traditional way. :)
The DMA driver's maintainer has the right to decide whether
to use the dmaenginem_async_device_register or not.
Thanks
Huang Shijie
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v8,2/3] dmaengine: fsl-edma: add edma version and configurable registers
From: Stefan Agner @ 2018-08-07 8:01 UTC (permalink / raw)
To: Angelo Dureghello
Cc: vinod.koul, dmaengine, linux-arm-kernel, linux-m68k, krzk
On 03.08.2018 21:32, Angelo Dureghello wrote:
> This patch adds configurable registers (using __iomem addresses)
> to allow the use of fsl-edma-common code with slightly different
> edma module versions, as Vybrid (v1) and ColdFire (v2) are.
>
> Removal of old membase-referenced registers, amd some fixes on
> macroes are included.
s/amd/and
s/macroes/macros
Also split the macro fixes into a separate commit. We really want one
commit for each change... Its better to have 9 patches which might even
touch the same macros again, but patches which do one logical step.
---
Stefan
>
> Signed-off-by: Angelo Dureghello <angelo@sysam.it>
> ---
> Changes from v7:
> - patch rewritten from scratch, this patch (2/3) has just been added.
> ---
> drivers/dma/fsl-edma-common.c | 138 ++++++++++++++++++++++++++--------
> drivers/dma/fsl-edma-common.h | 115 ++++++++++++++--------------
> drivers/dma/fsl-edma.c | 32 ++++----
> 3 files changed, 182 insertions(+), 103 deletions(-)
>
> diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> index 0ae7094f477a..948a3ee51bbb 100644
> --- a/drivers/dma/fsl-edma-common.c
> +++ b/drivers/dma/fsl-edma-common.c
> @@ -9,6 +9,38 @@
>
> #include "fsl-edma-common.h"
>
> +#define EDMA_CR 0x00
> +#define EDMA_ES 0x04
> +#define EDMA_ERQ 0x0C
> +#define EDMA_EEI 0x14
> +#define EDMA_SERQ 0x1B
> +#define EDMA_CERQ 0x1A
> +#define EDMA_SEEI 0x19
> +#define EDMA_CEEI 0x18
> +#define EDMA_CINT 0x1F
> +#define EDMA_CERR 0x1E
> +#define EDMA_SSRT 0x1D
> +#define EDMA_CDNE 0x1C
> +#define EDMA_INTR 0x24
> +#define EDMA_ERR 0x2C
> +
> +#define EDMA64_ERQH 0x08
> +#define EDMA64_EEIH 0x10
> +#define EDMA64_SERQ 0x18
> +#define EDMA64_CERQ 0x19
> +#define EDMA64_SEEI 0x1a
> +#define EDMA64_CEEI 0x1b
> +#define EDMA64_CINT 0x1c
> +#define EDMA64_CERR 0x1d
> +#define EDMA64_SSRT 0x1e
> +#define EDMA64_CDNE 0x1f
> +#define EDMA64_INTH 0x20
> +#define EDMA64_INTL 0x24
> +#define EDMA64_ERRH 0x28
> +#define EDMA64_ERRL 0x2c
> +
> +#define EDMA_TCD 0x1000
> +
> /*
> * R/W functions for big- or little-endian registers:
> * The eDMA controller's endian is independent of the CPU core's endian.
> @@ -67,20 +99,20 @@ EXPORT_SYMBOL_GPL(to_fsl_edma_desc);
>
> static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
> {
> - void __iomem *addr = fsl_chan->edma->membase;
> + struct edma_regs *regs = &fsl_chan->edma->regs;
> u32 ch = fsl_chan->vchan.chan.chan_id;
>
> - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI);
> - edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ);
> + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
> + edma_writeb(fsl_chan->edma, ch, regs->serq);
> }
>
> void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
> {
> - void __iomem *addr = fsl_chan->edma->membase;
> + struct edma_regs *regs = &fsl_chan->edma->regs;
> u32 ch = fsl_chan->vchan.chan.chan_id;
>
> - edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ);
> - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI);
> + edma_writeb(fsl_chan->edma, ch, regs->cerq);
> + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
> }
> EXPORT_SYMBOL_GPL(fsl_edma_disable_request);
>
> @@ -208,7 +240,7 @@ static size_t fsl_edma_desc_residue(struct
> fsl_edma_chan *fsl_chan,
> struct virt_dma_desc *vdesc, bool in_progress)
> {
> struct fsl_edma_desc *edesc = fsl_chan->edesc;
> - void __iomem *addr = fsl_chan->edma->membase;
> + struct edma_regs *regs = &fsl_chan->edma->regs;
> u32 ch = fsl_chan->vchan.chan.chan_id;
> enum dma_transfer_direction dir = fsl_chan->fsc.dir;
> dma_addr_t cur_addr, dma_addr;
> @@ -224,11 +256,9 @@ static size_t fsl_edma_desc_residue(struct
> fsl_edma_chan *fsl_chan,
> return len;
>
> if (dir == DMA_MEM_TO_DEV)
> - cur_addr = edma_readl(
> - fsl_chan->edma, addr + EDMA_TCD_SADDR(ch));
> + cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].saddr);
> else
> - cur_addr = edma_readl(
> - fsl_chan->edma, addr + EDMA_TCD_DADDR(ch));
> + cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].daddr);
>
> /* figure out the finished and calculate the residue */
> for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
> @@ -285,7 +315,7 @@ static void fsl_edma_set_tcd_regs(struct
> fsl_edma_chan *fsl_chan,
> struct fsl_edma_hw_tcd *tcd)
> {
> struct fsl_edma_engine *edma = fsl_chan->edma;
> - void __iomem *addr = fsl_chan->edma->membase;
> + struct edma_regs *regs = &fsl_chan->edma->regs;
> u32 ch = fsl_chan->vchan.chan.chan_id;
>
> /*
> @@ -293,24 +323,24 @@ static void fsl_edma_set_tcd_regs(struct
> fsl_edma_chan *fsl_chan,
> * endian format. However, we need to load the TCD registers in
> * big- or little-endian obeying the eDMA engine model endian.
> */
> - edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch));
> - edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch));
> - edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch));
> + edma_writew(edma, 0, ®s->tcd[ch].csr);
> + edma_writel(edma, le32_to_cpu(tcd->saddr), ®s->tcd[ch].saddr);
> + edma_writel(edma, le32_to_cpu(tcd->daddr), ®s->tcd[ch].daddr);
>
> - edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch));
> - edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch));
> + edma_writew(edma, le16_to_cpu(tcd->attr), ®s->tcd[ch].attr);
> + edma_writew(edma, le16_to_cpu(tcd->soff), ®s->tcd[ch].soff);
>
> - edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch));
> - edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch));
> + edma_writel(edma, le32_to_cpu(tcd->nbytes), ®s->tcd[ch].nbytes);
> + edma_writel(edma, le32_to_cpu(tcd->slast), ®s->tcd[ch].slast);
>
> - edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch));
> - edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch));
> - edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch));
> + edma_writew(edma, le16_to_cpu(tcd->citer), ®s->tcd[ch].citer);
> + edma_writew(edma, le16_to_cpu(tcd->biter), ®s->tcd[ch].biter);
> + edma_writew(edma, le16_to_cpu(tcd->doff), ®s->tcd[ch].doff);
>
> - edma_writel(edma,
> - le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch));
> + edma_writel(edma, le32_to_cpu(tcd->dlast_sga),
> + ®s->tcd[ch].dlast_sga);
>
> - edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch));
> + edma_writew(edma, le16_to_cpu(tcd->csr), ®s->tcd[ch].csr);
> }
>
> static inline
> @@ -332,15 +362,15 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd
> *tcd, u32 src, u32 dst,
>
> tcd->attr = cpu_to_le16(attr);
>
> - tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff));
> + tcd->soff = cpu_to_le16(soff);
>
> - tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes));
> - tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast));
> + tcd->nbytes = cpu_to_le32(nbytes);
> + tcd->slast = cpu_to_le32(slast);
>
> tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer));
> - tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff));
> + tcd->doff = cpu_to_le16(doff);
>
> - tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga));
> + tcd->dlast_sga = cpu_to_le32(dlast_sga);
>
> tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter));
> if (major_int)
> @@ -573,4 +603,52 @@ void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
> }
> EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan);
>
> +/*
> + * On the 32 channels Vybrid/mpc577x edma version (here called "v1"),
> + * register offsets are different compared to ColdFire mcf5441x 64 channels
> + * edma (here called "v2").
> + *
> + * This function sets up register offsets as per proper declared version
> + * so must be called in xxx_edma_probe() just after setting the
> + * edma "version" and "membase" appropriately.
> + */
> +void fsl_edma_setup_regs(struct fsl_edma_engine *edma)
> +{
> + edma->regs.cr = edma->membase + EDMA_CR;
> + edma->regs.es = edma->membase + EDMA_ES;
> + edma->regs.erql = edma->membase + EDMA_ERQ;
> + edma->regs.eeil = edma->membase + EDMA_EEI;
> +
> + edma->regs.serq = edma->membase + ((edma->version == v1) ?
> + EDMA_SERQ : EDMA64_SERQ);
> + edma->regs.cerq = edma->membase + ((edma->version == v1) ?
> + EDMA_CERQ : EDMA64_CERQ);
> + edma->regs.seei = edma->membase + ((edma->version == v1) ?
> + EDMA_SEEI : EDMA64_SEEI);
> + edma->regs.ceei = edma->membase + ((edma->version == v1) ?
> + EDMA_CEEI : EDMA64_CEEI);
> + edma->regs.cint = edma->membase + ((edma->version == v1) ?
> + EDMA_CINT : EDMA64_CINT);
> + edma->regs.cerr = edma->membase + ((edma->version == v1) ?
> + EDMA_CERR : EDMA64_CERR);
> + edma->regs.ssrt = edma->membase + ((edma->version == v1) ?
> + EDMA_SSRT : EDMA64_SSRT);
> + edma->regs.cdne = edma->membase + ((edma->version == v1) ?
> + EDMA_CDNE : EDMA64_CDNE);
> + edma->regs.intl = edma->membase + ((edma->version == v1) ?
> + EDMA_INTR : EDMA64_INTL);
> + edma->regs.errl = edma->membase + ((edma->version == v1) ?
> + EDMA_ERR : EDMA64_ERRL);
> +
> + if (edma->version == v2) {
> + edma->regs.erqh = edma->membase + EDMA64_ERQH;
> + edma->regs.eeih = edma->membase + EDMA64_EEIH;
> + edma->regs.errh = edma->membase + EDMA64_ERRH;
> + edma->regs.inth = edma->membase + EDMA64_INTH;
> + }
> +
> + edma->regs.tcd = edma->membase + EDMA_TCD;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_setup_regs);
> +
> MODULE_LICENSE("GPL v2");
> diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
> index f3ea68e15b23..7a9231e7639f 100644
> --- a/drivers/dma/fsl-edma-common.h
> +++ b/drivers/dma/fsl-edma-common.h
> @@ -8,35 +8,6 @@
>
> #include "virt-dma.h"
>
> -#define EDMA_CR 0x00
> -#define EDMA_ES 0x04
> -#define EDMA_ERQ 0x0C
> -#define EDMA_EEI 0x14
> -#define EDMA_SERQ 0x1B
> -#define EDMA_CERQ 0x1A
> -#define EDMA_SEEI 0x19
> -#define EDMA_CEEI 0x18
> -#define EDMA_CINT 0x1F
> -#define EDMA_CERR 0x1E
> -#define EDMA_SSRT 0x1D
> -#define EDMA_CDNE 0x1C
> -#define EDMA_INTR 0x24
> -#define EDMA_ERR 0x2C
> -
> -#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
> -#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
> -#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
> -#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
> -#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
> -#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
> -#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
> -#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
> -#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
> -#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
> -#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
> -#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
> -#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
> -
> #define EDMA_CR_EDBG BIT(1)
> #define EDMA_CR_ERCA BIT(2)
> #define EDMA_CR_ERGA BIT(3)
> @@ -47,34 +18,29 @@
> #define EDMA_CR_ECX BIT(16)
> #define EDMA_CR_CX BIT(17)
>
> -#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
> -#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
> -#define EDMA_CINT_CINT(x) ((x) & 0x1F)
> -#define EDMA_CERR_CERR(x) ((x) & 0x1F)
> -
> -#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
> -#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
> -#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
> -#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
> -#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
> -#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
> -#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
> -#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
> -#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
> -#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
> -#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
> -#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
> -#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
> -#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
> -
> -#define EDMA_TCD_SOFF_SOFF(x) (x)
> -#define EDMA_TCD_NBYTES_NBYTES(x) (x)
> -#define EDMA_TCD_SLAST_SLAST(x) (x)
> -#define EDMA_TCD_DADDR_DADDR(x) (x)
> -#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
> -#define EDMA_TCD_DOFF_DOFF(x) (x)
> -#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
> -#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
> +#define EDMA_SEEI_SEEI(x) ((x) & GENMASK(4, 0))
> +#define EDMA_CEEI_CEEI(x) ((x) & GENMASK(4, 0))
> +#define EDMA_CINT_CINT(x) ((x) & GENMASK(4, 0))
> +#define EDMA_CERR_CERR(x) ((x) & GENMASK(4, 0))
> +
> +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & GENMASK(2, 0)))
> +#define EDMA_TCD_ATTR_DMOD(x) (((x) & GENMASK(4, 0)) << 3)
> +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & GENMASK(2, 0)) << 8)
> +#define EDMA_TCD_ATTR_SMOD(x) (((x) & GENMASK(4, 0)) << 11)
> +
> +#define EDMA_TCD_ATTR_DSIZE_8BIT 0
> +#define EDMA_TCD_ATTR_DSIZE_16BIT BIT(0)
> +#define EDMA_TCD_ATTR_DSIZE_32BIT BIT(1)
> +#define EDMA_TCD_ATTR_DSIZE_64BIT (BIT(0) | BIT(1))
> +#define EDMA_TCD_ATTR_DSIZE_32BYTE (BIT(3) | BIT(0))
> +#define EDMA_TCD_ATTR_SSIZE_8BIT 0
> +#define EDMA_TCD_ATTR_SSIZE_16BIT (EDMA_TCD_ATTR_DSIZE_16BIT << 8)
> +#define EDMA_TCD_ATTR_SSIZE_32BIT (EDMA_TCD_ATTR_DSIZE_32BIT << 8)
> +#define EDMA_TCD_ATTR_SSIZE_64BIT (EDMA_TCD_ATTR_DSIZE_64BIT << 8)
> +#define EDMA_TCD_ATTR_SSIZE_32BYTE (EDMA_TCD_ATTR_DSIZE_32BYTE << 8)
> +
> +#define EDMA_TCD_CITER_CITER(x) ((x) & GENMASK(14, 0))
> +#define EDMA_TCD_BITER_BITER(x) ((x) & GENMASK(14, 0))
>
> #define EDMA_TCD_CSR_START BIT(0)
> #define EDMA_TCD_CSR_INT_MAJOR BIT(1)
> @@ -87,7 +53,7 @@
>
> #define EDMAMUX_CHCFG_DIS 0x0
> #define EDMAMUX_CHCFG_ENBL 0x80
> -#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
> +#define EDMAMUX_CHCFG_SOURCE(n) ((n) & GENMASK(6, 0))
>
> #define DMAMUX_NR 2
>
> @@ -114,6 +80,31 @@ struct fsl_edma_hw_tcd {
> __le16 biter;
> };
>
> +/*
> + * This are iomem pointers, for both v32 and v64.
> + */
> +struct edma_regs {
> + void __iomem *cr;
> + void __iomem *es;
> + void __iomem *erqh;
> + void __iomem *erql; /* aka erq on v32 */
> + void __iomem *eeih;
> + void __iomem *eeil; /* aka eei on v32 */
> + void __iomem *seei;
> + void __iomem *ceei;
> + void __iomem *serq;
> + void __iomem *cerq;
> + void __iomem *cint;
> + void __iomem *cerr;
> + void __iomem *ssrt;
> + void __iomem *cdne;
> + void __iomem *inth;
> + void __iomem *intl;
> + void __iomem *errh;
> + void __iomem *errl;
> + struct fsl_edma_hw_tcd __iomem *tcd;
> +};
> +
> struct fsl_edma_sw_tcd {
> dma_addr_t ptcd;
> struct fsl_edma_hw_tcd *vtcd;
> @@ -147,6 +138,11 @@ struct fsl_edma_desc {
> struct fsl_edma_sw_tcd tcd[];
> };
>
> +enum edma_version {
> + v1, /* 32ch, Vybdir, mpc57x, etc */
> + v2, /* 64ch Coldfire */
> +};
> +
> struct fsl_edma_engine {
> struct dma_device dma_dev;
> void __iomem *membase;
> @@ -157,6 +153,8 @@ struct fsl_edma_engine {
> int txirq;
> int errirq;
> bool big_endian;
> + enum edma_version version;
> + struct edma_regs regs;
> struct fsl_edma_chan chans[];
> };
>
> @@ -192,5 +190,6 @@ void fsl_edma_issue_pending(struct dma_chan *chan);
> int fsl_edma_alloc_chan_resources(struct dma_chan *chan);
> void fsl_edma_free_chan_resources(struct dma_chan *chan);
> void fsl_edma_cleanup_vchan(struct dma_device *dmadev);
> +void fsl_edma_setup_regs(struct fsl_edma_engine *edma);
>
> #endif /* _FSL_EDMA_COMMON_H_ */
> diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
> index 337ad0c0ea78..34d70112fcc9 100644
> --- a/drivers/dma/fsl-edma.c
> +++ b/drivers/dma/fsl-edma.c
> @@ -28,19 +28,16 @@ static irqreturn_t fsl_edma_tx_handler(int irq,
> void *dev_id)
> {
> struct fsl_edma_engine *fsl_edma = dev_id;
> unsigned int intr, ch;
> - void __iomem *base_addr;
> + struct edma_regs *regs = &fsl_edma->regs;
> struct fsl_edma_chan *fsl_chan;
>
> - base_addr = fsl_edma->membase;
> -
> - intr = edma_readl(fsl_edma, base_addr + EDMA_INTR);
> + intr = edma_readl(fsl_edma, regs->intl);
> if (!intr)
> return IRQ_NONE;
>
> for (ch = 0; ch < fsl_edma->n_chans; ch++) {
> if (intr & (0x1 << ch)) {
> - edma_writeb(fsl_edma, EDMA_CINT_CINT(ch),
> - base_addr + EDMA_CINT);
> + edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), regs->cint);
>
> fsl_chan = &fsl_edma->chans[ch];
>
> @@ -68,16 +65,16 @@ static irqreturn_t fsl_edma_err_handler(int irq,
> void *dev_id)
> {
> struct fsl_edma_engine *fsl_edma = dev_id;
> unsigned int err, ch;
> + struct edma_regs *regs = &fsl_edma->regs;
>
> - err = edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERR);
> + err = edma_readl(fsl_edma, regs->errl);
> if (!err)
> return IRQ_NONE;
>
> for (ch = 0; ch < fsl_edma->n_chans; ch++) {
> if (err & (0x1 << ch)) {
> fsl_edma_disable_request(&fsl_edma->chans[ch]);
> - edma_writeb(fsl_edma, EDMA_CERR_CERR(ch),
> - fsl_edma->membase + EDMA_CERR);
> + edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), regs->cerr);
> fsl_edma->chans[ch].status = DMA_ERROR;
> fsl_edma->chans[ch].idle = true;
> }
> @@ -192,6 +189,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
> struct device_node *np = pdev->dev.of_node;
> struct fsl_edma_engine *fsl_edma;
> struct fsl_edma_chan *fsl_chan;
> + struct edma_regs *regs;
> struct resource *res;
> int len, chans;
> int ret, i;
> @@ -207,6 +205,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
> if (!fsl_edma)
> return -ENOMEM;
>
> + fsl_edma->version = v1;
> fsl_edma->n_chans = chans;
> mutex_init(&fsl_edma->fsl_edma_mutex);
>
> @@ -215,6 +214,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
> if (IS_ERR(fsl_edma->membase))
> return PTR_ERR(fsl_edma->membase);
>
> + fsl_edma_setup_regs(fsl_edma);
> + regs = &fsl_edma->regs;
> +
> for (i = 0; i < DMAMUX_NR; i++) {
> char clkname[32];
>
> @@ -255,11 +257,11 @@ static int fsl_edma_probe(struct platform_device *pdev)
> fsl_chan->vchan.desc_free = fsl_edma_free_desc;
> vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
>
> - edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
> + edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr);
> fsl_edma_chan_mux(fsl_chan, 0, false);
> }
>
> - edma_writel(fsl_edma, ~0, fsl_edma->membase + EDMA_INTR);
> + edma_writel(fsl_edma, ~0, regs->intl);
> ret = fsl_edma_irq_init(pdev, fsl_edma);
> if (ret)
> return ret;
> @@ -306,7 +308,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
> }
>
> /* enable round robin arbitration */
> - edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, fsl_edma->membase
> + EDMA_CR);
> + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr);
>
> return 0;
> }
> @@ -353,18 +355,18 @@ static int fsl_edma_resume_early(struct device *dev)
> {
> struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
> struct fsl_edma_chan *fsl_chan;
> + struct edma_regs *regs = &fsl_edma->regs;
> int i;
>
> for (i = 0; i < fsl_edma->n_chans; i++) {
> fsl_chan = &fsl_edma->chans[i];
> fsl_chan->pm_state = RUNNING;
> - edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
> + edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr);
> if (fsl_chan->slave_id != 0)
> fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true);
> }
>
> - edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA,
> - fsl_edma->membase + EDMA_CR);
> + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr);
>
> return 0;
> }
--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v8,1/3] dmaengine: fsl-edma: extract common fsl-edma code (no changes in behavior intended)
From: Stefan Agner @ 2018-08-07 8:08 UTC (permalink / raw)
To: Angelo Dureghello
Cc: vinod.koul, dmaengine, linux-arm-kernel, linux-m68k, krzk
On 03.08.2018 21:32, Angelo Dureghello wrote:
> This patch adds a new fsl-edma-common module to allow new
> mcf-edma module code to use most of the fsl-edma code.
>
> Signed-off-by: Angelo Dureghello <angelo@sysam.it>
> ---
> Changes for v2:
> - patch splitted into 4
> - add mcf-edma as minimal different parts from fsl-edma
>
> Changes for v3:
> none
>
> Changes for v4:
> - patch simplified from 4/4 into 2/2
> - collecting all the mcf-edma-related changes
>
> Changes for v5:
> none
>
> Changes for v6:
> - adjusted comment header
> - fixed bit shift with BIT()
> - we need to free the interrupts at remove(), so removed all devm_
> interrupt related calls
>
> Changes for v7:
> none
>
> Changes for v8:
> - patch rewritten from scratch, splitted into 3, common code isolated,
> minimal changes from the original Freescale code have been done.
> The patch has been tested with both Iris + Colibri Vybrid VF50 and
> stmark2/mcf54415 Coldfire boards.
> ---
> drivers/dma/Makefile | 2 +-
> drivers/dma/fsl-edma-common.c | 576 ++++++++++++++++++++++++++++
> drivers/dma/fsl-edma-common.h | 196 ++++++++++
> drivers/dma/fsl-edma.c | 697 +---------------------------------
> 4 files changed, 774 insertions(+), 697 deletions(-)
> create mode 100644 drivers/dma/fsl-edma-common.c
> create mode 100644 drivers/dma/fsl-edma-common.h
>
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> index 203a99d68315..66022f59fca4 100644
> --- a/drivers/dma/Makefile
> +++ b/drivers/dma/Makefile
> @@ -31,7 +31,7 @@ obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/
> obj-$(CONFIG_DW_DMAC_CORE) += dw/
> obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
> obj-$(CONFIG_FSL_DMA) += fsldma.o
> -obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
> +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
> obj-$(CONFIG_FSL_RAID) += fsl_raid.o
> obj-$(CONFIG_HSU_DMA) += hsu/
> obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
> diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> new file mode 100644
> index 000000000000..0ae7094f477a
> --- /dev/null
> +++ b/drivers/dma/fsl-edma-common.c
> @@ -0,0 +1,576 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2013-2014 Freescale Semiconductor, Inc
> +// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it>
> +
> +#include <linux/dmapool.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include "fsl-edma-common.h"
> +
> +/*
> + * R/W functions for big- or little-endian registers:
> + * The eDMA controller's endian is independent of the CPU core's endian.
> + * For the big-endian IP module, the offset for 8-bit or 16-bit registers
> + * should also be swapped opposite to that in little-endian IP.
> + */
> +u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
> +{
> + if (edma->big_endian)
> + return ioread32be(addr);
> + else
> + return ioread32(addr);
> +}
> +EXPORT_SYMBOL_GPL(edma_readl);
In 3/3 you link the common object into the two modules individually:
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o
obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o
Therefor you do not access those functions from another module (they are
within the module). No exporting should be necessary. Drop all those
exports.
If possible I would prefer if you start with cleanup/conversions, then
split-up and finally add functionality.
So ideally:
1. Use macros for preprocessor defines (where you move to BIT/GENMASK)
2. Split
3. Add EDMA macros etc.
4. Add ColdFire mcf5441x edma support
---
Stefan
> +
> +void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr)
> +{
> + /* swap the reg offset for these in big-endian mode */
> + if (edma->big_endian)
> + iowrite8(val, (void __iomem *)((unsigned long)addr ^ 0x3));
> + else
> + iowrite8(val, addr);
> +}
> +EXPORT_SYMBOL_GPL(edma_writeb);
> +
> +void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr)
> +{
> + /* swap the reg offset for these in big-endian mode */
> + if (edma->big_endian)
> + iowrite16be(val, (void __iomem *)((unsigned long)addr ^ 0x2));
> + else
> + iowrite16(val, addr);
> +}
> +EXPORT_SYMBOL_GPL(edma_writew);
> +
> +void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr)
> +{
> + if (edma->big_endian)
> + iowrite32be(val, addr);
> + else
> + iowrite32(val, addr);
> +}
> +EXPORT_SYMBOL_GPL(edma_writel);
> +
> +struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan)
> +{
> + return container_of(chan, struct fsl_edma_chan, vchan.chan);
> +}
> +EXPORT_SYMBOL_GPL(to_fsl_edma_chan);
> +
> +struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd)
> +{
> + return container_of(vd, struct fsl_edma_desc, vdesc);
> +}
> +EXPORT_SYMBOL_GPL(to_fsl_edma_desc);
> +
> +static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
> +{
> + void __iomem *addr = fsl_chan->edma->membase;
> + u32 ch = fsl_chan->vchan.chan.chan_id;
> +
> + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI);
> + edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ);
> +}
> +
> +void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
> +{
> + void __iomem *addr = fsl_chan->edma->membase;
> + u32 ch = fsl_chan->vchan.chan.chan_id;
> +
> + edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ);
> + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI);
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_disable_request);
> +
> +void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
> + unsigned int slot, bool enable)
> +{
> + u32 ch = fsl_chan->vchan.chan.chan_id;
> + void __iomem *muxaddr;
> + unsigned int chans_per_mux, ch_off;
> +
> + chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR;
> + ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
> + muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
> + slot = EDMAMUX_CHCFG_SOURCE(slot);
> +
> + if (enable)
> + iowrite8(EDMAMUX_CHCFG_ENBL | slot, muxaddr + ch_off);
> + else
> + iowrite8(EDMAMUX_CHCFG_DIS, muxaddr + ch_off);
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_chan_mux);
> +
> +static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)
> +{
> + switch (addr_width) {
> + case 1:
> + return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT;
> + case 2:
> + return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT;
> + case 4:
> + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
> + case 8:
> + return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT;
> + default:
> + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
> + }
> +}
> +
> +void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
> +{
> + struct fsl_edma_desc *fsl_desc;
> + int i;
> +
> + fsl_desc = to_fsl_edma_desc(vdesc);
> + for (i = 0; i < fsl_desc->n_tcds; i++)
> + dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd,
> + fsl_desc->tcd[i].ptcd);
> + kfree(fsl_desc);
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_free_desc);
> +
> +int fsl_edma_terminate_all(struct dma_chan *chan)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + fsl_edma_disable_request(fsl_chan);
> + fsl_chan->edesc = NULL;
> + fsl_chan->idle = true;
> + vchan_get_all_descriptors(&fsl_chan->vchan, &head);
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_terminate_all);
> +
> +int fsl_edma_pause(struct dma_chan *chan)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + if (fsl_chan->edesc) {
> + fsl_edma_disable_request(fsl_chan);
> + fsl_chan->status = DMA_PAUSED;
> + fsl_chan->idle = true;
> + }
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_pause);
> +
> +int fsl_edma_resume(struct dma_chan *chan)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + if (fsl_chan->edesc) {
> + fsl_edma_enable_request(fsl_chan);
> + fsl_chan->status = DMA_IN_PROGRESS;
> + fsl_chan->idle = false;
> + }
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_resume);
> +
> +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);
> +
> + fsl_chan->fsc.dir = cfg->direction;
> + if (cfg->direction == DMA_DEV_TO_MEM) {
> + fsl_chan->fsc.dev_addr = cfg->src_addr;
> + fsl_chan->fsc.addr_width = cfg->src_addr_width;
> + fsl_chan->fsc.burst = cfg->src_maxburst;
> + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width);
> + } else if (cfg->direction == DMA_MEM_TO_DEV) {
> + fsl_chan->fsc.dev_addr = cfg->dst_addr;
> + fsl_chan->fsc.addr_width = cfg->dst_addr_width;
> + fsl_chan->fsc.burst = cfg->dst_maxburst;
> + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width);
> + } else
> + return -EINVAL;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_slave_config);
> +
> +static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
> + struct virt_dma_desc *vdesc, bool in_progress)
> +{
> + struct fsl_edma_desc *edesc = fsl_chan->edesc;
> + void __iomem *addr = fsl_chan->edma->membase;
> + u32 ch = fsl_chan->vchan.chan.chan_id;
> + enum dma_transfer_direction dir = fsl_chan->fsc.dir;
> + dma_addr_t cur_addr, dma_addr;
> + size_t len, size;
> + int i;
> +
> + /* calculate the total size in this desc */
> + for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++)
> + len += le32_to_cpu(edesc->tcd[i].vtcd->nbytes)
> + * le16_to_cpu(edesc->tcd[i].vtcd->biter);
> +
> + if (!in_progress)
> + return len;
> +
> + if (dir == DMA_MEM_TO_DEV)
> + cur_addr = edma_readl(
> + fsl_chan->edma, addr + EDMA_TCD_SADDR(ch));
> + else
> + cur_addr = edma_readl(
> + fsl_chan->edma, addr + EDMA_TCD_DADDR(ch));
> +
> + /* figure out the finished and calculate the residue */
> + for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
> + size = le32_to_cpu(edesc->tcd[i].vtcd->nbytes)
> + * le16_to_cpu(edesc->tcd[i].vtcd->biter);
> + if (dir == DMA_MEM_TO_DEV)
> + dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr);
> + else
> + dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr);
> +
> + len -= size;
> + if (cur_addr >= dma_addr && cur_addr < dma_addr + size) {
> + len += dma_addr + size - cur_addr;
> + break;
> + }
> + }
> +
> + return len;
> +}
> +
> +enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie, struct dma_tx_state *txstate)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + struct virt_dma_desc *vdesc;
> + enum dma_status status;
> + unsigned long flags;
> +
> + status = dma_cookie_status(chan, cookie, txstate);
> + if (status == DMA_COMPLETE)
> + return status;
> +
> + if (!txstate)
> + return fsl_chan->status;
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + vdesc = vchan_find_desc(&fsl_chan->vchan, cookie);
> + if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie)
> + txstate->residue =
> + fsl_edma_desc_residue(fsl_chan, vdesc, true);
> + else if (vdesc)
> + txstate->residue =
> + fsl_edma_desc_residue(fsl_chan, vdesc, false);
> + else
> + txstate->residue = 0;
> +
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> +
> + return fsl_chan->status;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_tx_status);
> +
> +static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
> + struct fsl_edma_hw_tcd *tcd)
> +{
> + struct fsl_edma_engine *edma = fsl_chan->edma;
> + void __iomem *addr = fsl_chan->edma->membase;
> + u32 ch = fsl_chan->vchan.chan.chan_id;
> +
> + /*
> + * TCD parameters are stored in struct fsl_edma_hw_tcd in little
> + * endian format. However, we need to load the TCD registers in
> + * big- or little-endian obeying the eDMA engine model endian.
> + */
> + edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch));
> + edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch));
> + edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch));
> +
> + edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch));
> + edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch));
> +
> + edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch));
> + edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch));
> +
> + edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch));
> + edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch));
> + edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch));
> +
> + edma_writel(edma,
> + le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch));
> +
> + edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch));
> +}
> +
> +static inline
> +void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
> + u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer,
> + u16 biter, u16 doff, u32 dlast_sga, bool major_int,
> + bool disable_req, bool enable_sg)
> +{
> + u16 csr = 0;
> +
> + /*
> + * eDMA hardware SGs require the TCDs to be stored in little
> + * endian format irrespective of the register endian model.
> + * So we put the value in little endian in memory, waiting
> + * for fsl_edma_set_tcd_regs doing the swap.
> + */
> + tcd->saddr = cpu_to_le32(src);
> + tcd->daddr = cpu_to_le32(dst);
> +
> + tcd->attr = cpu_to_le16(attr);
> +
> + tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff));
> +
> + tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes));
> + tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast));
> +
> + tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer));
> + tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff));
> +
> + tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga));
> +
> + tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter));
> + if (major_int)
> + csr |= EDMA_TCD_CSR_INT_MAJOR;
> +
> + if (disable_req)
> + csr |= EDMA_TCD_CSR_D_REQ;
> +
> + if (enable_sg)
> + csr |= EDMA_TCD_CSR_E_SG;
> +
> + tcd->csr = cpu_to_le16(csr);
> +}
> +
> +static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan
> *fsl_chan,
> + int sg_len)
> +{
> + struct fsl_edma_desc *fsl_desc;
> + int i;
> +
> + fsl_desc = kzalloc(sizeof(*fsl_desc) +
> + sizeof(struct fsl_edma_sw_tcd) *
> + sg_len, GFP_NOWAIT);
> + if (!fsl_desc)
> + return NULL;
> +
> + fsl_desc->echan = fsl_chan;
> + fsl_desc->n_tcds = sg_len;
> + for (i = 0; i < sg_len; i++) {
> + fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool,
> + GFP_NOWAIT, &fsl_desc->tcd[i].ptcd);
> + if (!fsl_desc->tcd[i].vtcd)
> + goto err;
> + }
> + return fsl_desc;
> +
> +err:
> + while (--i >= 0)
> + dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd,
> + fsl_desc->tcd[i].ptcd);
> + kfree(fsl_desc);
> + return NULL;
> +}
> +
> +struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
> + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
> + size_t period_len, enum dma_transfer_direction direction,
> + unsigned long flags)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + struct fsl_edma_desc *fsl_desc;
> + dma_addr_t dma_buf_next;
> + int sg_len, i;
> + u32 src_addr, dst_addr, last_sg, nbytes;
> + u16 soff, doff, iter;
> +
> + if (!is_slave_direction(fsl_chan->fsc.dir))
> + return NULL;
> +
> + sg_len = buf_len / period_len;
> + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> + if (!fsl_desc)
> + return NULL;
> + fsl_desc->iscyclic = true;
> +
> + dma_buf_next = dma_addr;
> + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
> + iter = period_len / nbytes;
> +
> + for (i = 0; i < sg_len; i++) {
> + if (dma_buf_next >= dma_addr + buf_len)
> + dma_buf_next = dma_addr;
> +
> + /* get next sg's physical address */
> + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
> +
> + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
> + src_addr = dma_buf_next;
> + dst_addr = fsl_chan->fsc.dev_addr;
> + soff = fsl_chan->fsc.addr_width;
> + doff = 0;
> + } else {
> + src_addr = fsl_chan->fsc.dev_addr;
> + dst_addr = dma_buf_next;
> + soff = 0;
> + doff = fsl_chan->fsc.addr_width;
> + }
> +
> + fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
> + fsl_chan->fsc.attr, soff, nbytes, 0, iter,
> + iter, doff, last_sg, true, false, true);
> + dma_buf_next += period_len;
> + }
> +
> + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_prep_dma_cyclic);
> +
> +struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
> + struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction direction,
> + unsigned long flags, void *context)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + struct fsl_edma_desc *fsl_desc;
> + struct scatterlist *sg;
> + u32 src_addr, dst_addr, last_sg, nbytes;
> + u16 soff, doff, iter;
> + int i;
> +
> + if (!is_slave_direction(fsl_chan->fsc.dir))
> + return NULL;
> +
> + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> + if (!fsl_desc)
> + return NULL;
> + fsl_desc->iscyclic = false;
> +
> + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
> + for_each_sg(sgl, sg, sg_len, i) {
> + /* get next sg's physical address */
> + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
> +
> + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
> + src_addr = sg_dma_address(sg);
> + dst_addr = fsl_chan->fsc.dev_addr;
> + soff = fsl_chan->fsc.addr_width;
> + doff = 0;
> + } else {
> + src_addr = fsl_chan->fsc.dev_addr;
> + dst_addr = sg_dma_address(sg);
> + soff = 0;
> + doff = fsl_chan->fsc.addr_width;
> + }
> +
> + iter = sg_dma_len(sg) / nbytes;
> + if (i < sg_len - 1) {
> + last_sg = fsl_desc->tcd[(i + 1)].ptcd;
> + fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
> + dst_addr, fsl_chan->fsc.attr, soff,
> + nbytes, 0, iter, iter, doff, last_sg,
> + false, false, true);
> + } else {
> + last_sg = 0;
> + fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
> + dst_addr, fsl_chan->fsc.attr, soff,
> + nbytes, 0, iter, iter, doff, last_sg,
> + true, true, false);
> + }
> + }
> +
> + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_prep_slave_sg);
> +
> +void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
> +{
> + struct virt_dma_desc *vdesc;
> +
> + vdesc = vchan_next_desc(&fsl_chan->vchan);
> + if (!vdesc)
> + return;
> + fsl_chan->edesc = to_fsl_edma_desc(vdesc);
> + fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd);
> + fsl_edma_enable_request(fsl_chan);
> + fsl_chan->status = DMA_IN_PROGRESS;
> + fsl_chan->idle = false;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_xfer_desc);
> +
> +void fsl_edma_issue_pending(struct dma_chan *chan)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> +
> + if (unlikely(fsl_chan->pm_state != RUNNING)) {
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + /* cannot submit due to suspend */
> + return;
> + }
> +
> + if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
> + fsl_edma_xfer_desc(fsl_chan);
> +
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_issue_pending);
> +
> +int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> +
> + fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
> + sizeof(struct fsl_edma_hw_tcd),
> + 32, 0);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_alloc_chan_resources);
> +
> +void fsl_edma_free_chan_resources(struct dma_chan *chan)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + fsl_edma_disable_request(fsl_chan);
> + fsl_edma_chan_mux(fsl_chan, 0, false);
> + fsl_chan->edesc = NULL;
> + vchan_get_all_descriptors(&fsl_chan->vchan, &head);
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> +
> + vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
> + dma_pool_destroy(fsl_chan->tcd_pool);
> + fsl_chan->tcd_pool = NULL;
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_free_chan_resources);
> +
> +void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
> +{
> + struct fsl_edma_chan *chan, *_chan;
> +
> + list_for_each_entry_safe(chan, _chan,
> + &dmadev->channels, vchan.chan.device_node) {
> + list_del(&chan->vchan.chan.device_node);
> + tasklet_kill(&chan->vchan.task);
> + }
> +}
> +EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan);
> +
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
> new file mode 100644
> index 000000000000..f3ea68e15b23
> --- /dev/null
> +++ b/drivers/dma/fsl-edma-common.h
> @@ -0,0 +1,196 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2013-2014 Freescale Semiconductor, Inc.
> + * Copyright 2018 Angelo Dureghello <angelo@sysam.it>
> + */
> +#ifndef _FSL_EDMA_COMMON_H_
> +#define _FSL_EDMA_COMMON_H_
> +
> +#include "virt-dma.h"
> +
> +#define EDMA_CR 0x00
> +#define EDMA_ES 0x04
> +#define EDMA_ERQ 0x0C
> +#define EDMA_EEI 0x14
> +#define EDMA_SERQ 0x1B
> +#define EDMA_CERQ 0x1A
> +#define EDMA_SEEI 0x19
> +#define EDMA_CEEI 0x18
> +#define EDMA_CINT 0x1F
> +#define EDMA_CERR 0x1E
> +#define EDMA_SSRT 0x1D
> +#define EDMA_CDNE 0x1C
> +#define EDMA_INTR 0x24
> +#define EDMA_ERR 0x2C
> +
> +#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
> +#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
> +#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
> +#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
> +#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
> +#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
> +#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
> +#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
> +#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
> +#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
> +#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
> +#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
> +#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
> +
> +#define EDMA_CR_EDBG BIT(1)
> +#define EDMA_CR_ERCA BIT(2)
> +#define EDMA_CR_ERGA BIT(3)
> +#define EDMA_CR_HOE BIT(4)
> +#define EDMA_CR_HALT BIT(5)
> +#define EDMA_CR_CLM BIT(6)
> +#define EDMA_CR_EMLM BIT(7)
> +#define EDMA_CR_ECX BIT(16)
> +#define EDMA_CR_CX BIT(17)
> +
> +#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
> +#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
> +#define EDMA_CINT_CINT(x) ((x) & 0x1F)
> +#define EDMA_CERR_CERR(x) ((x) & 0x1F)
> +
> +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
> +#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
> +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
> +#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
> +#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
> +#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
> +#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
> +#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
> +#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
> +#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
> +#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
> +#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
> +#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
> +#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
> +
> +#define EDMA_TCD_SOFF_SOFF(x) (x)
> +#define EDMA_TCD_NBYTES_NBYTES(x) (x)
> +#define EDMA_TCD_SLAST_SLAST(x) (x)
> +#define EDMA_TCD_DADDR_DADDR(x) (x)
> +#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
> +#define EDMA_TCD_DOFF_DOFF(x) (x)
> +#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
> +#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
> +
> +#define EDMA_TCD_CSR_START BIT(0)
> +#define EDMA_TCD_CSR_INT_MAJOR BIT(1)
> +#define EDMA_TCD_CSR_INT_HALF BIT(2)
> +#define EDMA_TCD_CSR_D_REQ BIT(3)
> +#define EDMA_TCD_CSR_E_SG BIT(4)
> +#define EDMA_TCD_CSR_E_LINK BIT(5)
> +#define EDMA_TCD_CSR_ACTIVE BIT(6)
> +#define EDMA_TCD_CSR_DONE BIT(7)
> +
> +#define EDMAMUX_CHCFG_DIS 0x0
> +#define EDMAMUX_CHCFG_ENBL 0x80
> +#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
> +
> +#define DMAMUX_NR 2
> +
> +#define FSL_EDMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
> + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
> + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
> + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
> +enum fsl_edma_pm_state {
> + RUNNING = 0,
> + SUSPENDED,
> +};
> +
> +struct fsl_edma_hw_tcd {
> + __le32 saddr;
> + __le16 soff;
> + __le16 attr;
> + __le32 nbytes;
> + __le32 slast;
> + __le32 daddr;
> + __le16 doff;
> + __le16 citer;
> + __le32 dlast_sga;
> + __le16 csr;
> + __le16 biter;
> +};
> +
> +struct fsl_edma_sw_tcd {
> + dma_addr_t ptcd;
> + struct fsl_edma_hw_tcd *vtcd;
> +};
> +
> +struct fsl_edma_slave_config {
> + enum dma_transfer_direction dir;
> + enum dma_slave_buswidth addr_width;
> + u32 dev_addr;
> + u32 burst;
> + u32 attr;
> +};
> +
> +struct fsl_edma_chan {
> + struct virt_dma_chan vchan;
> + enum dma_status status;
> + enum fsl_edma_pm_state pm_state;
> + bool idle;
> + u32 slave_id;
> + struct fsl_edma_engine *edma;
> + struct fsl_edma_desc *edesc;
> + struct fsl_edma_slave_config fsc;
> + struct dma_pool *tcd_pool;
> +};
> +
> +struct fsl_edma_desc {
> + struct virt_dma_desc vdesc;
> + struct fsl_edma_chan *echan;
> + bool iscyclic;
> + unsigned int n_tcds;
> + struct fsl_edma_sw_tcd tcd[];
> +};
> +
> +struct fsl_edma_engine {
> + struct dma_device dma_dev;
> + void __iomem *membase;
> + void __iomem *muxbase[DMAMUX_NR];
> + struct clk *muxclk[DMAMUX_NR];
> + struct mutex fsl_edma_mutex;
> + u32 n_chans;
> + int txirq;
> + int errirq;
> + bool big_endian;
> + struct fsl_edma_chan chans[];
> +};
> +
> +u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr);
> +void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr);
> +void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr);
> +void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr);
> +
> +struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan);
> +struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd);
> +
> +void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan);
> +void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
> + unsigned int slot, bool enable);
> +void fsl_edma_free_desc(struct virt_dma_desc *vdesc);
> +int fsl_edma_terminate_all(struct dma_chan *chan);
> +int fsl_edma_pause(struct dma_chan *chan);
> +int fsl_edma_resume(struct dma_chan *chan);
> +int fsl_edma_slave_config(struct dma_chan *chan,
> + struct dma_slave_config *cfg);
> +enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie, struct dma_tx_state *txstate);
> +struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
> + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
> + size_t period_len, enum dma_transfer_direction direction,
> + unsigned long flags);
> +struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
> + struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction direction,
> + unsigned long flags, void *context);
> +void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan);
> +void fsl_edma_issue_pending(struct dma_chan *chan);
> +int fsl_edma_alloc_chan_resources(struct dma_chan *chan);
> +void fsl_edma_free_chan_resources(struct dma_chan *chan);
> +void fsl_edma_cleanup_vchan(struct dma_device *dmadev);
> +
> +#endif /* _FSL_EDMA_COMMON_H_ */
> diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
> index c7568869284e..337ad0c0ea78 100644
> --- a/drivers/dma/fsl-edma.c
> +++ b/drivers/dma/fsl-edma.c
> @@ -13,653 +13,16 @@
> * option) any later version.
> */
>
> -#include <linux/init.h>
> #include <linux/module.h>
> #include <linux/interrupt.h>
> #include <linux/clk.h>
> -#include <linux/dma-mapping.h>
> -#include <linux/dmapool.h>
> -#include <linux/slab.h>
> -#include <linux/spinlock.h>
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/of_address.h>
> #include <linux/of_irq.h>
> #include <linux/of_dma.h>
>
> -#include "virt-dma.h"
> -
> -#define EDMA_CR 0x00
> -#define EDMA_ES 0x04
> -#define EDMA_ERQ 0x0C
> -#define EDMA_EEI 0x14
> -#define EDMA_SERQ 0x1B
> -#define EDMA_CERQ 0x1A
> -#define EDMA_SEEI 0x19
> -#define EDMA_CEEI 0x18
> -#define EDMA_CINT 0x1F
> -#define EDMA_CERR 0x1E
> -#define EDMA_SSRT 0x1D
> -#define EDMA_CDNE 0x1C
> -#define EDMA_INTR 0x24
> -#define EDMA_ERR 0x2C
> -
> -#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
> -#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
> -#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
> -#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
> -#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
> -#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
> -#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
> -#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
> -#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
> -#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
> -#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
> -#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
> -#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
> -
> -#define EDMA_CR_EDBG BIT(1)
> -#define EDMA_CR_ERCA BIT(2)
> -#define EDMA_CR_ERGA BIT(3)
> -#define EDMA_CR_HOE BIT(4)
> -#define EDMA_CR_HALT BIT(5)
> -#define EDMA_CR_CLM BIT(6)
> -#define EDMA_CR_EMLM BIT(7)
> -#define EDMA_CR_ECX BIT(16)
> -#define EDMA_CR_CX BIT(17)
> -
> -#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
> -#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
> -#define EDMA_CINT_CINT(x) ((x) & 0x1F)
> -#define EDMA_CERR_CERR(x) ((x) & 0x1F)
> -
> -#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
> -#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
> -#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
> -#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
> -#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
> -#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
> -#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
> -#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
> -#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
> -#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
> -#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
> -#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
> -#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
> -#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
> -
> -#define EDMA_TCD_SOFF_SOFF(x) (x)
> -#define EDMA_TCD_NBYTES_NBYTES(x) (x)
> -#define EDMA_TCD_SLAST_SLAST(x) (x)
> -#define EDMA_TCD_DADDR_DADDR(x) (x)
> -#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
> -#define EDMA_TCD_DOFF_DOFF(x) (x)
> -#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
> -#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
> -
> -#define EDMA_TCD_CSR_START BIT(0)
> -#define EDMA_TCD_CSR_INT_MAJOR BIT(1)
> -#define EDMA_TCD_CSR_INT_HALF BIT(2)
> -#define EDMA_TCD_CSR_D_REQ BIT(3)
> -#define EDMA_TCD_CSR_E_SG BIT(4)
> -#define EDMA_TCD_CSR_E_LINK BIT(5)
> -#define EDMA_TCD_CSR_ACTIVE BIT(6)
> -#define EDMA_TCD_CSR_DONE BIT(7)
> -
> -#define EDMAMUX_CHCFG_DIS 0x0
> -#define EDMAMUX_CHCFG_ENBL 0x80
> -#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
> -
> -#define DMAMUX_NR 2
> -
> -#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
> - BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
> - BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
> - BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)
> -enum fsl_edma_pm_state {
> - RUNNING = 0,
> - SUSPENDED,
> -};
> -
> -struct fsl_edma_hw_tcd {
> - __le32 saddr;
> - __le16 soff;
> - __le16 attr;
> - __le32 nbytes;
> - __le32 slast;
> - __le32 daddr;
> - __le16 doff;
> - __le16 citer;
> - __le32 dlast_sga;
> - __le16 csr;
> - __le16 biter;
> -};
> -
> -struct fsl_edma_sw_tcd {
> - dma_addr_t ptcd;
> - struct fsl_edma_hw_tcd *vtcd;
> -};
> -
> -struct fsl_edma_slave_config {
> - enum dma_transfer_direction dir;
> - enum dma_slave_buswidth addr_width;
> - u32 dev_addr;
> - u32 burst;
> - u32 attr;
> -};
> -
> -struct fsl_edma_chan {
> - struct virt_dma_chan vchan;
> - enum dma_status status;
> - enum fsl_edma_pm_state pm_state;
> - bool idle;
> - u32 slave_id;
> - struct fsl_edma_engine *edma;
> - struct fsl_edma_desc *edesc;
> - struct fsl_edma_slave_config fsc;
> - struct dma_pool *tcd_pool;
> -};
> -
> -struct fsl_edma_desc {
> - struct virt_dma_desc vdesc;
> - struct fsl_edma_chan *echan;
> - bool iscyclic;
> - unsigned int n_tcds;
> - struct fsl_edma_sw_tcd tcd[];
> -};
> -
> -struct fsl_edma_engine {
> - struct dma_device dma_dev;
> - void __iomem *membase;
> - void __iomem *muxbase[DMAMUX_NR];
> - struct clk *muxclk[DMAMUX_NR];
> - struct mutex fsl_edma_mutex;
> - u32 n_chans;
> - int txirq;
> - int errirq;
> - bool big_endian;
> - struct fsl_edma_chan chans[];
> -};
> -
> -/*
> - * R/W functions for big- or little-endian registers:
> - * The eDMA controller's endian is independent of the CPU core's endian.
> - * For the big-endian IP module, the offset for 8-bit or 16-bit registers
> - * should also be swapped opposite to that in little-endian IP.
> - */
> -
> -static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
> -{
> - if (edma->big_endian)
> - return ioread32be(addr);
> - else
> - return ioread32(addr);
> -}
> -
> -static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void
> __iomem *addr)
> -{
> - /* swap the reg offset for these in big-endian mode */
> - if (edma->big_endian)
> - iowrite8(val, (void __iomem *)((unsigned long)addr ^ 0x3));
> - else
> - iowrite8(val, addr);
> -}
> -
> -static void edma_writew(struct fsl_edma_engine *edma, u16 val, void
> __iomem *addr)
> -{
> - /* swap the reg offset for these in big-endian mode */
> - if (edma->big_endian)
> - iowrite16be(val, (void __iomem *)((unsigned long)addr ^ 0x2));
> - else
> - iowrite16(val, addr);
> -}
> -
> -static void edma_writel(struct fsl_edma_engine *edma, u32 val, void
> __iomem *addr)
> -{
> - if (edma->big_endian)
> - iowrite32be(val, addr);
> - else
> - iowrite32(val, addr);
> -}
> -
> -static struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan)
> -{
> - return container_of(chan, struct fsl_edma_chan, vchan.chan);
> -}
> -
> -static struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd)
> -{
> - return container_of(vd, struct fsl_edma_desc, vdesc);
> -}
> -
> -static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
> -{
> - void __iomem *addr = fsl_chan->edma->membase;
> - u32 ch = fsl_chan->vchan.chan.chan_id;
> -
> - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI);
> - edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ);
> -}
> -
> -static void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
> -{
> - void __iomem *addr = fsl_chan->edma->membase;
> - u32 ch = fsl_chan->vchan.chan.chan_id;
> -
> - edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ);
> - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI);
> -}
> -
> -static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
> - unsigned int slot, bool enable)
> -{
> - u32 ch = fsl_chan->vchan.chan.chan_id;
> - void __iomem *muxaddr;
> - unsigned chans_per_mux, ch_off;
> -
> - chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR;
> - ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
> - muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
> - slot = EDMAMUX_CHCFG_SOURCE(slot);
> -
> - if (enable)
> - iowrite8(EDMAMUX_CHCFG_ENBL | slot, muxaddr + ch_off);
> - else
> - iowrite8(EDMAMUX_CHCFG_DIS, muxaddr + ch_off);
> -}
> -
> -static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)
> -{
> - switch (addr_width) {
> - case 1:
> - return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT;
> - case 2:
> - return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT;
> - case 4:
> - return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
> - case 8:
> - return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT;
> - default:
> - return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
> - }
> -}
> -
> -static void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
> -{
> - struct fsl_edma_desc *fsl_desc;
> - int i;
> -
> - fsl_desc = to_fsl_edma_desc(vdesc);
> - for (i = 0; i < fsl_desc->n_tcds; i++)
> - dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd,
> - fsl_desc->tcd[i].ptcd);
> - kfree(fsl_desc);
> -}
> -
> -static int fsl_edma_terminate_all(struct dma_chan *chan)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - unsigned long flags;
> - LIST_HEAD(head);
> -
> - spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> - fsl_edma_disable_request(fsl_chan);
> - fsl_chan->edesc = NULL;
> - fsl_chan->idle = true;
> - vchan_get_all_descriptors(&fsl_chan->vchan, &head);
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> - vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
> - return 0;
> -}
> -
> -static int fsl_edma_pause(struct dma_chan *chan)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - unsigned long flags;
> -
> - spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> - if (fsl_chan->edesc) {
> - fsl_edma_disable_request(fsl_chan);
> - fsl_chan->status = DMA_PAUSED;
> - fsl_chan->idle = true;
> - }
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> - return 0;
> -}
> -
> -static int fsl_edma_resume(struct dma_chan *chan)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - unsigned long flags;
> -
> - spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> - if (fsl_chan->edesc) {
> - fsl_edma_enable_request(fsl_chan);
> - fsl_chan->status = DMA_IN_PROGRESS;
> - fsl_chan->idle = false;
> - }
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> - return 0;
> -}
> -
> -static 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);
> -
> - fsl_chan->fsc.dir = cfg->direction;
> - if (cfg->direction == DMA_DEV_TO_MEM) {
> - fsl_chan->fsc.dev_addr = cfg->src_addr;
> - fsl_chan->fsc.addr_width = cfg->src_addr_width;
> - fsl_chan->fsc.burst = cfg->src_maxburst;
> - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width);
> - } else if (cfg->direction == DMA_MEM_TO_DEV) {
> - fsl_chan->fsc.dev_addr = cfg->dst_addr;
> - fsl_chan->fsc.addr_width = cfg->dst_addr_width;
> - fsl_chan->fsc.burst = cfg->dst_maxburst;
> - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width);
> - } else {
> - return -EINVAL;
> - }
> - return 0;
> -}
> -
> -static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
> - struct virt_dma_desc *vdesc, bool in_progress)
> -{
> - struct fsl_edma_desc *edesc = fsl_chan->edesc;
> - void __iomem *addr = fsl_chan->edma->membase;
> - u32 ch = fsl_chan->vchan.chan.chan_id;
> - enum dma_transfer_direction dir = fsl_chan->fsc.dir;
> - dma_addr_t cur_addr, dma_addr;
> - size_t len, size;
> - int i;
> -
> - /* calculate the total size in this desc */
> - for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++)
> - len += le32_to_cpu(edesc->tcd[i].vtcd->nbytes)
> - * le16_to_cpu(edesc->tcd[i].vtcd->biter);
> -
> - if (!in_progress)
> - return len;
> -
> - if (dir == DMA_MEM_TO_DEV)
> - cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_SADDR(ch));
> - else
> - cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_DADDR(ch));
> -
> - /* figure out the finished and calculate the residue */
> - for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
> - size = le32_to_cpu(edesc->tcd[i].vtcd->nbytes)
> - * le16_to_cpu(edesc->tcd[i].vtcd->biter);
> - if (dir == DMA_MEM_TO_DEV)
> - dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr);
> - else
> - dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr);
> -
> - len -= size;
> - if (cur_addr >= dma_addr && cur_addr < dma_addr + size) {
> - len += dma_addr + size - cur_addr;
> - break;
> - }
> - }
> -
> - return len;
> -}
> -
> -static enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
> - dma_cookie_t cookie, struct dma_tx_state *txstate)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - struct virt_dma_desc *vdesc;
> - enum dma_status status;
> - unsigned long flags;
> -
> - status = dma_cookie_status(chan, cookie, txstate);
> - if (status == DMA_COMPLETE)
> - return status;
> -
> - if (!txstate)
> - return fsl_chan->status;
> -
> - spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> - vdesc = vchan_find_desc(&fsl_chan->vchan, cookie);
> - if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie)
> - txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true);
> - else if (vdesc)
> - txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false);
> - else
> - txstate->residue = 0;
> -
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> -
> - return fsl_chan->status;
> -}
> -
> -static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
> - struct fsl_edma_hw_tcd *tcd)
> -{
> - struct fsl_edma_engine *edma = fsl_chan->edma;
> - void __iomem *addr = fsl_chan->edma->membase;
> - u32 ch = fsl_chan->vchan.chan.chan_id;
> -
> - /*
> - * TCD parameters are stored in struct fsl_edma_hw_tcd in little
> - * endian format. However, we need to load the TCD registers in
> - * big- or little-endian obeying the eDMA engine model endian.
> - */
> - edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch));
> - edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch));
> - edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch));
> -
> - edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch));
> - edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch));
> -
> - edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch));
> - edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch));
> -
> - edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch));
> - edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch));
> - edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch));
> -
> - edma_writel(edma, le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch));
> -
> - edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch));
> -}
> -
> -static inline
> -void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
> - u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer,
> - u16 biter, u16 doff, u32 dlast_sga, bool major_int,
> - bool disable_req, bool enable_sg)
> -{
> - u16 csr = 0;
> -
> - /*
> - * eDMA hardware SGs require the TCDs to be stored in little
> - * endian format irrespective of the register endian model.
> - * So we put the value in little endian in memory, waiting
> - * for fsl_edma_set_tcd_regs doing the swap.
> - */
> - tcd->saddr = cpu_to_le32(src);
> - tcd->daddr = cpu_to_le32(dst);
> -
> - tcd->attr = cpu_to_le16(attr);
> -
> - tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff));
> -
> - tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes));
> - tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast));
> -
> - tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer));
> - tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff));
> -
> - tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga));
> -
> - tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter));
> - if (major_int)
> - csr |= EDMA_TCD_CSR_INT_MAJOR;
> -
> - if (disable_req)
> - csr |= EDMA_TCD_CSR_D_REQ;
> -
> - if (enable_sg)
> - csr |= EDMA_TCD_CSR_E_SG;
> -
> - tcd->csr = cpu_to_le16(csr);
> -}
> -
> -static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan
> *fsl_chan,
> - int sg_len)
> -{
> - struct fsl_edma_desc *fsl_desc;
> - int i;
> -
> - fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct
> fsl_edma_sw_tcd) * sg_len,
> - GFP_NOWAIT);
> - if (!fsl_desc)
> - return NULL;
> -
> - fsl_desc->echan = fsl_chan;
> - fsl_desc->n_tcds = sg_len;
> - for (i = 0; i < sg_len; i++) {
> - fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool,
> - GFP_NOWAIT, &fsl_desc->tcd[i].ptcd);
> - if (!fsl_desc->tcd[i].vtcd)
> - goto err;
> - }
> - return fsl_desc;
> -
> -err:
> - while (--i >= 0)
> - dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd,
> - fsl_desc->tcd[i].ptcd);
> - kfree(fsl_desc);
> - return NULL;
> -}
> -
> -static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
> - struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
> - size_t period_len, enum dma_transfer_direction direction,
> - unsigned long flags)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - struct fsl_edma_desc *fsl_desc;
> - dma_addr_t dma_buf_next;
> - int sg_len, i;
> - u32 src_addr, dst_addr, last_sg, nbytes;
> - u16 soff, doff, iter;
> -
> - if (!is_slave_direction(fsl_chan->fsc.dir))
> - return NULL;
> -
> - sg_len = buf_len / period_len;
> - fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> - if (!fsl_desc)
> - return NULL;
> - fsl_desc->iscyclic = true;
> -
> - dma_buf_next = dma_addr;
> - nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
> - iter = period_len / nbytes;
> -
> - for (i = 0; i < sg_len; i++) {
> - if (dma_buf_next >= dma_addr + buf_len)
> - dma_buf_next = dma_addr;
> -
> - /* get next sg's physical address */
> - last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
> -
> - if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
> - src_addr = dma_buf_next;
> - dst_addr = fsl_chan->fsc.dev_addr;
> - soff = fsl_chan->fsc.addr_width;
> - doff = 0;
> - } else {
> - src_addr = fsl_chan->fsc.dev_addr;
> - dst_addr = dma_buf_next;
> - soff = 0;
> - doff = fsl_chan->fsc.addr_width;
> - }
> -
> - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
> - fsl_chan->fsc.attr, soff, nbytes, 0, iter,
> - iter, doff, last_sg, true, false, true);
> - dma_buf_next += period_len;
> - }
> -
> - return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
> -}
> -
> -static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
> - struct dma_chan *chan, struct scatterlist *sgl,
> - unsigned int sg_len, enum dma_transfer_direction direction,
> - unsigned long flags, void *context)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - struct fsl_edma_desc *fsl_desc;
> - struct scatterlist *sg;
> - u32 src_addr, dst_addr, last_sg, nbytes;
> - u16 soff, doff, iter;
> - int i;
> -
> - if (!is_slave_direction(fsl_chan->fsc.dir))
> - return NULL;
> -
> - fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> - if (!fsl_desc)
> - return NULL;
> - fsl_desc->iscyclic = false;
> -
> - nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
> - for_each_sg(sgl, sg, sg_len, i) {
> - /* get next sg's physical address */
> - last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
> -
> - if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
> - src_addr = sg_dma_address(sg);
> - dst_addr = fsl_chan->fsc.dev_addr;
> - soff = fsl_chan->fsc.addr_width;
> - doff = 0;
> - } else {
> - src_addr = fsl_chan->fsc.dev_addr;
> - dst_addr = sg_dma_address(sg);
> - soff = 0;
> - doff = fsl_chan->fsc.addr_width;
> - }
> -
> - iter = sg_dma_len(sg) / nbytes;
> - if (i < sg_len - 1) {
> - last_sg = fsl_desc->tcd[(i + 1)].ptcd;
> - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
> - dst_addr, fsl_chan->fsc.attr, soff,
> - nbytes, 0, iter, iter, doff, last_sg,
> - false, false, true);
> - } else {
> - last_sg = 0;
> - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
> - dst_addr, fsl_chan->fsc.attr, soff,
> - nbytes, 0, iter, iter, doff, last_sg,
> - true, true, false);
> - }
> - }
> -
> - return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
> -}
> -
> -static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
> -{
> - struct virt_dma_desc *vdesc;
> -
> - vdesc = vchan_next_desc(&fsl_chan->vchan);
> - if (!vdesc)
> - return;
> - fsl_chan->edesc = to_fsl_edma_desc(vdesc);
> - fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd);
> - fsl_edma_enable_request(fsl_chan);
> - fsl_chan->status = DMA_IN_PROGRESS;
> - fsl_chan->idle = false;
> -}
> +#include "fsl-edma-common.h"
>
> static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
> {
> @@ -730,25 +93,6 @@ static irqreturn_t fsl_edma_irq_handler(int irq,
> void *dev_id)
> return fsl_edma_err_handler(irq, dev_id);
> }
>
> -static void fsl_edma_issue_pending(struct dma_chan *chan)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - unsigned long flags;
> -
> - spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> -
> - if (unlikely(fsl_chan->pm_state != RUNNING)) {
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> - /* cannot submit due to suspend */
> - return;
> - }
> -
> - if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
> - fsl_edma_xfer_desc(fsl_chan);
> -
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> -}
> -
> static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
> struct of_dma *ofdma)
> {
> @@ -781,34 +125,6 @@ static struct dma_chan *fsl_edma_xlate(struct
> of_phandle_args *dma_spec,
> return NULL;
> }
>
> -static int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> -
> - fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
> - sizeof(struct fsl_edma_hw_tcd),
> - 32, 0);
> - return 0;
> -}
> -
> -static void fsl_edma_free_chan_resources(struct dma_chan *chan)
> -{
> - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> - unsigned long flags;
> - LIST_HEAD(head);
> -
> - spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> - fsl_edma_disable_request(fsl_chan);
> - fsl_edma_chan_mux(fsl_chan, 0, false);
> - fsl_chan->edesc = NULL;
> - vchan_get_all_descriptors(&fsl_chan->vchan, &head);
> - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> -
> - vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
> - dma_pool_destroy(fsl_chan->tcd_pool);
> - fsl_chan->tcd_pool = NULL;
> -}
> -
> static int
> fsl_edma_irq_init(struct platform_device *pdev, struct
> fsl_edma_engine *fsl_edma)
> {
> @@ -995,17 +311,6 @@ static int fsl_edma_probe(struct platform_device *pdev)
> return 0;
> }
>
> -static void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
> -{
> - struct fsl_edma_chan *chan, *_chan;
> -
> - list_for_each_entry_safe(chan, _chan,
> - &dmadev->channels, vchan.chan.device_node) {
> - list_del(&chan->vchan.chan.device_node);
> - tasklet_kill(&chan->vchan.task);
> - }
> -}
> -
> static int fsl_edma_remove(struct platform_device *pdev)
> {
> struct device_node *np = pdev->dev.of_node;
--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v4,01/18] doc: dt-bindings: jz4780-dma: Update bindings to reflect driver changes
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The driver now expects the devicetree to supply a second memory
resource. This resource is mandatory on the newly supported SoCs.
For the JZ4780, new devicetree code must also provide it, although the
driver is still compatible with older devicetree binaries.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
Documentation/devicetree/bindings/dma/jz4780-dma.txt | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
v2: New patch in this series; regroups the changes made to the
jz4780-dma.txt doc file in the previous version of the patchset.
v3: Updated example to comply with devicetree specification
v4: Removed driver-specific comment in commit message
diff --git a/Documentation/devicetree/bindings/dma/jz4780-dma.txt b/Documentation/devicetree/bindings/dma/jz4780-dma.txt
index f25feee62b15..14f33305e194 100644
--- a/Documentation/devicetree/bindings/dma/jz4780-dma.txt
+++ b/Documentation/devicetree/bindings/dma/jz4780-dma.txt
@@ -2,8 +2,13 @@
Required properties:
-- compatible: Should be "ingenic,jz4780-dma"
-- reg: Should contain the DMA controller registers location and length.
+- compatible: Should be one of:
+ * ingenic,jz4740-dma
+ * ingenic,jz4725b-dma
+ * ingenic,jz4770-dma
+ * ingenic,jz4780-dma
+- reg: Should contain the DMA channel registers location and length, followed
+ by the DMA controller registers location and length.
- interrupts: Should contain the interrupt specifier of the DMA controller.
- interrupt-parent: Should be the phandle of the interrupt controller that
- clocks: Should contain a clock specifier for the JZ4780 PDMA clock.
@@ -20,9 +25,10 @@ Optional properties:
Example:
-dma: dma@13420000 {
+dma: dma-controller@13420000 {
compatible = "ingenic,jz4780-dma";
- reg = <0x13420000 0x10000>;
+ reg = <0x13420000 0x400
+ 0x13421000 0x40>;
interrupt-parent = <&intc>;
interrupts = <10>;
^ permalink raw reply related
* [v4,02/18] dmaengine: dma-jz4780: Return error if not probed from DT
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The driver calls clk_get() with the clock name set to NULL, which means
that the driver could only work when probed from devicetree. From now
on, we explicitly require the driver to be probed from devicetree.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 5 +++++
1 file changed, 5 insertions(+)
v2: New patch
v3: No change
v4: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 85820a2d69d4..987899610b46 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -761,6 +761,11 @@ static int jz4780_dma_probe(struct platform_device *pdev)
struct resource *res;
int i, ret;
+ if (!dev->of_node) {
+ dev_err(dev, "This driver must be probed from devicetree\n");
+ return -EINVAL;
+ }
+
jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL);
if (!jzdma)
return -ENOMEM;
^ permalink raw reply related
* [v4,03/18] dmaengine: dma-jz4780: Avoid hardcoding number of channels
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
As part of the work to support various other Ingenic JZ47xx SoC versions,
which don't feature the same number of DMA channels per core, we now
deduce the number of DMA channels available from the devicetree
compatible string.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
v2: - don't hardcode jz_version to ID_JZ4780 when not probed from DT,
because it cannot happen
- Put SoC-specific data into a jz4780_dma_soc_data structure
v3: No change
v4: Remove jz_version; pass the jz4780_dma_soc_data structure pointer
as devicetree match data, and use device_get_match_data().
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 987899610b46..9ec22cf15a33 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -23,8 +24,6 @@
#include "dmaengine.h"
#include "virt-dma.h"
-#define JZ_DMA_NR_CHANNELS 32
-
/* Global registers. */
#define JZ_DMA_REG_DMAC 0x1000
#define JZ_DMA_REG_DIRQP 0x1004
@@ -135,14 +134,19 @@ struct jz4780_dma_chan {
unsigned int curr_hwdesc;
};
+struct jz4780_dma_soc_data {
+ unsigned int nb_channels;
+};
+
struct jz4780_dma_dev {
struct dma_device dma_device;
void __iomem *base;
struct clk *clk;
unsigned int irq;
+ const struct jz4780_dma_soc_data *soc_data;
uint32_t chan_reserved;
- struct jz4780_dma_chan chan[JZ_DMA_NR_CHANNELS];
+ struct jz4780_dma_chan chan[];
};
struct jz4780_dma_filter_data {
@@ -648,7 +652,7 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP);
- for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) {
+ for (i = 0; i < jzdma->soc_data->nb_channels; i++) {
if (!(pending & (1<<i)))
continue;
@@ -728,7 +732,7 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
data.channel = dma_spec->args[1];
if (data.channel > -1) {
- if (data.channel >= JZ_DMA_NR_CHANNELS) {
+ if (data.channel >= jzdma->soc_data->nb_channels) {
dev_err(jzdma->dma_device.dev,
"device requested non-existent channel %u\n",
data.channel);
@@ -755,6 +759,7 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
static int jz4780_dma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ const struct jz4780_dma_soc_data *soc_data;
struct jz4780_dma_dev *jzdma;
struct jz4780_dma_chan *jzchan;
struct dma_device *dd;
@@ -766,10 +771,17 @@ static int jz4780_dma_probe(struct platform_device *pdev)
return -EINVAL;
}
- jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL);
+ soc_data = device_get_match_data(dev);
+ if (!soc_data)
+ return -EINVAL;
+
+ jzdma = devm_kzalloc(dev, sizeof(*jzdma)
+ + sizeof(*jzdma->chan) * soc_data->nb_channels,
+ GFP_KERNEL);
if (!jzdma)
return -ENOMEM;
+ jzdma->soc_data = soc_data;
platform_set_drvdata(pdev, jzdma);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -844,7 +856,7 @@ static int jz4780_dma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&dd->channels);
- for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) {
+ for (i = 0; i < soc_data->nb_channels; i++) {
jzchan = &jzdma->chan[i];
jzchan->id = i;
@@ -889,15 +901,19 @@ static int jz4780_dma_remove(struct platform_device *pdev)
free_irq(jzdma->irq, jzdma);
- for (i = 0; i < JZ_DMA_NR_CHANNELS; i++)
+ for (i = 0; i < jzdma->soc_data->nb_channels; i++)
tasklet_kill(&jzdma->chan[i].vchan.task);
dma_async_device_unregister(&jzdma->dma_device);
return 0;
}
+static const struct jz4780_dma_soc_data jz4780_dma_soc_data = {
+ .nb_channels = 32,
+};
+
static const struct of_device_id jz4780_dma_dt_match[] = {
- { .compatible = "ingenic,jz4780-dma", .data = NULL },
+ { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data },
{},
};
MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match);
^ permalink raw reply related
* [v4,04/18] dmaengine: dma-jz4780: Separate chan/ctrl registers
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The register area of the JZ4780 DMA core can be split into different
sections for different purposes:
* one set of registers is used to perform actions at the DMA core level,
that will generally affect all channels;
* one set of registers per DMA channel, to perform actions at the DMA
channel level, that will only affect the channel in question.
The problem rises when trying to support new versions of the JZ47xx
Ingenic SoC. For instance, the JZ4770 has two DMA cores, each one
with six DMA channels, and the register sets are interleaved:
<DMA0 chan regs> <DMA1 chan regs> <DMA0 ctrl regs> <DMA1 ctrl regs>
By using one memory resource for the channel-specific registers and
one memory resource for the core-specific registers, we can support
the JZ4770, by initializing the driver once per DMA core with different
addresses.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 112 ++++++++++++++++++++++++++++++-----------------
1 file changed, 71 insertions(+), 41 deletions(-)
v2: - Add a fallback mechanism for JZ4780 if the second memory resource
was not supplied in the devicetree.
- The documentation update was moved to patch 01/17
v3: No change
v4: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 9ec22cf15a33..adf4d5efced6 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -25,26 +25,26 @@
#include "virt-dma.h"
/* Global registers. */
-#define JZ_DMA_REG_DMAC 0x1000
-#define JZ_DMA_REG_DIRQP 0x1004
-#define JZ_DMA_REG_DDR 0x1008
-#define JZ_DMA_REG_DDRS 0x100c
-#define JZ_DMA_REG_DMACP 0x101c
-#define JZ_DMA_REG_DSIRQP 0x1020
-#define JZ_DMA_REG_DSIRQM 0x1024
-#define JZ_DMA_REG_DCIRQP 0x1028
-#define JZ_DMA_REG_DCIRQM 0x102c
+#define JZ_DMA_REG_DMAC 0x00
+#define JZ_DMA_REG_DIRQP 0x04
+#define JZ_DMA_REG_DDR 0x08
+#define JZ_DMA_REG_DDRS 0x0c
+#define JZ_DMA_REG_DMACP 0x1c
+#define JZ_DMA_REG_DSIRQP 0x20
+#define JZ_DMA_REG_DSIRQM 0x24
+#define JZ_DMA_REG_DCIRQP 0x28
+#define JZ_DMA_REG_DCIRQM 0x2c
/* Per-channel registers. */
#define JZ_DMA_REG_CHAN(n) (n * 0x20)
-#define JZ_DMA_REG_DSA(n) (0x00 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DTA(n) (0x04 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DTC(n) (0x08 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DRT(n) (0x0c + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DCS(n) (0x10 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DCM(n) (0x14 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DDA(n) (0x18 + JZ_DMA_REG_CHAN(n))
-#define JZ_DMA_REG_DSD(n) (0x1c + JZ_DMA_REG_CHAN(n))
+#define JZ_DMA_REG_DSA 0x00
+#define JZ_DMA_REG_DTA 0x04
+#define JZ_DMA_REG_DTC 0x08
+#define JZ_DMA_REG_DRT 0x0c
+#define JZ_DMA_REG_DCS 0x10
+#define JZ_DMA_REG_DCM 0x14
+#define JZ_DMA_REG_DDA 0x18
+#define JZ_DMA_REG_DSD 0x1c
#define JZ_DMA_DMAC_DMAE BIT(0)
#define JZ_DMA_DMAC_AR BIT(2)
@@ -85,6 +85,8 @@
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+#define JZ4780_DMA_CTRL_OFFSET 0x1000
+
/**
* struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller.
* @dcm: value for the DCM (channel command) register
@@ -140,7 +142,8 @@ struct jz4780_dma_soc_data {
struct jz4780_dma_dev {
struct dma_device dma_device;
- void __iomem *base;
+ void __iomem *chn_base;
+ void __iomem *ctrl_base;
struct clk *clk;
unsigned int irq;
const struct jz4780_dma_soc_data *soc_data;
@@ -173,16 +176,28 @@ static inline struct jz4780_dma_dev *jz4780_dma_chan_parent(
dma_device);
}
-static inline uint32_t jz4780_dma_readl(struct jz4780_dma_dev *jzdma,
+static inline uint32_t jz4780_dma_chn_readl(struct jz4780_dma_dev *jzdma,
+ unsigned int chn, unsigned int reg)
+{
+ return readl(jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn));
+}
+
+static inline void jz4780_dma_chn_writel(struct jz4780_dma_dev *jzdma,
+ unsigned int chn, unsigned int reg, uint32_t val)
+{
+ writel(val, jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn));
+}
+
+static inline uint32_t jz4780_dma_ctrl_readl(struct jz4780_dma_dev *jzdma,
unsigned int reg)
{
- return readl(jzdma->base + reg);
+ return readl(jzdma->ctrl_base + reg);
}
-static inline void jz4780_dma_writel(struct jz4780_dma_dev *jzdma,
+static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
unsigned int reg, uint32_t val)
{
- writel(val, jzdma->base + reg);
+ writel(val, jzdma->ctrl_base + reg);
}
static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
@@ -477,17 +492,18 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
}
/* Use 8-word descriptors. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), JZ_DMA_DCS_DES8);
+ jz4780_dma_chn_writel(jzdma, jzchan->id,
+ JZ_DMA_REG_DCS, JZ_DMA_DCS_DES8);
/* Write descriptor address and initiate descriptor fetch. */
desc_phys = jzchan->desc->desc_phys +
(jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc));
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DDA(jzchan->id), desc_phys);
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id));
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DDA, desc_phys);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id));
/* Enable the channel. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id),
- JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS,
+ JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
}
static void jz4780_dma_issue_pending(struct dma_chan *chan)
@@ -513,7 +529,7 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave(&jzchan->vchan.lock, flags);
/* Clear the DMA status and stop the transfer. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0);
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
if (jzchan->desc) {
vchan_terminate_vdesc(&jzchan->desc->vdesc);
jzchan->desc = NULL;
@@ -562,8 +578,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
residue += desc->desc[i].dtc << jzchan->transfer_shift;
if (next_sg != 0) {
- count = jz4780_dma_readl(jzdma,
- JZ_DMA_REG_DTC(jzchan->id));
+ count = jz4780_dma_chn_readl(jzdma, jzchan->id,
+ JZ_DMA_REG_DTC);
residue += count << jzchan->transfer_shift;
}
@@ -610,8 +626,8 @@ static void jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma,
spin_lock(&jzchan->vchan.lock);
- dcs = jz4780_dma_readl(jzdma, JZ_DMA_REG_DCS(jzchan->id));
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0);
+ dcs = jz4780_dma_chn_readl(jzdma, jzchan->id, JZ_DMA_REG_DCS);
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
if (dcs & JZ_DMA_DCS_AR) {
dev_warn(&jzchan->vchan.chan.dev->device,
@@ -650,7 +666,7 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
uint32_t pending, dmac;
int i;
- pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP);
+ pending = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DIRQP);
for (i = 0; i < jzdma->soc_data->nb_channels; i++) {
if (!(pending & (1<<i)))
@@ -660,12 +676,12 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data)
}
/* Clear halt and address error status of all channels. */
- dmac = jz4780_dma_readl(jzdma, JZ_DMA_REG_DMAC);
+ dmac = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DMAC);
dmac &= ~(JZ_DMA_DMAC_HLT | JZ_DMA_DMAC_AR);
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, dmac);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, dmac);
/* Clear interrupt pending status. */
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DIRQP, 0);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DIRQP, 0);
return IRQ_HANDLED;
}
@@ -790,9 +806,23 @@ static int jz4780_dma_probe(struct platform_device *pdev)
return -EINVAL;
}
- jzdma->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(jzdma->base))
- return PTR_ERR(jzdma->base);
+ jzdma->chn_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(jzdma->chn_base))
+ return PTR_ERR(jzdma->chn_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ jzdma->ctrl_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(jzdma->ctrl_base))
+ return PTR_ERR(jzdma->ctrl_base);
+ } else {
+ /*
+ * On JZ4780, if the second memory resource was not supplied,
+ * assume we're using an old devicetree, and calculate the
+ * offset to the control registers.
+ */
+ jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET;
+ }
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
@@ -850,9 +880,9 @@ static int jz4780_dma_probe(struct platform_device *pdev)
* Also set the FMSC bit - it increases MSC performance, so it makes
* little sense not to enable it.
*/
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC,
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
- jz4780_dma_writel(jzdma, JZ_DMA_REG_DMACP, 0);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
INIT_LIST_HEAD(&dd->channels);
^ permalink raw reply related
* [v4,05/18] dmaengine: dma-jz4780: Use 4-word descriptors
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The only information we use in the 8-word version of the hardware DMA
descriptor that is not present in the 4-word version is the transfer
type, aka. the ID of the source or recipient device.
Since the transfer type will never change for a DMA channel in use,
we can just set it once for all in the corresponding DMA register
before starting any transfer.
This has several benefits:
* the driver will handle twice as many hardware DMA descriptors;
* the driver is closer to support the JZ4740, which only supports 4-word
hardware DMA descriptors;
* the JZ4770 SoC needs the transfer type to be set in the corresponding
DMA register anyway, even if 8-word descriptors are in use.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
v2: No change
v3: No change
v4: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index adf4d5efced6..7683de9fb9ee 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -95,17 +95,12 @@
* @dtc: transfer count (number of blocks of the transfer size specified in DCM
* to transfer) in the low 24 bits, offset of the next descriptor from the
* descriptor base address in the upper 8 bits.
- * @sd: target/source stride difference (in stride transfer mode).
- * @drt: request type
*/
struct jz4780_dma_hwdesc {
uint32_t dcm;
uint32_t dsa;
uint32_t dta;
uint32_t dtc;
- uint32_t sd;
- uint32_t drt;
- uint32_t reserved[2];
};
/* Size of allocations for hardware descriptor blocks. */
@@ -281,7 +276,6 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
desc->dcm = JZ_DMA_DCM_SAI;
desc->dsa = addr;
desc->dta = config->dst_addr;
- desc->drt = jzchan->transfer_type;
width = config->dst_addr_width;
maxburst = config->dst_maxburst;
@@ -289,7 +283,6 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
desc->dcm = JZ_DMA_DCM_DAI;
desc->dsa = config->src_addr;
desc->dta = addr;
- desc->drt = jzchan->transfer_type;
width = config->src_addr_width;
maxburst = config->src_maxburst;
@@ -434,9 +427,10 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy(
tsz = jz4780_dma_transfer_size(dest | src | len,
&jzchan->transfer_shift);
+ jzchan->transfer_type = JZ_DMA_DRT_AUTO;
+
desc->desc[0].dsa = src;
desc->desc[0].dta = dest;
- desc->desc[0].drt = JZ_DMA_DRT_AUTO;
desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI |
tsz << JZ_DMA_DCM_TSZ_SHIFT |
JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT |
@@ -491,9 +485,12 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
(jzchan->curr_hwdesc + 1) % jzchan->desc->count;
}
- /* Use 8-word descriptors. */
- jz4780_dma_chn_writel(jzdma, jzchan->id,
- JZ_DMA_REG_DCS, JZ_DMA_DCS_DES8);
+ /* Use 4-word descriptors. */
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
+
+ /* Set transfer type. */
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT,
+ jzchan->transfer_type);
/* Write descriptor address and initiate descriptor fetch. */
desc_phys = jzchan->desc->desc_phys +
@@ -503,7 +500,7 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
/* Enable the channel. */
jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS,
- JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE);
+ JZ_DMA_DCS_CTE);
}
static void jz4780_dma_issue_pending(struct dma_chan *chan)
^ permalink raw reply related
* [v4,06/18] dmaengine: dma-jz4780: Don't depend on MACH_JZ4780
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
If we make this driver depend on MACH_JZ4780, that means it can be
enabled only if we're building a kernel specially crafted for a
JZ4780-based board, while most GNU/Linux distributions will want one
generic MIPS kernel that works on multiple boards.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---
drivers/dma/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
v3: New patch
v4: No change
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ca1680afa20a..0680e1eb0d73 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -143,7 +143,7 @@ config DMA_JZ4740
config DMA_JZ4780
tristate "JZ4780 DMA support"
- depends on MACH_JZ4780 || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
^ permalink raw reply related
* [v4,07/18] dmaengine: dma-jz4780: Add support for the JZ4770 SoC
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The JZ4770 SoC has two DMA cores, each one featuring six DMA channels.
The major change is that each channel's clock can be enabled or disabled
through register writes.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 61 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 54 insertions(+), 7 deletions(-)
v2: - Move transfer_ord_max variable to the new jz4780_dma_soc_data
structure
- The documentation update is now in patch 01/17
v3: The Kconfig update was dropped thanks to patch 06/18
v4: Pass jz4780_dma_soc_data structure pointer directly as devicetree
match data; Add a 'flags' field in that structure and macros for
it, to replace checking vs. the SoC version.
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 7683de9fb9ee..184d1a2bf9ba 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -29,6 +29,9 @@
#define JZ_DMA_REG_DIRQP 0x04
#define JZ_DMA_REG_DDR 0x08
#define JZ_DMA_REG_DDRS 0x0c
+#define JZ_DMA_REG_DCKE 0x10
+#define JZ_DMA_REG_DCKES 0x14
+#define JZ_DMA_REG_DCKEC 0x18
#define JZ_DMA_REG_DMACP 0x1c
#define JZ_DMA_REG_DSIRQP 0x20
#define JZ_DMA_REG_DSIRQM 0x24
@@ -87,6 +90,11 @@
#define JZ4780_DMA_CTRL_OFFSET 0x1000
+/* macros for use with jz4780_dma_soc_data.flags */
+#define JZ_SOC_DATA_ALLOW_LEGACY_DT BIT(0)
+#define JZ_SOC_DATA_PROGRAMMABLE_DMA BIT(1)
+#define JZ_SOC_DATA_PER_CHAN_PM BIT(2)
+
/**
* struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller.
* @dcm: value for the DCM (channel command) register
@@ -133,6 +141,8 @@ struct jz4780_dma_chan {
struct jz4780_dma_soc_data {
unsigned int nb_channels;
+ unsigned int transfer_ord_max;
+ unsigned long flags;
};
struct jz4780_dma_dev {
@@ -195,6 +205,20 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
writel(val, jzdma->ctrl_base + reg);
}
+static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma,
+ unsigned int chn)
+{
+ if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM)
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
+}
+
+static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
+ unsigned int chn)
+{
+ if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM)
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
+}
+
static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
struct jz4780_dma_chan *jzchan, unsigned int count,
enum dma_transaction_type type)
@@ -229,8 +253,10 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
kfree(desc);
}
-static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift)
+static uint32_t jz4780_dma_transfer_size(struct jz4780_dma_chan *jzchan,
+ unsigned long val, uint32_t *shift)
{
+ struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
int ord = ffs(val) - 1;
/*
@@ -242,8 +268,8 @@ static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift)
*/
if (ord == 3)
ord = 2;
- else if (ord > 7)
- ord = 7;
+ else if (ord > jzdma->soc_data->transfer_ord_max)
+ ord = jzdma->soc_data->transfer_ord_max;
*shift = ord;
@@ -295,7 +321,7 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
* divisible by the transfer size, and we must not use more than the
* maximum burst specified by the user.
*/
- tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst),
+ tsz = jz4780_dma_transfer_size(jzchan, addr | len | (width * maxburst),
&jzchan->transfer_shift);
switch (width) {
@@ -424,7 +450,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy(
if (!desc)
return NULL;
- tsz = jz4780_dma_transfer_size(dest | src | len,
+ tsz = jz4780_dma_transfer_size(jzchan, dest | src | len,
&jzchan->transfer_shift);
jzchan->transfer_type = JZ_DMA_DRT_AUTO;
@@ -485,6 +511,9 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
(jzchan->curr_hwdesc + 1) % jzchan->desc->count;
}
+ /* Enable the channel's clock. */
+ jz4780_dma_chan_enable(jzdma, jzchan->id);
+
/* Use 4-word descriptors. */
jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
@@ -532,6 +561,8 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
jzchan->desc = NULL;
}
+ jz4780_dma_chan_disable(jzdma, jzchan->id);
+
vchan_get_all_descriptors(&jzchan->vchan, &head);
spin_unlock_irqrestore(&jzchan->vchan.lock, flags);
@@ -543,8 +574,10 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
static void jz4780_dma_synchronize(struct dma_chan *chan)
{
struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
+ struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
vchan_synchronize(&jzchan->vchan);
+ jz4780_dma_chan_disable(jzdma, jzchan->id);
}
static int jz4780_dma_config(struct dma_chan *chan,
@@ -812,13 +845,16 @@ static int jz4780_dma_probe(struct platform_device *pdev)
jzdma->ctrl_base = devm_ioremap_resource(dev, res);
if (IS_ERR(jzdma->ctrl_base))
return PTR_ERR(jzdma->ctrl_base);
- } else {
+ } else if (soc_data->flags & JZ_SOC_DATA_ALLOW_LEGACY_DT) {
/*
* On JZ4780, if the second memory resource was not supplied,
* assume we're using an old devicetree, and calculate the
* offset to the control registers.
*/
jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET;
+ } else {
+ dev_err(dev, "failed to get I/O memory\n");
+ return -EINVAL;
}
ret = platform_get_irq(pdev, 0);
@@ -879,7 +915,9 @@ static int jz4780_dma_probe(struct platform_device *pdev)
*/
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
- jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
+
+ if (soc_data->flags & JZ_SOC_DATA_PROGRAMMABLE_DMA)
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
INIT_LIST_HEAD(&dd->channels);
@@ -935,11 +973,20 @@ static int jz4780_dma_remove(struct platform_device *pdev)
return 0;
}
+static const struct jz4780_dma_soc_data jz4770_dma_soc_data = {
+ .nb_channels = 6,
+ .transfer_ord_max = 6,
+ .flags = JZ_SOC_DATA_PER_CHAN_PM,
+};
+
static const struct jz4780_dma_soc_data jz4780_dma_soc_data = {
.nb_channels = 32,
+ .transfer_ord_max = 7,
+ .flags = JZ_SOC_DATA_ALLOW_LEGACY_DT | JZ_SOC_DATA_PROGRAMMABLE_DMA,
};
static const struct of_device_id jz4780_dma_dt_match[] = {
+ { .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data },
{ .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data },
{},
};
^ permalink raw reply related
* [v4,08/18] dmaengine: dma-jz4780: Add support for the JZ4740 SoC
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The JZ4740 SoC has a single DMA core starring six DMA channels.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 6 ++++++
1 file changed, 6 insertions(+)
v2: The documentation update is now in patch 01/17
v3: The Kconfig update was dropped thanks to patch 06/18
v4: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 184d1a2bf9ba..2d194dfa697e 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -973,6 +973,11 @@ static int jz4780_dma_remove(struct platform_device *pdev)
return 0;
}
+static const struct jz4780_dma_soc_data jz4740_dma_soc_data = {
+ .nb_channels = 6,
+ .transfer_ord_max = 5,
+};
+
static const struct jz4780_dma_soc_data jz4770_dma_soc_data = {
.nb_channels = 6,
.transfer_ord_max = 6,
@@ -986,6 +991,7 @@ static const struct jz4780_dma_soc_data jz4780_dma_soc_data = {
};
static const struct of_device_id jz4780_dma_dt_match[] = {
+ { .compatible = "ingenic,jz4740-dma", .data = &jz4740_dma_soc_data },
{ .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data },
{ .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data },
{},
^ permalink raw reply related
* [v4,09/18] dmaengine: dma-jz4780: Add support for the JZ4725B SoC
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
The JZ4725B has one DMA core starring six DMA channels.
As for the JZ4770, each DMA channel's clock can be enabled with
a register write, the difference here being that once started, it
is not possible to turn it off.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
v2: - Add comments about channel enabling/disabling
- The documentation update is now in patch 01/17
v3: No change
v4: Drop the SoC version ID and use the 'flags' field of the
jz4780_dma_soc_data structure
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 2d194dfa697e..565971c2a33c 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -94,6 +94,7 @@
#define JZ_SOC_DATA_ALLOW_LEGACY_DT BIT(0)
#define JZ_SOC_DATA_PROGRAMMABLE_DMA BIT(1)
#define JZ_SOC_DATA_PER_CHAN_PM BIT(2)
+#define JZ_SOC_DATA_NO_DCKES_DCKEC BIT(3)
/**
* struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller.
@@ -208,14 +209,23 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma,
unsigned int chn)
{
- if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM)
- jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
+ if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) {
+ unsigned int reg;
+
+ if (jzdma->soc_data->flags & JZ_SOC_DATA_NO_DCKES_DCKEC)
+ reg = JZ_DMA_REG_DCKE;
+ else
+ reg = JZ_DMA_REG_DCKES;
+
+ jz4780_dma_ctrl_writel(jzdma, reg, BIT(chn));
+ }
}
static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
unsigned int chn)
{
- if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM)
+ if ((jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) &&
+ !(jzdma->soc_data->flags & JZ_SOC_DATA_NO_DCKES_DCKEC))
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
}
@@ -978,6 +988,12 @@ static const struct jz4780_dma_soc_data jz4740_dma_soc_data = {
.transfer_ord_max = 5,
};
+static const struct jz4780_dma_soc_data jz4725b_dma_soc_data = {
+ .nb_channels = 6,
+ .transfer_ord_max = 5,
+ .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC,
+};
+
static const struct jz4780_dma_soc_data jz4770_dma_soc_data = {
.nb_channels = 6,
.transfer_ord_max = 6,
@@ -992,6 +1008,7 @@ static const struct jz4780_dma_soc_data jz4780_dma_soc_data = {
static const struct of_device_id jz4780_dma_dt_match[] = {
{ .compatible = "ingenic,jz4740-dma", .data = &jz4740_dma_soc_data },
+ { .compatible = "ingenic,jz4725b-dma", .data = &jz4725b_dma_soc_data },
{ .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data },
{ .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data },
{},
^ permalink raw reply related
* [v4,10/18] dmaengine: dma-jz4780: Enable Fast DMA to the AIC
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips
With the fast DMA bit set, the DMA will transfer twice as much data
per clock period to the AIC, so there is little point not to set it.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
v2: No change
v3: No change
v4: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 565971c2a33c..3a4d0a4b550d 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -52,6 +52,7 @@
#define JZ_DMA_DMAC_DMAE BIT(0)
#define JZ_DMA_DMAC_AR BIT(2)
#define JZ_DMA_DMAC_HLT BIT(3)
+#define JZ_DMA_DMAC_FAIC BIT(27)
#define JZ_DMA_DMAC_FMSC BIT(31)
#define JZ_DMA_DRT_AUTO 0x8
@@ -923,8 +924,8 @@ static int jz4780_dma_probe(struct platform_device *pdev)
* Also set the FMSC bit - it increases MSC performance, so it makes
* little sense not to enable it.
*/
- jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
- JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE |
+ JZ_DMA_DMAC_FAIC | JZ_DMA_DMAC_FMSC);
if (soc_data->flags & JZ_SOC_DATA_PROGRAMMABLE_DMA)
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
^ permalink raw reply related
* [v4,11/18] dmaengine: dma-jz4780: Add missing residue DTC mask
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips,
Daniel Silsby
From: Daniel Silsby <dansilsby@gmail.com>
The 'dtc' word in jz DMA descriptors contains two fields: The
lowest 24 bits are the transfer count, and upper 8 bits are the DOA
offset to next descriptor. The upper 8 bits are now correctly masked
off when computing residue in jz4780_dma_desc_residue(). Note that
reads of the DTCn hardware reg are automatically masked this way.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
v2: No change
v3: No change
v4: Add my Signed-off-by
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 3a4d0a4b550d..a4292ac4c686 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -616,7 +616,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
residue = 0;
for (i = next_sg; i < desc->count; i++)
- residue += desc->desc[i].dtc << jzchan->transfer_shift;
+ residue += (desc->desc[i].dtc & 0xffffff) <<
+ jzchan->transfer_shift;
if (next_sg != 0) {
count = jz4780_dma_chn_readl(jzdma, jzchan->id,
^ permalink raw reply related
* [v4,12/18] dmaengine: dma-jz4780: Simplify jz4780_dma_desc_residue()
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips,
Daniel Silsby
From: Daniel Silsby <dansilsby@gmail.com>
Simple cleanup, no changes to actual logic here.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
v2: No change
v3: No change
v4: Add my Signed-off-by
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index a4292ac4c686..019cebc2e5e5 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -610,22 +610,17 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
struct jz4780_dma_desc *desc, unsigned int next_sg)
{
struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
- unsigned int residue, count;
+ unsigned int count = 0;
unsigned int i;
- residue = 0;
-
for (i = next_sg; i < desc->count; i++)
- residue += (desc->desc[i].dtc & 0xffffff) <<
- jzchan->transfer_shift;
+ count += desc->desc[i].dtc & 0xffffff;
- if (next_sg != 0) {
- count = jz4780_dma_chn_readl(jzdma, jzchan->id,
+ if (next_sg != 0)
+ count += jz4780_dma_chn_readl(jzdma, jzchan->id,
JZ_DMA_REG_DTC);
- residue += count << jzchan->transfer_shift;
- }
- return residue;
+ return count << jzchan->transfer_shift;
}
static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
^ permalink raw reply related
* [v4,13/18] dmaengine: dma-jz4780: Set DTCn register explicitly
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips,
Daniel Silsby
From: Daniel Silsby <dansilsby@gmail.com>
Normally, we wouldn't set the channel transfer count register directly
when using descriptor-driven transfers. However, there is no harm in
doing so, and it allows jz4780_dma_desc_residue() to report the correct
residue of an ongoing transfer, no matter when it is called.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 9 +++++++++
1 file changed, 9 insertions(+)
v2: No change
v3: No change
v4: Add my Signed-off-by
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 019cebc2e5e5..d03711834a44 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -532,6 +532,15 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT,
jzchan->transfer_type);
+ /*
+ * Set the transfer count. This is redundant for a descriptor-driven
+ * transfer. However, there can be a delay between the transfer start
+ * time and when DTCn reg contains the new transfer count. Setting
+ * it explicitly ensures residue is computed correctly at all times.
+ */
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DTC,
+ jzchan->desc->desc[jzchan->curr_hwdesc].dtc);
+
/* Write descriptor address and initiate descriptor fetch. */
desc_phys = jzchan->desc->desc_phys +
(jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc));
^ permalink raw reply related
* [v4,14/18] dmaengine: dma-jz4780: Further residue status fix
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips,
Daniel Silsby
From: Daniel Silsby <dansilsby@gmail.com>
Func jz4780_dma_desc_residue() expects the index to the next hw
descriptor as its last parameter. Caller func jz4780_dma_tx_status(),
however, applied modulus before passing it. When the current hw
descriptor was last in the list, the index passed became zero.
The resulting excess of reported residue especially caused problems
with cyclic DMA transfer clients, i.e. ALSA AIC audio output, which
rely on this for determining current DMA location within buffer.
Combined with the recent and related residue-reporting fixes, spurious
ALSA audio underruns on jz4770 hardware are now fixed.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
v2: No change
v3: No change
v4: Add my Signed-off-by
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index d03711834a44..351dc8268515 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -653,7 +653,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
to_jz4780_dma_desc(vdesc), 0);
} else if (cookie == jzchan->desc->vdesc.tx.cookie) {
txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
- (jzchan->curr_hwdesc + 1) % jzchan->desc->count);
+ jzchan->curr_hwdesc + 1);
} else
txstate->residue = 0;
^ permalink raw reply related
* [v4,15/18] dmaengine: dma-jz4780: Use dma_set_residue()
From: Paul Cercueil @ 2018-08-07 11:42 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Paul Cercueil, dmaengine, devicetree, linux-kernel, linux-mips,
Daniel Silsby
From: Daniel Silsby <dansilsby@gmail.com>
This is the standard method provided by dmaengine header.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
v2: No change
v3: No change
v4: Add my Signed-off-by
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 351dc8268515..32e10edc55e0 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -639,6 +639,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
struct virt_dma_desc *vdesc;
enum dma_status status;
unsigned long flags;
+ unsigned long residue = 0;
status = dma_cookie_status(chan, cookie, txstate);
if ((status == DMA_COMPLETE) || (txstate == NULL))
@@ -649,13 +650,13 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
vdesc = vchan_find_desc(&jzchan->vchan, cookie);
if (vdesc) {
/* On the issued list, so hasn't been processed yet */
- txstate->residue = jz4780_dma_desc_residue(jzchan,
+ residue = jz4780_dma_desc_residue(jzchan,
to_jz4780_dma_desc(vdesc), 0);
} else if (cookie == jzchan->desc->vdesc.tx.cookie) {
- txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
+ residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
jzchan->curr_hwdesc + 1);
- } else
- txstate->residue = 0;
+ }
+ dma_set_residue(txstate, residue);
if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc
&& jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT))
^ 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