dmaengine: Extend NXP QDMA driver to check transmission errors
authorKoehrer Mathias (ETAS/EES-SL) <mathias.koehrer@etas.com>
Wed, 15 Jul 2020 08:41:19 +0000 (08:41 +0000)
committerVinod Koul <vkoul@kernel.org>
Fri, 17 Jul 2020 06:20:03 +0000 (11:50 +0530)
Extend NXP QDMA driver to check transmission errors

The NXP QDMA driver (fsl-qdma.c) does not check the status bits
that indicate if a DMA transfer has been completed successfully.
This patch extends the driver to do exactly this.

Signed-off-by: Mathias Koehrer <mathias.koehrer@etas.com>
Link: https://lore.kernel.org/r/744443c0462aac2df4754f99500a911527c0b235.camel@bosch.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/fsl-qdma.c

index c33f491..ed2ab46 100644 (file)
@@ -56,7 +56,7 @@
 
 /* Registers for bit and genmask */
 #define FSL_QDMA_CQIDR_SQT             BIT(15)
-#define QDMA_CCDF_FOTMAT               BIT(29)
+#define QDMA_CCDF_FORMAT               BIT(29)
 #define QDMA_CCDF_SER                  BIT(30)
 #define QDMA_SG_FIN                    BIT(30)
 #define QDMA_SG_LEN_MASK               GENMASK(29, 0)
 #define FSL_QDMA_CMD_DSEN_OFFSET       19
 #define FSL_QDMA_CMD_LWC_OFFSET                16
 
+/* Field definition for Descriptor status */
+#define QDMA_CCDF_STATUS_RTE           BIT(5)
+#define QDMA_CCDF_STATUS_WTE           BIT(4)
+#define QDMA_CCDF_STATUS_CDE           BIT(2)
+#define QDMA_CCDF_STATUS_SDE           BIT(1)
+#define QDMA_CCDF_STATUS_DDE           BIT(0)
+#define QDMA_CCDF_STATUS_MASK          (QDMA_CCDF_STATUS_RTE | \
+                                       QDMA_CCDF_STATUS_WTE | \
+                                       QDMA_CCDF_STATUS_CDE | \
+                                       QDMA_CCDF_STATUS_SDE | \
+                                       QDMA_CCDF_STATUS_DDE)
+
 /* Field definition for Descriptor offset */
-#define QDMA_CCDF_STATUS               20
 #define QDMA_CCDF_OFFSET               20
 #define QDMA_SDDF_CMD(x)               (((u64)(x)) << 32)
 
@@ -243,13 +254,14 @@ qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf)
 static inline void
 qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset)
 {
-       ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset);
+       ccdf->cfg = cpu_to_le32(QDMA_CCDF_FORMAT |
+                               (offset << QDMA_CCDF_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;
+       return (le32_to_cpu(ccdf->status) & QDMA_CCDF_STATUS_MASK);
 }
 
 static inline void
@@ -618,6 +630,7 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
 {
        bool duplicate;
        u32 reg, i, count;
+       u8 completion_status;
        struct fsl_qdma_queue *temp_queue;
        struct fsl_qdma_format *status_addr;
        struct fsl_qdma_comp *fsl_comp = NULL;
@@ -677,6 +690,8 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
                }
                list_del(&fsl_comp->list);
 
+               completion_status = qdma_ccdf_get_status(status_addr);
+
                reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
                reg |= FSL_QDMA_BSQMR_DI;
                qdma_desc_addr_set64(status_addr, 0x0);
@@ -686,6 +701,31 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma,
                qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
                spin_unlock(&temp_queue->queue_lock);
 
+               /* The completion_status is evaluated here
+                * (outside of spin lock)
+                */
+               if (completion_status) {
+                       /* A completion error occurred! */
+                       if (completion_status & QDMA_CCDF_STATUS_WTE) {
+                               /* Write transaction error */
+                               fsl_comp->vdesc.tx_result.result =
+                                       DMA_TRANS_WRITE_FAILED;
+                       } else if (completion_status & QDMA_CCDF_STATUS_RTE) {
+                               /* Read transaction error */
+                               fsl_comp->vdesc.tx_result.result =
+                                       DMA_TRANS_READ_FAILED;
+                       } else {
+                               /* Command/source/destination
+                                * description error
+                                */
+                               fsl_comp->vdesc.tx_result.result =
+                                       DMA_TRANS_ABORTED;
+                               dev_err(fsl_qdma->dma_dev.dev,
+                                       "DMA status descriptor error %x\n",
+                                       completion_status);
+                       }
+               }
+
                spin_lock(&fsl_comp->qchan->vchan.lock);
                vchan_cookie_complete(&fsl_comp->vdesc);
                fsl_comp->qchan->status = DMA_COMPLETE;
@@ -700,11 +740,22 @@ static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id)
        unsigned int intr;
        struct fsl_qdma_engine *fsl_qdma = dev_id;
        void __iomem *status = fsl_qdma->status_base;
+       unsigned int decfdw0r;
+       unsigned int decfdw1r;
+       unsigned int decfdw2r;
+       unsigned int decfdw3r;
 
        intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
 
-       if (intr)
-               dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
+       if (intr) {
+               decfdw0r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW0R);
+               decfdw1r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW1R);
+               decfdw2r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW2R);
+               decfdw3r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW3R);
+               dev_err(fsl_qdma->dma_dev.dev,
+                       "DMA transaction error! (%x: %x-%x-%x-%x)\n",
+                       intr, decfdw0r, decfdw1r, decfdw2r, decfdw3r);
+       }
 
        qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
        return IRQ_HANDLED;