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@xxxxxxxx> --- Changelog v2: - Fix patch format Changelog v3: - Fix missing parentheses Changelog v4: - Adapt source code formatting --- Index: linux-5.4/drivers/dma/fsl-qdma.c =================================================================== --- linux-5.4.orig/drivers/dma/fsl-qdma.c +++ linux-5.4/drivers/dma/fsl-qdma.c @@ -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) @@ -110,8 +110,19 @@ #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_qd 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 { 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 } 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 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_handle 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;