dmaegnine: fsl-edma: add edma error interrupt handler
authorJoy Zou <joy.zou@nxp.com>
Mon, 7 Apr 2025 16:46:36 +0000 (12:46 -0400)
committerVinod Koul <vkoul@kernel.org>
Wed, 23 Apr 2025 13:12:46 +0000 (18:42 +0530)
Add the edma error interrupt handler because it's useful to debug issue.

i.MX8ULP edma has per channel error interrupt.

i.MX91/93/95 and i.MX8QM/QXP/DXL edma share one error interrupt.

Signed-off-by: Joy Zou <joy.zou@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20250407-edma_err-v2-2-9d7e5b77fcc4@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/fsl-edma-common.c
drivers/dma/fsl-edma-common.h
drivers/dma/fsl-edma-main.c

index 443b2430466cbcf32c9bd8d487abd27fa19718b6..4976d7dde08090d16277af4b9f784b9745485320 100644 (file)
@@ -95,7 +95,7 @@ static void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan)
        }
 
        val = edma_readl_chreg(fsl_chan, ch_csr);
-       val |= EDMA_V3_CH_CSR_ERQ;
+       val |= EDMA_V3_CH_CSR_ERQ | EDMA_V3_CH_CSR_EEI;
        edma_writel_chreg(fsl_chan, val, ch_csr);
 }
 
@@ -821,7 +821,7 @@ void fsl_edma_issue_pending(struct dma_chan *chan)
 int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
 {
        struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
-       int ret;
+       int ret = 0;
 
        if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
                clk_prepare_enable(fsl_chan->clk);
@@ -831,17 +831,29 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
                                sizeof(struct fsl_edma_hw_tcd64) : sizeof(struct fsl_edma_hw_tcd),
                                32, 0);
 
-       if (fsl_chan->txirq) {
+       if (fsl_chan->txirq)
                ret = request_irq(fsl_chan->txirq, fsl_chan->irq_handler, IRQF_SHARED,
                                 fsl_chan->chan_name, fsl_chan);
 
-               if (ret) {
-                       dma_pool_destroy(fsl_chan->tcd_pool);
-                       return ret;
-               }
-       }
+       if (ret)
+               goto err_txirq;
+
+       if (fsl_chan->errirq > 0)
+               ret = request_irq(fsl_chan->errirq, fsl_chan->errirq_handler, IRQF_SHARED,
+                                 fsl_chan->errirq_name, fsl_chan);
+
+       if (ret)
+               goto err_errirq;
 
        return 0;
+
+err_errirq:
+       if (fsl_chan->txirq)
+               free_irq(fsl_chan->txirq, fsl_chan);
+err_txirq:
+       dma_pool_destroy(fsl_chan->tcd_pool);
+
+       return ret;
 }
 
 void fsl_edma_free_chan_resources(struct dma_chan *chan)
@@ -862,6 +874,8 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan)
 
        if (fsl_chan->txirq)
                free_irq(fsl_chan->txirq, fsl_chan);
+       if (fsl_chan->errirq)
+               free_irq(fsl_chan->errirq, fsl_chan);
 
        vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
        dma_pool_destroy(fsl_chan->tcd_pool);
index 10a5565ddfd76c5d609f4995f340377aed6c315a..205a96489094805aa728b72a51ae101cd88fa003 100644 (file)
 #define EDMA_V3_CH_ES_ERR          BIT(31)
 #define EDMA_V3_MP_ES_VLD          BIT(31)
 
+#define EDMA_V3_CH_ERR_DBE     BIT(0)
+#define EDMA_V3_CH_ERR_SBE     BIT(1)
+#define EDMA_V3_CH_ERR_SGE     BIT(2)
+#define EDMA_V3_CH_ERR_NCE     BIT(3)
+#define EDMA_V3_CH_ERR_DOE     BIT(4)
+#define EDMA_V3_CH_ERR_DAE     BIT(5)
+#define EDMA_V3_CH_ERR_SOE     BIT(6)
+#define EDMA_V3_CH_ERR_SAE     BIT(7)
+#define EDMA_V3_CH_ERR_ECX     BIT(8)
+#define EDMA_V3_CH_ERR_UCE     BIT(9)
+#define EDMA_V3_CH_ERR         BIT(31)
+
 enum fsl_edma_pm_state {
        RUNNING = 0,
        SUSPENDED,
@@ -162,6 +174,7 @@ struct fsl_edma_chan {
        u32                             dma_dev_size;
        enum dma_data_direction         dma_dir;
        char                            chan_name[32];
+       char                            errirq_name[36];
        void __iomem                    *tcd;
        void __iomem                    *mux_addr;
        u32                             real_count;
@@ -174,7 +187,9 @@ struct fsl_edma_chan {
        int                             priority;
        int                             hw_chanid;
        int                             txirq;
+       int                             errirq;
        irqreturn_t                     (*irq_handler)(int irq, void *dev_id);
+       irqreturn_t                     (*errirq_handler)(int irq, void *dev_id);
        bool                            is_rxchan;
        bool                            is_remote;
        bool                            is_multi_fifo;
@@ -208,6 +223,9 @@ struct fsl_edma_desc {
 /* Need clean CHn_CSR DONE before enable TCD's MAJORELINK */
 #define FSL_EDMA_DRV_CLEAR_DONE_E_LINK BIT(14)
 #define FSL_EDMA_DRV_TCD64             BIT(15)
+/* All channel ERR IRQ share one IRQ line */
+#define FSL_EDMA_DRV_ERRIRQ_SHARE       BIT(16)
+
 
 #define FSL_EDMA_DRV_EDMA3     (FSL_EDMA_DRV_SPLIT_REG |       \
                                 FSL_EDMA_DRV_BUS_8BYTE |       \
index 756d67325db5268c082e23bd4997a2f656f945a3..32a52a6acd60b3888ce3c407c5d8499156bc1d9a 100644 (file)
@@ -50,6 +50,83 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
        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;
@@ -309,7 +386,8 @@ fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma
 
 static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
 {
-       int i;
+       char *errirq_name;
+       int i, ret;
 
        for (i = 0; i < fsl_edma->n_chans; i++) {
 
@@ -324,6 +402,27 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi
                        return  -EINVAL;
 
                fsl_chan->irq_handler = fsl_edma3_tx_handler;
+
+               if (!(fsl_edma->drvdata->flags & FSL_EDMA_DRV_ERRIRQ_SHARE)) {
+                       fsl_chan->errirq = fsl_chan->txirq;
+                       fsl_chan->errirq_handler = fsl_edma3_err_handler_per_chan;
+               }
+       }
+
+       /* All channel err use one irq number */
+       if (fsl_edma->drvdata->flags & FSL_EDMA_DRV_ERRIRQ_SHARE) {
+               /* last one is error irq */
+               fsl_edma->errirq = platform_get_irq_optional(pdev, fsl_edma->n_chans);
+               if (fsl_edma->errirq < 0)
+                       return 0; /* dts miss err irq, treat as no err irq case */
+
+               errirq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-err",
+                                            dev_name(&pdev->dev));
+
+               ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, fsl_edma3_err_handler_shared,
+                                      0, errirq_name, fsl_edma);
+               if (ret)
+                       return dev_err_probe(&pdev->dev, ret, "Can't register eDMA err IRQ.\n");
        }
 
        return 0;
@@ -464,7 +563,8 @@ static struct fsl_edma_drvdata imx7ulp_data = {
 };
 
 static struct fsl_edma_drvdata imx8qm_data = {
-       .flags = FSL_EDMA_DRV_HAS_PD | FSL_EDMA_DRV_EDMA3 | FSL_EDMA_DRV_MEM_REMOTE,
+       .flags = FSL_EDMA_DRV_HAS_PD | FSL_EDMA_DRV_EDMA3 | FSL_EDMA_DRV_MEM_REMOTE
+                | FSL_EDMA_DRV_ERRIRQ_SHARE,
        .chreg_space_sz = 0x10000,
        .chreg_off = 0x10000,
        .setup_irq = fsl_edma3_irq_init,
@@ -481,14 +581,15 @@ static struct fsl_edma_drvdata imx8ulp_data = {
 };
 
 static struct fsl_edma_drvdata imx93_data3 = {
-       .flags = FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA3,
+       .flags = FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA3 | FSL_EDMA_DRV_ERRIRQ_SHARE,
        .chreg_space_sz = 0x10000,
        .chreg_off = 0x10000,
        .setup_irq = fsl_edma3_irq_init,
 };
 
 static struct fsl_edma_drvdata imx93_data4 = {
-       .flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA4,
+       .flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA4
+                | FSL_EDMA_DRV_ERRIRQ_SHARE,
        .chreg_space_sz = 0x8000,
        .chreg_off = 0x10000,
        .mux_off = 0x10000 + offsetof(struct fsl_edma3_ch_reg, ch_mux),
@@ -498,7 +599,7 @@ static struct fsl_edma_drvdata imx93_data4 = {
 
 static struct fsl_edma_drvdata imx95_data5 = {
        .flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA4 |
-                FSL_EDMA_DRV_TCD64,
+                FSL_EDMA_DRV_TCD64 | FSL_EDMA_DRV_ERRIRQ_SHARE,
        .chreg_space_sz = 0x8000,
        .chreg_off = 0x10000,
        .mux_off = 0x200,
@@ -700,6 +801,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
                snprintf(fsl_chan->chan_name, sizeof(fsl_chan->chan_name), "%s-CH%02d",
                                                           dev_name(&pdev->dev), i);
 
+               snprintf(fsl_chan->errirq_name, sizeof(fsl_chan->errirq_name),
+                        "%s-CH%02d-err", dev_name(&pdev->dev), i);
+
                fsl_chan->edma = fsl_edma;
                fsl_chan->pm_state = RUNNING;
                fsl_chan->srcid = 0;