Commit 1e2dbdef authored by Jingchang Lu's avatar Jingchang Lu Committed by Vinod Koul

dmaengine: fsl-edma: fixup reg offset and hw S/G support in big-endian model

The offset of all 8-/16-bit registers in big-endian eDMA model are
swapped in a 32-bit size opposite those in the little-endian model.

The hardware Scatter/Gather requires the subsequent TCDs stored in memory
in little endian independent of the register endian model, the eDMA engine
will do the swap if need.

This patch also use regular assignment for tcd variables r/w
instead of with io function previously that may not always be true.
Signed-off-by: default avatarJingchang Lu <jingchang.lu@freescale.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent 6ab55b21
...@@ -118,17 +118,17 @@ ...@@ -118,17 +118,17 @@
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)
struct fsl_edma_hw_tcd { struct fsl_edma_hw_tcd {
u32 saddr; __le32 saddr;
u16 soff; __le16 soff;
u16 attr; __le16 attr;
u32 nbytes; __le32 nbytes;
u32 slast; __le32 slast;
u32 daddr; __le32 daddr;
u16 doff; __le16 doff;
u16 citer; __le16 citer;
u32 dlast_sga; __le32 dlast_sga;
u16 csr; __le16 csr;
u16 biter; __le16 biter;
}; };
struct fsl_edma_sw_tcd { struct fsl_edma_sw_tcd {
...@@ -175,18 +175,12 @@ struct fsl_edma_engine { ...@@ -175,18 +175,12 @@ struct fsl_edma_engine {
}; };
/* /*
* R/W functions for big- or little-endian registers * R/W functions for big- or little-endian registers:
* the eDMA controller's endian is independent of the CPU core's endian. * 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 u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr)
{
if (edma->big_endian)
return ioread16be(addr);
else
return ioread16(addr);
}
static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
{ {
if (edma->big_endian) if (edma->big_endian)
...@@ -197,13 +191,18 @@ static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) ...@@ -197,13 +191,18 @@ static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *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); iowrite8(val, addr);
} }
static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *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) if (edma->big_endian)
iowrite16be(val, addr); iowrite16be(val, (void __iomem *)((unsigned long)addr ^ 0x2));
else else
iowrite16(val, addr); iowrite16(val, addr);
} }
...@@ -254,13 +253,12 @@ static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, ...@@ -254,13 +253,12 @@ static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR; chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR;
ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
slot = EDMAMUX_CHCFG_SOURCE(slot);
if (enable) if (enable)
edma_writeb(fsl_chan->edma, iowrite8(EDMAMUX_CHCFG_ENBL | slot, muxaddr + ch_off);
EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot),
muxaddr + ch_off);
else else
edma_writeb(fsl_chan->edma, EDMAMUX_CHCFG_DIS, muxaddr + ch_off); iowrite8(EDMAMUX_CHCFG_DIS, muxaddr + ch_off);
} }
static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)
...@@ -286,8 +284,7 @@ static void fsl_edma_free_desc(struct virt_dma_desc *vdesc) ...@@ -286,8 +284,7 @@ static void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
fsl_desc = to_fsl_edma_desc(vdesc); fsl_desc = to_fsl_edma_desc(vdesc);
for (i = 0; i < fsl_desc->n_tcds; i++) for (i = 0; i < fsl_desc->n_tcds; i++)
dma_pool_free(fsl_desc->echan->tcd_pool, dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd,
fsl_desc->tcd[i].vtcd,
fsl_desc->tcd[i].ptcd); fsl_desc->tcd[i].ptcd);
kfree(fsl_desc); kfree(fsl_desc);
} }
...@@ -363,8 +360,8 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, ...@@ -363,8 +360,8 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
/* calculate the total size in this desc */ /* calculate the total size in this desc */
for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++)
len += edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes)) len += le32_to_cpu(edesc->tcd[i].vtcd->nbytes)
* edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter)); * le16_to_cpu(edesc->tcd[i].vtcd->biter);
if (!in_progress) if (!in_progress)
return len; return len;
...@@ -376,14 +373,12 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, ...@@ -376,14 +373,12 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
/* figure out the finished and calculate the residue */ /* figure out the finished and calculate the residue */
for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
size = edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes)) size = le32_to_cpu(edesc->tcd[i].vtcd->nbytes)
* edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter)); * le16_to_cpu(edesc->tcd[i].vtcd->biter);
if (dir == DMA_MEM_TO_DEV) if (dir == DMA_MEM_TO_DEV)
dma_addr = edma_readl(fsl_chan->edma, dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr);
&(edesc->tcd[i].vtcd->saddr));
else else
dma_addr = edma_readl(fsl_chan->edma, dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr);
&(edesc->tcd[i].vtcd->daddr));
len -= size; len -= size;
if (cur_addr >= dma_addr && cur_addr < dma_addr + size) { if (cur_addr >= dma_addr && cur_addr < dma_addr + size) {
...@@ -424,34 +419,39 @@ static enum dma_status fsl_edma_tx_status(struct dma_chan *chan, ...@@ -424,34 +419,39 @@ static enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
return fsl_chan->status; return fsl_chan->status;
} }
static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan, static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
u32 src, u32 dst, u16 attr, u16 soff, u32 nbytes, struct fsl_edma_hw_tcd *tcd)
u32 slast, u16 citer, u16 biter, u32 doff, u32 dlast_sga,
u16 csr)
{ {
struct fsl_edma_engine *edma = fsl_chan->edma;
void __iomem *addr = fsl_chan->edma->membase; void __iomem *addr = fsl_chan->edma->membase;
u32 ch = fsl_chan->vchan.chan.chan_id; u32 ch = fsl_chan->vchan.chan.chan_id;
/* /*
* TCD parameters have been swapped in fill_tcd_params(), * TCD parameters are stored in struct fsl_edma_hw_tcd in little
* so just write them to registers in the cpu endian here * endian format. However, we need to load the TCD registers in
* big- or little-endian obeying the eDMA engine model endian.
*/ */
writew(0, addr + EDMA_TCD_CSR(ch)); edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch));
writel(src, addr + EDMA_TCD_SADDR(ch)); edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch));
writel(dst, addr + EDMA_TCD_DADDR(ch)); edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch));
writew(attr, addr + EDMA_TCD_ATTR(ch));
writew(soff, addr + EDMA_TCD_SOFF(ch)); edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch));
writel(nbytes, addr + EDMA_TCD_NBYTES(ch)); edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch));
writel(slast, addr + EDMA_TCD_SLAST(ch));
writew(citer, addr + EDMA_TCD_CITER(ch)); edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch));
writew(biter, addr + EDMA_TCD_BITER(ch)); edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch));
writew(doff, addr + EDMA_TCD_DOFF(ch));
writel(dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch)); edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch));
writew(csr, addr + EDMA_TCD_CSR(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));
static void fill_tcd_params(struct fsl_edma_engine *edma, edma_writel(edma, le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch));
struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
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 attr, u16 soff, u32 nbytes, u32 slast, u16 citer,
u16 biter, u16 doff, u32 dlast_sga, bool major_int, u16 biter, u16 doff, u32 dlast_sga, bool major_int,
bool disable_req, bool enable_sg) bool disable_req, bool enable_sg)
...@@ -459,20 +459,27 @@ static void fill_tcd_params(struct fsl_edma_engine *edma, ...@@ -459,20 +459,27 @@ static void fill_tcd_params(struct fsl_edma_engine *edma,
u16 csr = 0; u16 csr = 0;
/* /*
* eDMA hardware SGs require the TCD parameters stored in memory * eDMA hardware SGs require the TCDs to be stored in little
* the same endian as the eDMA module so that they can be loaded * endian format irrespective of the register endian model.
* automatically by the engine * So we put the value in little endian in memory, waiting
* for fsl_edma_set_tcd_regs doing the swap.
*/ */
edma_writel(edma, src, &(tcd->saddr)); tcd->saddr = cpu_to_le32(src);
edma_writel(edma, dst, &(tcd->daddr)); tcd->daddr = cpu_to_le32(dst);
edma_writew(edma, attr, &(tcd->attr));
edma_writew(edma, EDMA_TCD_SOFF_SOFF(soff), &(tcd->soff)); tcd->attr = cpu_to_le16(attr);
edma_writel(edma, EDMA_TCD_NBYTES_NBYTES(nbytes), &(tcd->nbytes));
edma_writel(edma, EDMA_TCD_SLAST_SLAST(slast), &(tcd->slast)); tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff));
edma_writew(edma, EDMA_TCD_CITER_CITER(citer), &(tcd->citer));
edma_writew(edma, EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff)); tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes));
edma_writel(edma, EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga)); tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast));
edma_writew(edma, EDMA_TCD_BITER_BITER(biter), &(tcd->biter));
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) if (major_int)
csr |= EDMA_TCD_CSR_INT_MAJOR; csr |= EDMA_TCD_CSR_INT_MAJOR;
...@@ -482,7 +489,7 @@ static void fill_tcd_params(struct fsl_edma_engine *edma, ...@@ -482,7 +489,7 @@ static void fill_tcd_params(struct fsl_edma_engine *edma,
if (enable_sg) if (enable_sg)
csr |= EDMA_TCD_CSR_E_SG; csr |= EDMA_TCD_CSR_E_SG;
edma_writew(edma, csr, &(tcd->csr)); tcd->csr = cpu_to_le16(csr);
} }
static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan,
...@@ -558,9 +565,9 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( ...@@ -558,9 +565,9 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
doff = fsl_chan->fsc.addr_width; doff = fsl_chan->fsc.addr_width;
} }
fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr, fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0, fsl_chan->fsc.attr, soff, nbytes, 0, iter,
iter, iter, doff, last_sg, true, false, true); iter, doff, last_sg, true, false, true);
dma_buf_next += period_len; dma_buf_next += period_len;
} }
...@@ -607,15 +614,15 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( ...@@ -607,15 +614,15 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
iter = sg_dma_len(sg) / nbytes; iter = sg_dma_len(sg) / nbytes;
if (i < sg_len - 1) { if (i < sg_len - 1) {
last_sg = fsl_desc->tcd[(i + 1)].ptcd; last_sg = fsl_desc->tcd[(i + 1)].ptcd;
fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
src_addr, dst_addr, fsl_chan->fsc.attr, dst_addr, fsl_chan->fsc.attr, soff,
soff, nbytes, 0, iter, iter, doff, last_sg, nbytes, 0, iter, iter, doff, last_sg,
false, false, true); false, false, true);
} else { } else {
last_sg = 0; last_sg = 0;
fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
src_addr, dst_addr, fsl_chan->fsc.attr, dst_addr, fsl_chan->fsc.attr, soff,
soff, nbytes, 0, iter, iter, doff, last_sg, nbytes, 0, iter, iter, doff, last_sg,
true, true, false); true, true, false);
} }
} }
...@@ -625,17 +632,13 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( ...@@ -625,17 +632,13 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
{ {
struct fsl_edma_hw_tcd *tcd;
struct virt_dma_desc *vdesc; struct virt_dma_desc *vdesc;
vdesc = vchan_next_desc(&fsl_chan->vchan); vdesc = vchan_next_desc(&fsl_chan->vchan);
if (!vdesc) if (!vdesc)
return; return;
fsl_chan->edesc = to_fsl_edma_desc(vdesc); fsl_chan->edesc = to_fsl_edma_desc(vdesc);
tcd = fsl_chan->edesc->tcd[0].vtcd; fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd);
fsl_edma_set_tcd_params(fsl_chan, tcd->saddr, tcd->daddr, tcd->attr,
tcd->soff, tcd->nbytes, tcd->slast, tcd->citer,
tcd->biter, tcd->doff, tcd->dlast_sga, tcd->csr);
fsl_edma_enable_request(fsl_chan); fsl_edma_enable_request(fsl_chan);
fsl_chan->status = DMA_IN_PROGRESS; fsl_chan->status = DMA_IN_PROGRESS;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment