diff mbox series

[13/18] mtd: nand: mxic-ecc: Support SPI pipelined mode

Message ID 20211020142809.349347-14-miquel.raynal@bootlin.com
State Changes Requested
Headers show
Series External ECC engines & Macronix support | expand

Commit Message

Miquel Raynal Oct. 20, 2021, 2:28 p.m. UTC
Introduce the support for another possible configuration: the ECC
engine may work as DMA master (pipelined) and move itself the data
to/from the NAND chip into the buffer, applying the necessary
corrections/computations on the fly.

This driver offers an ECC engine implementation that must be
instatiated from a SPI controller driver.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/ecc-mxic.c       | 148 +++++++++++++++++++++++++++++-
 include/linux/mtd/nand-ecc-mxic.h |  36 ++++++++
 2 files changed, 181 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/mtd/nand-ecc-mxic.h

Comments

kernel test robot Oct. 21, 2021, 5:22 a.m. UTC | #1
Hi Miquel,

I love your patch! Perhaps something to improve:

[auto build test WARNING on broonie-spi/for-next]
[also build test WARNING on mtd/nand/next mtd/mtd/next mtd/mtd/fixes v5.15-rc6 next-20211020]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Miquel-Raynal/External-ECC-engines-Macronix-support/20211020-223036
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
config: hexagon-randconfig-r014-20211021 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 3cea2505fd8d99a9ba0cb625aecfe28a47c4e3f8)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/ccf53b4df2cab6209b1c56f22e1a1955f59b8ea0
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Miquel-Raynal/External-ECC-engines-Macronix-support/20211020-223036
        git checkout ccf53b4df2cab6209b1c56f22e1a1955f59b8ea0
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=hexagon 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/mtd/nand/ecc-mxic.c:480:5: warning: no previous prototype for function 'mxic_ecc_process_data' [-Wmissing-prototypes]
   int mxic_ecc_process_data(struct mxic_ecc_engine *eng, dma_addr_t dirmap)
       ^
   drivers/mtd/nand/ecc-mxic.c:480:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int mxic_ecc_process_data(struct mxic_ecc_engine *eng, dma_addr_t dirmap)
   ^
   static 
>> drivers/mtd/nand/ecc-mxic.c:742:29: warning: no previous prototype for function 'mxic_ecc_get_pipelined_ops' [-Wmissing-prototypes]
   struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void)
                               ^
   drivers/mtd/nand/ecc-mxic.c:742:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void)
   ^
   static 
   2 warnings generated.


vim +/mxic_ecc_process_data +480 drivers/mtd/nand/ecc-mxic.c

   479	
 > 480	int mxic_ecc_process_data(struct mxic_ecc_engine *eng, dma_addr_t dirmap)
   481	{
   482		/* Retrieve the direction */
   483		unsigned int dir = (eng->req->type == NAND_PAGE_READ) ?
   484				   READ_NAND : WRITE_NAND;
   485	
   486		if (dirmap)
   487			writel(dirmap, eng->regs + HC_SLV_ADDR);
   488	
   489		/* Trigger processing */
   490		writel(SDMA_STRT | dir, eng->regs + SDMA_CTRL);
   491	
   492		/* Wait for completion */
   493		return mxic_ecc_data_xfer_wait_for_completion(eng);
   494	}
   495	EXPORT_SYMBOL_GPL(mxic_ecc_process_data);
   496	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/mtd/nand/ecc-mxic.c b/drivers/mtd/nand/ecc-mxic.c
index d9b251ab9201..555ba7f19027 100644
--- a/drivers/mtd/nand/ecc-mxic.c
+++ b/drivers/mtd/nand/ecc-mxic.c
@@ -39,7 +39,9 @@ 
 #define INTRPT_SIG_EN 0x0C
 /* Host Controller Configuration */
 #define HC_CONFIG 0x10
+#define   DEV2MEM 0 /* TRANS_TYP_DMA in the spec */
 #define   MEM2MEM BIT(4) /* TRANS_TYP_IO in the spec */
+#define   MAPPING BIT(5) /* TRANS_TYP_MAPPING in the spec */
 #define   ECC_PACKED 0 /* LAYOUT_TYP_INTEGRATED in the spec */
 #define   ECC_INTERLEAVED BIT(2) /* LAYOUT_TYP_DISTRIBUTED in the spec */
 #define   BURST_TYP_FIXED 0
@@ -101,6 +103,7 @@  struct mxic_ecc_engine {
 	u8 *oobwithstat;
 	struct scatterlist sg[2];
 	struct nand_page_io_req *req;
+	unsigned int pageoffs;
 };
 
 static int mxic_ecc_ooblayout_ecc(struct mtd_info *mtd, int section,
@@ -188,6 +191,31 @@  static irqreturn_t mxic_ecc_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static struct device *mxic_ecc_get_engine_dev(struct device *dev)
+{
+	struct platform_device *eccpdev;
+	struct device_node *np;
+
+	/*
+	 * If the device node contains this property, it means the device does
+	 * not represent the actual ECC engine.
+	 */
+	np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0);
+	if (!np)
+		return dev;
+
+	eccpdev = of_find_device_by_node(np);
+	if (!eccpdev) {
+		of_node_put(np);
+		return NULL;
+	}
+
+	platform_device_put(eccpdev);
+	of_node_put(np);
+
+	return &eccpdev->dev;
+}
+
 static int mxic_ecc_init_ctx(struct nand_device *nand, struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -379,6 +407,41 @@  static int mxic_ecc_init_ctx_external(struct nand_device *nand)
 	return 0;
 }
 
+static int mxic_ecc_init_ctx_pipelined(struct nand_device *nand)
+{
+	struct mxic_ecc_engine *eng;
+	struct device *dev;
+	int ret;
+
+	/*
+	 * In the case of a pipelined engine, the device registering the ECC
+	 * engine is not the actual ECC engine device but the host controller.
+	 */
+	dev = mxic_ecc_get_engine_dev(nand->ecc.engine->dev);
+	if (!dev)
+		return -EINVAL;
+
+	dev_info(dev, "Macronix ECC engine in pipelined/mapping mode\n");
+
+	ret = mxic_ecc_init_ctx(nand, dev);
+	if (ret)
+		return ret;
+
+	eng = nand->ecc.ctx.priv;
+
+	/* All steps should be handled in one go directly by the internal DMA */
+	writel(eng->steps, eng->regs + CHUNK_CNT);
+
+	/*
+	 * Interleaved ECC scheme cannot be used otherwise factory bad block
+	 * markers would be lost. A packed layout is mandatory.
+	 */
+	writel(BURST_TYP_INCREASING | ECC_PACKED | MAPPING,
+	       eng->regs + HC_CONFIG);
+
+	return 0;
+}
+
 static void mxic_ecc_cleanup_ctx(struct nand_device *nand)
 {
 	struct mxic_ecc_engine *eng = nand->ecc.ctx.priv;
@@ -414,18 +477,22 @@  static int mxic_ecc_data_xfer_wait_for_completion(struct mxic_ecc_engine *eng)
 	return 0;
 }
 
-static int mxic_ecc_process_data(struct mxic_ecc_engine *eng)
+int mxic_ecc_process_data(struct mxic_ecc_engine *eng, dma_addr_t dirmap)
 {
 	/* Retrieve the direction */
 	unsigned int dir = (eng->req->type == NAND_PAGE_READ) ?
 			   READ_NAND : WRITE_NAND;
 
+	if (dirmap)
+		writel(dirmap, eng->regs + HC_SLV_ADDR);
+
 	/* Trigger processing */
 	writel(SDMA_STRT | dir, eng->regs + SDMA_CTRL);
 
 	/* Wait for completion */
 	return mxic_ecc_data_xfer_wait_for_completion(eng);
 }
+EXPORT_SYMBOL_GPL(mxic_ecc_process_data);
 
 static void mxic_ecc_extract_status_bytes(struct mxic_ecc_engine *eng, u8 *buf)
 {
@@ -528,7 +595,7 @@  static int mxic_ecc_prepare_io_req_external(struct nand_device *nand,
 		       eng->regs + SDMA_MAIN_ADDR);
 		writel(sg_dma_address(&eng->sg[1]) + (step * (eng->oob_step_sz + STAT_BYTES)),
 		       eng->regs + SDMA_SPARE_ADDR);
-		ret = mxic_ecc_process_data(eng);
+		ret = mxic_ecc_process_data(eng, 0);
 		if (ret)
 			break;
 	}
@@ -582,7 +649,7 @@  static int mxic_ecc_finish_io_req_external(struct nand_device *nand,
 		       eng->regs + SDMA_MAIN_ADDR);
 		writel(sg_dma_address(&eng->sg[1]) + (step * (eng->oob_step_sz + STAT_BYTES)),
 		       eng->regs + SDMA_SPARE_ADDR);
-		ret = mxic_ecc_process_data(eng);
+		ret = mxic_ecc_process_data(eng, 0);
 		if (ret)
 			break;
 	}
@@ -600,6 +667,64 @@  static int mxic_ecc_finish_io_req_external(struct nand_device *nand,
 	return mxic_ecc_count_biterrs(eng, mtd);
 }
 
+/* Pipelined ECC engine helpers */
+static int mxic_ecc_prepare_io_req_pipelined(struct nand_device *nand,
+					     struct nand_page_io_req *req)
+{
+	struct mxic_ecc_engine *eng = nand->ecc.ctx.priv;
+	int nents;
+
+	if (req->mode == MTD_OPS_RAW)
+		return 0;
+
+	nand_ecc_tweak_req(&eng->req_ctx, req);
+	eng->req = req;
+
+	/* Copy the OOB buffer and add room for the ECC engine status bytes */
+	mxic_ecc_add_room_in_oobbuf(eng, eng->oobwithstat, eng->req->oobbuf.in);
+
+	sg_set_buf(&eng->sg[0], req->databuf.in, req->datalen);
+	sg_set_buf(&eng->sg[1], eng->oobwithstat,
+		   req->ooblen + (eng->steps * STAT_BYTES));
+
+	nents = dma_map_sg(eng->dev, eng->sg, 2, DMA_BIDIRECTIONAL);
+	if (!nents)
+		return -EINVAL;
+
+	writel(sg_dma_address(&eng->sg[0]), eng->regs + SDMA_MAIN_ADDR);
+	writel(sg_dma_address(&eng->sg[1]), eng->regs + SDMA_SPARE_ADDR);
+
+	mxic_ecc_enable_engine(eng);
+
+	return 0;
+}
+
+static int mxic_ecc_finish_io_req_pipelined(struct nand_device *nand,
+					    struct nand_page_io_req *req)
+{
+	struct mxic_ecc_engine *eng = nand->ecc.ctx.priv;
+	struct mtd_info *mtd = nanddev_to_mtd(nand);
+	int ret = 0;
+
+	if (req->mode == MTD_OPS_RAW)
+		return 0;
+
+	mxic_ecc_disable_engine(eng);
+
+	dma_unmap_sg(eng->dev, eng->sg, 2, DMA_BIDIRECTIONAL);
+
+	if (req->type == NAND_PAGE_READ) {
+		mxic_ecc_extract_status_bytes(eng, eng->oobwithstat);
+		mxic_ecc_reconstruct_oobbuf(eng, eng->req->oobbuf.in,
+					    eng->oobwithstat);
+		ret = mxic_ecc_count_biterrs(eng, mtd);
+	}
+
+	nand_ecc_restore_req(&eng->req_ctx, req);
+
+	return ret;
+}
+
 static struct nand_ecc_engine_ops mxic_ecc_engine_external_ops = {
 	.init_ctx = mxic_ecc_init_ctx_external,
 	.cleanup_ctx = mxic_ecc_cleanup_ctx,
@@ -607,6 +732,23 @@  static struct nand_ecc_engine_ops mxic_ecc_engine_external_ops = {
 	.finish_io_req = mxic_ecc_finish_io_req_external,
 };
 
+static struct nand_ecc_engine_ops mxic_ecc_engine_pipelined_ops = {
+	.init_ctx = mxic_ecc_init_ctx_pipelined,
+	.cleanup_ctx = mxic_ecc_cleanup_ctx,
+	.prepare_io_req = mxic_ecc_prepare_io_req_pipelined,
+	.finish_io_req = mxic_ecc_finish_io_req_pipelined,
+};
+
+struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void)
+{
+	return &mxic_ecc_engine_pipelined_ops;
+}
+EXPORT_SYMBOL_GPL(mxic_ecc_get_pipelined_ops);
+
+/*
+ * Only the external ECC engine is exported as the pipelined is SoC specific, so
+ * it is registered directly by the drivers that wrap it.
+ */
 static int mxic_ecc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
diff --git a/include/linux/mtd/nand-ecc-mxic.h b/include/linux/mtd/nand-ecc-mxic.h
new file mode 100644
index 000000000000..d48b7fe05644
--- /dev/null
+++ b/include/linux/mtd/nand-ecc-mxic.h
@@ -0,0 +1,36 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright © 2019 Macronix
+ * Author: Miquèl Raynal <miquel.raynal@bootlin.com>
+ *
+ * Header for the Macronix external ECC engine.
+ */
+
+#ifndef __MTD_NAND_ECC_MXIC_H__
+#define __MTD_NAND_ECC_MXIC_H__
+
+#include <linux/device.h>
+
+struct mxic_ecc_engine;
+
+#if IS_ENABLED(CONFIG_MTD_NAND_ECC_MXIC)
+
+struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void);
+int mxic_ecc_process_data(struct mxic_ecc_engine *eng, dma_addr_t dirmap);
+
+#else /* !CONFIG_MTD_NAND_ECC_MXIC */
+
+struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void)
+{
+	return NULL;
+}
+
+static inline
+int mxic_ecc_process_data(struct mxic_ecc_engine *eng, dma_addr_t dirmap)
+{
+	return -ENOTSUPP;
+}
+
+#endif /* CONFIG_MTD_NAND_ECC_MXIC */
+
+#endif /* __MTD_NAND_ECC_MXIC_H__ */