// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drivers/dma/fsl-edma.c
*
* Copyright 2013-2014 Freescale Semiconductor, Inc.
* Copyright 2024 NXP
*
* Driver for the Freescale eDMA engine with flexible channel multiplexing
* capability for DMA request sources. The eDMA block can be found on some
* Vybrid, Layerscape and S32G SoCs.
*/
#include <dt-bindings/dma/fsl-edma.h>
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/property.h>
#include "fsl-edma-common.h"
static void fsl_edma_synchronize(struct dma_chan *chan)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
vchan_synchronize(&fsl_chan->vchan);
}
static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
{
struct fsl_edma_engine *fsl_edma = dev_id;
unsigned int intr, ch;
struct edma_regs *regs = &fsl_edma->regs;
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), regs->cint);
fsl_edma_tx_chan_handler(&fsl_edma->chans[ch]);
}
}
return IRQ_HANDLED;
}
static void fsl_edma3_err_check(struct fsl_edma_chan *fsl_chan)
{
unsigned int ch_err;
u32 val;
scoped_guard(spinlock, &fsl_chan->vchan.lock) {
ch_err = edma_readl_chreg(fsl_chan, ch_es);
if (!(ch_err & EDMA_V3_CH_ERR))
return;
edma_writel_chreg(fsl_chan, EDMA_V3_CH_ERR, ch_es);
val = edma_readl_chreg(fsl_chan, ch_csr);
val &= ~EDMA_V3_CH_CSR_ERQ;
edma_writel_chreg(fsl_chan, val, ch_csr);
}
/* Ignore this interrupt since channel has been disabled already */
if (!fsl_chan->edesc)
return;
if (ch_err & EDMA_V3_CH_ERR_DBE)
dev_err(&fsl_chan->pdev->dev, "Destination Bus Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_SBE)
dev_err(&fsl_chan->pdev->dev, "Source Bus Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_SGE)
dev_err(&fsl_chan->pdev->dev, "Scatter/Gather Configuration Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_NCE)
dev_err(&fsl_chan->pdev->dev, "NBYTES/CITER Configuration Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_DOE)
dev_err(&fsl_chan->pdev->dev, "Destination Offset Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_DAE)
dev_err(&fsl_chan->pdev->dev, "Destination Address Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_SOE)
dev_err(&fsl_chan->pdev->dev, "Source Offset Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_SAE)
dev_err(&fsl_chan->pdev->dev, "Source Address Error interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_ECX)
dev_err(&fsl_chan->pdev->dev, "Transfer Canceled interrupt.\n");
if (ch_err & EDMA_V3_CH_ERR_UCE)
dev_err(&fsl_chan->pdev->dev, "Uncorrectable TCD error during channel execution interrupt.\n");
fsl_chan->status = DMA_ERROR;
}
static irqreturn_t fsl_edma3_err_handler_per_chan(int irq, void *dev_id)
{
struct fsl_edma_chan *fsl_chan = dev_id;
fsl_edma3_err_check(fsl_chan);
return IRQ_HANDLED;
}
static irqreturn_t fsl_edma3_err_handler_shared(int irq, void *dev_id)
{
struct fsl_edma_engine *fsl_edma = dev_id;
unsigned int ch;
for (ch = 0; ch < fsl_edma->n_chans; ch++) {
if (fsl_edma->chan_masked & BIT(ch))
continue;
fsl_edma3_err_check(&fsl_edma->chans[ch]);
}
return IRQ_HANDLED;
}
static irqreturn_t fsl_edma3_tx_handler(int irq, void *dev_id)
{
struct fsl_edma_chan *fsl_chan = dev_id;