Skip to content

Commit

Permalink
dmaengine: fsl-edma: integrate v3 support
Browse files Browse the repository at this point in the history
Significant alterations have been made to the EDMA v3's register layout.
Now, each channel possesses a separate address space, encapsulating all
channel-related controls and statuses, including IRQs. There are changes
in bit position definitions as well. However, the fundamental control flow
remains analogous to the previous versions.

EDMA v3 was utilized in imx8qm, imx93, and will be in forthcoming chips.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20230821161617.2142561-13-Frank.Li@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
  • Loading branch information
nxpfrankli authored and vinodkoul committed Aug 22, 2023
1 parent 6eb439d commit 72f5801
Show file tree
Hide file tree
Showing 3 changed files with 453 additions and 23 deletions.
158 changes: 143 additions & 15 deletions drivers/dma/fsl-edma-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>

#include "fsl-edma-common.h"

Expand Down Expand Up @@ -66,11 +68,46 @@ void fsl_edma_tx_chan_handler(struct fsl_edma_chan *fsl_chan)
spin_unlock(&fsl_chan->vchan.lock);
}

static void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan)
{
u32 val, flags;

flags = fsl_edma_drvflags(fsl_chan);
val = edma_readl_chreg(fsl_chan, ch_sbr);
/* Remote/local swapped wrongly on iMX8 QM Audio edma */
if (flags & FSL_EDMA_DRV_QUIRK_SWAPPED) {
if (!fsl_chan->is_rxchan)
val |= EDMA_V3_CH_SBR_RD;
else
val |= EDMA_V3_CH_SBR_WR;
} else {
if (fsl_chan->is_rxchan)
val |= EDMA_V3_CH_SBR_RD;
else
val |= EDMA_V3_CH_SBR_WR;
}

if (fsl_chan->is_remote)
val &= ~(EDMA_V3_CH_SBR_RD | EDMA_V3_CH_SBR_WR);

edma_writel_chreg(fsl_chan, val, ch_sbr);

if (flags & FSL_EDMA_DRV_HAS_CHMUX)
edma_writel_chreg(fsl_chan, fsl_chan->srcid, ch_mux);

val = edma_readl_chreg(fsl_chan, ch_csr);
val |= EDMA_V3_CH_CSR_ERQ;
edma_writel_chreg(fsl_chan, val, ch_csr);
}

static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
{
struct edma_regs *regs = &fsl_chan->edma->regs;
u32 ch = fsl_chan->vchan.chan.chan_id;

if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG)
return fsl_edma3_enable_request(fsl_chan);

if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) {
edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
edma_writeb(fsl_chan->edma, ch, regs->serq);
Expand All @@ -83,11 +120,28 @@ static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
}
}

static void fsl_edma3_disable_request(struct fsl_edma_chan *fsl_chan)
{
u32 val = edma_readl_chreg(fsl_chan, ch_csr);
u32 flags;

flags = fsl_edma_drvflags(fsl_chan);

if (flags & FSL_EDMA_DRV_HAS_CHMUX)
edma_writel_chreg(fsl_chan, 0, ch_mux);

val &= ~EDMA_V3_CH_CSR_ERQ;
edma_writel_chreg(fsl_chan, val, ch_csr);
}

void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
{
struct edma_regs *regs = &fsl_chan->edma->regs;
u32 ch = fsl_chan->vchan.chan.chan_id;

if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG)
return fsl_edma3_disable_request(fsl_chan);

if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) {
edma_writeb(fsl_chan->edma, ch, regs->cerq);
edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
Expand Down Expand Up @@ -135,6 +189,9 @@ void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
int endian_diff[4] = {3, 1, -1, -3};
u32 dmamux_nr = fsl_chan->edma->drvdata->dmamuxs;

if (!dmamux_nr)
return;

chans_per_mux = fsl_chan->edma->n_chans / dmamux_nr;
ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;

Expand Down Expand Up @@ -186,6 +243,10 @@ int fsl_edma_terminate_all(struct dma_chan *chan)
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);

if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_PD)
pm_runtime_allow(fsl_chan->pd_dev);

return 0;
}

Expand Down Expand Up @@ -286,12 +347,16 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
enum dma_transfer_direction dir = edesc->dirn;
dma_addr_t cur_addr, dma_addr;
size_t len, size;
u32 nbytes = 0;
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);
for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) {
nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes);
if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE))
nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes);
len += nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter);
}

if (!in_progress)
return len;
Expand All @@ -303,8 +368,12 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,

/* 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);
nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes);
if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE))
nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes);

size = 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
Expand Down Expand Up @@ -389,12 +458,15 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
}

static inline
void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
void fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan,
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)
{
struct dma_slave_config *cfg = &fsl_chan->cfg;
u16 csr = 0;
u32 burst;

/*
* eDMA hardware SGs require the TCDs to be stored in little
Expand All @@ -409,6 +481,21 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,

tcd->soff = cpu_to_le16(soff);

if (fsl_chan->is_multi_fifo) {
/* set mloff to support multiple fifo */
burst = cfg->direction == DMA_DEV_TO_MEM ?
cfg->src_addr_width : cfg->dst_addr_width;
nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-(burst * 4));
/* enable DMLOE/SMLOE */
if (cfg->direction == DMA_MEM_TO_DEV) {
nbytes |= EDMA_V3_TCD_NBYTES_DMLOE;
nbytes &= ~EDMA_V3_TCD_NBYTES_SMLOE;
} else {
nbytes |= EDMA_V3_TCD_NBYTES_SMLOE;
nbytes &= ~EDMA_V3_TCD_NBYTES_DMLOE;
}
}

tcd->nbytes = cpu_to_le32(nbytes);
tcd->slast = cpu_to_le32(slast);

Expand All @@ -427,6 +514,12 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
if (enable_sg)
csr |= EDMA_TCD_CSR_E_SG;

if (fsl_chan->is_rxchan)
csr |= EDMA_TCD_CSR_ACTIVE;

if (fsl_chan->is_sw)
csr |= EDMA_TCD_CSR_START;

tcd->csr = cpu_to_le16(csr);
}

Expand Down Expand Up @@ -466,6 +559,7 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
struct fsl_edma_desc *fsl_desc;
dma_addr_t dma_buf_next;
bool major_int = true;
int sg_len, i;
u32 src_addr, dst_addr, last_sg, nbytes;
u16 soff, doff, iter;
Expand Down Expand Up @@ -509,17 +603,23 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
src_addr = dma_buf_next;
dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
doff = fsl_chan->is_multi_fifo ? 4 : 0;
} else if (direction == DMA_DEV_TO_MEM) {
src_addr = fsl_chan->dma_dev_addr;
dst_addr = dma_buf_next;
soff = 0;
soff = fsl_chan->is_multi_fifo ? 4 : 0;
doff = fsl_chan->cfg.src_addr_width;
} else {
/* DMA_DEV_TO_DEV */
src_addr = fsl_chan->cfg.src_addr;
dst_addr = fsl_chan->cfg.dst_addr;
soff = doff = 0;
major_int = false;
}

fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
fsl_chan->attr, soff, nbytes, 0, iter,
iter, doff, last_sg, true, false, true);
iter, doff, last_sg, major_int, false, true);
dma_buf_next += period_len;
}

Expand Down Expand Up @@ -568,23 +668,51 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
dst_addr = fsl_chan->dma_dev_addr;
soff = fsl_chan->cfg.dst_addr_width;
doff = 0;
} else {
} else if (direction == DMA_DEV_TO_MEM) {
src_addr = fsl_chan->dma_dev_addr;
dst_addr = sg_dma_address(sg);
soff = 0;
doff = fsl_chan->cfg.src_addr_width;
} else {
/* DMA_DEV_TO_DEV */
src_addr = fsl_chan->cfg.src_addr;
dst_addr = fsl_chan->cfg.dst_addr;
soff = 0;
doff = 0;
}

/*
* Choose the suitable burst length if sg_dma_len is not
* multiple of burst length so that the whole transfer length is
* multiple of minor loop(burst length).
*/
if (sg_dma_len(sg) % nbytes) {
u32 width = (direction == DMA_DEV_TO_MEM) ? doff : soff;
u32 burst = (direction == DMA_DEV_TO_MEM) ?
fsl_chan->cfg.src_maxburst :
fsl_chan->cfg.dst_maxburst;
int j;

for (j = burst; j > 1; j--) {
if (!(sg_dma_len(sg) % (j * width))) {
nbytes = j * width;
break;
}
}
/* Set burst size as 1 if there's no suitable one */
if (j == 1)
nbytes = 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,
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
dst_addr, fsl_chan->attr, soff,
nbytes, 0, iter, iter, doff, last_sg,
false, false, true);
} else {
last_sg = 0;
fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr,
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
dst_addr, fsl_chan->attr, soff,
nbytes, 0, iter, iter, doff, last_sg,
true, true, false);
Expand All @@ -609,7 +737,7 @@ struct dma_async_tx_descriptor *fsl_edma_prep_memcpy(struct dma_chan *chan,
fsl_chan->is_sw = true;

/* To match with copy_align and max_seg_size so 1 tcd is enough */
fsl_edma_fill_tcd(fsl_desc->tcd[0].vtcd, dma_src, dma_dst,
fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[0].vtcd, dma_src, dma_dst,
fsl_edma_get_tcd_attr(DMA_SLAVE_BUSWIDTH_32_BYTES),
32, len, 0, 1, 1, 32, 0, true, true, false);

Expand Down
Loading

0 comments on commit 72f5801

Please sign in to comment.