Skip to contentSkip to conversation

mtd: sunxi-nand: enable mdma support for allwinner a23

PATCH Superseded Manuel Dipolt 2 participants 3 messages Oct 8, 2020 – Oct 12, 2020

Subsystems (3)

v5 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

PATCH v5 1/1 mtd: sunxi-nand: enable mdma support for allwinner a23

This patch enables nand mdma mode for the Allwinner socs A23/A33/H3

---
 drivers/mtd/nand/raw/sunxi_nand.c | 156 +++++++++++++++++++-----------
 1 file changed, 101 insertions(+), 55 deletions(-)
drivers/mtd/nand/raw/sunxi_nand.c +101 -55
index 2a7ca3072f35..8403785e85e1 100644
@@ -51,6 +51,7 @@
 #define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4))
 #define NFC_REG_SPARE_AREA	0x00A0
 #define NFC_REG_PAT_ID		0x00A4
+#define NFC_REG_MDMA_ADDR	0x00C0
 #define NFC_REG_MDMA_CNT	0x00C4
 #define NFC_RAM0_BASE		0x0400
 #define NFC_RAM1_BASE		0x0800
@@ -207,13 +208,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
  * NAND Controller capabilities structure: stores NAND controller capabilities
  * for distinction between compatible strings.
  *
- * @extra_mbus_conf:	Contrary to A10, A10s and A13, accessing internal RAM
+ * @has_mdma:		Use mbus dma mode, otherwise general dma
  *			through MBUS on A23/A33 needs extra configuration.
  * @reg_io_data:	I/O data register
  * @dma_maxburst:	DMA maxburst
  */
 struct sunxi_nfc_caps {
-	bool extra_mbus_conf;
+	bool has_mdma;
 	unsigned int reg_io_data;
 	unsigned int dma_maxburst;
 };
@@ -346,7 +347,7 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
 static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 				    int chunksize, int nchunks,
 				    enum dma_data_direction ddir,
-				    struct scatterlist *sg)
+				    struct scatterlist *sg, dma_addr_t *mem_addr)
 {
 	struct dma_async_tx_descriptor *dmad;
 	enum dma_transfer_direction tdir;
@@ -358,29 +359,46 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 	else
 		tdir = DMA_MEM_TO_DEV;
 
-	sg_init_one(sg, buf, nchunks * chunksize);
-	ret = dma_map_sg(nfc->dev, sg, 1, ddir);
-	if (!ret)
-		return -ENOMEM;
+	if (nfc->caps->has_mdma) {
+		*mem_addr = dma_map_single(nfc->dev, (void *)buf, nchunks * chunksize, tdir);
+		ret = dma_mapping_error(nfc->dev, *mem_addr);
+		if (ret) {
+			dev_err(nfc->dev, "DMA mapping error\n");
+			return ret;
+		}
+	} else {
+		sg_init_one(sg, buf, nchunks * chunksize);
+		ret = dma_map_sg(nfc->dev, sg, 1, ddir);
+		if (!ret)
+			return -ENOMEM;
 
-	dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
-	if (!dmad) {
-		ret = -EINVAL;
-		goto err_unmap_buf;
+		dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
+		if (!dmad) {
+			ret = -EINVAL;
+			goto err_unmap_buf;
+		}
 	}
 
 	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
 	       nfc->regs + NFC_REG_CTL);
 	writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
 	writel(chunksize, nfc->regs + NFC_REG_CNT);
-	if (nfc->caps->extra_mbus_conf)
+
+	if (nfc->caps->has_mdma) {
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
+		       nfc->regs + NFC_REG_CTL);
 		writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
+		writel(*mem_addr, nfc->regs + NFC_REG_MDMA_ADDR);
+	} else {
+		writel(readl(nfc->regs + NFC_REG_CTL) | NFC_DMA_TYPE_NORMAL,
+		       nfc->regs + NFC_REG_CTL);
 
-	dmat = dmaengine_submit(dmad);
+		dmat = dmaengine_submit(dmad);
 
-	ret = dma_submit_error(dmat);
-	if (ret)
-		goto err_clr_dma_flag;
+		ret = dma_submit_error(dmat);
+		if (ret)
+			goto err_clr_dma_flag;
+	}
 
 	return 0;
 
@@ -395,11 +413,24 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 
 static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
 				     enum dma_data_direction ddir,
-				     struct scatterlist *sg)
+				     struct scatterlist *sg, dma_addr_t mem_addr, size_t size)
 {
-	dma_unmap_sg(nfc->dev, sg, 1, ddir);
-	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
-	       nfc->regs + NFC_REG_CTL);
+	enum dma_transfer_direction tdir;
+
+	if (ddir == DMA_FROM_DEVICE)
+		tdir = DMA_DEV_TO_MEM;
+	else
+		tdir = DMA_MEM_TO_DEV;
+
+	if (nfc->caps->has_mdma) {
+		dma_unmap_single(nfc->dev, mem_addr, size, tdir);
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+		       nfc->regs + NFC_REG_CTL);
+	} else {
+		dma_unmap_sg(nfc->dev, sg, 1, ddir);
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+		       nfc->regs + NFC_REG_CTL);
+	}
 }
 
 static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
@@ -911,6 +942,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 	unsigned int max_bitflips = 0;
 	int ret, i, raw_mode = 0;
 	struct scatterlist sg;
+	dma_addr_t mem_addr;
 	u32 status;
 
 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
@@ -918,7 +950,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 		return ret;
 
 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
-				       DMA_FROM_DEVICE, &sg);
+				       DMA_FROM_DEVICE, &sg, &mem_addr);
+
 	if (ret)
 		return ret;
 
@@ -929,19 +962,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 	writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
 	       NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
 
-	dma_async_issue_pending(nfc->dmac);
+	if (!nfc->caps->has_mdma)
+		dma_async_issue_pending(nfc->dmac);
 
 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
 	       nfc->regs + NFC_REG_CMD);
 
 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
-	if (ret)
+	if (ret && !nfc->caps->has_mdma)
 		dmaengine_terminate_all(nfc->dmac);
 
 	sunxi_nfc_randomizer_disable(nand);
 	sunxi_nfc_hw_ecc_disable(nand);
 
-	sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
+	sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg, mem_addr, nchunks * ecc->size);
 
 	if (ret)
 		return ret;
@@ -1276,6 +1310,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
 	struct nand_ecc_ctrl *ecc = &nand->ecc;
 	struct scatterlist sg;
+	dma_addr_t mem_addr;
 	int ret, i;
 
 	sunxi_nfc_select_chip(nand, nand->cur_cs);
@@ -1285,7 +1320,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 		return ret;
 
 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps,
-				       DMA_TO_DEVICE, &sg);
+				       DMA_TO_DEVICE, &sg, &mem_addr);
+
 	if (ret)
 		goto pio_fallback;
 
@@ -1304,20 +1340,22 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 	writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
 	       nfc->regs + NFC_REG_WCMD_SET);
 
-	dma_async_issue_pending(nfc->dmac);
+	if (!nfc->caps->has_mdma)
+		dma_async_issue_pending(nfc->dmac);
 
 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
 	       NFC_DATA_TRANS | NFC_ACCESS_DIR,
 	       nfc->regs + NFC_REG_CMD);
 
 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
-	if (ret)
+	if (ret && !nfc->caps->has_mdma)
 		dmaengine_terminate_all(nfc->dmac);
 
 	sunxi_nfc_randomizer_disable(nand);
 	sunxi_nfc_hw_ecc_disable(nand);
 
-	sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
+	sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg, mem_addr,
+				 ecc->size * ecc->steps);
 
 	if (ret)
 		return ret;
@@ -1695,7 +1733,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
 	mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
 	ecc->priv = data;
 
-	if (nfc->dmac) {
+	if (nfc->dmac || nfc->caps->has_mdma) {
 		ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
 		ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
 		ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
@@ -2058,6 +2096,36 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
 	}
 }
 
+static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
+{
+	int ret;
+
+	if (nfc->caps->has_mdma)
+		return 0;
+
+	nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
+	if (IS_ERR(nfc->dmac)) {
+		ret = PTR_ERR(nfc->dmac);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+
+		/* Ignore errors to fall back to PIO mode */
+		dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
+		nfc->dmac = NULL;
+	} else {
+		struct dma_slave_config dmac_cfg = { };
+
+		dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
+		dmac_cfg.dst_addr = dmac_cfg.src_addr;
+		dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
+		dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
+		dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
+		dmaengine_slave_config(nfc->dmac, &dmac_cfg);
+	}
+	return 0;
+}
+
 static int sunxi_nfc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -2132,30 +2200,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
 	if (ret)
 		goto out_ahb_reset_reassert;
 
-	nfc->dmac = dma_request_chan(dev, "rxtx");
-	if (IS_ERR(nfc->dmac)) {
-		ret = PTR_ERR(nfc->dmac);
-		if (ret == -EPROBE_DEFER)
-			goto out_ahb_reset_reassert;
-
-		/* Ignore errors to fall back to PIO mode */
-		dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret);
-		nfc->dmac = NULL;
-	} else {
-		struct dma_slave_config dmac_cfg = { };
-
-		dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
-		dmac_cfg.dst_addr = dmac_cfg.src_addr;
-		dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
-		dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
-		dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
-		dmaengine_slave_config(nfc->dmac, &dmac_cfg);
+	ret = sunxi_nfc_dma_init(nfc, r);
 
-		if (nfc->caps->extra_mbus_conf)
-			writel(readl(nfc->regs + NFC_REG_CTL) |
-			       NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL);
-	}
+	if (ret)
+		goto out_ahb_reset_reassert;
 
 	platform_set_drvdata(pdev, nfc);
 
@@ -2202,9 +2250,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
 };
 
 static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
-	.extra_mbus_conf = true,
-	.reg_io_data = NFC_REG_A23_IO_DATA,
-	.dma_maxburst = 8,
+	.has_mdma = true,
 };
 
 static const struct of_device_id sunxi_nfc_ids[] = {
Signature · 7 lines
2.20.1



______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
#1 lore raw

Hello Manuel,

The subject prefix should be "mtd: rawnand: sunxi:" not "mtd:
sunxi-nand:"

Quoted 1 line · On Thu, 8 Oct 2020 16:58:47 +0200 (CEST) Manuel Dipolt <[email protected]>…

This patch enables nand mdma mode for the Allwinner socs A23/A33/H3

^NAND MDMA (Master DMA)

And you need a period at the of your sentence.

Quoted 35 lines
---
 drivers/mtd/nand/raw/sunxi_nand.c | 156 +++++++++++++++++++-----------
 1 file changed, 101 insertions(+), 55 deletions(-)
drivers/mtd/nand/raw/sunxi_nand.c +3 -2
index 2a7ca3072f35..8403785e85e1 100644
@@ -51,6 +51,7 @@
 #define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4))
 #define NFC_REG_SPARE_AREA	0x00A0
 #define NFC_REG_PAT_ID		0x00A4
+#define NFC_REG_MDMA_ADDR	0x00C0
 #define NFC_REG_MDMA_CNT	0x00C4
 #define NFC_RAM0_BASE		0x0400
 #define NFC_RAM1_BASE		0x0800
@@ -207,13 +208,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
  * NAND Controller capabilities structure: stores NAND controller capabilities
  * for distinction between compatible strings.
  *
- * @extra_mbus_conf:	Contrary to A10, A10s and A13, accessing internal RAM
+ * @has_mdma:		Use mbus dma mode, otherwise general dma
  *			through MBUS on A23/A33 needs extra configuration.
  * @reg_io_data:	I/O data register
  * @dma_maxburst:	DMA maxburst
  */
 struct sunxi_nfc_caps {
-	bool extra_mbus_conf;
+	bool has_mdma;
 	unsigned int reg_io_data;
 	unsigned int dma_maxburst;
 };

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_rst(struct sunxi_nfc *nfc) · line 350

Quoted 5 lines
drivers/mtd/nand/raw/sunxi_nand.c +1 -1
@@ -346,7 +347,7 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
 static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 				    int chunksize, int nchunks,
 				    enum dma_data_direction ddir,
-				    struct scatterlist *sg)
+				    struct scatterlist *sg, dma_addr_t *mem_addr)

You don't need to pass the mem_addr, it will be extracted from the sg list.

Quoted 4 lines
drivers/mtd/nand/raw/sunxi_nand.c +0 -0
 {
 	struct dma_async_tx_descriptor *dmad;
 	enum dma_transfer_direction tdir;

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, · line 364

Quoted 10 lines
drivers/mtd/nand/raw/sunxi_nand.c +3 -4
@@ -358,29 +359,46 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 	else
 		tdir = DMA_MEM_TO_DEV;
 
-	sg_init_one(sg, buf, nchunks * chunksize);
-	ret = dma_map_sg(nfc->dev, sg, 1, ddir);
-	if (!ret)
-		return -ENOMEM;
+	if (nfc->caps->has_mdma) {
+		*mem_addr = dma_map_single(nfc->dev, (void *)buf, nchunks * chunksize, tdir);
+		ret = dma_mapping_error(nfc->dev, *mem_addr);

You can use sg_init_one() here as well.

Quoted 22 lines
drivers/mtd/nand/raw/sunxi_nand.c +14 -4
+		if (ret) {
+			dev_err(nfc->dev, "DMA mapping error\n");
+			return ret;
+		}
+	} else {
+		sg_init_one(sg, buf, nchunks * chunksize);
+		ret = dma_map_sg(nfc->dev, sg, 1, ddir);
+		if (!ret)
+			return -ENOMEM;
 
-	dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
-	if (!dmad) {
-		ret = -EINVAL;
-		goto err_unmap_buf;
+		dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
+		if (!dmad) {
+			ret = -EINVAL;
+			goto err_unmap_buf;
+		}
 	}
 
 	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, · line 391

Quoted 10 lines
drivers/mtd/nand/raw/sunxi_nand.c +5 -1
 	       nfc->regs + NFC_REG_CTL);
 	writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
 	writel(chunksize, nfc->regs + NFC_REG_CNT);
-	if (nfc->caps->extra_mbus_conf)
+
+	if (nfc->caps->has_mdma) {
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
+		       nfc->regs + NFC_REG_CTL);
 		writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
+		writel(*mem_addr, nfc->regs + NFC_REG_MDMA_ADDR);

^ sg_dma_address(sg)

Quoted 34 lines
drivers/mtd/nand/raw/sunxi_nand.c +16 -8
+	} else {
+		writel(readl(nfc->regs + NFC_REG_CTL) | NFC_DMA_TYPE_NORMAL,
+		       nfc->regs + NFC_REG_CTL);
 
-	dmat = dmaengine_submit(dmad);
+		dmat = dmaengine_submit(dmad);
 
-	ret = dma_submit_error(dmat);
-	if (ret)
-		goto err_clr_dma_flag;
+		ret = dma_submit_error(dmat);
+		if (ret)
+			goto err_clr_dma_flag;
+	}
 
 	return 0;
 
@@ -395,11 +413,24 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 
 static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
 				     enum dma_data_direction ddir,
-				     struct scatterlist *sg)
+				     struct scatterlist *sg, dma_addr_t mem_addr, size_t size)
 {
-	dma_unmap_sg(nfc->dev, sg, 1, ddir);
-	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
-	       nfc->regs + NFC_REG_CTL);
+	enum dma_transfer_direction tdir;
+
+	if (ddir == DMA_FROM_DEVICE)
+		tdir = DMA_DEV_TO_MEM;
+	else
+		tdir = DMA_MEM_TO_DEV;
+

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, · line 434

Quoted 10 lines
drivers/mtd/nand/raw/sunxi_nand.c +9 -0
+	if (nfc->caps->has_mdma) {
+		dma_unmap_single(nfc->dev, mem_addr, size, tdir);
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+		       nfc->regs + NFC_REG_CTL);
+	} else {
+		dma_unmap_sg(nfc->dev, sg, 1, ddir);
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+		       nfc->regs + NFC_REG_CTL);
+	}
 }

As I said in my previous review, if you get rid of the mem_addr argument, you shouldn't need any changes to this function. Have you tried using sg_dma_address()?

Regards,

Boris

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

#2 lore raw
In reply to Manuel Dipolt

Hi Boris,

see answers below, new patch will be provided as V6

Quoted 2 lines

The subject prefix should be "mtd: rawnand: sunxi:" not "mtd:
sunxi-nand:"

corrected

Quoted 9 lines
Quoted 1 line · On Thu, 8 Oct 2020 16:58:47 +0200 (CEST) Manuel Dipolt <[email protected]>…

This patch enables nand mdma mode for the Allwinner socs A23/A33/H3

^NAND MDMA (Master DMA)

And you need a period at the of your sentence.

corrected

Quoted 35 lines
Quoted 35 lines
---
 drivers/mtd/nand/raw/sunxi_nand.c | 156 +++++++++++++++++++-----------
 1 file changed, 101 insertions(+), 55 deletions(-)
drivers/mtd/nand/raw/sunxi_nand.c +3 -2
index 2a7ca3072f35..8403785e85e1 100644
@@ -51,6 +51,7 @@
 #define NFC_REG_USER_DATA(x)        (0x0050 + ((x) * 4))
 #define NFC_REG_SPARE_AREA        0x00A0
 #define NFC_REG_PAT_ID                0x00A4
+#define NFC_REG_MDMA_ADDR        0x00C0
 #define NFC_REG_MDMA_CNT        0x00C4
 #define NFC_RAM0_BASE                0x0400
 #define NFC_RAM1_BASE                0x0800
@@ -207,13 +208,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
  * NAND Controller capabilities structure: stores NAND controller capabilities
  * for distinction between compatible strings.
  *
- * @extra_mbus_conf:        Contrary to A10, A10s and A13, accessing internal RAM
+ * @has_mdma:                Use mbus dma mode, otherwise general dma
  *                        through MBUS on A23/A33 needs extra configuration.
  * @reg_io_data:        I/O data register
  * @dma_maxburst:        DMA maxburst
  */
 struct sunxi_nfc_caps {
-        bool extra_mbus_conf;
+        bool has_mdma;
         unsigned int reg_io_data;
         unsigned int dma_maxburst;
 };
@@ -346,7 +347,7 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
Quoted 9 lines

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_rst(struct sunxi_nfc *nfc) · line 350

Quoted 5 lines
drivers/mtd/nand/raw/sunxi_nand.c +1 -1
 static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
                                     int chunksize, int nchunks,
                                     enum dma_data_direction ddir,
-                                    struct scatterlist *sg)
+                                    struct scatterlist *sg, dma_addr_t *mem_addr)

You don't need to pass the mem_addr, it will be extracted from the sg list.

wasn't aware of that, good point don't have to change functions params now

Quoted 7 lines
Quoted 7 lines
drivers/mtd/nand/raw/sunxi_nand.c +0 -0
 {
         struct dma_async_tx_descriptor *dmad;
         enum dma_transfer_direction tdir;
@@ -358,29 +359,46 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
         else
                 tdir = DMA_MEM_TO_DEV;
 
Quoted 10 lines

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, · line 364

Quoted 7 lines
drivers/mtd/nand/raw/sunxi_nand.c +3 -4
-        sg_init_one(sg, buf, nchunks * chunksize);
-        ret = dma_map_sg(nfc->dev, sg, 1, ddir);
-        if (!ret)
-                return -ENOMEM;
+        if (nfc->caps->has_mdma) {
+                *mem_addr = dma_map_single(nfc->dev, (void *)buf, nchunks * chunksize, tdir);
+                ret = dma_mapping_error(nfc->dev, *mem_addr);

You can use sg_init_one() here as well.

changed to sg_init_one

Quoted 87 lines
Quoted 22 lines
drivers/mtd/nand/raw/sunxi_nand.c +14 -4
+                if (ret) {
+                        dev_err(nfc->dev, "DMA mapping error\n");
+                        return ret;
+                }
+        } else {
+                sg_init_one(sg, buf, nchunks * chunksize);
+                ret = dma_map_sg(nfc->dev, sg, 1, ddir);
+                if (!ret)
+                        return -ENOMEM;
 
-        dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
-        if (!dmad) {
-                ret = -EINVAL;
-                goto err_unmap_buf;
+                dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
+                if (!dmad) {
+                        ret = -EINVAL;
+                        goto err_unmap_buf;
+                }
         }
 
         writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, · line 391

Quoted 10 lines
drivers/mtd/nand/raw/sunxi_nand.c +5 -1
                nfc->regs + NFC_REG_CTL);
         writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
         writel(chunksize, nfc->regs + NFC_REG_CNT);
-        if (nfc->caps->extra_mbus_conf)
+
+        if (nfc->caps->has_mdma) {
+                writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
+                       nfc->regs + NFC_REG_CTL);
                 writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
+                writel(*mem_addr, nfc->regs + NFC_REG_MDMA_ADDR);

^ sg_dma_address(sg)

Quoted 34 lines
drivers/mtd/nand/raw/sunxi_nand.c +16 -8
+        } else {
+                writel(readl(nfc->regs + NFC_REG_CTL) | NFC_DMA_TYPE_NORMAL,
+                       nfc->regs + NFC_REG_CTL);
 
-        dmat = dmaengine_submit(dmad);
+                dmat = dmaengine_submit(dmad);
 
-        ret = dma_submit_error(dmat);
-        if (ret)
-                goto err_clr_dma_flag;
+                ret = dma_submit_error(dmat);
+                if (ret)
+                        goto err_clr_dma_flag;
+        }
 
         return 0;
 
@@ -395,11 +413,24 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 
 static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
                                      enum dma_data_direction ddir,
-                                     struct scatterlist *sg)
+                                     struct scatterlist *sg, dma_addr_t mem_addr, size_t size)
 {
-        dma_unmap_sg(nfc->dev, sg, 1, ddir);
-        writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
-               nfc->regs + NFC_REG_CTL);
+        enum dma_transfer_direction tdir;
+
+        if (ddir == DMA_FROM_DEVICE)
+                tdir = DMA_DEV_TO_MEM;
+        else
+                tdir = DMA_MEM_TO_DEV;
+

drivers/mtd/nand/raw/sunxi_nand.c static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, · line 434

Quoted 10 lines
drivers/mtd/nand/raw/sunxi_nand.c +9 -0
+        if (nfc->caps->has_mdma) {
+                dma_unmap_single(nfc->dev, mem_addr, size, tdir);
+                writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+                       nfc->regs + NFC_REG_CTL);
+        } else {
+                dma_unmap_sg(nfc->dev, sg, 1, ddir);
+                writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+                       nfc->regs + NFC_REG_CTL);
+        }
 }

As I said in my previous review, if you get rid of the mem_addr argument, you shouldn't need any changes to this function. Have you tried using sg_dma_address()?

Quoted 1 line
Quoted 3 lines
Quoted 1 line

removed variable cause waiting again only for nfc interrupt

I think you should wait for both.

ok waiting now for both to be on safe side.

Quoted 6 lines
Quoted 6 lines
Quoted 4 lines

Are you sure you don't need the NFC_CMD_INT_FLAG flag in that case? I'm pretty sure the DMA transfer is done before the PAGEPROG command is issued, meaning that you might be queuing new operations before the operation is actually finished.

Quoted 7 lines
Quoted 3 lines

can never be sure without any proper documentation about this nand controller ):
was guessing cause i saw dma int triggered,
tested again always both flags were set, so changed it to NFC_CMD_INT_FLAG to be on safe side

Actually, if you want to be on safe side, you should probably set both (and that applies to the read path as well, unless no commands are issued in that case).

waiting for both in read and write path now

Yours,
Manuel

----- Original Message -----
From: "Manuel Dipolt" <[email protected]>
To: "linux-mtd" <[email protected]>
Cc: "Boris Brezillon" <[email protected]>, "maxime" <[email protected]>, "miquel raynal" <[email protected]>, "Roland Ruckerbauer" <[email protected]>
Sent: Thursday, October 8, 2020 4:58:47 PM
Subject: [PATCH v5] mtd: sunxi-nand: enable mdma support for allwinner a23

This patch enables nand mdma mode for the Allwinner socs A23/A33/H3

---
 drivers/mtd/nand/raw/sunxi_nand.c | 156 +++++++++++++++++++-----------
 1 file changed, 101 insertions(+), 55 deletions(-)
drivers/mtd/nand/raw/sunxi_nand.c +101 -55
index 2a7ca3072f35..8403785e85e1 100644
@@ -51,6 +51,7 @@
 #define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4))
 #define NFC_REG_SPARE_AREA	0x00A0
 #define NFC_REG_PAT_ID		0x00A4
+#define NFC_REG_MDMA_ADDR	0x00C0
 #define NFC_REG_MDMA_CNT	0x00C4
 #define NFC_RAM0_BASE		0x0400
 #define NFC_RAM1_BASE		0x0800
@@ -207,13 +208,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
  * NAND Controller capabilities structure: stores NAND controller capabilities
  * for distinction between compatible strings.
  *
- * @extra_mbus_conf:	Contrary to A10, A10s and A13, accessing internal RAM
+ * @has_mdma:		Use mbus dma mode, otherwise general dma
  *			through MBUS on A23/A33 needs extra configuration.
  * @reg_io_data:	I/O data register
  * @dma_maxburst:	DMA maxburst
  */
 struct sunxi_nfc_caps {
-	bool extra_mbus_conf;
+	bool has_mdma;
 	unsigned int reg_io_data;
 	unsigned int dma_maxburst;
 };
@@ -346,7 +347,7 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
 static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 				    int chunksize, int nchunks,
 				    enum dma_data_direction ddir,
-				    struct scatterlist *sg)
+				    struct scatterlist *sg, dma_addr_t *mem_addr)
 {
 	struct dma_async_tx_descriptor *dmad;
 	enum dma_transfer_direction tdir;
@@ -358,29 +359,46 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 	else
 		tdir = DMA_MEM_TO_DEV;
 
-	sg_init_one(sg, buf, nchunks * chunksize);
-	ret = dma_map_sg(nfc->dev, sg, 1, ddir);
-	if (!ret)
-		return -ENOMEM;
+	if (nfc->caps->has_mdma) {
+		*mem_addr = dma_map_single(nfc->dev, (void *)buf, nchunks * chunksize, tdir);
+		ret = dma_mapping_error(nfc->dev, *mem_addr);
+		if (ret) {
+			dev_err(nfc->dev, "DMA mapping error\n");
+			return ret;
+		}
+	} else {
+		sg_init_one(sg, buf, nchunks * chunksize);
+		ret = dma_map_sg(nfc->dev, sg, 1, ddir);
+		if (!ret)
+			return -ENOMEM;
 
-	dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
-	if (!dmad) {
-		ret = -EINVAL;
-		goto err_unmap_buf;
+		dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
+		if (!dmad) {
+			ret = -EINVAL;
+			goto err_unmap_buf;
+		}
 	}
 
 	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
 	       nfc->regs + NFC_REG_CTL);
 	writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
 	writel(chunksize, nfc->regs + NFC_REG_CNT);
-	if (nfc->caps->extra_mbus_conf)
+
+	if (nfc->caps->has_mdma) {
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
+		       nfc->regs + NFC_REG_CTL);
 		writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
+		writel(*mem_addr, nfc->regs + NFC_REG_MDMA_ADDR);
+	} else {
+		writel(readl(nfc->regs + NFC_REG_CTL) | NFC_DMA_TYPE_NORMAL,
+		       nfc->regs + NFC_REG_CTL);
 
-	dmat = dmaengine_submit(dmad);
+		dmat = dmaengine_submit(dmad);
 
-	ret = dma_submit_error(dmat);
-	if (ret)
-		goto err_clr_dma_flag;
+		ret = dma_submit_error(dmat);
+		if (ret)
+			goto err_clr_dma_flag;
+	}
 
 	return 0;
 
@@ -395,11 +413,24 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
 
 static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
 				     enum dma_data_direction ddir,
-				     struct scatterlist *sg)
+				     struct scatterlist *sg, dma_addr_t mem_addr, size_t size)
 {
-	dma_unmap_sg(nfc->dev, sg, 1, ddir);
-	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
-	       nfc->regs + NFC_REG_CTL);
+	enum dma_transfer_direction tdir;
+
+	if (ddir == DMA_FROM_DEVICE)
+		tdir = DMA_DEV_TO_MEM;
+	else
+		tdir = DMA_MEM_TO_DEV;
+
+	if (nfc->caps->has_mdma) {
+		dma_unmap_single(nfc->dev, mem_addr, size, tdir);
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+		       nfc->regs + NFC_REG_CTL);
+	} else {
+		dma_unmap_sg(nfc->dev, sg, 1, ddir);
+		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+		       nfc->regs + NFC_REG_CTL);
+	}
 }
 
 static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
@@ -911,6 +942,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 	unsigned int max_bitflips = 0;
 	int ret, i, raw_mode = 0;
 	struct scatterlist sg;
+	dma_addr_t mem_addr;
 	u32 status;
 
 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
@@ -918,7 +950,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 		return ret;
 
 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
-				       DMA_FROM_DEVICE, &sg);
+				       DMA_FROM_DEVICE, &sg, &mem_addr);
+
 	if (ret)
 		return ret;
 
@@ -929,19 +962,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 	writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
 	       NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
 
-	dma_async_issue_pending(nfc->dmac);
+	if (!nfc->caps->has_mdma)
+		dma_async_issue_pending(nfc->dmac);
 
 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
 	       nfc->regs + NFC_REG_CMD);
 
 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
-	if (ret)
+	if (ret && !nfc->caps->has_mdma)
 		dmaengine_terminate_all(nfc->dmac);
 
 	sunxi_nfc_randomizer_disable(nand);
 	sunxi_nfc_hw_ecc_disable(nand);
 
-	sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
+	sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg, mem_addr, nchunks * ecc->size);
 
 	if (ret)
 		return ret;
@@ -1276,6 +1310,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
 	struct nand_ecc_ctrl *ecc = &nand->ecc;
 	struct scatterlist sg;
+	dma_addr_t mem_addr;
 	int ret, i;
 
 	sunxi_nfc_select_chip(nand, nand->cur_cs);
@@ -1285,7 +1320,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 		return ret;
 
 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps,
-				       DMA_TO_DEVICE, &sg);
+				       DMA_TO_DEVICE, &sg, &mem_addr);
+
 	if (ret)
 		goto pio_fallback;
 
@@ -1304,20 +1340,22 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 	writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
 	       nfc->regs + NFC_REG_WCMD_SET);
 
-	dma_async_issue_pending(nfc->dmac);
+	if (!nfc->caps->has_mdma)
+		dma_async_issue_pending(nfc->dmac);
 
 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
 	       NFC_DATA_TRANS | NFC_ACCESS_DIR,
 	       nfc->regs + NFC_REG_CMD);
 
 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
-	if (ret)
+	if (ret && !nfc->caps->has_mdma)
 		dmaengine_terminate_all(nfc->dmac);
 
 	sunxi_nfc_randomizer_disable(nand);
 	sunxi_nfc_hw_ecc_disable(nand);
 
-	sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
+	sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg, mem_addr,
+				 ecc->size * ecc->steps);
 
 	if (ret)
 		return ret;
@@ -1695,7 +1733,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
 	mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
 	ecc->priv = data;
 
-	if (nfc->dmac) {
+	if (nfc->dmac || nfc->caps->has_mdma) {
 		ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
 		ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
 		ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
@@ -2058,6 +2096,36 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
 	}
 }
 
+static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
+{
+	int ret;
+
+	if (nfc->caps->has_mdma)
+		return 0;
+
+	nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
+	if (IS_ERR(nfc->dmac)) {
+		ret = PTR_ERR(nfc->dmac);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+
+		/* Ignore errors to fall back to PIO mode */
+		dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
+		nfc->dmac = NULL;
+	} else {
+		struct dma_slave_config dmac_cfg = { };
+
+		dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
+		dmac_cfg.dst_addr = dmac_cfg.src_addr;
+		dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
+		dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
+		dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
+		dmaengine_slave_config(nfc->dmac, &dmac_cfg);
+	}
+	return 0;
+}
+
 static int sunxi_nfc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -2132,30 +2200,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
 	if (ret)
 		goto out_ahb_reset_reassert;
 
-	nfc->dmac = dma_request_chan(dev, "rxtx");
-	if (IS_ERR(nfc->dmac)) {
-		ret = PTR_ERR(nfc->dmac);
-		if (ret == -EPROBE_DEFER)
-			goto out_ahb_reset_reassert;
-
-		/* Ignore errors to fall back to PIO mode */
-		dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret);
-		nfc->dmac = NULL;
-	} else {
-		struct dma_slave_config dmac_cfg = { };
-
-		dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
-		dmac_cfg.dst_addr = dmac_cfg.src_addr;
-		dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
-		dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
-		dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
-		dmaengine_slave_config(nfc->dmac, &dmac_cfg);
+	ret = sunxi_nfc_dma_init(nfc, r);
 
-		if (nfc->caps->extra_mbus_conf)
-			writel(readl(nfc->regs + NFC_REG_CTL) |
-			       NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL);
-	}
+	if (ret)
+		goto out_ahb_reset_reassert;
 
 	platform_set_drvdata(pdev, nfc);
 
@@ -2202,9 +2250,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
 };
 
 static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
-	.extra_mbus_conf = true,
-	.reg_io_data = NFC_REG_A23_IO_DATA,
-	.dma_maxburst = 8,
+	.has_mdma = true,
 };
 
 static const struct of_device_id sunxi_nfc_ids[] = {
Signature · 5 lines
2.20.1

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/