[PATCH 15/15] mtd: rawnand: sunxi: Add support for H616 nand controller

Richard Genoud posted 15 patches 2 months, 1 week ago
There is a newer version of this series
[PATCH 15/15] mtd: rawnand: sunxi: Add support for H616 nand controller
Posted by Richard Genoud 2 months, 1 week ago
The H616 nand controller has the same base as A10/A23, with some
differences:
- mdma is based on chained buffers
- its ECC supports up to 80bit per 1024bytes
- some registers layouts are a bit different, mainly due do the stronger
  ECC.
- it uses USER_DATA_LEN registers along USER_DATA registers.
- it needs a specific clock for ECC and MBUS.

This patch introduce the basic support, without DMA/MDMA.

Tested on Whatsminer H616 board (with and without scrambling, ECC)

Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
 drivers/mtd/nand/raw/sunxi_nand.c | 182 ++++++++++++++++++++++++++++--
 1 file changed, 174 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index e81d74c6633a..1e1185f8d980 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -49,17 +49,40 @@
 #define NFC_REG_A23_IO_DATA	0x0300
 #define NFC_REG_ECC_CTL		0x0034
 #define NFC_REG_ECC_ST		0x0038
-#define NFC_REG_DEBUG		0x003C
+#define NFC_REG_H6_PAT_FOUND	0x003C
 #define NFC_REG_A10_ECC_ERR_CNT	0x0040
+#define NFC_REG_H6_ECC_ERR_CNT	0x0050
 #define NFC_REG_ECC_ERR_CNT(nfc, x)	((nfc->caps->reg_ecc_err_cnt + (x)) & ~0x3)
+#define NFC_REG_H6_RDATA_CTL	0x0044
+#define NFC_REG_H6_RDATA_0	0x0048
+#define NFC_REG_H6_RDATA_1	0x004C
 #define NFC_REG_A10_USER_DATA	0x0050
+#define NFC_REG_H6_USER_DATA	0x0080
 #define NFC_REG_USER_DATA(nfc, x)	(nfc->caps->reg_user_data + ((x) * 4))
+#define NFC_REG_H6_USER_DATA_LEN 0x0070
+/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */
+#define NFC_REG_USER_DATA_LEN_CAPACITY 8
+#define NFC_REG_USER_DATA_LEN(nfc, step) \
+	 (nfc->caps->reg_user_data_len + \
+	 ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4)
 #define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area)
 #define NFC_REG_A10_SPARE_AREA	0x00A0
 #define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id)
 #define NFC_REG_A10_PAT_ID	0x00A4
 #define NFC_REG_MDMA_ADDR	0x00C0
 #define NFC_REG_MDMA_CNT	0x00C4
+#define NFC_REG_H6_EFNAND_STATUS 0x0110
+#define NFC_REG_H6_SPARE_AREA	0x0114
+#define NFC_REG_H6_PAT_ID	0x0118
+#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C
+#define NFC_REG_H6_NDMA_MODE_CTL 0x0120
+#define NFC_REG_H6_MDMA_DLBA_REG 0x0200
+#define NFC_REG_H6_MDMA_STA	0x0204
+#define NFC_REG_H6_MDMA_INT_MAS	0x0208
+#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C
+#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210
+#define NFC_REG_H6_MDMA_CNT	0x0214
+
 #define NFC_RAM0_BASE		0x0400
 #define NFC_RAM1_BASE		0x0800
 
@@ -71,6 +94,7 @@
 #define NFC_BUS_WIDTH_16	(1 << 2)
 #define NFC_RB_SEL_MSK		BIT(3)
 #define NFC_RB_SEL(x)		((x) << 3)
+/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */
 #define NFC_CE_SEL_MSK		GENMASK(26, 24)
 #define NFC_CE_SEL(x)		((x) << 24)
 #define NFC_CE_CTL		BIT(6)
@@ -89,6 +113,9 @@
 #define NFC_STA			BIT(4)
 #define NFC_NATCH_INT_FLAG	BIT(5)
 #define NFC_RB_STATE(x)		BIT(x + 8)
+#define NFC_RB_STATE_MSK	GENMASK(11, 8)
+#define NDFC_RDATA_STA_1	BIT(12)
+#define NDFC_RDATA_STA_0	BIT(13)
 
 /* define bit use in NFC_INT */
 #define NFC_B2R_INT_ENABLE	BIT(0)
@@ -100,6 +127,7 @@
 
 /* define bit use in NFC_TIMING_CTL */
 #define NFC_TIMING_CTL_EDO	BIT(8)
+#define NFC_TIMING_CTL_E_EDO	BIT(9)
 
 /* define NFC_TIMING_CFG register layout */
 #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\
@@ -107,9 +135,15 @@
 	(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\
 	(((tCAD) & 0x7) << 8))
 
+#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC)		\
+	((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) |	\
+	 (((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) |	\
+	 (((tWC) & 0x3) << 18))
+
 /* define bit use in NFC_CMD */
 #define NFC_CMD_LOW_BYTE_MSK	GENMASK(7, 0)
-#define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)
+#define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)  // 15-10 reserved on H6
+#define NFC_CMD_ADR_NUM_MSK	GENMASK(9, 8)
 #define NFC_CMD(x)		(x)
 #define NFC_ADR_NUM_MSK		GENMASK(18, 16)
 #define NFC_ADR_NUM(x)		(((x) - 1) << 16)
@@ -122,6 +156,7 @@
 #define NFC_SEQ			BIT(25)
 #define NFC_DATA_SWAP_METHOD	BIT(26)
 #define NFC_ROW_AUTO_INC	BIT(27)
+#define NFC_H6_SEND_RND_CMD2	BIT(27)
 #define NFC_SEND_CMD3		BIT(28)
 #define NFC_SEND_CMD4		BIT(29)
 #define NFC_CMD_TYPE_MSK	GENMASK(31, 30)
@@ -133,6 +168,7 @@
 #define NFC_READ_CMD_MSK	GENMASK(7, 0)
 #define NFC_RND_READ_CMD0_MSK	GENMASK(15, 8)
 #define NFC_RND_READ_CMD1_MSK	GENMASK(23, 16)
+#define NFC_RND_READ_CMD2_MSK	GENMASK(31, 24)
 
 /* define bit use in NFC_WCMD_SET */
 #define NFC_PROGRAM_CMD_MSK	GENMASK(7, 0)
@@ -150,6 +186,9 @@
 #define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_mask)
 #define NFC_ECC_MODE_MSK(nfc)	(nfc->caps->ecc_mode_mask)
 #define NFC_ECC_MODE(nfc, x)	field_prep(NFC_ECC_MODE_MSK(nfc), (x))
+/* RANDOM_PAGE_SIZE: 0: ECC block size  1: page size */
+#define NFC_A23_RANDOM_PAGE_SIZE	BIT(11)
+#define NFC_H6_RANDOM_PAGE_SIZE	BIT(7)
 #define NFC_RANDOM_SEED_MSK	GENMASK(30, 16)
 #define NFC_RANDOM_SEED(x)	((x) << 16)
 
@@ -165,6 +204,9 @@
 
 #define NFC_ECC_ERR_CNT(b, x)	(((x) >> (((b) % 4) * 8)) & 0xff)
 
+#define NFC_USER_DATA_LEN_MSK(step) \
+	(0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4))
+
 #define NFC_DEFAULT_TIMEOUT_MS	1000
 
 #define NFC_MAX_CS		7
@@ -224,9 +266,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
  * @has_mdma:		Use mbus dma mode, otherwise general dma
  *			through MBUS on A23/A33 needs extra configuration.
  * @has_ecc_block_512:	If the ECC can handle 512B or only 1024B chuncks
+ * @has_mbus_clk:	If the controller needs a mbus clock.
  * @reg_io_data:	I/O data register
  * @reg_ecc_err_cnt:	ECC error counter register
  * @reg_user_data:	User data register
+ * @reg_user_data_len:	User data length register
  * @reg_spare_area:	Spare Area Register
  * @reg_pat_id:		Pattern ID Register
  * @reg_pat_found:	Data Pattern Status Register
@@ -238,14 +282,23 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
  * @dma_maxburst:	DMA maxburst
  * @ecc_strengths:	Available ECC strengths array
  * @nstrengths:		Size of @ecc_strengths
+ * @max_ecc_steps:	Maximum supported steps for ECC, this is also the
+ *			number of user data registers
+ * @user_data_len_tab:  Table of lenghts supported by USER_DATA_LEN register
+ *			The table index is the value to set in NFC_USER_DATA_LEN
+ *			registers, and the corresponding value is the number of
+ *			bytes to write
+ * @nuser_data_tab:	Size of @user_data_len_tab
  * @sram_size:		Size of the NAND controller SRAM
  */
 struct sunxi_nfc_caps {
 	bool has_mdma;
 	bool has_ecc_block_512;
+	bool has_mbus_clk;
 	unsigned int reg_io_data;
 	unsigned int reg_ecc_err_cnt;
 	unsigned int reg_user_data;
+	unsigned int reg_user_data_len;
 	unsigned int reg_spare_area;
 	unsigned int reg_pat_id;
 	unsigned int reg_pat_found;
@@ -257,6 +310,9 @@ struct sunxi_nfc_caps {
 	unsigned int dma_maxburst;
 	const u8 *ecc_strengths;
 	unsigned int nstrengths;
+	const u8 *user_data_len_tab;
+	unsigned int nuser_data_tab;
+	unsigned int max_ecc_steps;
 	int sram_size;
 };
 
@@ -268,6 +324,7 @@ struct sunxi_nfc_caps {
  * @regs: NAND controller registers
  * @ahb_clk: NAND controller AHB clock
  * @mod_clk: NAND controller mod clock
+ * @ecc_clk: NAND controller ECC clock
  * @reset: NAND controller reset line
  * @assigned_cs: bitmask describing already assigned CS lines
  * @clk_rate: NAND controller current clock rate
@@ -282,7 +339,9 @@ struct sunxi_nfc {
 	struct device *dev;
 	void __iomem *regs;
 	struct clk *ahb_clk;
+	struct clk *mbus_clk;
 	struct clk *mod_clk;
+	struct clk *ecc_clk;
 	struct reset_control *reset;
 	unsigned long assigned_cs;
 	unsigned long clk_rate;
@@ -764,6 +823,53 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
 		sunxi_nfc_randomize_bbm(nand, page, oob);
 }
 
+/*
+ * On H6/H6 the user_data length has to be set in specific registers
+ * before writing.
+ */
+static void sunxi_nfc_reset_user_data_len(struct sunxi_nfc *nfc)
+{
+	int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY;
+
+	/* not all SoCs have this register */
+	if (!nfc->caps->reg_user_data_len)
+		return;
+
+	for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step)
+		writel(0, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, i));
+}
+
+static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,
+					int len, int step)
+{
+	bool found = false;
+	u32 val;
+	int i;
+
+	/* not all SoCs have this register */
+	if (!nfc->caps->reg_user_data_len)
+		return;
+
+	for (i = 0; i < nfc->caps->nuser_data_tab; i++) {
+		if (len == nfc->caps->user_data_len_tab[i]) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		dev_warn(nfc->dev,
+			 "Unsupported length for user data reg: %d\n", len);
+		return;
+	}
+
+	val = readl(nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
+
+	val &= ~NFC_USER_DATA_LEN_MSK(step);
+	val |= field_prep(NFC_USER_DATA_LEN_MSK(step), i);
+	writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
+}
+
 static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
 						const u8 *oob, int step,
 						bool bbm, int page)
@@ -858,6 +964,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
 	if (ret)
 		return ret;
 
+	sunxi_nfc_reset_user_data_len(nfc);
+	sunxi_nfc_set_user_data_len(nfc, 4, 0);
 	sunxi_nfc_randomizer_config(nand, page, false);
 	sunxi_nfc_randomizer_enable(nand);
 	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
@@ -968,6 +1076,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
 		return ret;
 
 	sunxi_nfc_hw_ecc_enable(nand);
+	sunxi_nfc_reset_user_data_len(nfc);
+	sunxi_nfc_set_user_data_len(nfc, 4, 0);
 	sunxi_nfc_randomizer_config(nand, page, false);
 	sunxi_nfc_randomizer_enable(nand);
 
@@ -1100,6 +1210,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
 
 	sunxi_nfc_randomizer_config(nand, page, false);
 	sunxi_nfc_randomizer_enable(nand);
+	sunxi_nfc_reset_user_data_len(nfc);
+	sunxi_nfc_set_user_data_len(nfc, 4, 0);
 	sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
 
 	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
@@ -1344,10 +1456,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
 	if (ret)
 		goto pio_fallback;
 
+	sunxi_nfc_reset_user_data_len(nfc);
 	for (i = 0; i < ecc->steps; i++) {
 		const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
 
 		sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
+		sunxi_nfc_set_user_data_len(nfc, 4, i);
 	}
 
 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
@@ -2148,18 +2262,36 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
 	if (irq < 0)
 		return irq;
 
+	nfc->caps = of_device_get_match_data(dev);
+	if (!nfc->caps)
+		return -EINVAL;
+
 	nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
 	if (IS_ERR(nfc->ahb_clk)) {
 		dev_err(dev, "failed to retrieve ahb clk\n");
 		return PTR_ERR(nfc->ahb_clk);
 	}
 
+	if (nfc->caps->has_mbus_clk) {
+		nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus");
+		if (IS_ERR(nfc->mbus_clk)) {
+			dev_err(dev, "No mbus clock specified\n");
+			return PTR_ERR(nfc->mbus_clk);
+		}
+	}
+
 	nfc->mod_clk = devm_clk_get_enabled(dev, "mod");
 	if (IS_ERR(nfc->mod_clk)) {
 		dev_err(dev, "failed to retrieve mod clk\n");
 		return PTR_ERR(nfc->mod_clk);
 	}
 
+	nfc->ecc_clk = devm_clk_get_optional_enabled(dev, "ecc");
+	if (IS_ERR(nfc->ecc_clk)) {
+		dev_err(dev, "failed to retrieve ecc clk\n");
+		return PTR_ERR(nfc->ecc_clk);
+	}
+
 	nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
 	if (IS_ERR(nfc->reset))
 		return PTR_ERR(nfc->reset);
@@ -2170,12 +2302,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	nfc->caps = of_device_get_match_data(&pdev->dev);
-	if (!nfc->caps) {
-		ret = -EINVAL;
-		goto out_ahb_reset_reassert;
-	}
-
 	ret = sunxi_nfc_rst(nfc);
 	if (ret)
 		goto out_ahb_reset_reassert;
@@ -2226,8 +2352,17 @@ static const u8 sunxi_ecc_strengths_a10[] = {
 	16, 24, 28, 32, 40, 48, 56, 60, 64
 };
 
+static const u8 sunxi_ecc_strengths_h6[] = {
+	16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80
+};
+
+static const u8 sunxi_user_data_len_h6[] = {
+	0, 4, 8, 12, 16, 20, 24, 28, 32
+};
+
 static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
 	.has_ecc_block_512 = true,
+	.has_mbus_clk = false,
 	.reg_io_data = NFC_REG_A10_IO_DATA,
 	.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
 	.reg_user_data = NFC_REG_A10_USER_DATA,
@@ -2242,11 +2377,13 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
 	.dma_maxburst = 4,
 	.ecc_strengths = sunxi_ecc_strengths_a10,
 	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
+	.max_ecc_steps = 16,
 	.sram_size = 1024,
 };
 
 static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
 	.has_mdma = true,
+	.has_mbus_clk = false,
 	.has_ecc_block_512 = true,
 	.reg_io_data = NFC_REG_A23_IO_DATA,
 	.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
@@ -2262,9 +2399,34 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
 	.dma_maxburst = 8,
 	.ecc_strengths = sunxi_ecc_strengths_a10,
 	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
+	.max_ecc_steps = 16,
 	.sram_size = 1024,
 };
 
+static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
+	.has_mdma = false, // H6 supports only chained descriptors
+	.has_mbus_clk = true,
+	.reg_io_data = NFC_REG_A23_IO_DATA,
+	.reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT,
+	.reg_user_data = NFC_REG_H6_USER_DATA,
+	.reg_user_data_len = NFC_REG_H6_USER_DATA_LEN,
+	.reg_spare_area = NFC_REG_H6_SPARE_AREA,
+	.reg_pat_id = NFC_REG_H6_PAT_ID,
+	.reg_pat_found = NFC_REG_H6_PAT_FOUND,
+	.random_en_mask = BIT(5),
+	.random_dir_mask = BIT(6),
+	.ecc_mode_mask = GENMASK(15, 8),
+	.ecc_err_mask = GENMASK(31, 0),
+	.pat_found_mask = GENMASK(31, 0),
+	.dma_maxburst = 8,
+	.ecc_strengths = sunxi_ecc_strengths_h6,
+	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6),
+	.user_data_len_tab = sunxi_user_data_len_h6,
+	.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
+	.max_ecc_steps = 32,
+	.sram_size = 8192,
+};
+
 static const struct of_device_id sunxi_nfc_ids[] = {
 	{
 		.compatible = "allwinner,sun4i-a10-nand",
@@ -2274,6 +2436,10 @@ static const struct of_device_id sunxi_nfc_ids[] = {
 		.compatible = "allwinner,sun8i-a23-nand-controller",
 		.data = &sunxi_nfc_a23_caps,
 	},
+	{
+		.compatible = "allwinner,sun50i-h616-nand-controller",
+		.data = &sunxi_nfc_h616_caps,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
-- 
2.47.3
Re: [PATCH 15/15] mtd: rawnand: sunxi: Add support for H616 nand controller
Posted by kernel test robot 2 months, 1 week ago
Hi Richard,

kernel test robot noticed the following build warnings:

[auto build test WARNING on mtd/nand/next]
[also build test WARNING on mtd/mtd/next mtd/mtd/fixes sunxi/sunxi/for-next robh/for-next linus/master v6.17 next-20251010]
[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#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Richard-Genoud/mtd-rawnand-sunxi-Remove-superfluous-register-readings/20251010-164637
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next
patch link:    https://lore.kernel.org/r/20251010084042.341224-16-richard.genoud%40bootlin.com
patch subject: [PATCH 15/15] mtd: rawnand: sunxi: Add support for H616 nand controller
config: riscv-randconfig-002-20251011 (https://download.01.org/0day-ci/archive/20251011/202510110828.Xb6hqQvx-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 39f292ffa13d7ca0d1edff27ac8fd55024bb4d19)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251011/202510110828.Xb6hqQvx-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510110828.Xb6hqQvx-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> Warning: drivers/mtd/nand/raw/sunxi_nand.c:351 struct member 'mbus_clk' not described in 'sunxi_nfc'

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 15/15] mtd: rawnand: sunxi: Add support for H616 nand controller
Posted by Jernej Škrabec 2 months, 1 week ago
Dne petek, 10. oktober 2025 ob 10:40:42 Srednjeevropski poletni čas je Richard Genoud napisal(a):
> The H616 nand controller has the same base as A10/A23, with some
> differences:
> - mdma is based on chained buffers
> - its ECC supports up to 80bit per 1024bytes
> - some registers layouts are a bit different, mainly due do the stronger
>   ECC.
> - it uses USER_DATA_LEN registers along USER_DATA registers.
> - it needs a specific clock for ECC and MBUS.
> 
> This patch introduce the basic support, without DMA/MDMA.
> 
> Tested on Whatsminer H616 board (with and without scrambling, ECC)
> 
> Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
> ---
>  drivers/mtd/nand/raw/sunxi_nand.c | 182 ++++++++++++++++++++++++++++--
>  1 file changed, 174 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
> index e81d74c6633a..1e1185f8d980 100644
> --- a/drivers/mtd/nand/raw/sunxi_nand.c
> +++ b/drivers/mtd/nand/raw/sunxi_nand.c
> @@ -49,17 +49,40 @@
>  #define NFC_REG_A23_IO_DATA	0x0300
>  #define NFC_REG_ECC_CTL		0x0034
>  #define NFC_REG_ECC_ST		0x0038
> -#define NFC_REG_DEBUG		0x003C
> +#define NFC_REG_H6_PAT_FOUND	0x003C
>  #define NFC_REG_A10_ECC_ERR_CNT	0x0040
> +#define NFC_REG_H6_ECC_ERR_CNT	0x0050
>  #define NFC_REG_ECC_ERR_CNT(nfc, x)	((nfc->caps->reg_ecc_err_cnt + (x)) & ~0x3)
> +#define NFC_REG_H6_RDATA_CTL	0x0044
> +#define NFC_REG_H6_RDATA_0	0x0048
> +#define NFC_REG_H6_RDATA_1	0x004C
>  #define NFC_REG_A10_USER_DATA	0x0050
> +#define NFC_REG_H6_USER_DATA	0x0080
>  #define NFC_REG_USER_DATA(nfc, x)	(nfc->caps->reg_user_data + ((x) * 4))
> +#define NFC_REG_H6_USER_DATA_LEN 0x0070
> +/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */
> +#define NFC_REG_USER_DATA_LEN_CAPACITY 8
> +#define NFC_REG_USER_DATA_LEN(nfc, step) \
> +	 (nfc->caps->reg_user_data_len + \
> +	 ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4)
>  #define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area)
>  #define NFC_REG_A10_SPARE_AREA	0x00A0
>  #define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id)
>  #define NFC_REG_A10_PAT_ID	0x00A4
>  #define NFC_REG_MDMA_ADDR	0x00C0
>  #define NFC_REG_MDMA_CNT	0x00C4
> +#define NFC_REG_H6_EFNAND_STATUS 0x0110
> +#define NFC_REG_H6_SPARE_AREA	0x0114
> +#define NFC_REG_H6_PAT_ID	0x0118
> +#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C
> +#define NFC_REG_H6_NDMA_MODE_CTL 0x0120
> +#define NFC_REG_H6_MDMA_DLBA_REG 0x0200
> +#define NFC_REG_H6_MDMA_STA	0x0204
> +#define NFC_REG_H6_MDMA_INT_MAS	0x0208
> +#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C
> +#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210
> +#define NFC_REG_H6_MDMA_CNT	0x0214
> +
>  #define NFC_RAM0_BASE		0x0400
>  #define NFC_RAM1_BASE		0x0800
>  
> @@ -71,6 +94,7 @@
>  #define NFC_BUS_WIDTH_16	(1 << 2)
>  #define NFC_RB_SEL_MSK		BIT(3)
>  #define NFC_RB_SEL(x)		((x) << 3)
> +/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */
>  #define NFC_CE_SEL_MSK		GENMASK(26, 24)
>  #define NFC_CE_SEL(x)		((x) << 24)
>  #define NFC_CE_CTL		BIT(6)
> @@ -89,6 +113,9 @@
>  #define NFC_STA			BIT(4)
>  #define NFC_NATCH_INT_FLAG	BIT(5)
>  #define NFC_RB_STATE(x)		BIT(x + 8)
> +#define NFC_RB_STATE_MSK	GENMASK(11, 8)
> +#define NDFC_RDATA_STA_1	BIT(12)
> +#define NDFC_RDATA_STA_0	BIT(13)
>  
>  /* define bit use in NFC_INT */
>  #define NFC_B2R_INT_ENABLE	BIT(0)
> @@ -100,6 +127,7 @@
>  
>  /* define bit use in NFC_TIMING_CTL */
>  #define NFC_TIMING_CTL_EDO	BIT(8)
> +#define NFC_TIMING_CTL_E_EDO	BIT(9)
>  
>  /* define NFC_TIMING_CFG register layout */
>  #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\
> @@ -107,9 +135,15 @@
>  	(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\
>  	(((tCAD) & 0x7) << 8))
>  
> +#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC)		\
> +	((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) |	\
> +	 (((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) |	\
> +	 (((tWC) & 0x3) << 18))
> +
>  /* define bit use in NFC_CMD */
>  #define NFC_CMD_LOW_BYTE_MSK	GENMASK(7, 0)
> -#define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)
> +#define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)  // 15-10 reserved on H6
> +#define NFC_CMD_ADR_NUM_MSK	GENMASK(9, 8)
>  #define NFC_CMD(x)		(x)
>  #define NFC_ADR_NUM_MSK		GENMASK(18, 16)
>  #define NFC_ADR_NUM(x)		(((x) - 1) << 16)
> @@ -122,6 +156,7 @@
>  #define NFC_SEQ			BIT(25)
>  #define NFC_DATA_SWAP_METHOD	BIT(26)
>  #define NFC_ROW_AUTO_INC	BIT(27)
> +#define NFC_H6_SEND_RND_CMD2	BIT(27)
>  #define NFC_SEND_CMD3		BIT(28)
>  #define NFC_SEND_CMD4		BIT(29)
>  #define NFC_CMD_TYPE_MSK	GENMASK(31, 30)
> @@ -133,6 +168,7 @@
>  #define NFC_READ_CMD_MSK	GENMASK(7, 0)
>  #define NFC_RND_READ_CMD0_MSK	GENMASK(15, 8)
>  #define NFC_RND_READ_CMD1_MSK	GENMASK(23, 16)
> +#define NFC_RND_READ_CMD2_MSK	GENMASK(31, 24)
>  
>  /* define bit use in NFC_WCMD_SET */
>  #define NFC_PROGRAM_CMD_MSK	GENMASK(7, 0)
> @@ -150,6 +186,9 @@
>  #define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_mask)
>  #define NFC_ECC_MODE_MSK(nfc)	(nfc->caps->ecc_mode_mask)
>  #define NFC_ECC_MODE(nfc, x)	field_prep(NFC_ECC_MODE_MSK(nfc), (x))
> +/* RANDOM_PAGE_SIZE: 0: ECC block size  1: page size */
> +#define NFC_A23_RANDOM_PAGE_SIZE	BIT(11)
> +#define NFC_H6_RANDOM_PAGE_SIZE	BIT(7)
>  #define NFC_RANDOM_SEED_MSK	GENMASK(30, 16)
>  #define NFC_RANDOM_SEED(x)	((x) << 16)
>  
> @@ -165,6 +204,9 @@
>  
>  #define NFC_ECC_ERR_CNT(b, x)	(((x) >> (((b) % 4) * 8)) & 0xff)
>  
> +#define NFC_USER_DATA_LEN_MSK(step) \
> +	(0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4))
> +
>  #define NFC_DEFAULT_TIMEOUT_MS	1000
>  
>  #define NFC_MAX_CS		7
> @@ -224,9 +266,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
>   * @has_mdma:		Use mbus dma mode, otherwise general dma
>   *			through MBUS on A23/A33 needs extra configuration.
>   * @has_ecc_block_512:	If the ECC can handle 512B or only 1024B chuncks
> + * @has_mbus_clk:	If the controller needs a mbus clock.
>   * @reg_io_data:	I/O data register
>   * @reg_ecc_err_cnt:	ECC error counter register
>   * @reg_user_data:	User data register
> + * @reg_user_data_len:	User data length register
>   * @reg_spare_area:	Spare Area Register
>   * @reg_pat_id:		Pattern ID Register
>   * @reg_pat_found:	Data Pattern Status Register
> @@ -238,14 +282,23 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
>   * @dma_maxburst:	DMA maxburst
>   * @ecc_strengths:	Available ECC strengths array
>   * @nstrengths:		Size of @ecc_strengths
> + * @max_ecc_steps:	Maximum supported steps for ECC, this is also the
> + *			number of user data registers
> + * @user_data_len_tab:  Table of lenghts supported by USER_DATA_LEN register
> + *			The table index is the value to set in NFC_USER_DATA_LEN
> + *			registers, and the corresponding value is the number of
> + *			bytes to write
> + * @nuser_data_tab:	Size of @user_data_len_tab
>   * @sram_size:		Size of the NAND controller SRAM
>   */
>  struct sunxi_nfc_caps {
>  	bool has_mdma;
>  	bool has_ecc_block_512;
> +	bool has_mbus_clk;
>  	unsigned int reg_io_data;
>  	unsigned int reg_ecc_err_cnt;
>  	unsigned int reg_user_data;
> +	unsigned int reg_user_data_len;
>  	unsigned int reg_spare_area;
>  	unsigned int reg_pat_id;
>  	unsigned int reg_pat_found;
> @@ -257,6 +310,9 @@ struct sunxi_nfc_caps {
>  	unsigned int dma_maxburst;
>  	const u8 *ecc_strengths;
>  	unsigned int nstrengths;
> +	const u8 *user_data_len_tab;
> +	unsigned int nuser_data_tab;
> +	unsigned int max_ecc_steps;
>  	int sram_size;
>  };
>  
> @@ -268,6 +324,7 @@ struct sunxi_nfc_caps {
>   * @regs: NAND controller registers
>   * @ahb_clk: NAND controller AHB clock
>   * @mod_clk: NAND controller mod clock
> + * @ecc_clk: NAND controller ECC clock
>   * @reset: NAND controller reset line
>   * @assigned_cs: bitmask describing already assigned CS lines
>   * @clk_rate: NAND controller current clock rate
> @@ -282,7 +339,9 @@ struct sunxi_nfc {
>  	struct device *dev;
>  	void __iomem *regs;
>  	struct clk *ahb_clk;
> +	struct clk *mbus_clk;
>  	struct clk *mod_clk;
> +	struct clk *ecc_clk;
>  	struct reset_control *reset;
>  	unsigned long assigned_cs;
>  	unsigned long clk_rate;
> @@ -764,6 +823,53 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
>  		sunxi_nfc_randomize_bbm(nand, page, oob);
>  }
>  
> +/*
> + * On H6/H6 the user_data length has to be set in specific registers
> + * before writing.
> + */
> +static void sunxi_nfc_reset_user_data_len(struct sunxi_nfc *nfc)
> +{
> +	int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY;
> +
> +	/* not all SoCs have this register */
> +	if (!nfc->caps->reg_user_data_len)
> +		return;
> +
> +	for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step)
> +		writel(0, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, i));
> +}
> +
> +static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,
> +					int len, int step)
> +{
> +	bool found = false;
> +	u32 val;
> +	int i;
> +
> +	/* not all SoCs have this register */
> +	if (!nfc->caps->reg_user_data_len)
> +		return;
> +
> +	for (i = 0; i < nfc->caps->nuser_data_tab; i++) {
> +		if (len == nfc->caps->user_data_len_tab[i]) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		dev_warn(nfc->dev,
> +			 "Unsupported length for user data reg: %d\n", len);
> +		return;
> +	}
> +
> +	val = readl(nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
> +
> +	val &= ~NFC_USER_DATA_LEN_MSK(step);
> +	val |= field_prep(NFC_USER_DATA_LEN_MSK(step), i);
> +	writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
> +}
> +
>  static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
>  						const u8 *oob, int step,
>  						bool bbm, int page)
> @@ -858,6 +964,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
>  	if (ret)
>  		return ret;
>  
> +	sunxi_nfc_reset_user_data_len(nfc);
> +	sunxi_nfc_set_user_data_len(nfc, 4, 0);
>  	sunxi_nfc_randomizer_config(nand, page, false);
>  	sunxi_nfc_randomizer_enable(nand);
>  	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
> @@ -968,6 +1076,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
>  		return ret;
>  
>  	sunxi_nfc_hw_ecc_enable(nand);
> +	sunxi_nfc_reset_user_data_len(nfc);
> +	sunxi_nfc_set_user_data_len(nfc, 4, 0);
>  	sunxi_nfc_randomizer_config(nand, page, false);
>  	sunxi_nfc_randomizer_enable(nand);
>  
> @@ -1100,6 +1210,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
>  
>  	sunxi_nfc_randomizer_config(nand, page, false);
>  	sunxi_nfc_randomizer_enable(nand);
> +	sunxi_nfc_reset_user_data_len(nfc);
> +	sunxi_nfc_set_user_data_len(nfc, 4, 0);
>  	sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
>  
>  	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
> @@ -1344,10 +1456,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
>  	if (ret)
>  		goto pio_fallback;
>  
> +	sunxi_nfc_reset_user_data_len(nfc);
>  	for (i = 0; i < ecc->steps; i++) {
>  		const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
>  
>  		sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
> +		sunxi_nfc_set_user_data_len(nfc, 4, i);
>  	}
>  
>  	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> @@ -2148,18 +2262,36 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
>  	if (irq < 0)
>  		return irq;
>  
> +	nfc->caps = of_device_get_match_data(dev);
> +	if (!nfc->caps)
> +		return -EINVAL;
> +
>  	nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
>  	if (IS_ERR(nfc->ahb_clk)) {
>  		dev_err(dev, "failed to retrieve ahb clk\n");
>  		return PTR_ERR(nfc->ahb_clk);
>  	}
>  
> +	if (nfc->caps->has_mbus_clk) {
> +		nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus");
> +		if (IS_ERR(nfc->mbus_clk)) {
> +			dev_err(dev, "No mbus clock specified\n");
> +			return PTR_ERR(nfc->mbus_clk);
> +		}
> +	}
> +
>  	nfc->mod_clk = devm_clk_get_enabled(dev, "mod");
>  	if (IS_ERR(nfc->mod_clk)) {
>  		dev_err(dev, "failed to retrieve mod clk\n");
>  		return PTR_ERR(nfc->mod_clk);
>  	}
>  
> +	nfc->ecc_clk = devm_clk_get_optional_enabled(dev, "ecc");
> +	if (IS_ERR(nfc->ecc_clk)) {
> +		dev_err(dev, "failed to retrieve ecc clk\n");
> +		return PTR_ERR(nfc->ecc_clk);
> +	}
> +
>  	nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
>  	if (IS_ERR(nfc->reset))
>  		return PTR_ERR(nfc->reset);
> @@ -2170,12 +2302,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> -	nfc->caps = of_device_get_match_data(&pdev->dev);
> -	if (!nfc->caps) {
> -		ret = -EINVAL;
> -		goto out_ahb_reset_reassert;
> -	}
> -
>  	ret = sunxi_nfc_rst(nfc);
>  	if (ret)
>  		goto out_ahb_reset_reassert;
> @@ -2226,8 +2352,17 @@ static const u8 sunxi_ecc_strengths_a10[] = {
>  	16, 24, 28, 32, 40, 48, 56, 60, 64
>  };
>  
> +static const u8 sunxi_ecc_strengths_h6[] = {
> +	16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80
> +};
> +
> +static const u8 sunxi_user_data_len_h6[] = {
> +	0, 4, 8, 12, 16, 20, 24, 28, 32
> +};
> +
>  static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
>  	.has_ecc_block_512 = true,
> +	.has_mbus_clk = false,
>  	.reg_io_data = NFC_REG_A10_IO_DATA,
>  	.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
>  	.reg_user_data = NFC_REG_A10_USER_DATA,
> @@ -2242,11 +2377,13 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
>  	.dma_maxburst = 4,
>  	.ecc_strengths = sunxi_ecc_strengths_a10,
>  	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
> +	.max_ecc_steps = 16,
>  	.sram_size = 1024,
>  };
>  
>  static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
>  	.has_mdma = true,
> +	.has_mbus_clk = false,
>  	.has_ecc_block_512 = true,
>  	.reg_io_data = NFC_REG_A23_IO_DATA,
>  	.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
> @@ -2262,9 +2399,34 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
>  	.dma_maxburst = 8,
>  	.ecc_strengths = sunxi_ecc_strengths_a10,
>  	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
> +	.max_ecc_steps = 16,
>  	.sram_size = 1024,
>  };
>  
> +static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
> +	.has_mdma = false, // H6 supports only chained descriptors

H6 or H616?

Fix the comment.

Best regards,
Jernej

> +	.has_mbus_clk = true,
> +	.reg_io_data = NFC_REG_A23_IO_DATA,
> +	.reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT,
> +	.reg_user_data = NFC_REG_H6_USER_DATA,
> +	.reg_user_data_len = NFC_REG_H6_USER_DATA_LEN,
> +	.reg_spare_area = NFC_REG_H6_SPARE_AREA,
> +	.reg_pat_id = NFC_REG_H6_PAT_ID,
> +	.reg_pat_found = NFC_REG_H6_PAT_FOUND,
> +	.random_en_mask = BIT(5),
> +	.random_dir_mask = BIT(6),
> +	.ecc_mode_mask = GENMASK(15, 8),
> +	.ecc_err_mask = GENMASK(31, 0),
> +	.pat_found_mask = GENMASK(31, 0),
> +	.dma_maxburst = 8,
> +	.ecc_strengths = sunxi_ecc_strengths_h6,
> +	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6),
> +	.user_data_len_tab = sunxi_user_data_len_h6,
> +	.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
> +	.max_ecc_steps = 32,
> +	.sram_size = 8192,
> +};
> +
>  static const struct of_device_id sunxi_nfc_ids[] = {
>  	{
>  		.compatible = "allwinner,sun4i-a10-nand",
> @@ -2274,6 +2436,10 @@ static const struct of_device_id sunxi_nfc_ids[] = {
>  		.compatible = "allwinner,sun8i-a23-nand-controller",
>  		.data = &sunxi_nfc_a23_caps,
>  	},
> +	{
> +		.compatible = "allwinner,sun50i-h616-nand-controller",
> +		.data = &sunxi_nfc_h616_caps,
> +	},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
> 
Re: [PATCH 15/15] mtd: rawnand: sunxi: Add support for H616 nand controller
Posted by Richard GENOUD 2 months ago
Le 11/10/2025 à 12:48, Jernej Škrabec a écrit :
> Dne petek, 10. oktober 2025 ob 10:40:42 Srednjeevropski poletni čas je Richard Genoud napisal(a):
>> The H616 nand controller has the same base as A10/A23, with some
>> differences:
>> - mdma is based on chained buffers
>> - its ECC supports up to 80bit per 1024bytes
>> - some registers layouts are a bit different, mainly due do the stronger
>>    ECC.
>> - it uses USER_DATA_LEN registers along USER_DATA registers.
>> - it needs a specific clock for ECC and MBUS.
>>
>> This patch introduce the basic support, without DMA/MDMA.
>>
>> Tested on Whatsminer H616 board (with and without scrambling, ECC)
>>
>> Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
>> ---
>>   drivers/mtd/nand/raw/sunxi_nand.c | 182 ++++++++++++++++++++++++++++--
>>   1 file changed, 174 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
>> index e81d74c6633a..1e1185f8d980 100644
>> --- a/drivers/mtd/nand/raw/sunxi_nand.c
>> +++ b/drivers/mtd/nand/raw/sunxi_nand.c
>> @@ -49,17 +49,40 @@
>>   #define NFC_REG_A23_IO_DATA	0x0300
>>   #define NFC_REG_ECC_CTL		0x0034
>>   #define NFC_REG_ECC_ST		0x0038
>> -#define NFC_REG_DEBUG		0x003C
>> +#define NFC_REG_H6_PAT_FOUND	0x003C
>>   #define NFC_REG_A10_ECC_ERR_CNT	0x0040
>> +#define NFC_REG_H6_ECC_ERR_CNT	0x0050
>>   #define NFC_REG_ECC_ERR_CNT(nfc, x)	((nfc->caps->reg_ecc_err_cnt + (x)) & ~0x3)
>> +#define NFC_REG_H6_RDATA_CTL	0x0044
>> +#define NFC_REG_H6_RDATA_0	0x0048
>> +#define NFC_REG_H6_RDATA_1	0x004C
>>   #define NFC_REG_A10_USER_DATA	0x0050
>> +#define NFC_REG_H6_USER_DATA	0x0080
>>   #define NFC_REG_USER_DATA(nfc, x)	(nfc->caps->reg_user_data + ((x) * 4))
>> +#define NFC_REG_H6_USER_DATA_LEN 0x0070
>> +/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */
>> +#define NFC_REG_USER_DATA_LEN_CAPACITY 8
>> +#define NFC_REG_USER_DATA_LEN(nfc, step) \
>> +	 (nfc->caps->reg_user_data_len + \
>> +	 ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4)
>>   #define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area)
>>   #define NFC_REG_A10_SPARE_AREA	0x00A0
>>   #define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id)
>>   #define NFC_REG_A10_PAT_ID	0x00A4
>>   #define NFC_REG_MDMA_ADDR	0x00C0
>>   #define NFC_REG_MDMA_CNT	0x00C4
>> +#define NFC_REG_H6_EFNAND_STATUS 0x0110
>> +#define NFC_REG_H6_SPARE_AREA	0x0114
>> +#define NFC_REG_H6_PAT_ID	0x0118
>> +#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C
>> +#define NFC_REG_H6_NDMA_MODE_CTL 0x0120
>> +#define NFC_REG_H6_MDMA_DLBA_REG 0x0200
>> +#define NFC_REG_H6_MDMA_STA	0x0204
>> +#define NFC_REG_H6_MDMA_INT_MAS	0x0208
>> +#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C
>> +#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210
>> +#define NFC_REG_H6_MDMA_CNT	0x0214
>> +
>>   #define NFC_RAM0_BASE		0x0400
>>   #define NFC_RAM1_BASE		0x0800
>>   
>> @@ -71,6 +94,7 @@
>>   #define NFC_BUS_WIDTH_16	(1 << 2)
>>   #define NFC_RB_SEL_MSK		BIT(3)
>>   #define NFC_RB_SEL(x)		((x) << 3)
>> +/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */
>>   #define NFC_CE_SEL_MSK		GENMASK(26, 24)
>>   #define NFC_CE_SEL(x)		((x) << 24)
>>   #define NFC_CE_CTL		BIT(6)
>> @@ -89,6 +113,9 @@
>>   #define NFC_STA			BIT(4)
>>   #define NFC_NATCH_INT_FLAG	BIT(5)
>>   #define NFC_RB_STATE(x)		BIT(x + 8)
>> +#define NFC_RB_STATE_MSK	GENMASK(11, 8)
>> +#define NDFC_RDATA_STA_1	BIT(12)
>> +#define NDFC_RDATA_STA_0	BIT(13)
>>   
>>   /* define bit use in NFC_INT */
>>   #define NFC_B2R_INT_ENABLE	BIT(0)
>> @@ -100,6 +127,7 @@
>>   
>>   /* define bit use in NFC_TIMING_CTL */
>>   #define NFC_TIMING_CTL_EDO	BIT(8)
>> +#define NFC_TIMING_CTL_E_EDO	BIT(9)
>>   
>>   /* define NFC_TIMING_CFG register layout */
>>   #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\
>> @@ -107,9 +135,15 @@
>>   	(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\
>>   	(((tCAD) & 0x7) << 8))
>>   
>> +#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC)		\
>> +	((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) |	\
>> +	 (((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) |	\
>> +	 (((tWC) & 0x3) << 18))
>> +
>>   /* define bit use in NFC_CMD */
>>   #define NFC_CMD_LOW_BYTE_MSK	GENMASK(7, 0)
>> -#define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)
>> +#define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)  // 15-10 reserved on H6
>> +#define NFC_CMD_ADR_NUM_MSK	GENMASK(9, 8)
>>   #define NFC_CMD(x)		(x)
>>   #define NFC_ADR_NUM_MSK		GENMASK(18, 16)
>>   #define NFC_ADR_NUM(x)		(((x) - 1) << 16)
>> @@ -122,6 +156,7 @@
>>   #define NFC_SEQ			BIT(25)
>>   #define NFC_DATA_SWAP_METHOD	BIT(26)
>>   #define NFC_ROW_AUTO_INC	BIT(27)
>> +#define NFC_H6_SEND_RND_CMD2	BIT(27)
>>   #define NFC_SEND_CMD3		BIT(28)
>>   #define NFC_SEND_CMD4		BIT(29)
>>   #define NFC_CMD_TYPE_MSK	GENMASK(31, 30)
>> @@ -133,6 +168,7 @@
>>   #define NFC_READ_CMD_MSK	GENMASK(7, 0)
>>   #define NFC_RND_READ_CMD0_MSK	GENMASK(15, 8)
>>   #define NFC_RND_READ_CMD1_MSK	GENMASK(23, 16)
>> +#define NFC_RND_READ_CMD2_MSK	GENMASK(31, 24)
>>   
>>   /* define bit use in NFC_WCMD_SET */
>>   #define NFC_PROGRAM_CMD_MSK	GENMASK(7, 0)
>> @@ -150,6 +186,9 @@
>>   #define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_mask)
>>   #define NFC_ECC_MODE_MSK(nfc)	(nfc->caps->ecc_mode_mask)
>>   #define NFC_ECC_MODE(nfc, x)	field_prep(NFC_ECC_MODE_MSK(nfc), (x))
>> +/* RANDOM_PAGE_SIZE: 0: ECC block size  1: page size */
>> +#define NFC_A23_RANDOM_PAGE_SIZE	BIT(11)
>> +#define NFC_H6_RANDOM_PAGE_SIZE	BIT(7)
>>   #define NFC_RANDOM_SEED_MSK	GENMASK(30, 16)
>>   #define NFC_RANDOM_SEED(x)	((x) << 16)
>>   
>> @@ -165,6 +204,9 @@
>>   
>>   #define NFC_ECC_ERR_CNT(b, x)	(((x) >> (((b) % 4) * 8)) & 0xff)
>>   
>> +#define NFC_USER_DATA_LEN_MSK(step) \
>> +	(0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4))
>> +
>>   #define NFC_DEFAULT_TIMEOUT_MS	1000
>>   
>>   #define NFC_MAX_CS		7
>> @@ -224,9 +266,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
>>    * @has_mdma:		Use mbus dma mode, otherwise general dma
>>    *			through MBUS on A23/A33 needs extra configuration.
>>    * @has_ecc_block_512:	If the ECC can handle 512B or only 1024B chuncks
>> + * @has_mbus_clk:	If the controller needs a mbus clock.
>>    * @reg_io_data:	I/O data register
>>    * @reg_ecc_err_cnt:	ECC error counter register
>>    * @reg_user_data:	User data register
>> + * @reg_user_data_len:	User data length register
>>    * @reg_spare_area:	Spare Area Register
>>    * @reg_pat_id:		Pattern ID Register
>>    * @reg_pat_found:	Data Pattern Status Register
>> @@ -238,14 +282,23 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
>>    * @dma_maxburst:	DMA maxburst
>>    * @ecc_strengths:	Available ECC strengths array
>>    * @nstrengths:		Size of @ecc_strengths
>> + * @max_ecc_steps:	Maximum supported steps for ECC, this is also the
>> + *			number of user data registers
>> + * @user_data_len_tab:  Table of lenghts supported by USER_DATA_LEN register
>> + *			The table index is the value to set in NFC_USER_DATA_LEN
>> + *			registers, and the corresponding value is the number of
>> + *			bytes to write
>> + * @nuser_data_tab:	Size of @user_data_len_tab
>>    * @sram_size:		Size of the NAND controller SRAM
>>    */
>>   struct sunxi_nfc_caps {
>>   	bool has_mdma;
>>   	bool has_ecc_block_512;
>> +	bool has_mbus_clk;
>>   	unsigned int reg_io_data;
>>   	unsigned int reg_ecc_err_cnt;
>>   	unsigned int reg_user_data;
>> +	unsigned int reg_user_data_len;
>>   	unsigned int reg_spare_area;
>>   	unsigned int reg_pat_id;
>>   	unsigned int reg_pat_found;
>> @@ -257,6 +310,9 @@ struct sunxi_nfc_caps {
>>   	unsigned int dma_maxburst;
>>   	const u8 *ecc_strengths;
>>   	unsigned int nstrengths;
>> +	const u8 *user_data_len_tab;
>> +	unsigned int nuser_data_tab;
>> +	unsigned int max_ecc_steps;
>>   	int sram_size;
>>   };
>>   
>> @@ -268,6 +324,7 @@ struct sunxi_nfc_caps {
>>    * @regs: NAND controller registers
>>    * @ahb_clk: NAND controller AHB clock
>>    * @mod_clk: NAND controller mod clock
>> + * @ecc_clk: NAND controller ECC clock
>>    * @reset: NAND controller reset line
>>    * @assigned_cs: bitmask describing already assigned CS lines
>>    * @clk_rate: NAND controller current clock rate
>> @@ -282,7 +339,9 @@ struct sunxi_nfc {
>>   	struct device *dev;
>>   	void __iomem *regs;
>>   	struct clk *ahb_clk;
>> +	struct clk *mbus_clk;
>>   	struct clk *mod_clk;
>> +	struct clk *ecc_clk;
>>   	struct reset_control *reset;
>>   	unsigned long assigned_cs;
>>   	unsigned long clk_rate;
>> @@ -764,6 +823,53 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
>>   		sunxi_nfc_randomize_bbm(nand, page, oob);
>>   }
>>   
>> +/*
>> + * On H6/H6 the user_data length has to be set in specific registers
>> + * before writing.
>> + */
>> +static void sunxi_nfc_reset_user_data_len(struct sunxi_nfc *nfc)
>> +{
>> +	int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY;
>> +
>> +	/* not all SoCs have this register */
>> +	if (!nfc->caps->reg_user_data_len)
>> +		return;
>> +
>> +	for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step)
>> +		writel(0, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, i));
>> +}
>> +
>> +static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,
>> +					int len, int step)
>> +{
>> +	bool found = false;
>> +	u32 val;
>> +	int i;
>> +
>> +	/* not all SoCs have this register */
>> +	if (!nfc->caps->reg_user_data_len)
>> +		return;
>> +
>> +	for (i = 0; i < nfc->caps->nuser_data_tab; i++) {
>> +		if (len == nfc->caps->user_data_len_tab[i]) {
>> +			found = true;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (!found) {
>> +		dev_warn(nfc->dev,
>> +			 "Unsupported length for user data reg: %d\n", len);
>> +		return;
>> +	}
>> +
>> +	val = readl(nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
>> +
>> +	val &= ~NFC_USER_DATA_LEN_MSK(step);
>> +	val |= field_prep(NFC_USER_DATA_LEN_MSK(step), i);
>> +	writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
>> +}
>> +
>>   static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
>>   						const u8 *oob, int step,
>>   						bool bbm, int page)
>> @@ -858,6 +964,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
>>   	if (ret)
>>   		return ret;
>>   
>> +	sunxi_nfc_reset_user_data_len(nfc);
>> +	sunxi_nfc_set_user_data_len(nfc, 4, 0);
>>   	sunxi_nfc_randomizer_config(nand, page, false);
>>   	sunxi_nfc_randomizer_enable(nand);
>>   	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
>> @@ -968,6 +1076,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
>>   		return ret;
>>   
>>   	sunxi_nfc_hw_ecc_enable(nand);
>> +	sunxi_nfc_reset_user_data_len(nfc);
>> +	sunxi_nfc_set_user_data_len(nfc, 4, 0);
>>   	sunxi_nfc_randomizer_config(nand, page, false);
>>   	sunxi_nfc_randomizer_enable(nand);
>>   
>> @@ -1100,6 +1210,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
>>   
>>   	sunxi_nfc_randomizer_config(nand, page, false);
>>   	sunxi_nfc_randomizer_enable(nand);
>> +	sunxi_nfc_reset_user_data_len(nfc);
>> +	sunxi_nfc_set_user_data_len(nfc, 4, 0);
>>   	sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
>>   
>>   	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
>> @@ -1344,10 +1456,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
>>   	if (ret)
>>   		goto pio_fallback;
>>   
>> +	sunxi_nfc_reset_user_data_len(nfc);
>>   	for (i = 0; i < ecc->steps; i++) {
>>   		const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
>>   
>>   		sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
>> +		sunxi_nfc_set_user_data_len(nfc, 4, i);
>>   	}
>>   
>>   	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>> @@ -2148,18 +2262,36 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
>>   	if (irq < 0)
>>   		return irq;
>>   
>> +	nfc->caps = of_device_get_match_data(dev);
>> +	if (!nfc->caps)
>> +		return -EINVAL;
>> +
>>   	nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
>>   	if (IS_ERR(nfc->ahb_clk)) {
>>   		dev_err(dev, "failed to retrieve ahb clk\n");
>>   		return PTR_ERR(nfc->ahb_clk);
>>   	}
>>   
>> +	if (nfc->caps->has_mbus_clk) {
>> +		nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus");
>> +		if (IS_ERR(nfc->mbus_clk)) {
>> +			dev_err(dev, "No mbus clock specified\n");
>> +			return PTR_ERR(nfc->mbus_clk);
>> +		}
>> +	}
>> +
>>   	nfc->mod_clk = devm_clk_get_enabled(dev, "mod");
>>   	if (IS_ERR(nfc->mod_clk)) {
>>   		dev_err(dev, "failed to retrieve mod clk\n");
>>   		return PTR_ERR(nfc->mod_clk);
>>   	}
>>   
>> +	nfc->ecc_clk = devm_clk_get_optional_enabled(dev, "ecc");
>> +	if (IS_ERR(nfc->ecc_clk)) {
>> +		dev_err(dev, "failed to retrieve ecc clk\n");
>> +		return PTR_ERR(nfc->ecc_clk);
>> +	}
>> +
>>   	nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
>>   	if (IS_ERR(nfc->reset))
>>   		return PTR_ERR(nfc->reset);
>> @@ -2170,12 +2302,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
>>   		return ret;
>>   	}
>>   
>> -	nfc->caps = of_device_get_match_data(&pdev->dev);
>> -	if (!nfc->caps) {
>> -		ret = -EINVAL;
>> -		goto out_ahb_reset_reassert;
>> -	}
>> -
>>   	ret = sunxi_nfc_rst(nfc);
>>   	if (ret)
>>   		goto out_ahb_reset_reassert;
>> @@ -2226,8 +2352,17 @@ static const u8 sunxi_ecc_strengths_a10[] = {
>>   	16, 24, 28, 32, 40, 48, 56, 60, 64
>>   };
>>   
>> +static const u8 sunxi_ecc_strengths_h6[] = {
>> +	16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80
>> +};
>> +
>> +static const u8 sunxi_user_data_len_h6[] = {
>> +	0, 4, 8, 12, 16, 20, 24, 28, 32
>> +};
>> +
>>   static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
>>   	.has_ecc_block_512 = true,
>> +	.has_mbus_clk = false,
>>   	.reg_io_data = NFC_REG_A10_IO_DATA,
>>   	.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
>>   	.reg_user_data = NFC_REG_A10_USER_DATA,
>> @@ -2242,11 +2377,13 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
>>   	.dma_maxburst = 4,
>>   	.ecc_strengths = sunxi_ecc_strengths_a10,
>>   	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
>> +	.max_ecc_steps = 16,
>>   	.sram_size = 1024,
>>   };
>>   
>>   static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
>>   	.has_mdma = true,
>> +	.has_mbus_clk = false,
>>   	.has_ecc_block_512 = true,
>>   	.reg_io_data = NFC_REG_A23_IO_DATA,
>>   	.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
>> @@ -2262,9 +2399,34 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
>>   	.dma_maxburst = 8,
>>   	.ecc_strengths = sunxi_ecc_strengths_a10,
>>   	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
>> +	.max_ecc_steps = 16,
>>   	.sram_size = 1024,
>>   };
>>   
>> +static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
>> +	.has_mdma = false, // H6 supports only chained descriptors
> 
> H6 or H616?
Yes, it should be H616. I'll change that.

Thanks!

> 
> Fix the comment.
> 
> Best regards,
> Jernej
> 
>> +	.has_mbus_clk = true,
>> +	.reg_io_data = NFC_REG_A23_IO_DATA,
>> +	.reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT,
>> +	.reg_user_data = NFC_REG_H6_USER_DATA,
>> +	.reg_user_data_len = NFC_REG_H6_USER_DATA_LEN,
>> +	.reg_spare_area = NFC_REG_H6_SPARE_AREA,
>> +	.reg_pat_id = NFC_REG_H6_PAT_ID,
>> +	.reg_pat_found = NFC_REG_H6_PAT_FOUND,
>> +	.random_en_mask = BIT(5),
>> +	.random_dir_mask = BIT(6),
>> +	.ecc_mode_mask = GENMASK(15, 8),
>> +	.ecc_err_mask = GENMASK(31, 0),
>> +	.pat_found_mask = GENMASK(31, 0),
>> +	.dma_maxburst = 8,
>> +	.ecc_strengths = sunxi_ecc_strengths_h6,
>> +	.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6),
>> +	.user_data_len_tab = sunxi_user_data_len_h6,
>> +	.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
>> +	.max_ecc_steps = 32,
>> +	.sram_size = 8192,
>> +};
>> +
>>   static const struct of_device_id sunxi_nfc_ids[] = {
>>   	{
>>   		.compatible = "allwinner,sun4i-a10-nand",
>> @@ -2274,6 +2436,10 @@ static const struct of_device_id sunxi_nfc_ids[] = {
>>   		.compatible = "allwinner,sun8i-a23-nand-controller",
>>   		.data = &sunxi_nfc_a23_caps,
>>   	},
>> +	{
>> +		.compatible = "allwinner,sun50i-h616-nand-controller",
>> +		.data = &sunxi_nfc_h616_caps,
>> +	},
>>   	{ /* sentinel */ }
>>   };
>>   MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
>>
> 
> 
> 
> 


-- 
Richard Genoud, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com