Commit ab6041e4 authored by Koehrer Mathias (ETAS/EES-SL)'s avatar Koehrer Mathias (ETAS/EES-SL) Committed by Vinod Koul
Browse files

dmaengine: Extend NXP QDMA driver to check transmission errors



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: default avatarMathias Koehrer <mathias.koehrer@etas.com>
Link: https://lore.kernel.org/r/744443c0462aac2df4754f99500a911527c0b235.camel@bosch.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent c3846c4c
Loading
Loading
Loading
Loading
+57 −6
Original line number Diff line number Diff line
@@ -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_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;