* [v10,3/4] dmaengine: fsl-edma: fix macros
From: Angelo Dureghello @ 2018-08-19 17:27 UTC (permalink / raw)
To: vinod.koul
Cc: dmaengine, linux-arm-kernel, linux-m68k, stefan, krzk,
Angelo Dureghello
This patch fixes macros to use BIT() and GENMASK(), removing
also some unneeded.
Signed-off-by: Angelo Dureghello <angelo@sysam.it>
Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
---
Changes for v9:
- this patch (3/4) has just been added.
Changes for v10:
- none
---
drivers/dma/fsl-edma-common.h | 50 +++++++++++++++--------------------
1 file changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
index b2ee89ea895a..a6f5b99ee95f 100644
--- a/drivers/dma/fsl-edma-common.h
+++ b/drivers/dma/fsl-edma-common.h
@@ -18,34 +18,28 @@
#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)
^ permalink raw reply related
* [v10,2/4] dmaengine: fsl-edma: add edma version and configurable registers
From: Angelo Dureghello @ 2018-08-19 17:27 UTC (permalink / raw)
To: vinod.koul
Cc: dmaengine, linux-arm-kernel, linux-m68k, stefan, krzk,
Angelo Dureghello
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.
Signed-off-by: Angelo Dureghello <angelo@sysam.it>
Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
---
Changes for v8:
- patch rewritten from scratch, this patch (2/3) has just been added.
Changes for v9:
- comment syntax fixed,
- splitted macro changes away from this patch.
Changes for v10:
- none
---
drivers/dma/fsl-edma-common.c | 106 ++++++++++++++++++++++++----------
drivers/dma/fsl-edma-common.h | 62 ++++++++++----------
drivers/dma/fsl-edma.c | 32 +++++-----
3 files changed, 126 insertions(+), 74 deletions(-)
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index 3fc786b3d03c..227034de256e 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -43,20 +43,20 @@
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);
@@ -184,7 +184,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;
@@ -200,11 +200,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++) {
@@ -261,7 +259,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;
/*
@@ -269,24 +267,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
@@ -308,15 +306,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)
@@ -549,4 +547,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 5458dd3b6da8..b2ee89ea895a 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)
@@ -114,6 +85,31 @@ struct fsl_edma_hw_tcd {
__le16 biter;
};
+/*
+ * These 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 +143,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 +158,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[];
};
@@ -237,5 +240,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;
}
^ permalink raw reply related
* [v10,1/4] dmaengine: fsl-edma: extract common fsl-edma code (no changes in behavior intended)
From: Angelo Dureghello @ 2018-08-19 17:27 UTC (permalink / raw)
To: vinod.koul
Cc: dmaengine, linux-arm-kernel, linux-m68k, stefan, krzk,
Angelo Dureghello
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>
Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
---
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.
Changes for v9:
- changed small accessors (as edma_read/write functions) as inlined.
- changed licenses to GPL2.0+
Changes for v10:
- none
---
drivers/dma/Makefile | 2 +-
drivers/dma/fsl-edma-common.c | 552 +++++++++++++++++++++++++++
drivers/dma/fsl-edma-common.h | 241 ++++++++++++
drivers/dma/fsl-edma.c | 697 +---------------------------------
4 files changed, 795 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..3fc786b3d03c
--- /dev/null
+++ b/drivers/dma/fsl-edma-common.c
@@ -0,0 +1,552 @@
+// 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"
+
+#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
+
+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..5458dd3b6da8
--- /dev/null
+++ b/drivers/dma/fsl-edma-common.h
@@ -0,0 +1,241 @@
+/* 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[];
+};
+
+/*
+ * 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 inline u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
+{
+ if (edma->big_endian)
+ return ioread32be(addr);
+ else
+ return ioread32(addr);
+}
+
+static inline 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 inline 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 inline 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 inline struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct fsl_edma_chan, vchan.chan);
+}
+
+static inline struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct fsl_edma_desc, vdesc);
+}
+
+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;
^ permalink raw reply related
* [3/3] dmaengine: at_xdmac: move spin_lock_bh to spin_lock in tasklet
From: Barry Song @ 2018-08-17 13:03 UTC (permalink / raw)
To: ludovic.desroches, vkoul, dan.j.williams, linux-arm-kernel,
dmaengine, linux-kernel
Cc: Barry Song
as you are already in a tasklet, it is unnecessary to call spin_lock_bh.
Signed-off-by: Barry Song <21cnbao@gmail.com>
---
drivers/dma/at_xdmac.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index 4bf7256..4e55768 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -1600,7 +1600,7 @@ static void at_xdmac_tasklet(unsigned long data)
if (atchan->status & AT_XDMAC_CIS_ROIS)
dev_err(chan2dev(&atchan->chan), "request overflow error!!!");
- spin_lock_bh(&atchan->lock);
+ spin_lock(&atchan->lock);
desc = list_first_entry(&atchan->xfers_list,
struct at_xdmac_desc,
xfer_node);
@@ -1610,7 +1610,7 @@ static void at_xdmac_tasklet(unsigned long data)
txd = &desc->tx_dma_desc;
at_xdmac_remove_xfer(atchan, desc);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock(&atchan->lock);
if (!at_xdmac_chan_is_cyclic(atchan)) {
dma_cookie_complete(txd);
^ permalink raw reply related
* [2/3] dmaengine: mv_xor: move spin_lock_bh to spin_lock in tasklet
From: Barry Song @ 2018-08-17 13:02 UTC (permalink / raw)
To: dan.j.williams, vkoul, dmaengine, linux-kernel; +Cc: Barry Song
as you are already in a tasklet, it is unnecessary to call spin_lock_bh.
Signed-off-by: Barry Song <21cnbao@gmail.com>
---
drivers/dma/mv_xor.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 969534c..7f59535 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -348,9 +348,9 @@ static void mv_xor_tasklet(unsigned long data)
{
struct mv_xor_chan *chan = (struct mv_xor_chan *) data;
- spin_lock_bh(&chan->lock);
+ spin_lock(&chan->lock);
mv_chan_slot_cleanup(chan);
- spin_unlock_bh(&chan->lock);
+ spin_unlock(&chan->lock);
}
static struct mv_xor_desc_slot *
^ permalink raw reply related
* [1/3] dmaengine: fsldma: move spin_lock_bh to spin_lock in tasklet
From: Barry Song @ 2018-08-17 13:00 UTC (permalink / raw)
To: leoyang.li, zw, dan.j.williams, vkoul, linuxppc-dev, dmaengine,
linux-kernel
Cc: Barry Song
as you are already in a tasklet, it is unnecessary to call spin_lock_bh.
Signed-off-by: Barry Song <21cnbao@gmail.com>
---
drivers/dma/fsldma.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 1117b51..9d360a3 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -987,7 +987,7 @@ static void dma_do_tasklet(unsigned long data)
chan_dbg(chan, "tasklet entry\n");
- spin_lock_bh(&chan->desc_lock);
+ spin_lock(&chan->desc_lock);
/* the hardware is now idle and ready for more */
chan->idle = true;
@@ -995,7 +995,7 @@ static void dma_do_tasklet(unsigned long data)
/* Run all cleanup for descriptors which have been completed */
fsldma_cleanup_descriptors(chan);
- spin_unlock_bh(&chan->desc_lock);
+ spin_unlock(&chan->desc_lock);
chan_dbg(chan, "tasklet exit\n");
}
^ permalink raw reply related
* dmaengine: rcar-dmac: Document R8A774A1 bindings
From: Simon Horman @ 2018-08-17 9:48 UTC (permalink / raw)
To: Fabrizio Castro
Cc: Vinod Koul, Rob Herring, Mark Rutland, dmaengine, devicetree,
linux-kernel, Geert Uytterhoeven, Chris Paterson, Biju Das,
linux-renesas-soc
On Tue, Aug 14, 2018 at 01:32:15PM +0100, Fabrizio Castro wrote:
> Renesas' RZ/G2M (R8A774A1) SoC has DMA controllers compatible
> with this driver, therefore document RZ/G2M specific bindings.
>
> Signed-off-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das@bp.renesas.com>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
^ permalink raw reply
* [RFC] dmaengine: Add metadata_ops for dma_async_tx_descriptor
From: Peter Ujfalusi @ 2018-08-17 6:30 UTC (permalink / raw)
To: Radhey Shyam Pandey, dan.j.williams@intel.com, vkoul@kernel.org
Cc: dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org,
lars@metafoo.de
Radhey,
On 2018-08-16 16:29, Radhey Shyam Pandey wrote:
>> +/**
>> + * enum dma_desc_metadata_mode - per descriptor metadata mode types
>> supported
>> + * @DESC_METADATA_CLIENT - the metadata buffer is allocated/provided by
>> the
>> + * client driver and it is attached (via the dmaengine_desc_attach_metadata()
>> + * helper) to the descriptor.
>> + * @DESC_METADATA_EMBEDDED - the metadata buffer is
>> allocated/managed by the DMA
> Just a thought - We can rename it to DESC_METADATA_ENGINE?
> i.e metadata allocation place - > dma client/engine.
Sounds good.
>
>> + * driver. The client driver can ask for the pointer, maximum size and the
>> + * currently used size of the metadata and can directly updata or read it.
> /s/updata/update
>
>> + * dmaengine_desc_get_metadata_ptr() and
>> dmaengine_desc_set_metadata_len() is
>> + * provided as helper functions.
> It will be helpful if we add description for both DESC_METADATA_EMBEDDED
> modes i.e DMA_DEV_TO_MEM and MEM_TO_DEV types. I think in DEV_TO_MEM
> we don't need to set_metadata_len(). Length will provided by DMA engine.
I agree, it is better to extend the explanation on the two modes.
...
>> +static inline int _desc_check_and_set_metadata_mode(
>> + struct dma_async_tx_descriptor *desc, enum
>> dma_desc_metadata_mode mode)
>> +{
>> + /* Make sure that the metadata mode is not mixed */
>> + if (!desc->desc_metadata_mode) {
> Minor nit - we can refactor this code to have failure path early.
I don't think it would help readability, to move the failure case first:
if (desc->desc_metadata_mode &&
(desc->desc_metadata_mode != mode)) {
return -EINVAL;
} else if (!desc->desc_metadata_mode) {
if (dmaengine_is_metadata_mode_supported(desc->chan, mode))
desc->desc_metadata_mode = mode;
else
return -ENOTSUPP;
}
return 0;
>
>> + if (dmaengine_is_metadata_mode_supported(desc->chan,
>> mode))
>> + desc->desc_metadata_mode = mode;
>> + else
>> + return -ENOTSUPP;
>> + } else if (desc->desc_metadata_mode != mode) {
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
- Péter
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* [v8,7/7] arm: dts: ls1021a: add qdma device tree nodes
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
add the qDMA device tree nodes for LS1021A devices.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
arch/arm/boot/dts/ls1021a.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index c55d479..cdff6bd 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -571,6 +571,21 @@
<&clockgen 4 1>;
};
+ qdma: dma-controller@8390000 {
+ compatible = "fsl,ls1021a-qdma";
+ reg = <0x0 0x8398000 0x0 0x1000>, /* Controller regs */
+ <0x0 0x8399000 0x0 0x1000>, /* Status regs */
+ <0x0 0x839a000 0x0 0x2000>; /* Block regs */
+ interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ fsl,dma-queues = <2>;
+ status-sizes = <64>;
+ queue-sizes = <64 64>;
+ big-endian;
+ };
+
dcu: dcu@2ce0000 {
compatible = "fsl,ls1021a-dcu";
reg = <0x0 0x2ce0000 0x0 0x10000>;
^ permalink raw reply related
* [v8,6/7] arm64: dts: ls1046a: add qdma device tree nodes
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
add the qDMA device tree nodes for LS1046A devices.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index 136ebfa..2bad8c8 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -597,6 +597,21 @@
<&clockgen 4 1>;
};
+ qdma: dma-controller@8380000 {
+ compatible = "fsl,ls1021a-qdma", "fsl,ls1046a-qdma";
+ reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
+ <0x0 0x8390000 0x0 0x10000>, /* Status regs */
+ <0x0 0x83a0000 0x0 0x40000>; /* Block regs */
+ interrupts = <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ fsl,dma-queues = <2>;
+ status-sizes = <64>;
+ queue-sizes = <64 64>;
+ big-endian;
+ };
+
usb0: usb@2f00000 {
compatible = "snps,dwc3";
reg = <0x0 0x2f00000 0x0 0x10000>;
^ permalink raw reply related
* [v8,5/7] arm64: dts: ls1043a: add qdma device tree nodes
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
add the qDMA device tree nodes for LS1043A devices.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
index 1109f22..b39ddfa 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
@@ -673,6 +673,21 @@
dma-coherent;
};
+ qdma: dma-controller@8380000 {
+ compatible = "fsl,ls1021a-qdma", "fsl,ls1043a-qdma";
+ reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
+ <0x0 0x8390000 0x0 0x10000>, /* Status regs */
+ <0x0 0x83a0000 0x0 0x40000>; /* Block regs */
+ interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ fsl,dma-queues = <2>;
+ status-sizes = <64>;
+ queue-sizes = <64 64>;
+ big-endian;
+ };
+
msi1: msi-controller1@1571000 {
compatible = "fsl,ls1043a-msi";
reg = <0x0 0x1571000 0x0 0x8>;
^ permalink raw reply related
* [v8,4/7] dt-bindings: fsl-qdma: Add NXP Layerscpae qDMA controller bindings
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
Document the devicetree bindings for NXP Layerscape qDMA controller
which could be found on NXP QorIQ Layerscape SoCs.
Signed-off-by: Wen He <wen.he_1@nxp.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/dma/fsl-qdma.txt | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/fsl-qdma.txt
diff --git a/Documentation/devicetree/bindings/dma/fsl-qdma.txt b/Documentation/devicetree/bindings/dma/fsl-qdma.txt
new file mode 100644
index 0000000..879e62e
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/fsl-qdma.txt
@@ -0,0 +1,41 @@
+NXP Layerscape SoC qDMA Controller
+==================================
+
+This device follows the generic DMA bindings defined in dma/dma.txt.
+
+Required properties:
+
+- compatible: Must be one of
+ "fsl,ls1021a-qdma": for ls1021A Board
+ "fsl,ls1043a-qdma": for ls1043A Board
+ "fsl,ls1046a-qdma": for ls1046A Board
+- reg: Should contain the register's base address and length.
+- interrupts: Should contain a reference to the interrupt used by this
+ device.
+- interrupt-names: Should contain interrupt names:
+ "qdma-error": the error interrupt
+ "qdma-queue": the queue interrupt
+- fsl,dma-queues: Should contain number of queues supported.
+
+Optional properties:
+
+- dma-channels: Number of DMA channels supported by the controller.
+- big-endian: If present registers and hardware scatter/gather descriptors
+ of the qDMA are implemented in big endian mode, otherwise in little
+ mode.
+
+Examples:
+
+ qdma: dma-controller@8390000 {
+ compatible = "fsl,ls1021a-qdma";
+ reg = <0x0 0x8398000 0x0 0x2000 /* Controller registers */
+ 0x0 0x839a000 0x0 0x2000>; /* Block registers */
+ interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ fsl,dma-queues = <2>;
+ big-endian;
+ };
+
+DMA clients must use the format described in dma/dma.txt file.
^ permalink raw reply related
* [v8,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel
virtuallization by allowing DMA jobs to be enqueued into different
command queues.
Signed-off-by: Wen He <wen.he_1@nxp.com>
Signed-off-by: Jiaheng Fan <jiaheng.fan@nxp.com>
---
drivers/dma/Kconfig | 13 +
drivers/dma/Makefile | 1 +
drivers/dma/fsl-qdma.c | 1105 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1119 insertions(+)
create mode 100644 drivers/dma/fsl-qdma.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ca1680a..2914cafb 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -218,6 +218,19 @@ config FSL_EDMA
multiplexing capability for DMA request sources(slot).
This module can be found on Freescale Vybrid and LS-1 SoCs.
+config FSL_QDMA
+ tristate "NXP Layerscape qDMA engine support"
+ depends on ARM || ARM64
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ select DMA_ENGINE_RAID
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
+ help
+ Support the NXP Layerscape qDMA engine with command queue and legacy mode.
+ Channel virtualization is supported through enqueuing of DMA jobs to,
+ or dequeuing DMA jobs from, different work queues.
+ This module can be found on NXP Layerscape SoCs.
+
config FSL_RAID
tristate "Freescale RAID engine Support"
depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 203a99d..0556f9d 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
+obj-$(CONFIG_FSL_QDMA) += fsl-qdma.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-qdma.c b/drivers/dma/fsl-qdma.c
new file mode 100644
index 0000000..f612f5a
--- /dev/null
+++ b/drivers/dma/fsl-qdma.c
@@ -0,0 +1,1105 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2018 NXP
+
+/*
+ * Driver for NXP Layerscape Queue Direct Memory Access Controller
+ *
+ * Author:
+ * Wen He <wen.he_1@nxp.com>
+ * Jiaheng Fan <jiaheng.fan@nxp.com>
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/dmaengine.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include "fsldma.h"
+
+/* Register related definition */
+#define FSL_QDMA_DMR 0x0
+#define FSL_QDMA_DSR 0x4
+#define FSL_QDMA_DEIER 0xe00
+#define FSL_QDMA_DEDR 0xe04
+#define FSL_QDMA_DECFDW0R 0xe10
+#define FSL_QDMA_DECFDW1R 0xe14
+#define FSL_QDMA_DECFDW2R 0xe18
+#define FSL_QDMA_DECFDW3R 0xe1c
+#define FSL_QDMA_DECFQIDR 0xe30
+#define FSL_QDMA_DECBR 0xe34
+
+#define FSL_QDMA_BCQMR(x) (0xc0 + 0x100 * (x))
+#define FSL_QDMA_BCQSR(x) (0xc4 + 0x100 * (x))
+#define FSL_QDMA_BCQEDPA_SADDR(x) (0xc8 + 0x100 * (x))
+#define FSL_QDMA_BCQDPA_SADDR(x) (0xcc + 0x100 * (x))
+#define FSL_QDMA_BCQEEPA_SADDR(x) (0xd0 + 0x100 * (x))
+#define FSL_QDMA_BCQEPA_SADDR(x) (0xd4 + 0x100 * (x))
+#define FSL_QDMA_BCQIER(x) (0xe0 + 0x100 * (x))
+#define FSL_QDMA_BCQIDR(x) (0xe4 + 0x100 * (x))
+
+#define FSL_QDMA_SQDPAR 0x80c
+#define FSL_QDMA_SQEPAR 0x814
+#define FSL_QDMA_BSQMR 0x800
+#define FSL_QDMA_BSQSR 0x804
+#define FSL_QDMA_BSQICR 0x828
+#define FSL_QDMA_CQMR 0xa00
+#define FSL_QDMA_CQDSCR1 0xa08
+#define FSL_QDMA_CQDSCR2 0xa0c
+#define FSL_QDMA_CQIER 0xa10
+#define FSL_QDMA_CQEDR 0xa14
+#define FSL_QDMA_SQCCMR 0xa20
+
+/* Registers for bit and genmask */
+#define FSL_QDMA_CQIDR_SQT BIT(15)
+#define QDMA_CCDF_FOTMAT BIT(29)
+#define QDMA_CCDF_SER BIT(30)
+#define QDMA_SG_FIN BIT(30)
+#define QDMA_SG_EXT BIT(31)
+#define QDMA_SG_LEN_MASK GENMASK(29, 0)
+#define QDMA_CCDF_MASK GENMASK(28, 20)
+
+#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0)
+#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0)
+#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0)
+
+#define FSL_QDMA_BCQIER_CQTIE BIT(15)
+#define FSL_QDMA_BCQIER_CQPEIE BIT(23)
+#define FSL_QDMA_BSQICR_ICEN BIT(31)
+
+#define FSL_QDMA_BSQICR_ICST(x) ((x) << 16)
+#define FSL_QDMA_CQIER_MEIE BIT(31)
+#define FSL_QDMA_CQIER_TEIE BIT(0)
+#define FSL_QDMA_SQCCMR_ENTER_WM BIT(21)
+
+#define FSL_QDMA_BCQMR_EN BIT(31)
+#define FSL_QDMA_BCQMR_EI BIT(30)
+#define FSL_QDMA_BCQMR_CD_THLD(x) ((x) << 20)
+#define FSL_QDMA_BCQMR_CQ_SIZE(x) ((x) << 16)
+
+#define FSL_QDMA_BCQSR_QF BIT(16)
+#define FSL_QDMA_BCQSR_XOFF BIT(0)
+
+#define FSL_QDMA_BSQMR_EN BIT(31)
+#define FSL_QDMA_BSQMR_DI BIT(30)
+#define FSL_QDMA_BSQMR_CQ_SIZE(x) ((x) << 16)
+
+#define FSL_QDMA_BSQSR_QE BIT(17)
+
+#define FSL_QDMA_DMR_DQD BIT(30)
+#define FSL_QDMA_DSR_DB BIT(31)
+
+/* Size related definition */
+#define FSL_QDMA_QUEUE_MAX 8
+#define FSL_QDMA_BASE_BUFFER_SIZE 96
+#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN 64
+#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX 16384
+#define FSL_QDMA_QUEUE_NUM_MAX 8
+
+/* Field definition for CMD */
+#define FSL_QDMA_CMD_RWTTYPE 0x4
+#define FSL_QDMA_CMD_LWC 0x2
+#define FSL_QDMA_CMD_RWTTYPE_OFFSET 28
+#define FSL_QDMA_CMD_NS_OFFSET 27
+#define FSL_QDMA_CMD_DQOS_OFFSET 24
+#define FSL_QDMA_CMD_WTHROTL_OFFSET 20
+#define FSL_QDMA_CMD_DSEN_OFFSET 19
+#define FSL_QDMA_CMD_LWC_OFFSET 16
+
+#define FSL_QDMA_E_SG_TABLE 1
+#define FSL_QDMA_E_DATA_BUFFER 0
+#define FSL_QDMA_F_LAST_ENTRY 1
+
+/* Field definition for safe loop count*/
+#define FSL_QDMA_HALT_COUNT 1500
+#define FSL_QDMA_MAX_SIZE 16385
+
+/* Field definition for Descriptor offset */
+#define QDMA_CCDF_STATUS 20
+#define QDMA_CCDF_OFFSET 20
+
+/**
+ * struct fsl_qdma_format - This is the struct holding describing compound
+ * descriptor format with qDMA.
+ * @status: Command status and enqueue status notification.
+ * @cfg: Frame offset and frame format.
+ * @addr_lo: Holding the compound descriptor of the lower
+ * 32-bits address in memory 40-bit address.
+ * @addr_hi: Same as above member, but point high 8-bits in
+ * memory 40-bit address.
+ * @__reserved1: Reserved field.
+ * @cfg8b_w1: Compound descriptor command queue origin produced
+ * by qDMA and dynamic debug field.
+ * @data Pointer to the memory 40-bit address, describes DMA
+ * source information and DMA destination information.
+ */
+struct fsl_qdma_format {
+ __le32 status;
+ __le32 cfg;
+ union {
+ struct {
+ __le32 addr_lo;
+ u8 addr_hi;
+ u8 __reserved1[2];
+ u8 cfg8b_w1;
+ } __packed;
+ __le64 data;
+ };
+} __packed;
+
+/* qDMA status notification pre information */
+struct fsl_pre_status {
+ u64 queue;
+ u64 addr;
+};
+
+struct fsl_qdma_chan {
+ struct virt_dma_chan vchan;
+ struct virt_dma_desc vdesc;
+ enum dma_status status;
+ u32 slave_id;
+ struct fsl_qdma_engine *qdma;
+ struct fsl_qdma_queue *queue;
+ struct list_head qcomp;
+};
+
+struct fsl_qdma_queue {
+ struct fsl_qdma_format *virt_head;
+ struct fsl_qdma_format *virt_tail;
+ struct list_head comp_used;
+ struct list_head comp_free;
+ struct dma_pool *comp_pool;
+ spinlock_t queue_lock;
+ dma_addr_t bus_addr;
+ u32 n_cq;
+ u32 id;
+ struct fsl_qdma_format *cq;
+};
+
+struct fsl_qdma_comp {
+ dma_addr_t bus_addr;
+ struct fsl_qdma_format *virt_addr;
+ struct fsl_qdma_chan *qchan;
+ struct virt_dma_desc vdesc;
+ struct list_head list;
+};
+
+struct fsl_qdma_engine {
+ struct dma_device dma_dev;
+ void __iomem *ctrl_base;
+ void __iomem *status_base;
+ void __iomem *block_base;
+ u32 n_chans;
+ u32 n_queues;
+ struct mutex fsl_qdma_mutex;
+ int error_irq;
+ int queue_irq;
+ bool feature;
+ struct fsl_qdma_queue *queue;
+ struct fsl_qdma_queue *status;
+ struct fsl_qdma_chan chans[];
+
+};
+
+static inline u64
+qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf)
+{
+ return le64_to_cpu(ccdf->data) & (U64_MAX >> 24);
+}
+
+static inline void
+qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr)
+{
+ ccdf->addr_hi = upper_32_bits(addr);
+ ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr));
+}
+
+static inline u64
+qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf)
+{
+ return ccdf->cfg8b_w1 & U8_MAX;
+}
+
+static inline int
+qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf)
+{
+ return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >> QDMA_CCDF_OFFSET;
+}
+
+static inline void
+qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset)
+{
+ ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset);
+}
+
+static inline int
+qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf)
+{
+ return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS;
+}
+
+static inline void
+qdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status)
+{
+ ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status);
+}
+
+static inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int len)
+{
+ csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK);
+}
+
+static inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int len)
+{
+ csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len & QDMA_SG_LEN_MASK));
+}
+
+static inline void qdma_csgf_set_e(struct fsl_qdma_format *csgf, int len)
+{
+ csgf->cfg = cpu_to_le32(QDMA_SG_EXT | (len & QDMA_SG_LEN_MASK));
+}
+
+static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr)
+{
+ return FSL_DMA_IN(qdma, addr, 32);
+}
+
+static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val,
+ void __iomem *addr)
+{
+ FSL_DMA_OUT(qdma, addr, val, 32);
+}
+
+static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct fsl_qdma_chan, vchan.chan);
+}
+
+static struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct fsl_qdma_comp, vdesc);
+}
+
+static void fsl_qdma_free_chan_resources(struct dma_chan *chan)
+{
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ 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);
+}
+
+static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp,
+ dma_addr_t dst, dma_addr_t src, u32 len)
+{
+ struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest;
+ struct fsl_qdma_format *sdf, *ddf;
+
+ ccdf = fsl_comp->virt_addr;
+ csgf_desc = fsl_comp->virt_addr + 1;
+ csgf_src = fsl_comp->virt_addr + 2;
+ csgf_dest = fsl_comp->virt_addr + 3;
+ sdf = fsl_comp->virt_addr + 4;
+ ddf = fsl_comp->virt_addr + 5;
+
+ memset(fsl_comp->virt_addr, 0, FSL_QDMA_BASE_BUFFER_SIZE);
+ /* Head Command Descriptor(Frame Descriptor) */
+ qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16);
+ qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf));
+ qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf));
+
+ /* Status notification is enqueued to status queue. */
+ /* Compound Command Descriptor(Frame List Table) */
+ qdma_desc_addr_set64(csgf_desc, fsl_comp->bus_addr + 64);
+ /* It must be 32 as Compound S/G Descriptor */
+ qdma_csgf_set_len(csgf_desc, 32);
+ qdma_desc_addr_set64(csgf_src, src);
+ qdma_csgf_set_len(csgf_src, len);
+ qdma_desc_addr_set64(csgf_dest, dst);
+ qdma_csgf_set_len(csgf_dest, len);
+ /* This entry is the last entry. */
+ qdma_csgf_set_f(csgf_dest, len);
+ /* Descriptor Buffer */
+ sdf->data = cpu_to_le64(
+ FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET);
+ ddf->data = cpu_to_le64(
+ FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET);
+ ddf->data |= cpu_to_le64(
+ FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET);
+}
+
+/*
+ * Pre-request full command descriptor for enqueue.
+ */
+static int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue *queue)
+{
+ struct fsl_qdma_comp *comp_temp, *_comp_temp;
+ int i;
+
+ for (i = 0; i < queue->n_cq; i++) {
+ comp_temp = kzalloc(sizeof(*comp_temp), GFP_NOWAIT);
+ if (!comp_temp)
+ goto err;
+
+ comp_temp->virt_addr = dma_pool_alloc(queue->comp_pool,
+ GFP_NOWAIT,
+ &comp_temp->bus_addr);
+ if (!comp_temp->virt_addr)
+ goto err;
+
+ list_add_tail(&comp_temp->list, &queue->comp_free);
+ }
+ return 0;
+
+err:
+ if (i == 0 && comp_temp) {
+ kfree(comp_temp);
+ return -ENOMEM;
+ }
+
+ while (--i >= 1) {
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue->comp_free, list) {
+ dma_pool_free(queue->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+ }
+ return -ENOMEM;
+}
+
+/*
+ * Request a command descriptor for enqueue.
+ */
+static struct fsl_qdma_comp *fsl_qdma_request_enqueue_desc(
+ struct fsl_qdma_chan *fsl_chan,
+ unsigned int dst_nents,
+ unsigned int src_nents)
+{
+ struct fsl_qdma_comp *comp_temp;
+ struct fsl_qdma_queue *queue = fsl_chan->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ if (list_empty(&queue->comp_free)) {
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+ comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL);
+ if (!comp_temp)
+ return NULL;
+
+ comp_temp->virt_addr = dma_pool_alloc(queue->comp_pool,
+ GFP_KERNEL,
+ &comp_temp->bus_addr);
+ if (!comp_temp->virt_addr) {
+ kfree(comp_temp);
+ return NULL;
+ }
+
+ } else {
+ comp_temp = list_first_entry(&queue->comp_free,
+ struct fsl_qdma_comp,
+ list);
+ list_del(&comp_temp->list);
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+ }
+
+ comp_temp->qchan = fsl_chan;
+
+ return comp_temp;
+}
+
+static struct fsl_qdma_queue *fsl_qdma_alloc_queue_resources(
+ struct platform_device *pdev,
+ unsigned int queue_num)
+{
+ struct fsl_qdma_queue *queue_head, *queue_temp;
+ int ret, i;
+ unsigned int queue_size[FSL_QDMA_QUEUE_MAX];
+
+ if (queue_num > FSL_QDMA_QUEUE_MAX)
+ queue_num = FSL_QDMA_QUEUE_MAX;
+ queue_head = devm_kcalloc(&pdev->dev, queue_num,
+ sizeof(*queue_head), GFP_KERNEL);
+ if (!queue_head)
+ return NULL;
+
+ ret = device_property_read_u32_array(&pdev->dev, "queue-sizes",
+ queue_size, queue_num);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get queue-sizes.\n");
+ return NULL;
+ }
+
+ for (i = 0; i < queue_num; i++) {
+ if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX ||
+ queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
+ dev_err(&pdev->dev, "Get wrong queue-sizes.\n");
+ return NULL;
+ }
+ queue_temp = queue_head + i;
+ queue_temp->cq = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ queue_size[i],
+ &queue_temp->bus_addr,
+ GFP_KERNEL);
+ if (!queue_temp->cq) {
+ devm_kfree(&pdev->dev, queue_head);
+ return NULL;
+ }
+ queue_temp->n_cq = queue_size[i];
+ queue_temp->id = i;
+ queue_temp->virt_head = queue_temp->cq;
+ queue_temp->virt_tail = queue_temp->cq;
+
+ /*
+ * Create a comp dma pool that size
+ * is 'FSL_QDMA_BASE_BUFFER_SIZE'.
+ * The dma pool for queue command buffer.
+ */
+ queue_temp->comp_pool = dma_pool_create("comp_pool",
+ &pdev->dev,
+ FSL_QDMA_BASE_BUFFER_SIZE,
+ 16, 0);
+ if (!queue_temp->comp_pool)
+ goto err;
+
+ /*
+ * List for queue command buffer
+ */
+ INIT_LIST_HEAD(&queue_temp->comp_used);
+ INIT_LIST_HEAD(&queue_temp->comp_free);
+ spin_lock_init(&queue_temp->queue_lock);
+ }
+
+ return queue_head;
+
+err:
+ if (i == 0 && queue_temp->comp_pool)
+ dma_pool_destroy(queue_temp->comp_pool);
+ while (--i >= 1) {
+ queue_temp = queue_head + i;
+ if (i == 1 && unlikely(queue_temp->comp_pool))
+ dma_pool_destroy(queue_temp->comp_pool);
+ }
+
+ dev_err(&pdev->dev,
+ "unable to allocate channel %d descriptor pool\n",
+ queue_temp->id);
+
+ while (--i >= 0) {
+ queue_temp = queue_head + i;
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ queue_size[i],
+ queue_temp->cq,
+ queue_temp->bus_addr);
+ }
+ devm_kfree(&pdev->dev, queue_head);
+ return NULL;
+}
+
+static struct fsl_qdma_queue *fsl_qdma_prep_status_queue(
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_qdma_queue *status_head;
+ unsigned int status_size;
+ int ret;
+
+ ret = of_property_read_u32(np, "status-sizes", &status_size);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get status-sizes.\n");
+ return NULL;
+ }
+ if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX
+ || status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
+ dev_err(&pdev->dev, "Get wrong status_size.\n");
+ return NULL;
+ }
+ status_head = devm_kzalloc(&pdev->dev, sizeof(*status_head),
+ GFP_KERNEL);
+ if (!status_head)
+ return NULL;
+
+ /*
+ * Buffer for queue command
+ */
+ status_head->cq = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ status_size,
+ &status_head->bus_addr,
+ GFP_KERNEL);
+ if (!status_head->cq) {
+ devm_kfree(&pdev->dev, status_head);
+ return NULL;
+ }
+
+ status_head->n_cq = status_size;
+ status_head->virt_head = status_head->cq;
+ status_head->virt_tail = status_head->cq;
+ status_head->comp_pool = NULL;
+
+ return status_head;
+}
+
+static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma)
+{
+ void __iomem *ctrl = fsl_qdma->ctrl_base;
+ void __iomem *block = fsl_qdma->block_base;
+ int i, count = FSL_QDMA_HALT_COUNT;
+ u32 reg;
+
+ /* Disable the command queue and wait for idle state. */
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+ reg |= FSL_QDMA_DMR_DQD;
+ qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+ for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++)
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i));
+
+ while (1) {
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR);
+ if (!(reg & FSL_QDMA_DSR_DB))
+ break;
+ if (count-- < 0)
+ return -EBUSY;
+ udelay(100);
+ }
+
+ /* Disable status queue. */
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR);
+
+ /* Clear all detected events and interrupts for all queues. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ return 0;
+}
+
+static int fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma)
+{
+ struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+ struct fsl_qdma_queue *fsl_status = fsl_qdma->status;
+ struct fsl_qdma_queue *temp_queue;
+ struct fsl_qdma_comp *fsl_comp;
+ struct fsl_qdma_format *status_addr;
+ struct fsl_qdma_format *csgf_src;
+ struct fsl_pre_status pre;
+ void __iomem *block = fsl_qdma->block_base;
+ u32 reg, i, count;
+ bool duplicate;
+
+ memset(&pre, 0, sizeof(struct fsl_pre_status));
+ count = FSL_QDMA_MAX_SIZE;
+
+ while (count--) {
+ duplicate = 0;
+
+ reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR);
+ if (reg & FSL_QDMA_BSQSR_QE)
+ return 0;
+
+ status_addr = fsl_status->virt_head;
+ if (qdma_ccdf_get_queue(status_addr) == pre.queue &&
+ qdma_ccdf_addr_get64(status_addr) == pre.addr)
+ duplicate = 1;
+
+ i = qdma_ccdf_get_queue(status_addr);
+ pre.queue = qdma_ccdf_get_queue(status_addr);
+ pre.addr = qdma_ccdf_addr_get64(status_addr);
+ temp_queue = fsl_queue + i;
+ spin_lock(&temp_queue->queue_lock);
+ if (list_empty(&temp_queue->comp_used)) {
+ if (!duplicate) {
+ spin_unlock(&temp_queue->queue_lock);
+ return -EAGAIN;
+ }
+ } else {
+ fsl_comp = list_first_entry(&temp_queue->comp_used,
+ struct fsl_qdma_comp,
+ list);
+ csgf_src = fsl_comp->virt_addr + 2;
+ if (fsl_comp->bus_addr + 16 != pre.addr) {
+ if (duplicate) {
+ spin_unlock(&temp_queue->queue_lock);
+ return -EAGAIN;
+ }
+ }
+ }
+
+ if (duplicate) {
+ reg = qdma_readl(fsl_qdma, block +
+ FSL_QDMA_BSQMR);
+ reg |= FSL_QDMA_BSQMR_DI;
+ qdma_desc_addr_set64(status_addr, 0x0);
+ fsl_status->virt_head++;
+ if (fsl_status->virt_head == fsl_status->cq
+ + fsl_status->n_cq)
+ fsl_status->virt_head = fsl_status->cq;
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+ spin_unlock(&temp_queue->queue_lock);
+ continue;
+ }
+ list_del(&fsl_comp->list);
+
+ reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+ reg |= FSL_QDMA_BSQMR_DI;
+ qdma_desc_addr_set64(status_addr, 0x0);
+ fsl_status->virt_head++;
+ if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq)
+ fsl_status->virt_head = fsl_status->cq;
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+ spin_unlock(&temp_queue->queue_lock);
+
+ spin_lock(&fsl_comp->qchan->vchan.lock);
+ vchan_cookie_complete(&fsl_comp->vdesc);
+ fsl_comp->qchan->status = DMA_COMPLETE;
+ spin_unlock(&fsl_comp->qchan->vchan.lock);
+ }
+
+ return 0;
+}
+
+static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id)
+{
+ struct fsl_qdma_engine *fsl_qdma = dev_id;
+ unsigned int intr;
+ void __iomem *status = fsl_qdma->status_base;
+
+ intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
+
+ if (intr)
+ dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
+
+ /* Clear all error conditions and interrupts. */
+ qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id)
+{
+ struct fsl_qdma_engine *fsl_qdma = dev_id;
+ unsigned int intr, reg;
+ void __iomem *block = fsl_qdma->block_base;
+ void __iomem *ctrl = fsl_qdma->ctrl_base;
+
+ intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0));
+
+ if ((intr & FSL_QDMA_CQIDR_SQT) != 0)
+ intr = fsl_qdma_queue_transfer_complete(fsl_qdma);
+
+ if (intr != 0) {
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+ reg |= FSL_QDMA_DMR_DQD;
+ qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0));
+ dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n");
+ }
+
+ /* Clear all detected events and interrupts. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ return IRQ_HANDLED;
+}
+
+static int
+fsl_qdma_irq_init(struct platform_device *pdev,
+ struct fsl_qdma_engine *fsl_qdma)
+{
+ int ret;
+
+ fsl_qdma->error_irq = platform_get_irq_byname(pdev,
+ "qdma-error");
+ if (fsl_qdma->error_irq < 0) {
+ dev_err(&pdev->dev, "Can't get qdma controller irq.\n");
+ return fsl_qdma->error_irq;
+ }
+
+ fsl_qdma->queue_irq = platform_get_irq_byname(pdev, "qdma-queue");
+ if (fsl_qdma->queue_irq < 0) {
+ dev_err(&pdev->dev, "Can't get qdma queue irq.\n");
+ return fsl_qdma->queue_irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq,
+ fsl_qdma_error_handler, 0, "qDMA error", fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n");
+ return ret;
+ }
+ ret = devm_request_irq(&pdev->dev, fsl_qdma->queue_irq,
+ fsl_qdma_queue_handler, 0, "qDMA queue", fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register qDMA queue IRQ.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fsl_qdma_irq_exit(
+ struct platform_device *pdev, struct fsl_qdma_engine *fsl_qdma)
+{
+ if (fsl_qdma->queue_irq == fsl_qdma->error_irq) {
+ devm_free_irq(&pdev->dev, fsl_qdma->queue_irq, fsl_qdma);
+ } else {
+ devm_free_irq(&pdev->dev, fsl_qdma->queue_irq, fsl_qdma);
+ devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma);
+ }
+}
+
+static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma)
+{
+ struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+ struct fsl_qdma_queue *temp;
+ void __iomem *ctrl = fsl_qdma->ctrl_base;
+ void __iomem *status = fsl_qdma->status_base;
+ void __iomem *block = fsl_qdma->block_base;
+ int i, ret;
+ u32 reg;
+
+ /* Try to halt the qDMA engine first. */
+ ret = fsl_qdma_halt(fsl_qdma);
+ if (ret) {
+ dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!");
+ return ret;
+ }
+
+ /* Clear all detected events and interrupts for all queues. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ for (i = 0; i < fsl_qdma->n_queues; i++) {
+ temp = fsl_queue + i;
+ /*
+ * Initialize Command Queue registers to point to the first
+ * command descriptor in memory.
+ * Dequeue Pointer Address Registers
+ * Enqueue Pointer Address Registers
+ */
+ qdma_writel(fsl_qdma, temp->bus_addr,
+ block + FSL_QDMA_BCQDPA_SADDR(i));
+ qdma_writel(fsl_qdma, temp->bus_addr,
+ block + FSL_QDMA_BCQEPA_SADDR(i));
+
+ /* Initialize the queue mode. */
+ reg = FSL_QDMA_BCQMR_EN;
+ reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4);
+ reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6);
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i));
+ }
+
+ /*
+ * Workaround for erratum: ERR010812.
+ * We must enable XOFF to avoid the enqueue rejection occurs.
+ * Setting SQCCMR ENTER_WM to 0x20.
+ */
+ qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM,
+ block + FSL_QDMA_SQCCMR);
+ /*
+ * Initialize status queue registers to point to the first
+ * command descriptor in memory.
+ * Dequeue Pointer Address Registers
+ * Enqueue Pointer Address Registers
+ */
+ qdma_writel(fsl_qdma, fsl_qdma->status->bus_addr,
+ block + FSL_QDMA_SQEPAR);
+ qdma_writel(fsl_qdma, fsl_qdma->status->bus_addr,
+ block + FSL_QDMA_SQDPAR);
+ /* Initialize status queue interrupt. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE,
+ block + FSL_QDMA_BCQIER(0));
+ qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN | FSL_QDMA_BSQICR_ICST(5)
+ | 0x8000,
+ block + FSL_QDMA_BSQICR);
+ qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE | FSL_QDMA_CQIER_TEIE,
+ block + FSL_QDMA_CQIER);
+ /* Initialize controller interrupt register. */
+ qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
+ qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status + FSL_QDMA_DEIER);
+
+ /* Initialize the status queue mode. */
+ reg = FSL_QDMA_BSQMR_EN;
+ reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2(fsl_qdma->status->n_cq)-6);
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+
+ reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+ reg &= ~FSL_QDMA_DMR_DQD;
+ qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+
+ return 0;
+}
+
+static struct dma_async_tx_descriptor *
+fsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+ struct fsl_qdma_comp *fsl_comp;
+
+ fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan, 0, 0);
+ fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len);
+
+ return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags);
+}
+
+static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan)
+{
+ void __iomem *block = fsl_chan->qdma->block_base;
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+ struct fsl_qdma_comp *fsl_comp;
+ struct virt_dma_desc *vdesc;
+ u32 reg;
+
+ reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQSR(fsl_queue->id));
+ if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF))
+ return;
+ vdesc = vchan_next_desc(&fsl_chan->vchan);
+ if (!vdesc)
+ return;
+ list_del(&vdesc->node);
+ fsl_comp = to_fsl_qdma_comp(vdesc);
+
+ memcpy(fsl_queue->virt_head++, fsl_comp->virt_addr,
+ sizeof(struct fsl_qdma_format));
+ if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq)
+ fsl_queue->virt_head = fsl_queue->cq;
+
+ list_add_tail(&fsl_comp->list, &fsl_queue->comp_used);
+ barrier();
+ reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQMR(fsl_queue->id));
+ reg |= FSL_QDMA_BCQMR_EI;
+ qdma_writel(fsl_chan->qdma, reg, block + FSL_QDMA_BCQMR(fsl_queue->id));
+ fsl_chan->status = DMA_IN_PROGRESS;
+}
+
+static enum dma_status fsl_qdma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+ enum dma_status ret;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_COMPLETE || !txstate)
+ return ret;
+
+ return ret;
+}
+
+static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc)
+{
+ struct fsl_qdma_comp *fsl_comp;
+ struct fsl_qdma_queue *fsl_queue;
+ unsigned long flags;
+
+ fsl_comp = to_fsl_qdma_comp(vdesc);
+ fsl_queue = fsl_comp->qchan->queue;
+
+ spin_lock_irqsave(&fsl_queue->queue_lock, flags);
+ list_add_tail(&fsl_comp->list, &fsl_queue->comp_free);
+ spin_unlock_irqrestore(&fsl_queue->queue_lock, flags);
+}
+
+static void fsl_qdma_issue_pending(struct dma_chan *chan)
+{
+ struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fsl_queue->queue_lock, flags);
+ spin_lock(&fsl_chan->vchan.lock);
+ if (vchan_issue_pending(&fsl_chan->vchan))
+ fsl_qdma_enqueue_desc(fsl_chan);
+ spin_unlock(&fsl_chan->vchan.lock);
+ spin_unlock_irqrestore(&fsl_queue->queue_lock, flags);
+}
+
+static int fsl_qdma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_qdma_engine *fsl_qdma;
+ struct fsl_qdma_chan *fsl_chan;
+ struct resource *res;
+ unsigned int len, chans, queues;
+ int ret, i;
+
+ ret = of_property_read_u32(np, "dma-channels", &chans);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get dma-channels.\n");
+ return ret;
+ }
+
+ len = sizeof(*fsl_qdma) + sizeof(*fsl_chan) * chans;
+ fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_qdma)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(np, "fsl,dma-queues", &queues);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get queues.\n");
+ return ret;
+ }
+
+ fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, queues);
+ if (!fsl_qdma->queue)
+ return -ENOMEM;
+
+ fsl_qdma->status = fsl_qdma_prep_status_queue(pdev);
+ if (!fsl_qdma->status)
+ return -ENOMEM;
+
+ fsl_qdma->n_chans = chans;
+ fsl_qdma->n_queues = queues;
+ mutex_init(&fsl_qdma->fsl_qdma_mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_qdma->ctrl_base))
+ return PTR_ERR(fsl_qdma->ctrl_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_qdma->status_base))
+ return PTR_ERR(fsl_qdma->status_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_qdma->block_base))
+ return PTR_ERR(fsl_qdma->block_base);
+
+ ret = fsl_qdma_irq_init(pdev, fsl_qdma);
+ if (ret)
+ return ret;
+
+ fsl_qdma->feature = of_property_read_bool(np, "big-endian");
+ INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels);
+ for (i = 0; i < fsl_qdma->n_chans; i++) {
+ struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i];
+
+ fsl_chan->qdma = fsl_qdma;
+ fsl_chan->queue = fsl_qdma->queue + i % fsl_qdma->n_queues;
+ fsl_chan->vchan.desc_free = fsl_qdma_free_desc;
+ INIT_LIST_HEAD(&fsl_chan->qcomp);
+ vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev);
+ }
+ for (i = 0; i < fsl_qdma->n_queues; i++)
+ fsl_qdma_pre_request_enqueue_desc(fsl_qdma->queue + i);
+
+ dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask);
+
+ fsl_qdma->dma_dev.dev = &pdev->dev;
+ fsl_qdma->dma_dev.device_free_chan_resources
+ = fsl_qdma_free_chan_resources;
+ fsl_qdma->dma_dev.device_tx_status = fsl_qdma_tx_status;
+ fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy;
+ fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending;
+
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+
+ platform_set_drvdata(pdev, fsl_qdma);
+
+ ret = dma_async_device_register(&fsl_qdma->dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register NXP Layerscape qDMA engine.\n");
+ return ret;
+ }
+
+ ret = fsl_qdma_reg_init(fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev)
+{
+ struct fsl_qdma_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_qdma_remove(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev);
+ struct fsl_qdma_queue *queue_temp;
+ struct fsl_qdma_queue *status = fsl_qdma->status;
+ struct fsl_qdma_comp *comp_temp, *_comp_temp;
+ int i;
+
+ fsl_qdma_irq_exit(pdev, fsl_qdma);
+ fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev);
+ of_dma_controller_free(np);
+ dma_async_device_unregister(&fsl_qdma->dma_dev);
+
+ /* Free descriptor areas */
+ for (i = 0; i < fsl_qdma->n_queues; i++) {
+ queue_temp = fsl_qdma->queue + i;
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue_temp->comp_used, list) {
+ dma_pool_free(queue_temp->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue_temp->comp_free, list) {
+ dma_pool_free(queue_temp->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+ dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
+ queue_temp->n_cq, queue_temp->cq,
+ queue_temp->bus_addr);
+ dma_pool_destroy(queue_temp->comp_pool);
+ }
+
+ dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
+ status->n_cq, status->cq, status->bus_addr);
+ return 0;
+}
+
+static const struct of_device_id fsl_qdma_dt_ids[] = {
+ { .compatible = "fsl,ls1021a-qdma", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids);
+
+static struct platform_driver fsl_qdma_driver = {
+ .driver = {
+ .name = "fsl-qdma",
+ .of_match_table = fsl_qdma_dt_ids,
+ },
+ .probe = fsl_qdma_probe,
+ .remove = fsl_qdma_remove,
+};
+
+module_platform_driver(fsl_qdma_driver);
+
+MODULE_ALIAS("platform:fsl-qdma");
+MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver");
+MODULE_LICENSE("GPL v2");
^ permalink raw reply related
* [v8,2/7] dmaengine: fsldma: Adding macro FSL_DMA_IN/OUT implement for ARM platform
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
This patch add the macro FSL_DMA_IN/OUT implement for ARM platform.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
drivers/dma/fsldma.h | 57 ++++++++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 19 deletions(-)
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index 982845b..1dc64c9 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -196,39 +196,58 @@ struct fsldma_chan {
#define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node)
#define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx)
+#ifdef CONFIG_PPC
+#define fsl_ioread32(p) in_le32(p)
+#define fsl_ioread32be(p) in_be32(p)
+#define fsl_iowrite32(v, p) out_le32(p, v)
+#define fsl_iowrite32be(v, p) out_be32(p, v)
+
#ifndef __powerpc64__
-static u64 in_be64(const u64 __iomem *addr)
+static u64 fsl_ioread64(const u64 __iomem *addr)
{
- return ((u64)in_be32((u32 __iomem *)addr) << 32) |
- (in_be32((u32 __iomem *)addr + 1));
+ return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
+ (in_le32((u32 __iomem *)addr));
}
-static void out_be64(u64 __iomem *addr, u64 val)
+static void fsl_iowrite64(u64 val, u64 __iomem *addr)
{
- out_be32((u32 __iomem *)addr, val >> 32);
- out_be32((u32 __iomem *)addr + 1, (u32)val);
+ out_le32((u32 __iomem *)addr + 1, val >> 32);
+ out_le32((u32 __iomem *)addr, (u32)val);
}
-/* There is no asm instructions for 64 bits reverse loads and stores */
-static u64 in_le64(const u64 __iomem *addr)
+static u64 fsl_ioread64be(const u64 __iomem *addr)
{
- return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
- (in_le32((u32 __iomem *)addr));
+ return ((u64)in_be32((u32 __iomem *)addr) << 32) |
+ (in_be32((u32 __iomem *)addr + 1));
}
-static void out_le64(u64 __iomem *addr, u64 val)
+static void fsl_iowrite64be(u64 val, u64 __iomem *addr)
{
- out_le32((u32 __iomem *)addr + 1, val >> 32);
- out_le32((u32 __iomem *)addr, (u32)val);
+ out_be32((u32 __iomem *)addr, val >> 32);
+ out_be32((u32 __iomem *)addr + 1, (u32)val);
}
#endif
+#endif
-#define FSL_DMA_IN(fsl_chan, addr, width) \
- (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
- in_be##width(addr) : in_le##width(addr))
-#define FSL_DMA_OUT(fsl_chan, addr, val, width) \
- (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
- out_be##width(addr, val) : out_le##width(addr, val))
+#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
+#define fsl_ioread32(p) ioread32(p)
+#define fsl_ioread32be(p) ioread32be(p)
+#define fsl_iowrite32(v, p) iowrite32(v, p)
+#define fsl_iowrite32be(v, p) iowrite32be(v, p)
+#define fsl_ioread64(p) ioread64(p)
+#define fsl_ioread64be(p) ioread64be(p)
+#define fsl_iowrite64(v, p) iowrite64(v, p)
+#define fsl_iowrite64be(v, p) iowrite64be(v, p)
+#endif
+
+#define FSL_DMA_IN(fsl_dma, addr, width) \
+ (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \
+ fsl_ioread##width##be(addr) : fsl_ioread##width(addr))
+
+#define FSL_DMA_OUT(fsl_dma, addr, val, width) \
+ (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \
+ fsl_iowrite##width##be(val, addr) : fsl_iowrite \
+ ##width(val, addr))
#define DMA_TO_CPU(fsl_chan, d, width) \
(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
^ permalink raw reply related
* [v8,1/7] dmaengine: fsldma: Replace DMA_IN/OUT by FSL_DMA_IN/OUT
From: Wen He @ 2018-08-17 2:54 UTC (permalink / raw)
To: vkoul, dmaengine; +Cc: robh+dt, leoyang.li, jiafei.pan, peng.ma, wen.he_1
This patch implement a standard macro call functions is
used to NXP dma drivers.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
drivers/dma/fsldma.c | 16 ++++++++--------
drivers/dma/fsldma.h | 4 ++--
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 1117b51..39871e0 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -53,42 +53,42 @@ static const char msg_ld_oom[] = "No free memory for link descriptor";
static void set_sr(struct fsldma_chan *chan, u32 val)
{
- DMA_OUT(chan, &chan->regs->sr, val, 32);
+ FSL_DMA_OUT(chan, &chan->regs->sr, val, 32);
}
static u32 get_sr(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->sr, 32);
+ return FSL_DMA_IN(chan, &chan->regs->sr, 32);
}
static void set_mr(struct fsldma_chan *chan, u32 val)
{
- DMA_OUT(chan, &chan->regs->mr, val, 32);
+ FSL_DMA_OUT(chan, &chan->regs->mr, val, 32);
}
static u32 get_mr(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->mr, 32);
+ return FSL_DMA_IN(chan, &chan->regs->mr, 32);
}
static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
{
- DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
+ FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
}
static dma_addr_t get_cdar(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
+ return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
}
static void set_bcr(struct fsldma_chan *chan, u32 val)
{
- DMA_OUT(chan, &chan->regs->bcr, val, 32);
+ FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32);
}
static u32 get_bcr(struct fsldma_chan *chan)
{
- return DMA_IN(chan, &chan->regs->bcr, 32);
+ return FSL_DMA_IN(chan, &chan->regs->bcr, 32);
}
/*
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index 4787d48..982845b 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -223,10 +223,10 @@ static void out_le64(u64 __iomem *addr, u64 val)
}
#endif
-#define DMA_IN(fsl_chan, addr, width) \
+#define FSL_DMA_IN(fsl_chan, addr, width) \
(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
in_be##width(addr) : in_le##width(addr))
-#define DMA_OUT(fsl_chan, addr, val, width) \
+#define FSL_DMA_OUT(fsl_chan, addr, val, width) \
(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \
out_be##width(addr, val) : out_le##width(addr, val))
^ permalink raw reply related
* [RFC] dmaengine: Add metadata_ops for dma_async_tx_descriptor
From: Radhey Shyam Pandey @ 2018-08-16 13:29 UTC (permalink / raw)
To: Peter Ujfalusi, dan.j.williams@intel.com, vkoul@kernel.org
Cc: dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org,
lars@metafoo.de
> -----Original Message-----
> From: Peter Ujfalusi <peter.ujfalusi@ti.com>
> Sent: Wednesday, August 15, 2018 4:27 PM
> To: dan.j.williams@intel.com; vkoul@kernel.org
> Cc: dmaengine@vger.kernel.org; linux-kernel@vger.kernel.org;
> lars@metafoo.de; Radhey Shyam Pandey <radheys@xilinx.com>
> Subject: [RFC] dmaengine: Add metadata_ops for dma_async_tx_descriptor
>
> The metadata is best described as side band data or parameters traveling
> alongside the data DMAd by the DMA engine. It is data
> which is understood by the peripheral and the peripheral driver only, the
> DMA engine see it only as data block and it is not interpreting it in any
> way.
>
> The metadata can be different per descriptor as it is a parameter for the
> data being transferred.
>
> If the DMA supports per descriptor metadata it can implement the attach,
> get_ptr/set_len callbacks.
>
> Client drivers must only use either attach or get_ptr/set_len to avoid
> miss configuration.
>
> Client driver can check if a given metadata mode is supported by the
> channel during probe time with
> dmaengine_is_metadata_mode_supported(chan, DESC_METADATA_CLIENT);
> dmaengine_is_metadata_mode_supported(chan,
> DESC_METADATA_EMBEDDED);
>
> and based on this information can use either mode.
>
> Wrappers are also added for the metadata_ops.
>
> To be used in DESC_METADATA_CLIENT mode:
> dmaengine_desc_attach_metadata()
>
> To be used in DESC_METADATA_EMBEDDED mode:
> dmaengine_desc_get_metadata_ptr()
> dmaengine_desc_set_metadata_len()
>
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
> include/linux/dmaengine.h | 112
> ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 112 insertions(+)
>
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 3db833a8c542..2200f8985adf 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -231,6 +231,25 @@ typedef struct { DECLARE_BITMAP(bits,
> DMA_TX_TYPE_END); } dma_cap_mask_t;
> * @bytes_transferred: byte counter
> */
>
> +/**
> + * enum dma_desc_metadata_mode - per descriptor metadata mode types
> supported
> + * @DESC_METADATA_CLIENT - the metadata buffer is allocated/provided by
> the
> + * client driver and it is attached (via the dmaengine_desc_attach_metadata()
> + * helper) to the descriptor.
> + * @DESC_METADATA_EMBEDDED - the metadata buffer is
> allocated/managed by the DMA
Just a thought - We can rename it to DESC_METADATA_ENGINE?
i.e metadata allocation place - > dma client/engine.
> + * driver. The client driver can ask for the pointer, maximum size and the
> + * currently used size of the metadata and can directly updata or read it.
/s/updata/update
> + * dmaengine_desc_get_metadata_ptr() and
> dmaengine_desc_set_metadata_len() is
> + * provided as helper functions.
It will be helpful if we add description for both DESC_METADATA_EMBEDDED
modes i.e DMA_DEV_TO_MEM and MEM_TO_DEV types. I think in DEV_TO_MEM
we don't need to set_metadata_len(). Length will provided by DMA engine.
> + *
> + * Note: the two mode is not compatible and clients must use one mode for a
> + * descriptor.
> + */
> +enum dma_desc_metadata_mode {
> + DESC_METADATA_CLIENT = (1 << 0),
> + DESC_METADATA_EMBEDDED = (1 << 1),
> +};
> +
> struct dma_chan_percpu {
> /* stats */
> unsigned long memcpy_count;
> @@ -494,6 +513,18 @@ struct dmaengine_unmap_data {
> dma_addr_t addr[0];
> };
>
> +struct dma_async_tx_descriptor;
> +
> +struct dma_descriptor_metadata_ops {
> + int (*attach)(struct dma_async_tx_descriptor *desc, void *data,
> + size_t len);
> +
> + void *(*get_ptr)(struct dma_async_tx_descriptor *desc,
> + size_t *payload_len, size_t *max_len);
> + int (*set_len)(struct dma_async_tx_descriptor *desc,
> + size_t payload_len);
> +};
> +
> /**
> * struct dma_async_tx_descriptor - async transaction descriptor
> * ---dma generic offload fields---
> @@ -523,6 +554,8 @@ struct dma_async_tx_descriptor {
> dma_async_tx_callback_result callback_result;
> void *callback_param;
> struct dmaengine_unmap_data *unmap;
> + enum dma_desc_metadata_mode desc_metadata_mode;
> + struct dma_descriptor_metadata_ops *metadata_ops;
> #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
> struct dma_async_tx_descriptor *next;
> struct dma_async_tx_descriptor *parent;
> @@ -685,6 +718,7 @@ struct dma_filter {
> * @global_node: list_head for global dma_device_list
> * @filter: information for device/slave to filter function/param mapping
> * @cap_mask: one or more dma_capability flags
> + * @desc_metadata_modes: supported metadata modes by the DMA device
> * @max_xor: maximum number of xor sources, 0 if no capability
> * @max_pq: maximum number of PQ sources and PQ-continue capability
> * @copy_align: alignment shift for memcpy operations
> @@ -749,6 +783,7 @@ struct dma_device {
> struct list_head global_node;
> struct dma_filter filter;
> dma_cap_mask_t cap_mask;
> + enum dma_desc_metadata_mode desc_metadata_modes;
> unsigned short max_xor;
> unsigned short max_pq;
> enum dmaengine_alignment copy_align;
> @@ -935,6 +970,83 @@ static inline struct dma_async_tx_descriptor
> *dmaengine_prep_dma_memcpy(
> len, flags);
> }
>
> +static inline bool dmaengine_is_metadata_mode_supported(struct dma_chan
> *chan,
> + enum dma_desc_metadata_mode mode)
> +{
> + return !!(chan->device->desc_metadata_modes & mode);
> +}
> +
> +static inline int _desc_check_and_set_metadata_mode(
> + struct dma_async_tx_descriptor *desc, enum
> dma_desc_metadata_mode mode)
> +{
> + /* Make sure that the metadata mode is not mixed */
> + if (!desc->desc_metadata_mode) {
Minor nit - we can refactor this code to have failure path early.
> + if (dmaengine_is_metadata_mode_supported(desc->chan,
> mode))
> + desc->desc_metadata_mode = mode;
> + else
> + return -ENOTSUPP;
> + } else if (desc->desc_metadata_mode != mode) {
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static inline int dmaengine_desc_attach_metadata(
> + struct dma_async_tx_descriptor *desc, void *data, size_t len)
> +{
> + int ret;
> +
> + if (!desc)
> + return -EINVAL;
> +
> + ret = _desc_check_and_set_metadata_mode(desc,
> DESC_METADATA_CLIENT);
> + if (ret)
> + return ret;
> +
> + if (!desc->metadata_ops || !desc->metadata_ops->attach)
> + return -ENOTSUPP;
> +
> + return desc->metadata_ops->attach(desc, data, len);
> +}
> +
> +static inline void *dmaengine_desc_get_metadata_ptr(
> + struct dma_async_tx_descriptor *desc, size_t *payload_len,
> + size_t *max_len)
> +{
> + int ret;
> +
> + if (!desc)
> + return ERR_PTR(-EINVAL);
> +
> + ret = _desc_check_and_set_metadata_mode(desc,
> DESC_METADATA_EMBEDDED);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + if (!desc->metadata_ops || !desc->metadata_ops->get_ptr)
> + return ERR_PTR(-ENOTSUPP);
> +
> + return desc->metadata_ops->get_ptr(desc, payload_len, max_len);
> +}
> +
> +static inline int dmaengine_desc_set_metadata_len(
> + struct dma_async_tx_descriptor *desc, size_t payload_len)
> +{
> + int ret;
> +
> + if (!desc)
> + return -EINVAL;
> +
> + ret = _desc_check_and_set_metadata_mode(desc,
> DESC_METADATA_EMBEDDED);
> + if (ret)
> + return ret;
> +
> + if (!desc->metadata_ops || !desc->metadata_ops->set_len)
> + return -ENOTSUPP;
> +
> + return desc->metadata_ops->set_len(desc, payload_len);
> +}
> +
> /**
> * dmaengine_terminate_all() - Terminate all active DMA transfers
> * @chan: The channel for which to terminate the transfers
> --
> Peter
>
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* [RFC,1/2] dt-bindings: dmaengine: xilinx_dma: Add binding for Xilinx MCDMA IP
From: Radhey Shyam Pandey @ 2018-08-16 11:41 UTC (permalink / raw)
To: Rob Herring
Cc: vkoul@kernel.org, mark.rutland@arm.com, Michal Simek,
dan.j.williams@intel.com, Appana Durga Kedareswara Rao,
lars@metafoo.de, dmaengine@vger.kernel.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Hi Rob,
Thanks for the review.
> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Tuesday, August 14, 2018 9:43 PM
> To: Radhey Shyam Pandey <radheys@xilinx.com>
> Cc: vkoul@kernel.org; mark.rutland@arm.com; Michal Simek
> <michals@xilinx.com>; dan.j.williams@intel.com; Appana Durga Kedareswara
> Rao <appanad@xilinx.com>; lars@metafoo.de; dmaengine@vger.kernel.org;
> devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> kernel@vger.kernel.org
> Subject: Re: [RFC PATCH 1/2] dt-bindings: dmaengine: xilinx_dma: Add binding
> for Xilinx MCDMA IP
>
> On Tue, Jul 31, 2018 at 11:16:12PM +0530, Radhey Shyam Pandey wrote:
> > Add devicetree binding for Xilinx AXI Multichannel Direct Memory
> > Access (AXI MCDMA) IP. The AXI MCDMA provides high-bandwidth direct
> > memory access between memory and AXI4-Stream target peripherals. The
> > AXI MCDMA core provides scatter-gather interface with multiple channel
> support.
> >
> > Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> > ---
> > Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt | 10
> > ++++++++--
> > 1 file changed, 8 insertions(+), 2 deletions(-)
> >
> > diff --git
> > a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> > b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> > index 174af2c..57bb02e 100644
> > --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> > +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> > @@ -11,9 +11,13 @@ is to receive from the device.
> > Xilinx AXI CDMA engine, it does transfers between memory-mapped
> > source address and a memory-mapped destination address.
> >
> > +Xilinx AXI MCDMA engine, it does transfer between memory and AXI4
> > +stream target devices. It can be configured to have up to 16
> > +independent transmit and receive channels.
> > +
> > Required properties:
> > - compatible: Should be "xlnx,axi-vdma-1.00.a" or "xlnx,axi-dma-1.00.a" or
> > - "xlnx,axi-cdma-1.00.a""
> > + "xlnx,axi-cdma-1.00.a" or "xlnx,axi-mcdma-1.00.a".
>
> Please reformat to 1 per line.
Yes, I will fix it in v2.
>
> > - #dma-cells: Should be <1>, see "dmas" property below
> > - reg: Should contain VDMA registers location and length.
> > - xlnx,addrwidth: Should be the vdma addressing size in bits(ex: 32 bits).
> > @@ -56,6 +60,8 @@ Required child node properties:
> > For CDMA: It should be "xlnx,axi-cdma-channel".
> > For AXIDMA: It should be either "xlnx,axi-dma-mm2s-channel" or
> > "xlnx,axi-dma-s2mm-channel".
> > + For MCDMA: It should be either "xlnx,axi-mcdma-mm2s-channel" or
> > + "xlnx,axi-mcdma-s2mm-channel".
>
> What's wrong with reusing the existing xlnx,axi-dma-* names?
Valid point. I think we can reuse it (Reason for adding was to follow
similar convention as of DMA , VDMA IPs). I will address it in v2.
>
> > - interrupts: Should contain per channel VDMA interrupts.
> > - xlnx,datawidth: Should contain the stream data width, take values
> > {32,64...1024}.
> > @@ -68,7 +74,7 @@ Optional child node properties for VDMA:
> > enabled/disabled in hardware.
> > - xlnx,enable-vert-flip: Tells vertical flip is
> > enabled/disabled in hardware(S2MM path).
> > -Optional child node properties for AXI DMA:
> > +Optional child node properties for AXI DMA and MCDMA:
> > -dma-channels: Number of dma channels in child node.
> >
> > Example:
> > --
> > 2.7.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe devicetree"
> > 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
* [v7,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Vinod Koul @ 2018-08-16 10:42 UTC (permalink / raw)
To: Wen He
Cc: dmaengine@vger.kernel.org, robh+dt@kernel.org,
devicetree@vger.kernel.org, Leo Li, Jiafei Pan, Peng Ma
On 16-08-18, 08:19, Wen He wrote:
> > > Hi Vinod,
> > >
> > > Do you have any other comments for this patch?
> > > If not, can we merge it to upstream now?
> >
> > I don't have this in my queue, so can you please resend this after merge
> > window closes.
> >
> > Thanks
>
> OK, so I need resend all of patch, right?
Yes please
^ permalink raw reply
* [v7,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Wen He @ 2018-08-16 8:19 UTC (permalink / raw)
To: Vinod
Cc: dmaengine@vger.kernel.org, robh+dt@kernel.org,
devicetree@vger.kernel.org, Leo Li, Jiafei Pan, Peng Ma
> -----Original Message-----
> From: dmaengine-owner@vger.kernel.org
> [mailto:dmaengine-owner@vger.kernel.org] On Behalf Of Vinod
> Sent: 2018年8月16日 12:39
> To: Wen He <wen.he_1@nxp.com>
> Cc: dmaengine@vger.kernel.org; robh+dt@kernel.org;
> devicetree@vger.kernel.org; Leo Li <leoyang.li@nxp.com>; Jiafei Pan
> <jiafei.pan@nxp.com>; Peng Ma <peng.ma@nxp.com>
> Subject: Re: [v7 3/7] dmaengine: fsl-qdma: Add qDMA controller driver for
> Layerscape SoCs
>
> On 15-08-18, 06:46, Wen He wrote:
> >
> >
> > > -----Original Message-----
> > > From: Wen He [mailto:wen.he_1@nxp.com]
> > > Sent: 2018年7月25日 19:29
> > > To: vkoul@kernel.org; dmaengine@vger.kernel.org
> > > Cc: robh+dt@kernel.org; devicetree@vger.kernel.org; Leo Li
> > > <leoyang.li@nxp.com>; Jiafei Pan <jiafei.pan@nxp.com>; Jiaheng Fan
> > > <jiaheng.fan@nxp.com>; Wen He <wen.he_1@nxp.com>
> > > Subject: [v7 3/7] dmaengine: fsl-qdma: Add qDMA controller driver
> > > for Layerscape SoCs
> > >
> > > NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel
> > > virtuallization by allowing DMA jobs to be enqueued into different
> > > command queues.
> > >
> >
> > Hi Vinod,
> >
> > Do you have any other comments for this patch?
> > If not, can we merge it to upstream now?
>
> I don't have this in my queue, so can you please resend this after merge
> window closes.
>
> Thanks
OK, so I need resend all of patch, right?
> --
> ~Vinod
^ permalink raw reply
* [v7,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Vinod Koul @ 2018-08-16 4:39 UTC (permalink / raw)
To: Wen He
Cc: dmaengine@vger.kernel.org, robh+dt@kernel.org,
devicetree@vger.kernel.org, Leo Li, Jiafei Pan, Peng Ma
On 15-08-18, 06:46, Wen He wrote:
>
>
> > -----Original Message-----
> > From: Wen He [mailto:wen.he_1@nxp.com]
> > Sent: 2018年7月25日 19:29
> > To: vkoul@kernel.org; dmaengine@vger.kernel.org
> > Cc: robh+dt@kernel.org; devicetree@vger.kernel.org; Leo Li
> > <leoyang.li@nxp.com>; Jiafei Pan <jiafei.pan@nxp.com>; Jiaheng Fan
> > <jiaheng.fan@nxp.com>; Wen He <wen.he_1@nxp.com>
> > Subject: [v7 3/7] dmaengine: fsl-qdma: Add qDMA controller driver for
> > Layerscape SoCs
> >
> > NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel
> > virtuallization by allowing DMA jobs to be enqueued into different command
> > queues.
> >
>
> Hi Vinod,
>
> Do you have any other comments for this patch?
> If not, can we merge it to upstream now?
I don't have this in my queue, so can you please resend this after merge
window closes.
Thanks
^ permalink raw reply
* dmaengine: rcar-dmac: Document R8A774A1 bindings
From: Rob Herring @ 2018-08-15 16:12 UTC (permalink / raw)
To: Fabrizio Castro
Cc: Vinod Koul, Mark Rutland, dmaengine, devicetree, linux-kernel,
Simon Horman, Geert Uytterhoeven, Chris Paterson, Biju Das,
linux-renesas-soc
On Tue, 14 Aug 2018 13:32:15 +0100, Fabrizio Castro wrote:
> Renesas' RZ/G2M (R8A774A1) SoC has DMA controllers compatible
> with this driver, therefore document RZ/G2M specific bindings.
>
> Signed-off-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das@bp.renesas.com>
> ---
> Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
I'm assuming subsystem maintainers will take this series of Renesas
patches. They are bindings only, but you all update them frequently so
we don't want any conflicts.
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [RFC] dmaengine: Add metadata_ops for dma_async_tx_descriptor
From: Peter Ujfalusi @ 2018-08-15 10:57 UTC (permalink / raw)
To: dan.j.williams, vkoul; +Cc: dmaengine, linux-kernel, lars, radheys
The metadata is best described as side band data or parameters traveling
alongside the data DMAd by the DMA engine. It is data
which is understood by the peripheral and the peripheral driver only, the
DMA engine see it only as data block and it is not interpreting it in any
way.
The metadata can be different per descriptor as it is a parameter for the
data being transferred.
If the DMA supports per descriptor metadata it can implement the attach,
get_ptr/set_len callbacks.
Client drivers must only use either attach or get_ptr/set_len to avoid
miss configuration.
Client driver can check if a given metadata mode is supported by the
channel during probe time with
dmaengine_is_metadata_mode_supported(chan, DESC_METADATA_CLIENT);
dmaengine_is_metadata_mode_supported(chan, DESC_METADATA_EMBEDDED);
and based on this information can use either mode.
Wrappers are also added for the metadata_ops.
To be used in DESC_METADATA_CLIENT mode:
dmaengine_desc_attach_metadata()
To be used in DESC_METADATA_EMBEDDED mode:
dmaengine_desc_get_metadata_ptr()
dmaengine_desc_set_metadata_len()
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
include/linux/dmaengine.h | 112 ++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 3db833a8c542..2200f8985adf 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -231,6 +231,25 @@ typedef struct { DECLARE_BITMAP(bits, DMA_TX_TYPE_END); } dma_cap_mask_t;
* @bytes_transferred: byte counter
*/
+/**
+ * enum dma_desc_metadata_mode - per descriptor metadata mode types supported
+ * @DESC_METADATA_CLIENT - the metadata buffer is allocated/provided by the
+ * client driver and it is attached (via the dmaengine_desc_attach_metadata()
+ * helper) to the descriptor.
+ * @DESC_METADATA_EMBEDDED - the metadata buffer is allocated/managed by the DMA
+ * driver. The client driver can ask for the pointer, maximum size and the
+ * currently used size of the metadata and can directly updata or read it.
+ * dmaengine_desc_get_metadata_ptr() and dmaengine_desc_set_metadata_len() is
+ * provided as helper functions.
+ *
+ * Note: the two mode is not compatible and clients must use one mode for a
+ * descriptor.
+ */
+enum dma_desc_metadata_mode {
+ DESC_METADATA_CLIENT = (1 << 0),
+ DESC_METADATA_EMBEDDED = (1 << 1),
+};
+
struct dma_chan_percpu {
/* stats */
unsigned long memcpy_count;
@@ -494,6 +513,18 @@ struct dmaengine_unmap_data {
dma_addr_t addr[0];
};
+struct dma_async_tx_descriptor;
+
+struct dma_descriptor_metadata_ops {
+ int (*attach)(struct dma_async_tx_descriptor *desc, void *data,
+ size_t len);
+
+ void *(*get_ptr)(struct dma_async_tx_descriptor *desc,
+ size_t *payload_len, size_t *max_len);
+ int (*set_len)(struct dma_async_tx_descriptor *desc,
+ size_t payload_len);
+};
+
/**
* struct dma_async_tx_descriptor - async transaction descriptor
* ---dma generic offload fields---
@@ -523,6 +554,8 @@ struct dma_async_tx_descriptor {
dma_async_tx_callback_result callback_result;
void *callback_param;
struct dmaengine_unmap_data *unmap;
+ enum dma_desc_metadata_mode desc_metadata_mode;
+ struct dma_descriptor_metadata_ops *metadata_ops;
#ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
struct dma_async_tx_descriptor *next;
struct dma_async_tx_descriptor *parent;
@@ -685,6 +718,7 @@ struct dma_filter {
* @global_node: list_head for global dma_device_list
* @filter: information for device/slave to filter function/param mapping
* @cap_mask: one or more dma_capability flags
+ * @desc_metadata_modes: supported metadata modes by the DMA device
* @max_xor: maximum number of xor sources, 0 if no capability
* @max_pq: maximum number of PQ sources and PQ-continue capability
* @copy_align: alignment shift for memcpy operations
@@ -749,6 +783,7 @@ struct dma_device {
struct list_head global_node;
struct dma_filter filter;
dma_cap_mask_t cap_mask;
+ enum dma_desc_metadata_mode desc_metadata_modes;
unsigned short max_xor;
unsigned short max_pq;
enum dmaengine_alignment copy_align;
@@ -935,6 +970,83 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy(
len, flags);
}
+static inline bool dmaengine_is_metadata_mode_supported(struct dma_chan *chan,
+ enum dma_desc_metadata_mode mode)
+{
+ return !!(chan->device->desc_metadata_modes & mode);
+}
+
+static inline int _desc_check_and_set_metadata_mode(
+ struct dma_async_tx_descriptor *desc, enum dma_desc_metadata_mode mode)
+{
+ /* Make sure that the metadata mode is not mixed */
+ if (!desc->desc_metadata_mode) {
+ if (dmaengine_is_metadata_mode_supported(desc->chan, mode))
+ desc->desc_metadata_mode = mode;
+ else
+ return -ENOTSUPP;
+ } else if (desc->desc_metadata_mode != mode) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int dmaengine_desc_attach_metadata(
+ struct dma_async_tx_descriptor *desc, void *data, size_t len)
+{
+ int ret;
+
+ if (!desc)
+ return -EINVAL;
+
+ ret = _desc_check_and_set_metadata_mode(desc, DESC_METADATA_CLIENT);
+ if (ret)
+ return ret;
+
+ if (!desc->metadata_ops || !desc->metadata_ops->attach)
+ return -ENOTSUPP;
+
+ return desc->metadata_ops->attach(desc, data, len);
+}
+
+static inline void *dmaengine_desc_get_metadata_ptr(
+ struct dma_async_tx_descriptor *desc, size_t *payload_len,
+ size_t *max_len)
+{
+ int ret;
+
+ if (!desc)
+ return ERR_PTR(-EINVAL);
+
+ ret = _desc_check_and_set_metadata_mode(desc, DESC_METADATA_EMBEDDED);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (!desc->metadata_ops || !desc->metadata_ops->get_ptr)
+ return ERR_PTR(-ENOTSUPP);
+
+ return desc->metadata_ops->get_ptr(desc, payload_len, max_len);
+}
+
+static inline int dmaengine_desc_set_metadata_len(
+ struct dma_async_tx_descriptor *desc, size_t payload_len)
+{
+ int ret;
+
+ if (!desc)
+ return -EINVAL;
+
+ ret = _desc_check_and_set_metadata_mode(desc, DESC_METADATA_EMBEDDED);
+ if (ret)
+ return ret;
+
+ if (!desc->metadata_ops || !desc->metadata_ops->set_len)
+ return -ENOTSUPP;
+
+ return desc->metadata_ops->set_len(desc, payload_len);
+}
+
/**
* dmaengine_terminate_all() - Terminate all active DMA transfers
* @chan: The channel for which to terminate the transfers
^ permalink raw reply related
* [v7,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Wen He @ 2018-08-15 6:46 UTC (permalink / raw)
To: Wen He, vkoul@kernel.org, dmaengine@vger.kernel.org
Cc: robh+dt@kernel.org, devicetree@vger.kernel.org, Leo Li,
Jiafei Pan, Peng Ma
> -----Original Message-----
> From: Wen He [mailto:wen.he_1@nxp.com]
> Sent: 2018年7月25日 19:29
> To: vkoul@kernel.org; dmaengine@vger.kernel.org
> Cc: robh+dt@kernel.org; devicetree@vger.kernel.org; Leo Li
> <leoyang.li@nxp.com>; Jiafei Pan <jiafei.pan@nxp.com>; Jiaheng Fan
> <jiaheng.fan@nxp.com>; Wen He <wen.he_1@nxp.com>
> Subject: [v7 3/7] dmaengine: fsl-qdma: Add qDMA controller driver for
> Layerscape SoCs
>
> NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel
> virtuallization by allowing DMA jobs to be enqueued into different command
> queues.
>
Hi Vinod,
Do you have any other comments for this patch?
If not, can we merge it to upstream now?
Best Regards,
Wen
> Signed-off-by: Wen He <wen.he_1@nxp.com>
> Signed-off-by: Jiaheng Fan <jiaheng.fan@nxp.com>
> ---
> drivers/dma/Kconfig | 13 +
> drivers/dma/Makefile | 1 +
> drivers/dma/fsl-qdma.c | 1105
> ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1119 insertions(+), 0 deletions(-) create mode 100644
> drivers/dma/fsl-qdma.c
>
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index
> ca1680a..2914caf 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -218,6 +218,19 @@ config FSL_EDMA
> multiplexing capability for DMA request sources(slot).
> This module can be found on Freescale Vybrid and LS-1 SoCs.
>
> +config FSL_QDMA
> + tristate "NXP Layerscape qDMA engine support"
> + depends on ARM || ARM64
> + select DMA_ENGINE
> + select DMA_VIRTUAL_CHANNELS
> + select DMA_ENGINE_RAID
> + select ASYNC_TX_ENABLE_CHANNEL_SWITCH
> + help
> + Support the NXP Layerscape qDMA engine with command queue
> and legacy mode.
> + Channel virtualization is supported through enqueuing of DMA
> jobs to,
> + or dequeuing DMA jobs from, different work queues.
> + This module can be found on NXP Layerscape SoCs.
> +
> config FSL_RAID
> tristate "Freescale RAID engine Support"
> depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index
> 203a99d..0556f9d 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
> +obj-$(CONFIG_FSL_QDMA) += fsl-qdma.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-qdma.c b/drivers/dma/fsl-qdma.c new file mode 100644
> index 0000000..1cecf9b
> --- /dev/null
> +++ b/drivers/dma/fsl-qdma.c
> @@ -0,0 +1,1105 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright 2018 NXP
> +
> +/*
> + * Driver for NXP Layerscape Queue Direct Memory Access Controller
> + *
> + * Author:
> + * Wen He <wen.he_1@nxp.com>
> + * Jiaheng Fan <jiaheng.fan@nxp.com>
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_dma.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmapool.h>
> +#include <linux/dmaengine.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#include "virt-dma.h"
> +#include "fsldma.h"
> +
> +/* Register related definition */
> +#define FSL_QDMA_DMR 0x0
> +#define FSL_QDMA_DSR 0x4
> +#define FSL_QDMA_DEIER 0xe00
> +#define FSL_QDMA_DEDR 0xe04
> +#define FSL_QDMA_DECFDW0R 0xe10
> +#define FSL_QDMA_DECFDW1R 0xe14
> +#define FSL_QDMA_DECFDW2R 0xe18
> +#define FSL_QDMA_DECFDW3R 0xe1c
> +#define FSL_QDMA_DECFQIDR 0xe30
> +#define FSL_QDMA_DECBR 0xe34
> +
> +#define FSL_QDMA_BCQMR(x) (0xc0 + 0x100 * (x))
> +#define FSL_QDMA_BCQSR(x) (0xc4 + 0x100 * (x))
> +#define FSL_QDMA_BCQEDPA_SADDR(x) (0xc8 + 0x100 * (x))
> +#define FSL_QDMA_BCQDPA_SADDR(x) (0xcc + 0x100 * (x))
> +#define FSL_QDMA_BCQEEPA_SADDR(x) (0xd0 + 0x100 * (x))
> +#define FSL_QDMA_BCQEPA_SADDR(x) (0xd4 + 0x100 * (x))
> +#define FSL_QDMA_BCQIER(x) (0xe0 + 0x100 * (x))
> +#define FSL_QDMA_BCQIDR(x) (0xe4 + 0x100 * (x))
> +
> +#define FSL_QDMA_SQDPAR 0x80c
> +#define FSL_QDMA_SQEPAR 0x814
> +#define FSL_QDMA_BSQMR 0x800
> +#define FSL_QDMA_BSQSR 0x804
> +#define FSL_QDMA_BSQICR 0x828
> +#define FSL_QDMA_CQMR 0xa00
> +#define FSL_QDMA_CQDSCR1 0xa08
> +#define FSL_QDMA_CQDSCR2 0xa0c
> +#define FSL_QDMA_CQIER 0xa10
> +#define FSL_QDMA_CQEDR 0xa14
> +#define FSL_QDMA_SQCCMR 0xa20
> +
> +/* Registers for bit and genmask */
> +#define FSL_QDMA_CQIDR_SQT BIT(15)
> +#define QDMA_CCDF_FOTMAT BIT(29)
> +#define QDMA_CCDF_SER BIT(30)
> +#define QDMA_SG_FIN BIT(30)
> +#define QDMA_SG_EXT BIT(31)
> +#define QDMA_SG_LEN_MASK GENMASK(29, 0)
> +#define QDMA_CCDF_MASK GENMASK(28, 20)
> +
> +#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0)
> +#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0)
> +#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0)
> +
> +#define FSL_QDMA_BCQIER_CQTIE BIT(15)
> +#define FSL_QDMA_BCQIER_CQPEIE BIT(23)
> +#define FSL_QDMA_BSQICR_ICEN BIT(31)
> +
> +#define FSL_QDMA_BSQICR_ICST(x) ((x) << 16)
> +#define FSL_QDMA_CQIER_MEIE BIT(31)
> +#define FSL_QDMA_CQIER_TEIE BIT(0)
> +#define FSL_QDMA_SQCCMR_ENTER_WM BIT(21)
> +
> +#define FSL_QDMA_BCQMR_EN BIT(31)
> +#define FSL_QDMA_BCQMR_EI BIT(30)
> +#define FSL_QDMA_BCQMR_CD_THLD(x) ((x) << 20)
> +#define FSL_QDMA_BCQMR_CQ_SIZE(x) ((x) << 16)
> +
> +#define FSL_QDMA_BCQSR_QF BIT(16)
> +#define FSL_QDMA_BCQSR_XOFF BIT(0)
> +
> +#define FSL_QDMA_BSQMR_EN BIT(31)
> +#define FSL_QDMA_BSQMR_DI BIT(30)
> +#define FSL_QDMA_BSQMR_CQ_SIZE(x) ((x) << 16)
> +
> +#define FSL_QDMA_BSQSR_QE BIT(17)
> +
> +#define FSL_QDMA_DMR_DQD BIT(30)
> +#define FSL_QDMA_DSR_DB BIT(31)
> +
> +/* Size related definition */
> +#define FSL_QDMA_QUEUE_MAX 8
> +#define FSL_QDMA_BASE_BUFFER_SIZE 96
> +#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN 64
> +#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX 16384
> +#define FSL_QDMA_QUEUE_NUM_MAX 8
> +
> +/* Field definition for CMD */
> +#define FSL_QDMA_CMD_RWTTYPE 0x4
> +#define FSL_QDMA_CMD_LWC 0x2
> +#define FSL_QDMA_CMD_RWTTYPE_OFFSET 28
> +#define FSL_QDMA_CMD_NS_OFFSET 27
> +#define FSL_QDMA_CMD_DQOS_OFFSET 24
> +#define FSL_QDMA_CMD_WTHROTL_OFFSET 20
> +#define FSL_QDMA_CMD_DSEN_OFFSET 19
> +#define FSL_QDMA_CMD_LWC_OFFSET 16
> +
> +#define FSL_QDMA_E_SG_TABLE 1
> +#define FSL_QDMA_E_DATA_BUFFER 0
> +#define FSL_QDMA_F_LAST_ENTRY 1
> +
> +/* Field definition for safe loop count*/
> +#define FSL_QDMA_HALT_COUNT 1500
> +#define FSL_QDMA_MAX_SIZE 16385
> +
> +/* Field definition for Descriptor offset */
> +#define QDMA_CCDF_STATUS 20
> +#define QDMA_CCDF_OFFSET 20
> +
> +/**
> + * struct fsl_qdma_format - This is the struct holding describing compound
> + * descriptor format with qDMA.
> + * @status: Command status and enqueue status notification.
> + * @cfg: Frame offset and frame format.
> + * @addr_lo: Holding the compound descriptor of the lower
> + * 32-bits address in memory 40-bit address.
> + * @addr_hi: Same as above member, but point high 8-bits in
> + * memory 40-bit address.
> + * @__reserved1: Reserved field.
> + * @cfg8b_w1: Compound descriptor command queue origin
> produced
> + * by qDMA and dynamic debug field.
> + * @data Pointer to the memory 40-bit address, describes
> DMA
> + * source information and DMA destination information.
> + */
> +struct fsl_qdma_format {
> + __le32 status;
> + __le32 cfg;
> + union {
> + struct {
> + __le32 addr_lo;
> + u8 addr_hi;
> + u8 __reserved1[2];
> + u8 cfg8b_w1;
> + } __packed;
> + __le64 data;
> + };
> +} __packed;
> +
> +/* qDMA status notification pre information */ struct fsl_pre_status {
> + u64 queue;
> + u64 addr;
> +};
> +
> +struct fsl_qdma_chan {
> + struct virt_dma_chan vchan;
> + struct virt_dma_desc vdesc;
> + enum dma_status status;
> + u32 slave_id;
> + struct fsl_qdma_engine *qdma;
> + struct fsl_qdma_queue *queue;
> + struct list_head qcomp;
> +};
> +
> +struct fsl_qdma_queue {
> + struct fsl_qdma_format *virt_head;
> + struct fsl_qdma_format *virt_tail;
> + struct list_head comp_used;
> + struct list_head comp_free;
> + struct dma_pool *comp_pool;
> + spinlock_t queue_lock;
> + dma_addr_t bus_addr;
> + u32 n_cq;
> + u32 id;
> + struct fsl_qdma_format *cq;
> +};
> +
> +struct fsl_qdma_comp {
> + dma_addr_t bus_addr;
> + struct fsl_qdma_format *virt_addr;
> + struct fsl_qdma_chan *qchan;
> + struct virt_dma_desc vdesc;
> + struct list_head list;
> +};
> +
> +struct fsl_qdma_engine {
> + struct dma_device dma_dev;
> + void __iomem *ctrl_base;
> + void __iomem *status_base;
> + void __iomem *block_base;
> + u32 n_chans;
> + u32 n_queues;
> + struct mutex fsl_qdma_mutex;
> + int error_irq;
> + int queue_irq;
> + bool feature;
> + struct fsl_qdma_queue *queue;
> + struct fsl_qdma_queue *status;
> + struct fsl_qdma_chan chans[];
> +
> +};
> +
> +static inline u64
> +qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf) {
> + return le64_to_cpu(ccdf->data) & (U64_MAX >> 24); }
> +
> +static inline void
> +qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr) {
> + ccdf->addr_hi = upper_32_bits(addr);
> + ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr)); }
> +
> +static inline u64
> +qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf) {
> + return ccdf->cfg8b_w1 & U8_MAX;
> +}
> +
> +static inline int
> +qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf) {
> + return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >>
> QDMA_CCDF_OFFSET;
> +}
> +
> +static inline void
> +qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset) {
> + ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset); }
> +
> +static inline int
> +qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf) {
> + return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >>
> +QDMA_CCDF_STATUS; }
> +
> +static inline void
> +qdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status) {
> + ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status); }
> +
> +static inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int
> +len) {
> + csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK); }
> +
> +static inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int
> +len) {
> + csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len &
> QDMA_SG_LEN_MASK)); }
> +
> +static inline void qdma_csgf_set_e(struct fsl_qdma_format *csgf, int
> +len) {
> + csgf->cfg = cpu_to_le32(QDMA_SG_EXT | (len &
> QDMA_SG_LEN_MASK)); }
> +
> +static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr)
> +{
> + return FSL_DMA_IN(qdma, addr, 32);
> +}
> +
> +static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val,
> + void __iomem *addr)
> +{
> + FSL_DMA_OUT(qdma, addr, val, 32);
> +}
> +
> +static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan) {
> + return container_of(chan, struct fsl_qdma_chan, vchan.chan); }
> +
> +static struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd)
> +{
> + return container_of(vd, struct fsl_qdma_comp, vdesc); }
> +
> +static void fsl_qdma_free_chan_resources(struct dma_chan *chan) {
> + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + 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); }
> +
> +static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp,
> + dma_addr_t dst, dma_addr_t src, u32 len) {
> + struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest;
> + struct fsl_qdma_format *sdf, *ddf;
> +
> + ccdf = fsl_comp->virt_addr;
> + csgf_desc = fsl_comp->virt_addr + 1;
> + csgf_src = fsl_comp->virt_addr + 2;
> + csgf_dest = fsl_comp->virt_addr + 3;
> + sdf = fsl_comp->virt_addr + 4;
> + ddf = fsl_comp->virt_addr + 5;
> +
> + memset(fsl_comp->virt_addr, 0, FSL_QDMA_BASE_BUFFER_SIZE);
> + /* Head Command Descriptor(Frame Descriptor) */
> + qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16);
> + qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf));
> + qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf));
> +
> + /* Status notification is enqueued to status queue. */
> + /* Compound Command Descriptor(Frame List Table) */
> + qdma_desc_addr_set64(csgf_desc, fsl_comp->bus_addr + 64);
> + /* It must be 32 as Compound S/G Descriptor */
> + qdma_csgf_set_len(csgf_desc, 32);
> + qdma_desc_addr_set64(csgf_src, src);
> + qdma_csgf_set_len(csgf_src, len);
> + qdma_desc_addr_set64(csgf_dest, dst);
> + qdma_csgf_set_len(csgf_dest, len);
> + /* This entry is the last entry. */
> + qdma_csgf_set_f(csgf_dest, len);
> + /* Descriptor Buffer */
> + sdf->data = cpu_to_le64(
> + FSL_QDMA_CMD_RWTTYPE <<
> FSL_QDMA_CMD_RWTTYPE_OFFSET);
> + ddf->data = cpu_to_le64(
> + FSL_QDMA_CMD_RWTTYPE <<
> FSL_QDMA_CMD_RWTTYPE_OFFSET);
> + ddf->data |= cpu_to_le64(
> + FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET); }
> +
> +/*
> + * Pre-request full command descriptor for enqueue.
> + */
> +static int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue
> +*queue) {
> + struct fsl_qdma_comp *comp_temp, *_comp_temp;
> + int i;
> +
> + for (i = 0; i < queue->n_cq; i++) {
> + comp_temp = kzalloc(sizeof(*comp_temp), GFP_NOWAIT);
> + if (!comp_temp)
> + goto err;
> +
> + comp_temp->virt_addr = dma_pool_alloc(queue->comp_pool,
> + GFP_NOWAIT,
> + &comp_temp->bus_addr);
> + if (!comp_temp->virt_addr)
> + goto err;
> +
> + list_add_tail(&comp_temp->list, &queue->comp_free);
> + }
> + return 0;
> +
> +err:
> + if (i == 0 && comp_temp) {
> + kfree(comp_temp);
> + return -ENOMEM;
> + }
> +
> + while (--i >= 1) {
> + list_for_each_entry_safe(comp_temp, _comp_temp,
> + &queue->comp_free, list) {
> + dma_pool_free(queue->comp_pool,
> + comp_temp->virt_addr,
> + comp_temp->bus_addr);
> + list_del(&comp_temp->list);
> + kfree(comp_temp);
> + }
> + }
> + return -ENOMEM;
> +}
> +
> +/*
> + * Request a command descriptor for enqueue.
> + */
> +static struct fsl_qdma_comp *fsl_qdma_request_enqueue_desc(
> + struct fsl_qdma_chan *fsl_chan,
> + unsigned int dst_nents,
> + unsigned int src_nents)
> +{
> + struct fsl_qdma_comp *comp_temp;
> + struct fsl_qdma_queue *queue = fsl_chan->queue;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&queue->queue_lock, flags);
> + if (list_empty(&queue->comp_free)) {
> + spin_unlock_irqrestore(&queue->queue_lock, flags);
> + comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL);
> + if (!comp_temp)
> + return NULL;
> +
> + comp_temp->virt_addr = dma_pool_alloc(queue->comp_pool,
> + GFP_KERNEL,
> + &comp_temp->bus_addr);
> + if (!comp_temp->virt_addr) {
> + kfree(comp_temp);
> + return NULL;
> + }
> +
> + } else {
> + comp_temp = list_first_entry(&queue->comp_free,
> + struct fsl_qdma_comp,
> + list);
> + list_del(&comp_temp->list);
> + spin_unlock_irqrestore(&queue->queue_lock, flags);
> + }
> +
> + comp_temp->qchan = fsl_chan;
> +
> + return comp_temp;
> +}
> +
> +static struct fsl_qdma_queue *fsl_qdma_alloc_queue_resources(
> + struct platform_device *pdev,
> + unsigned int queue_num)
> +{
> + struct fsl_qdma_queue *queue_head, *queue_temp;
> + int ret, i;
> + unsigned int queue_size[FSL_QDMA_QUEUE_MAX];
> +
> + if (queue_num > FSL_QDMA_QUEUE_MAX)
> + queue_num = FSL_QDMA_QUEUE_MAX;
> + queue_head = devm_kcalloc(&pdev->dev, queue_num,
> + sizeof(*queue_head), GFP_KERNEL);
> + if (!queue_head)
> + return NULL;
> +
> + ret = device_property_read_u32_array(&pdev->dev, "queue-sizes",
> + queue_size, queue_num);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't get queue-sizes.\n");
> + return NULL;
> + }
> +
> + for (i = 0; i < queue_num; i++) {
> + if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX ||
> + queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN)
> {
> + dev_err(&pdev->dev, "Get wrong queue-sizes.\n");
> + return NULL;
> + }
> + queue_temp = queue_head + i;
> + queue_temp->cq = dma_alloc_coherent(&pdev->dev,
> + sizeof(struct fsl_qdma_format) *
> + queue_size[i],
> + &queue_temp->bus_addr,
> + GFP_KERNEL);
> + if (!queue_temp->cq) {
> + devm_kfree(&pdev->dev, queue_head);
> + return NULL;
> + }
> + queue_temp->n_cq = queue_size[i];
> + queue_temp->id = i;
> + queue_temp->virt_head = queue_temp->cq;
> + queue_temp->virt_tail = queue_temp->cq;
> +
> + /*
> + * Create a comp dma pool that size
> + * is 'FSL_QDMA_BASE_BUFFER_SIZE'.
> + * The dma pool for queue command buffer.
> + */
> + queue_temp->comp_pool = dma_pool_create("comp_pool",
> + &pdev->dev,
> + FSL_QDMA_BASE_BUFFER_SIZE,
> + 16, 0);
> + if (!queue_temp->comp_pool)
> + goto err;
> +
> + /*
> + * List for queue command buffer
> + */
> + INIT_LIST_HEAD(&queue_temp->comp_used);
> + INIT_LIST_HEAD(&queue_temp->comp_free);
> + spin_lock_init(&queue_temp->queue_lock);
> + }
> +
> + return queue_head;
> +
> +err:
> + if (i == 0 && queue_temp->comp_pool)
> + dma_pool_destroy(queue_temp->comp_pool);
> + while (--i >= 1) {
> + queue_temp = queue_head + i;
> + if (i == 1 && unlikely(queue_temp->comp_pool))
> + dma_pool_destroy(queue_temp->comp_pool);
> + }
> +
> + dev_err(&pdev->dev,
> + "unable to allocate channel %d descriptor pool\n",
> + queue_temp->id);
> +
> + while (--i >= 0) {
> + queue_temp = queue_head + i;
> + dma_free_coherent(&pdev->dev,
> + sizeof(struct fsl_qdma_format) *
> + queue_size[i],
> + queue_temp->cq,
> + queue_temp->bus_addr);
> + }
> + devm_kfree(&pdev->dev, queue_head);
> + return NULL;
> +}
> +
> +static struct fsl_qdma_queue *fsl_qdma_prep_status_queue(
> + struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct fsl_qdma_queue *status_head;
> + unsigned int status_size;
> + int ret;
> +
> + ret = of_property_read_u32(np, "status-sizes", &status_size);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't get status-sizes.\n");
> + return NULL;
> + }
> + if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX
> + || status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
> + dev_err(&pdev->dev, "Get wrong status_size.\n");
> + return NULL;
> + }
> + status_head = devm_kzalloc(&pdev->dev, sizeof(*status_head),
> + GFP_KERNEL);
> + if (!status_head)
> + return NULL;
> +
> + /*
> + * Buffer for queue command
> + */
> + status_head->cq = dma_alloc_coherent(&pdev->dev,
> + sizeof(struct fsl_qdma_format) *
> + status_size,
> + &status_head->bus_addr,
> + GFP_KERNEL);
> + if (!status_head->cq) {
> + devm_kfree(&pdev->dev, status_head);
> + return NULL;
> + }
> +
> + status_head->n_cq = status_size;
> + status_head->virt_head = status_head->cq;
> + status_head->virt_tail = status_head->cq;
> + status_head->comp_pool = NULL;
> +
> + return status_head;
> +}
> +
> +static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma) {
> + void __iomem *ctrl = fsl_qdma->ctrl_base;
> + void __iomem *block = fsl_qdma->block_base;
> + int i, count = FSL_QDMA_HALT_COUNT;
> + u32 reg;
> +
> + /* Disable the command queue and wait for idle state. */
> + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
> + reg |= FSL_QDMA_DMR_DQD;
> + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
> + for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++)
> + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i));
> +
> + while (1) {
> + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR);
> + if (!(reg & FSL_QDMA_DSR_DB))
> + break;
> + if (count-- < 0)
> + return -EBUSY;
> + udelay(100);
> + }
> +
> + /* Disable status queue. */
> + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR);
> +
> + /* Clear all detected events and interrupts for all queues. */
> + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
> + block + FSL_QDMA_BCQIDR(0));
> +
> + return 0;
> +}
> +
> +static int fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine
> +*fsl_qdma) {
> + struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
> + struct fsl_qdma_queue *fsl_status = fsl_qdma->status;
> + struct fsl_qdma_queue *temp_queue;
> + struct fsl_qdma_comp *fsl_comp;
> + struct fsl_qdma_format *status_addr;
> + struct fsl_qdma_format *csgf_src;
> + struct fsl_pre_status pre;
> + void __iomem *block = fsl_qdma->block_base;
> + u32 reg, i, count;
> + bool duplicate, duplicate_handle;
> +
> + memset(&pre, 0, sizeof(struct fsl_pre_status));
> + count = FSL_QDMA_MAX_SIZE;
> +
> + while (count--) {
> + duplicate = 0;
> +
> + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR);
> + if (reg & FSL_QDMA_BSQSR_QE)
> + return 0;
> +
> + status_addr = fsl_status->virt_head;
> + if (qdma_ccdf_get_queue(status_addr) == pre.queue &&
> + qdma_ccdf_addr_get64(status_addr) == pre.addr)
> + duplicate = 1;
> +
> + i = qdma_ccdf_get_queue(status_addr);
> + pre.queue = qdma_ccdf_get_queue(status_addr);
> + pre.addr = qdma_ccdf_addr_get64(status_addr);
> + temp_queue = fsl_queue + i;
> + spin_lock(&temp_queue->queue_lock);
> + if (list_empty(&temp_queue->comp_used)) {
> + if (!duplicate) {
> + spin_unlock(&temp_queue->queue_lock);
> + return -EAGAIN;
> + }
> + } else {
> + fsl_comp = list_first_entry(&temp_queue->comp_used,
> + struct fsl_qdma_comp,
> + list);
> + csgf_src = fsl_comp->virt_addr + 2;
> + if (fsl_comp->bus_addr + 16 != pre.addr) {
> + if (duplicate) {
> + spin_unlock(&temp_queue->queue_lock);
> + return -EAGAIN;
> + }
> + }
> + }
> +
> + if (duplicate) {
> + reg = qdma_readl(fsl_qdma, block +
> + FSL_QDMA_BSQMR);
> + reg |= FSL_QDMA_BSQMR_DI;
> + qdma_desc_addr_set64(status_addr, 0x0);
> + fsl_status->virt_head++;
> + if (fsl_status->virt_head == fsl_status->cq
> + + fsl_status->n_cq)
> + fsl_status->virt_head = fsl_status->cq;
> + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
> + spin_unlock(&temp_queue->queue_lock);
> + continue;
> + }
> + list_del(&fsl_comp->list);
> +
> + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
> + reg |= FSL_QDMA_BSQMR_DI;
> + qdma_desc_addr_set64(status_addr, 0x0);
> + fsl_status->virt_head++;
> + if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq)
> + fsl_status->virt_head = fsl_status->cq;
> + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
> + spin_unlock(&temp_queue->queue_lock);
> +
> + spin_lock(&fsl_comp->qchan->vchan.lock);
> + vchan_cookie_complete(&fsl_comp->vdesc);
> + fsl_comp->qchan->status = DMA_COMPLETE;
> + spin_unlock(&fsl_comp->qchan->vchan.lock);
> + }
> +
> + return 0;
> +}
> +
> +static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id) {
> + struct fsl_qdma_engine *fsl_qdma = dev_id;
> + unsigned int intr;
> + void __iomem *status = fsl_qdma->status_base;
> +
> + intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
> +
> + if (intr)
> + dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
> +
> + /* Clear all error conditions and interrupts. */
> + qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status +
> FSL_QDMA_DEDR);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id) {
> + struct fsl_qdma_engine *fsl_qdma = dev_id;
> + unsigned int intr, reg;
> + void __iomem *block = fsl_qdma->block_base;
> + void __iomem *ctrl = fsl_qdma->ctrl_base;
> +
> + intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0));
> +
> + if ((intr & FSL_QDMA_CQIDR_SQT) != 0)
> + intr = fsl_qdma_queue_transfer_complete(fsl_qdma);
> +
> + if (intr != 0) {
> + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
> + reg |= FSL_QDMA_DMR_DQD;
> + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
> + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0));
> + dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n");
> + }
> +
> + /* Clear all detected events and interrupts. */
> + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
> + block + FSL_QDMA_BCQIDR(0));
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int
> +fsl_qdma_irq_init(struct platform_device *pdev,
> + struct fsl_qdma_engine *fsl_qdma)
> +{
> + int ret;
> +
> + fsl_qdma->error_irq = platform_get_irq_byname(pdev,
> + "qdma-error");
> + if (fsl_qdma->error_irq < 0) {
> + dev_err(&pdev->dev, "Can't get qdma controller irq.\n");
> + return fsl_qdma->error_irq;
> + }
> +
> + fsl_qdma->queue_irq = platform_get_irq_byname(pdev, "qdma-queue");
> + if (fsl_qdma->queue_irq < 0) {
> + dev_err(&pdev->dev, "Can't get qdma queue irq.\n");
> + return fsl_qdma->queue_irq;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq,
> + fsl_qdma_error_handler, 0, "qDMA error", fsl_qdma);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n");
> + return ret;
> + }
> + ret = devm_request_irq(&pdev->dev, fsl_qdma->queue_irq,
> + fsl_qdma_queue_handler, 0, "qDMA queue", fsl_qdma);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't register qDMA queue IRQ.\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void fsl_qdma_irq_exit(
> + struct platform_device *pdev, struct fsl_qdma_engine *fsl_qdma) {
> + if (fsl_qdma->queue_irq == fsl_qdma->error_irq) {
> + devm_free_irq(&pdev->dev, fsl_qdma->queue_irq, fsl_qdma);
> + } else {
> + devm_free_irq(&pdev->dev, fsl_qdma->queue_irq, fsl_qdma);
> + devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma);
> + }
> +}
> +
> +static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma) {
> + struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
> + struct fsl_qdma_queue *temp;
> + void __iomem *ctrl = fsl_qdma->ctrl_base;
> + void __iomem *status = fsl_qdma->status_base;
> + void __iomem *block = fsl_qdma->block_base;
> + int i, ret;
> + u32 reg;
> +
> + /* Try to halt the qDMA engine first. */
> + ret = fsl_qdma_halt(fsl_qdma);
> + if (ret) {
> + dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!");
> + return ret;
> + }
> +
> + /* Clear all detected events and interrupts for all queues. */
> + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
> + block + FSL_QDMA_BCQIDR(0));
> +
> + for (i = 0; i < fsl_qdma->n_queues; i++) {
> + temp = fsl_queue + i;
> + /*
> + * Initialize Command Queue registers to point to the first
> + * command descriptor in memory.
> + * Dequeue Pointer Address Registers
> + * Enqueue Pointer Address Registers
> + */
> + qdma_writel(fsl_qdma, temp->bus_addr,
> + block + FSL_QDMA_BCQDPA_SADDR(i));
> + qdma_writel(fsl_qdma, temp->bus_addr,
> + block + FSL_QDMA_BCQEPA_SADDR(i));
> +
> + /* Initialize the queue mode. */
> + reg = FSL_QDMA_BCQMR_EN;
> + reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4);
> + reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6);
> + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i));
> + }
> +
> + /*
> + * Workaround for erratum: ERR010812.
> + * We must enable XOFF to avoid the enqueue rejection occurs.
> + * Setting SQCCMR ENTER_WM to 0x20.
> + */
> + qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM,
> + block + FSL_QDMA_SQCCMR);
> + /*
> + * Initialize status queue registers to point to the first
> + * command descriptor in memory.
> + * Dequeue Pointer Address Registers
> + * Enqueue Pointer Address Registers
> + */
> + qdma_writel(fsl_qdma, fsl_qdma->status->bus_addr,
> + block + FSL_QDMA_SQEPAR);
> + qdma_writel(fsl_qdma, fsl_qdma->status->bus_addr,
> + block + FSL_QDMA_SQDPAR);
> + /* Initialize status queue interrupt. */
> + qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE,
> + block + FSL_QDMA_BCQIER(0));
> + qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN |
> FSL_QDMA_BSQICR_ICST(5)
> + | 0x8000,
> + block + FSL_QDMA_BSQICR);
> + qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE |
> FSL_QDMA_CQIER_TEIE,
> + block + FSL_QDMA_CQIER);
> + /* Initialize controller interrupt register. */
> + qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status +
> FSL_QDMA_DEDR);
> + qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status +
> FSL_QDMA_DEIER);
> +
> + /* Initialize the status queue mode. */
> + reg = FSL_QDMA_BSQMR_EN;
> + reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2(fsl_qdma->status->n_cq)-6);
> + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
> +
> + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
> + reg &= ~FSL_QDMA_DMR_DQD;
> + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
> +
> + return 0;
> +}
> +
> +static struct dma_async_tx_descriptor * fsl_qdma_prep_memcpy(struct
> +dma_chan *chan, dma_addr_t dst,
> + dma_addr_t src, size_t len, unsigned long flags) {
> + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
> + struct fsl_qdma_comp *fsl_comp;
> +
> + fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan, 0, 0);
> + fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len);
> +
> + return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags); }
> +
> +static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan) {
> + void __iomem *block = fsl_chan->qdma->block_base;
> + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
> + struct fsl_qdma_comp *fsl_comp;
> + struct virt_dma_desc *vdesc;
> + u32 reg;
> +
> + reg = qdma_readl(fsl_chan->qdma, block +
> FSL_QDMA_BCQSR(fsl_queue->id));
> + if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF))
> + return;
> + vdesc = vchan_next_desc(&fsl_chan->vchan);
> + if (!vdesc)
> + return;
> + list_del(&vdesc->node);
> + fsl_comp = to_fsl_qdma_comp(vdesc);
> +
> + memcpy(fsl_queue->virt_head++, fsl_comp->virt_addr,
> + sizeof(struct fsl_qdma_format));
> + if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq)
> + fsl_queue->virt_head = fsl_queue->cq;
> +
> + list_add_tail(&fsl_comp->list, &fsl_queue->comp_used);
> + barrier();
> + reg = qdma_readl(fsl_chan->qdma, block +
> FSL_QDMA_BCQMR(fsl_queue->id));
> + reg |= FSL_QDMA_BCQMR_EI;
> + qdma_writel(fsl_chan->qdma, reg, block +
> FSL_QDMA_BCQMR(fsl_queue->id));
> + fsl_chan->status = DMA_IN_PROGRESS;
> +}
> +
> +static enum dma_status fsl_qdma_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie, struct dma_tx_state *txstate) {
> + enum dma_status ret;
> +
> + ret = dma_cookie_status(chan, cookie, txstate);
> + if (ret == DMA_COMPLETE || !txstate)
> + return ret;
> +
> + return ret;
> +}
> +
> +static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc) {
> + struct fsl_qdma_comp *fsl_comp;
> + struct fsl_qdma_queue *fsl_queue;
> + unsigned long flags;
> +
> + fsl_comp = to_fsl_qdma_comp(vdesc);
> + fsl_queue = fsl_comp->qchan->queue;
> +
> + spin_lock_irqsave(&fsl_queue->queue_lock, flags);
> + list_add_tail(&fsl_comp->list, &fsl_queue->comp_free);
> + spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); }
> +
> +static void fsl_qdma_issue_pending(struct dma_chan *chan) {
> + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
> + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&fsl_queue->queue_lock, flags);
> + spin_lock(&fsl_chan->vchan.lock);
> + if (vchan_issue_pending(&fsl_chan->vchan))
> + fsl_qdma_enqueue_desc(fsl_chan);
> + spin_unlock(&fsl_chan->vchan.lock);
> + spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); }
> +
> +static int fsl_qdma_probe(struct platform_device *pdev) {
> + struct device_node *np = pdev->dev.of_node;
> + struct fsl_qdma_engine *fsl_qdma;
> + struct fsl_qdma_chan *fsl_chan;
> + struct resource *res;
> + unsigned int len, chans, queues;
> + int ret, i;
> +
> + ret = of_property_read_u32(np, "dma-channels", &chans);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't get dma-channels.\n");
> + return ret;
> + }
> +
> + len = sizeof(*fsl_qdma) + sizeof(*fsl_chan) * chans;
> + fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
> + if (!fsl_qdma)
> + return -ENOMEM;
> +
> + ret = of_property_read_u32(np, "fsl,queues", &queues);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't get queues.\n");
> + return ret;
> + }
> +
> + fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, queues);
> + if (!fsl_qdma->queue)
> + return -ENOMEM;
> +
> + fsl_qdma->status = fsl_qdma_prep_status_queue(pdev);
> + if (!fsl_qdma->status)
> + return -ENOMEM;
> +
> + fsl_qdma->n_chans = chans;
> + fsl_qdma->n_queues = queues;
> + mutex_init(&fsl_qdma->fsl_qdma_mutex);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(fsl_qdma->ctrl_base))
> + return PTR_ERR(fsl_qdma->ctrl_base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(fsl_qdma->status_base))
> + return PTR_ERR(fsl_qdma->status_base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(fsl_qdma->block_base))
> + return PTR_ERR(fsl_qdma->block_base);
> +
> + ret = fsl_qdma_irq_init(pdev, fsl_qdma);
> + if (ret)
> + return ret;
> +
> + fsl_qdma->feature = of_property_read_bool(np, "big-endian");
> + INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels);
> + for (i = 0; i < fsl_qdma->n_chans; i++) {
> + struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i];
> +
> + fsl_chan->qdma = fsl_qdma;
> + fsl_chan->queue = fsl_qdma->queue + i % fsl_qdma->n_queues;
> + fsl_chan->vchan.desc_free = fsl_qdma_free_desc;
> + INIT_LIST_HEAD(&fsl_chan->qcomp);
> + vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev);
> + }
> + for (i = 0; i < fsl_qdma->n_queues; i++)
> + fsl_qdma_pre_request_enqueue_desc(fsl_qdma->queue + i);
> +
> + dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask);
> +
> + fsl_qdma->dma_dev.dev = &pdev->dev;
> + fsl_qdma->dma_dev.device_free_chan_resources
> + = fsl_qdma_free_chan_resources;
> + fsl_qdma->dma_dev.device_tx_status = fsl_qdma_tx_status;
> + fsl_qdma->dma_dev.device_prep_dma_memcpy =
> fsl_qdma_prep_memcpy;
> + fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending;
> +
> + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
> +
> + platform_set_drvdata(pdev, fsl_qdma);
> +
> + ret = dma_async_device_register(&fsl_qdma->dma_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't register NXP Layerscape qDMA
> engine.\n");
> + return ret;
> + }
> +
> + ret = fsl_qdma_reg_init(fsl_qdma);
> + if (ret) {
> + dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev) {
> + struct fsl_qdma_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_qdma_remove(struct platform_device *pdev) {
> + struct device_node *np = pdev->dev.of_node;
> + struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev);
> + struct fsl_qdma_queue *queue_temp;
> + struct fsl_qdma_queue *status = fsl_qdma->status;
> + struct fsl_qdma_comp *comp_temp, *_comp_temp;
> + int i;
> +
> + fsl_qdma_irq_exit(pdev, fsl_qdma);
> + fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev);
> + of_dma_controller_free(np);
> + dma_async_device_unregister(&fsl_qdma->dma_dev);
> +
> + /* Free descriptor areas */
> + for (i = 0; i < fsl_qdma->n_queues; i++) {
> + queue_temp = fsl_qdma->queue + i;
> + list_for_each_entry_safe(comp_temp, _comp_temp,
> + &queue_temp->comp_used, list) {
> + dma_pool_free(queue_temp->comp_pool,
> + comp_temp->virt_addr,
> + comp_temp->bus_addr);
> + list_del(&comp_temp->list);
> + kfree(comp_temp);
> + }
> + list_for_each_entry_safe(comp_temp, _comp_temp,
> + &queue_temp->comp_free, list) {
> + dma_pool_free(queue_temp->comp_pool,
> + comp_temp->virt_addr,
> + comp_temp->bus_addr);
> + list_del(&comp_temp->list);
> + kfree(comp_temp);
> + }
> + dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
> + queue_temp->n_cq, queue_temp->cq,
> + queue_temp->bus_addr);
> + dma_pool_destroy(queue_temp->comp_pool);
> + }
> +
> + dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
> + status->n_cq, status->cq, status->bus_addr);
> + return 0;
> +}
> +
> +static const struct of_device_id fsl_qdma_dt_ids[] = {
> + { .compatible = "fsl,ls1021a-qdma", },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids);
> +
> +static struct platform_driver fsl_qdma_driver = {
> + .driver = {
> + .name = "fsl-qdma",
> + .of_match_table = fsl_qdma_dt_ids,
> + },
> + .probe = fsl_qdma_probe,
> + .remove = fsl_qdma_remove,
> +};
> +
> +module_platform_driver(fsl_qdma_driver);
> +
> +MODULE_ALIAS("platform:fsl-qdma");
> +MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.1
^ permalink raw reply
* [RFC,1/2] dt-bindings: dmaengine: xilinx_dma: Add binding for Xilinx MCDMA IP
From: Rob Herring @ 2018-08-14 16:13 UTC (permalink / raw)
To: Radhey Shyam Pandey
Cc: vkoul, mark.rutland, michal.simek, dan.j.williams, appanad, lars,
dmaengine, devicetree, linux-arm-kernel, linux-kernel
On Tue, Jul 31, 2018 at 11:16:12PM +0530, Radhey Shyam Pandey wrote:
> Add devicetree binding for Xilinx AXI Multichannel Direct Memory Access
> (AXI MCDMA) IP. The AXI MCDMA provides high-bandwidth direct memory
> access between memory and AXI4-Stream target peripherals. The AXI MCDMA
> core provides scatter-gather interface with multiple channel support.
>
> Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> ---
> Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> index 174af2c..57bb02e 100644
> --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> @@ -11,9 +11,13 @@ is to receive from the device.
> Xilinx AXI CDMA engine, it does transfers between memory-mapped source
> address and a memory-mapped destination address.
>
> +Xilinx AXI MCDMA engine, it does transfer between memory and AXI4 stream
> +target devices. It can be configured to have up to 16 independent transmit
> +and receive channels.
> +
> Required properties:
> - compatible: Should be "xlnx,axi-vdma-1.00.a" or "xlnx,axi-dma-1.00.a" or
> - "xlnx,axi-cdma-1.00.a""
> + "xlnx,axi-cdma-1.00.a" or "xlnx,axi-mcdma-1.00.a".
Please reformat to 1 per line.
> - #dma-cells: Should be <1>, see "dmas" property below
> - reg: Should contain VDMA registers location and length.
> - xlnx,addrwidth: Should be the vdma addressing size in bits(ex: 32 bits).
> @@ -56,6 +60,8 @@ Required child node properties:
> For CDMA: It should be "xlnx,axi-cdma-channel".
> For AXIDMA: It should be either "xlnx,axi-dma-mm2s-channel" or
> "xlnx,axi-dma-s2mm-channel".
> + For MCDMA: It should be either "xlnx,axi-mcdma-mm2s-channel" or
> + "xlnx,axi-mcdma-s2mm-channel".
What's wrong with reusing the existing xlnx,axi-dma-* names?
> - interrupts: Should contain per channel VDMA interrupts.
> - xlnx,datawidth: Should contain the stream data width, take values
> {32,64...1024}.
> @@ -68,7 +74,7 @@ Optional child node properties for VDMA:
> enabled/disabled in hardware.
> - xlnx,enable-vert-flip: Tells vertical flip is
> enabled/disabled in hardware(S2MM path).
> -Optional child node properties for AXI DMA:
> +Optional child node properties for AXI DMA and MCDMA:
> -dma-channels: Number of dma channels in child node.
>
> Example:
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" 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
* dmaengine: rcar-dmac: Document R8A774A1 bindings
From: Fabrizio Castro @ 2018-08-14 12:32 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland
Cc: Fabrizio Castro, dmaengine, devicetree, linux-kernel,
Simon Horman, Geert Uytterhoeven, Chris Paterson, Biju Das,
linux-renesas-soc
Renesas' RZ/G2M (R8A774A1) SoC has DMA controllers compatible
with this driver, therefore document RZ/G2M specific bindings.
Signed-off-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com>
Reviewed-by: Biju Das <biju.das@bp.renesas.com>
---
Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt
index 946229c..2de2eed 100644
--- a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt
+++ b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt
@@ -1,6 +1,6 @@
* Renesas R-Car (RZ/G) DMA Controller Device Tree bindings
-Renesas R-Car Generation 2 SoCs have multiple multi-channel DMA
+Renesas R-Car (Gen 2/3) and RZ/G SoCs have multiple multi-channel DMA
controller instances named DMAC capable of serving multiple clients. Channels
can be dedicated to specific clients or shared between a large number of
clients.
@@ -19,6 +19,7 @@ Required Properties:
- "renesas,dmac-r8a7743" (RZ/G1M)
- "renesas,dmac-r8a7745" (RZ/G1E)
- "renesas,dmac-r8a77470" (RZ/G1C)
+ - "renesas,dmac-r8a774a1" (RZ/G2M)
- "renesas,dmac-r8a7790" (R-Car H2)
- "renesas,dmac-r8a7791" (R-Car M2-W)
- "renesas,dmac-r8a7792" (R-Car V2H)
^ 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