From 0eb8de350e936ed22698a65197413842f55c393f Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:36 +0100 Subject: [PATCH 01/26] mtd: rawnand: sunxi: cosmetic: remove needless comment Remove 'complete' member from struct sunxi_nfc The 'complete' member isn't part of the structure, let's remove it. Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 1ce09b56b80..c6b9b2a4eba 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -270,8 +270,6 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * @clk_rate: NAND controller current clock rate * @chips: a list containing all the NAND chips attached to * this NAND controller - * @complete: a completion object used to wait for NAND - * controller events */ struct sunxi_nfc { struct nand_hw_control controller; From 79afb70a9393ac6d77ec03c9251c7460205805fd Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:37 +0100 Subject: [PATCH 02/26] mtd: rawnand: sunxi_spl: fix pointer from integer without a cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix pointer from interget warning when compiling for ARM64 When compiling for arm64, we get this error: error: passing argument 2 of ‘__memcpy_fromio’ makes pointer from integer without a cast [-Wint-conversion] Moreover the copy should be made with dedicated readl(), like for any register access on this peripheral, since they are 32bit wide. So, instead of memcpy_fromio(), just use a readl() loop. Introduce nand_readlcpy() to implement this loop. Fixes: 6ddbb1e936c7 ("spl: nand: sunxi: use PIO instead of DMA") Suggested-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 4f1e2d9a577..4441990dd93 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -251,6 +251,15 @@ static int nand_change_column(u16 column) static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116}; +static void nand_readlcpy(u32 *dest, u32 * __iomem src, size_t len) +{ + /* NB: len should be multiple of 4 (32bits access) */ + len >>= 2; + + while (len--) + *dest++ = readl(src++); +} + static int nand_read_page(const struct nfc_config *conf, u32 offs, void *dest, int len) { @@ -310,7 +319,8 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, return 1; /* Retrieve the data from SRAM */ - memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE, + nand_readlcpy((u32 *)data, + (void *)(uintptr_t)SUNXI_NFC_BASE + NFC_RAM0_BASE, conf->ecc_size); /* Stop the ECC engine */ From 322470fe733a4680d8df28e36ee86008bdccc985 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:38 +0100 Subject: [PATCH 03/26] mtd: rawnand: sunxi_spl: harmonize register defines with non spl file Harmonize registers definition in sunxi_nand{,_spl}.c files This is a first step to then include the same file from both sunxi_nand{,_spl}.c files Unused defines are also removed Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 148 ++++++++++++-------------- 1 file changed, 66 insertions(+), 82 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 4441990dd93..a4a73dcf8d0 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -14,48 +14,32 @@ #include /* registers */ -#define NFC_CTL 0x00000000 -#define NFC_ST 0x00000004 -#define NFC_INT 0x00000008 -#define NFC_TIMING_CTL 0x0000000C -#define NFC_TIMING_CFG 0x00000010 -#define NFC_ADDR_LOW 0x00000014 -#define NFC_ADDR_HIGH 0x00000018 -#define NFC_SECTOR_NUM 0x0000001C -#define NFC_CNT 0x00000020 -#define NFC_CMD 0x00000024 -#define NFC_RCMD_SET 0x00000028 -#define NFC_WCMD_SET 0x0000002C -#define NFC_IO_DATA 0x00000030 -#define NFC_ECC_CTL 0x00000034 -#define NFC_ECC_ST 0x00000038 -#define NFC_DEBUG 0x0000003C -#define NFC_ECC_CNT0 0x00000040 -#define NFC_ECC_CNT1 0x00000044 -#define NFC_ECC_CNT2 0x00000048 -#define NFC_ECC_CNT3 0x0000004C -#define NFC_USER_DATA_BASE 0x00000050 -#define NFC_EFNAND_STATUS 0x00000090 -#define NFC_SPARE_AREA 0x000000A0 -#define NFC_PATTERN_ID 0x000000A4 +#define NFC_REG_CTL 0x00000000 +#define NFC_REG_ST 0x00000004 +#define NFC_REG_ADDR_LOW 0x00000014 +#define NFC_REG_ADDR_HIGH 0x00000018 +#define NFC_REG_CNT 0x00000020 +#define NFC_REG_CMD 0x00000024 +#define NFC_REG_RCMD_SET 0x00000028 +#define NFC_REG_ECC_CTL 0x00000034 +#define NFC_REG_ECC_ST 0x00000038 +#define NFC_REG_SPARE_AREA 0x000000A0 #define NFC_RAM0_BASE 0x00000400 #define NFC_RAM1_BASE 0x00000800 -#define NFC_CTL_EN (1 << 0) -#define NFC_CTL_RESET (1 << 1) -#define NFC_CTL_RAM_METHOD (1 << 14) -#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8) -#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8) +#define NFC_EN (1 << 0) +#define NFC_RESET (1 << 1) +#define NFC_PAGE_SHIFT_MSK (0xf << 8) +#define NFC_PAGE_SIZE(a) ((fls(a) - 11) << 8) #define NFC_ECC_EN (1 << 0) -#define NFC_ECC_PIPELINE (1 << 3) #define NFC_ECC_EXCEPTION (1 << 4) -#define NFC_ECC_BLOCK_SIZE (1 << 5) -#define NFC_ECC_RANDOM_EN (1 << 9) -#define NFC_ECC_RANDOM_DIRECTION (1 << 10) +#define NFC_ECC_BLOCK_512 (1 << 5) +#define NFC_RANDOM_EN (1 << 9) +#define NFC_RANDOM_DIRECTION (1 << 10) -#define NFC_ADDR_NUM_OFFSET 16 -#define NFC_SEND_ADDR (1 << 19) +#define NFC_ADR_NUM_OFFSET 16 +#define NFC_SEND_ADR (1 << 19) #define NFC_ACCESS_DIR (1 << 20) #define NFC_DATA_TRANS (1 << 21) #define NFC_SEND_CMD1 (1 << 22) @@ -66,17 +50,17 @@ #define NFC_ROW_AUTO_INC (1 << 27) #define NFC_SEND_CMD3 (1 << 28) #define NFC_SEND_CMD4 (1 << 29) -#define NFC_RAW_CMD (0 << 30) -#define NFC_ECC_CMD (1 << 30) -#define NFC_PAGE_CMD (2 << 30) +#define NFC_NORMAL_OP (0 << 30) +#define NFC_ECC_OP (1 << 30) +#define NFC_PAGE_OP (2 << 30) -#define NFC_ST_CMD_INT_FLAG (1 << 1) -#define NFC_ST_DMA_INT_FLAG (1 << 2) -#define NFC_ST_CMD_FIFO_STAT (1 << 3) +#define NFC_CMD_INT_FLAG (1 << 1) +#define NFC_DMA_INT_FLAG (1 << 2) +#define NFC_CMD_FIFO_STATUS (1 << 3) #define NFC_READ_CMD_OFFSET 0 -#define NFC_RANDOM_READ_CMD0_OFFSET 8 -#define NFC_RANDOM_READ_CMD1_OFFSET 16 +#define NFC_RND_READ_CMD0_OFFSET 8 +#define NFC_RND_READ_CMD1_OFFSET 16 #define NFC_CMD_RNDOUTSTART 0xE0 #define NFC_CMD_RNDOUT 0x05 @@ -143,8 +127,8 @@ static inline int check_value_negated(int offset, int unexpected_bits, static int nand_wait_cmd_fifo_empty(void) { - if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT, - DEFAULT_TIMEOUT_US)) { + if (!check_value_negated(SUNXI_NFC_BASE + NFC_REG_ST, + NFC_CMD_FIFO_STATUS, DEFAULT_TIMEOUT_US)) { printf("nand: timeout waiting for empty cmd FIFO\n"); return -ETIMEDOUT; } @@ -154,7 +138,7 @@ static int nand_wait_cmd_fifo_empty(void) static int nand_wait_int(void) { - if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, + if (!check_value(SUNXI_NFC_BASE + NFC_REG_ST, NFC_CMD_INT_FLAG, DEFAULT_TIMEOUT_US)) { printf("nand: timeout waiting for interruption\n"); return -ETIMEDOUT; @@ -171,8 +155,8 @@ static int nand_exec_cmd(u32 cmd) if (ret) return ret; - writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); - writel(cmd, SUNXI_NFC_BASE + NFC_CMD); + writel(NFC_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_REG_ST); + writel(cmd, SUNXI_NFC_BASE + NFC_REG_CMD); return nand_wait_int(); } @@ -183,13 +167,13 @@ void nand_init(void) board_nand_init(); - val = readl(SUNXI_NFC_BASE + NFC_CTL); + val = readl(SUNXI_NFC_BASE + NFC_REG_CTL); /* enable and reset CTL */ - writel(val | NFC_CTL_EN | NFC_CTL_RESET, - SUNXI_NFC_BASE + NFC_CTL); + writel(val | NFC_EN | NFC_RESET, + SUNXI_NFC_BASE + NFC_REG_CTL); - if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, - NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) { + if (!check_value_negated(SUNXI_NFC_BASE + NFC_REG_CTL, + NFC_RESET, DEFAULT_TIMEOUT_US)) { printf("Couldn't initialize nand\n"); } @@ -203,42 +187,42 @@ static void nand_apply_config(const struct nfc_config *conf) nand_wait_cmd_fifo_empty(); - val = readl(SUNXI_NFC_BASE + NFC_CTL); - val &= ~NFC_CTL_PAGE_SIZE_MASK; - writel(val | NFC_CTL_PAGE_SIZE(conf->page_size), - SUNXI_NFC_BASE + NFC_CTL); - writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); - writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA); + val = readl(SUNXI_NFC_BASE + NFC_REG_CTL); + val &= ~NFC_PAGE_SHIFT_MSK; + writel(val | NFC_PAGE_SIZE(conf->page_size), + SUNXI_NFC_BASE + NFC_REG_CTL); + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_REG_CNT); + writel(conf->page_size, SUNXI_NFC_BASE + NFC_REG_SPARE_AREA); } static int nand_load_page(const struct nfc_config *conf, u32 offs) { int page = offs / conf->page_size; - writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | + writel((NFC_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | + (NFC_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_RCMD_SET); - writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW); - writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); + SUNXI_NFC_BASE + NFC_REG_RCMD_SET); + writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_REG_ADDR_LOW); + writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_REG_ADDR_HIGH); - return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | - NFC_SEND_ADDR | NFC_WAIT_FLAG | - ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET)); + return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_NORMAL_OP | + NFC_SEND_ADR | NFC_WAIT_FLAG | + ((conf->addr_cycles - 1) << NFC_ADR_NUM_OFFSET)); } static int nand_change_column(u16 column) { int ret; - writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | + writel((NFC_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | + (NFC_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_RCMD_SET); - writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW); + SUNXI_NFC_BASE + NFC_REG_RCMD_SET); + writel(column, SUNXI_NFC_BASE + NFC_REG_ADDR_LOW); - ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | - (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | + ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_NORMAL_OP | + (1 << NFC_ADR_NUM_OFFSET) | NFC_SEND_ADR | NFC_CMD_RNDOUT); if (ret) return ret; @@ -285,16 +269,16 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, u8 *data = dest + data_off; /* Clear ECC status and restart ECC engine */ - writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); + writel(0, SUNXI_NFC_BASE + NFC_REG_ECC_ST); writel((rand_seed << 16) | (conf->ecc_strength << 12) | - (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | - (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | + (conf->randomize ? NFC_RANDOM_EN : 0) | + (conf->ecc_size == 512 ? NFC_ECC_BLOCK_512 : 0) | NFC_ECC_EN | NFC_ECC_EXCEPTION, - SUNXI_NFC_BASE + NFC_ECC_CTL); + SUNXI_NFC_BASE + NFC_REG_ECC_CTL); /* Move the data in SRAM */ nand_change_column(data_off); - writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_REG_CNT); nand_exec_cmd(NFC_DATA_TRANS); /* @@ -302,10 +286,10 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, * the data. */ nand_change_column(oob_off); - nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD); + nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); /* Get the ECC status */ - ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST); + ecc_st = readl(SUNXI_NFC_BASE + NFC_REG_ECC_ST); /* ECC error detected. */ if (ecc_st & 0xffff) @@ -324,8 +308,8 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, conf->ecc_size); /* Stop the ECC engine */ - writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN, - SUNXI_NFC_BASE + NFC_ECC_CTL); + writel(readl(SUNXI_NFC_BASE + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, + SUNXI_NFC_BASE + NFC_REG_ECC_CTL); if (data_off + conf->ecc_size >= len) break; From 46d5ef0416a68e2e7084c5443cf0c76f44593b1a Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:39 +0100 Subject: [PATCH 04/26] mtd: rawnand: sunxi_spl: cosmetic: use definitions from linux/mtd/rawnand.h Remove unneeded definitions NFC_CMD_R* in sunxi_nand_spl.c No need to define NFC_CMD_RNDOUTSTART, NFC_CMD_RNDOUT and NFC_CMD_READSTART here since they are already in linux/mtd/rawnand.h Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index a4a73dcf8d0..bd6fcd07be5 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -62,10 +62,6 @@ #define NFC_RND_READ_CMD0_OFFSET 8 #define NFC_RND_READ_CMD1_OFFSET 16 -#define NFC_CMD_RNDOUTSTART 0xE0 -#define NFC_CMD_RNDOUT 0x05 -#define NFC_CMD_READSTART 0x30 - struct nfc_config { int page_size; int ecc_strength; @@ -199,9 +195,9 @@ static int nand_load_page(const struct nfc_config *conf, u32 offs) { int page = offs / conf->page_size; - writel((NFC_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | - (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET), + writel((NAND_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | + (NAND_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | + (NAND_CMD_READSTART << NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE + NFC_REG_RCMD_SET); writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_REG_ADDR_LOW); writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_REG_ADDR_HIGH); @@ -215,15 +211,15 @@ static int nand_change_column(u16 column) { int ret; - writel((NFC_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | - (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), + writel((NAND_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | + (NAND_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | + (NAND_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE + NFC_REG_RCMD_SET); writel(column, SUNXI_NFC_BASE + NFC_REG_ADDR_LOW); ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_NORMAL_OP | (1 << NFC_ADR_NUM_OFFSET) | NFC_SEND_ADR | - NFC_CMD_RNDOUT); + NAND_CMD_RNDOUT); if (ret) return ret; From 8034c41d63c985de3bab9980ce81aa70342f64bf Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:40 +0100 Subject: [PATCH 05/26] mtd: rawnand: sunxi: remove usage of struct sunxi_ccm_reg The sunxi_ccm_reg is legacy, drop its usage from nand related code For that, CCU_NAND0_CLK_CFG and CCU_AHB_GATE1 are added to the clock files when missing. And clock code in sunxi_nand{,_spl}.c and board.c are changed to use the new scheme. Moreover, drop AHB_DIV_1 in favor of the more readable CCM_NAND_CTRL_M/N Suggested-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 1 + arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 1 + arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h | 1 + arch/arm/include/asm/arch-sunxi/clock_sun9i.h | 2 ++ board/sunxi/board.c | 11 ++++++----- drivers/mtd/nand/raw/sunxi_nand.c | 11 +++++------ drivers/mtd/nand/raw/sunxi_nand_spl.c | 13 +++++++------ 7 files changed, 23 insertions(+), 17 deletions(-) diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index 00bdd5f938d..caa4b62b3e2 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -11,6 +11,7 @@ #define _SUNXI_CLOCK_SUN4I_H #define CCU_AHB_GATE0 0x60 +#define CCU_NAND0_CLK_CFG 0x80 #define CCU_MMC0_CLK_CFG 0x88 #define CCU_MMC1_CLK_CFG 0x8c #define CCU_MMC2_CLK_CFG 0x90 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 28c3faccbbc..c8f3a16e7d0 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -11,6 +11,7 @@ #define _SUNXI_CLOCK_SUN6I_H #define CCU_AHB_GATE0 0x060 +#define CCU_NAND0_CLK_CFG 0x080 #define CCU_MMC0_CLK_CFG 0x088 #define CCU_MMC1_CLK_CFG 0x08c #define CCU_MMC2_CLK_CFG 0x090 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h b/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h index 5ad2163926a..98c69f47f32 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h @@ -14,6 +14,7 @@ #define _SUNXI_CLOCK_SUN8I_A83T_H #define CCU_AHB_GATE0 0x060 +#define CCU_NAND0_CLK_CFG 0x080 #define CCU_MMC0_CLK_CFG 0x088 #define CCU_MMC1_CLK_CFG 0x08c #define CCU_MMC2_CLK_CFG 0x090 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h index 8d696e533f8..3448f3fb322 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h @@ -12,11 +12,13 @@ #include #endif +#define CCU_NAND0_CLK_CFG 0x400 #define CCU_MMC0_CLK_CFG 0x410 #define CCU_MMC1_CLK_CFG 0x414 #define CCU_MMC2_CLK_CFG 0x418 #define CCU_MMC3_CLK_CFG 0x41c #define CCU_AHB_GATE0 0x580 +#define CCU_AHB_GATE1 0x584 #define CCU_AHB_RESET0_CFG 0x5a0 struct sunxi_ccm_reg { diff --git a/board/sunxi/board.c b/board/sunxi/board.c index e9e3fb9a571..85f20ffe085 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -307,15 +307,16 @@ static void nand_pinmux_setup(void) static void nand_clock_setup(void) { - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + void * const ccm = (void *)SUNXI_CCM_BASE; - setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); + setbits_le32(ccm + CCU_AHB_GATE0, + (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I || \ defined CONFIG_MACH_SUN9I || defined CONFIG_MACH_SUN50I - setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_NAND0)); + setbits_le32(ccm + CCU_AHB_RESET0_CFG, (1 << AHB_GATE_OFFSET_NAND0)); #endif - setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); + setbits_le32(ccm + CCU_NAND0_CLK_CFG, CCM_NAND_CTRL_ENABLE | + CCM_NAND_CTRL_N(0) | CCM_NAND_CTRL_M(1)); } void board_nand_init(void) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index c6b9b2a4eba..ba2740ed187 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -289,8 +289,7 @@ static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) static void sunxi_nfc_set_clk_rate(unsigned long hz) { - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + void * const ccm = (void *)SUNXI_CCM_BASE; int div_m, div_n; div_m = (clock_get_pll6() + hz - 1) / hz; @@ -305,14 +304,14 @@ static void sunxi_nfc_set_clk_rate(unsigned long hz) /* config mod clock */ writel(CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_PLL6 | CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m), - &ccm->nand0_clk_cfg); + ccm + CCU_NAND0_CLK_CFG); /* gate on nand clock */ - setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0)); + setbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_NAND0)); #ifdef CONFIG_MACH_SUN9I - setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); + setbits_le32(ccm + CCU_AHB_GATE1, (1 << AHB_GATE_OFFSET_DMA)); #else - setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); + setbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_DMA)); #endif } diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index bd6fcd07be5..ab569d8086a 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -531,14 +531,15 @@ unsigned int nand_page_size(void) void nand_deselect(void) { - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + void * const ccm = (void *)SUNXI_CCM_BASE; - clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); + clrbits_le32(ccm + CCU_AHB_GATE0, + (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); #ifdef CONFIG_MACH_SUN9I - clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); + clrbits_le32(ccm + CCU_AHB_GATE1, (1 << AHB_GATE_OFFSET_DMA)); #else - clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); + clrbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_DMA)); #endif - clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); + clrbits_le32(ccm + CCU_NAND0_CLK_CFG, CCM_NAND_CTRL_ENABLE | + CCM_NAND_CTRL_N(0) | CCM_NAND_CTRL_M(1)); } From dadf8a8decf33b4440f2387267062278dc764f0c Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:41 +0100 Subject: [PATCH 06/26] mtd: rawnand: sunxi: merge register definitions for sunxi_nand{, _spl}.c Merge common register definitions from sunxi_nand{,_spl}.c The Allwinner NAND controller registers where in both files, so let's just merge all that in a header, it will be easier for maintenance. NB: the defines are also harmonized with Linux driver No functional change Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 123 +------------------- drivers/mtd/nand/raw/sunxi_nand.h | 156 ++++++++++++++++++++++++++ drivers/mtd/nand/raw/sunxi_nand_spl.c | 45 +------- 3 files changed, 158 insertions(+), 166 deletions(-) create mode 100644 drivers/mtd/nand/raw/sunxi_nand.h diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index ba2740ed187..20028d539ac 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -46,128 +46,7 @@ #include #include -#define NFC_REG_CTL 0x0000 -#define NFC_REG_ST 0x0004 -#define NFC_REG_INT 0x0008 -#define NFC_REG_TIMING_CTL 0x000C -#define NFC_REG_TIMING_CFG 0x0010 -#define NFC_REG_ADDR_LOW 0x0014 -#define NFC_REG_ADDR_HIGH 0x0018 -#define NFC_REG_SECTOR_NUM 0x001C -#define NFC_REG_CNT 0x0020 -#define NFC_REG_CMD 0x0024 -#define NFC_REG_RCMD_SET 0x0028 -#define NFC_REG_WCMD_SET 0x002C -#define NFC_REG_IO_DATA 0x0030 -#define NFC_REG_ECC_CTL 0x0034 -#define NFC_REG_ECC_ST 0x0038 -#define NFC_REG_DEBUG 0x003C -#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) -#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) -#define NFC_REG_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID 0x00A4 -#define NFC_RAM0_BASE 0x0400 -#define NFC_RAM1_BASE 0x0800 - -/* define bit use in NFC_CTL */ -#define NFC_EN BIT(0) -#define NFC_RESET BIT(1) -#define NFC_BUS_WIDTH_MSK BIT(2) -#define NFC_BUS_WIDTH_8 (0 << 2) -#define NFC_BUS_WIDTH_16 (1 << 2) -#define NFC_RB_SEL_MSK BIT(3) -#define NFC_RB_SEL(x) ((x) << 3) -#define NFC_CE_SEL_MSK (0x7 << 24) -#define NFC_CE_SEL(x) ((x) << 24) -#define NFC_CE_CTL BIT(6) -#define NFC_PAGE_SHIFT_MSK (0xf << 8) -#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) -#define NFC_SAM BIT(12) -#define NFC_RAM_METHOD BIT(14) -#define NFC_DEBUG_CTL BIT(31) - -/* define bit use in NFC_ST */ -#define NFC_RB_B2R BIT(0) -#define NFC_CMD_INT_FLAG BIT(1) -#define NFC_DMA_INT_FLAG BIT(2) -#define NFC_CMD_FIFO_STATUS BIT(3) -#define NFC_STA BIT(4) -#define NFC_NATCH_INT_FLAG BIT(5) -#define NFC_RB_STATE(x) BIT(x + 8) - -/* define bit use in NFC_INT */ -#define NFC_B2R_INT_ENABLE BIT(0) -#define NFC_CMD_INT_ENABLE BIT(1) -#define NFC_DMA_INT_ENABLE BIT(2) -#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ - NFC_CMD_INT_ENABLE | \ - NFC_DMA_INT_ENABLE) - -/* define bit use in NFC_TIMING_CTL */ -#define NFC_TIMING_CTL_EDO BIT(8) - -/* define NFC_TIMING_CFG register layout */ -#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ - (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ - (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ - (((tCAD) & 0x7) << 8)) - -/* define bit use in NFC_CMD */ -#define NFC_CMD_LOW_BYTE_MSK 0xff -#define NFC_CMD_HIGH_BYTE_MSK (0xff << 8) -#define NFC_CMD(x) (x) -#define NFC_ADR_NUM_MSK (0x7 << 16) -#define NFC_ADR_NUM(x) (((x) - 1) << 16) -#define NFC_SEND_ADR BIT(19) -#define NFC_ACCESS_DIR BIT(20) -#define NFC_DATA_TRANS BIT(21) -#define NFC_SEND_CMD1 BIT(22) -#define NFC_WAIT_FLAG BIT(23) -#define NFC_SEND_CMD2 BIT(24) -#define NFC_SEQ BIT(25) -#define NFC_DATA_SWAP_METHOD BIT(26) -#define NFC_ROW_AUTO_INC BIT(27) -#define NFC_SEND_CMD3 BIT(28) -#define NFC_SEND_CMD4 BIT(29) -#define NFC_CMD_TYPE_MSK (0x3 << 30) -#define NFC_NORMAL_OP (0 << 30) -#define NFC_ECC_OP (1 << 30) -#define NFC_PAGE_OP (2 << 30) - -/* define bit use in NFC_RCMD_SET */ -#define NFC_READ_CMD_MSK 0xff -#define NFC_RND_READ_CMD0_MSK (0xff << 8) -#define NFC_RND_READ_CMD1_MSK (0xff << 16) - -/* define bit use in NFC_WCMD_SET */ -#define NFC_PROGRAM_CMD_MSK 0xff -#define NFC_RND_WRITE_CMD_MSK (0xff << 8) -#define NFC_READ_CMD0_MSK (0xff << 16) -#define NFC_READ_CMD1_MSK (0xff << 24) - -/* define bit use in NFC_ECC_CTL */ -#define NFC_ECC_EN BIT(0) -#define NFC_ECC_PIPELINE BIT(3) -#define NFC_ECC_EXCEPTION BIT(4) -#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) -#define NFC_ECC_BLOCK_512 (1 << 5) -#define NFC_RANDOM_EN BIT(9) -#define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_MSK (0xf << 12) -#define NFC_ECC_MODE(x) ((x) << 12) -#define NFC_RANDOM_SEED_MSK (0x7fff << 16) -#define NFC_RANDOM_SEED(x) ((x) << 16) - -/* define bit use in NFC_ECC_ST */ -#define NFC_ECC_ERR(x) BIT(x) -#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) -#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) - -#define NFC_DEFAULT_TIMEOUT_MS 1000 - -#define NFC_SRAM_SIZE 1024 - -#define NFC_MAX_CS 7 +#include "sunxi_nand.h" /* * Ready/Busy detection type: describes the Ready/Busy detection modes diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h new file mode 100644 index 00000000000..59803ccc9f2 --- /dev/null +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Bootlin, Richard GENOUD + * + * merged defines from sunxi_nand{,_spl}.c + * Containing the following copyrights: + * Copyright (C) 2013 Boris BREZILLON + * Copyright (C) 2015 Roy Spliet + * Copyright (c) 2014-2015, Antmicro Ltd + * Copyright (c) 2015, AW-SOM Technologies + * Derived from: + * https://github.com/yuq/sunxi-nfc-mtd + * Copyright (C) 2013 Qiang Yu + * + * https://github.com/hno/Allwinner-Info + * Copyright (C) 2013 Henrik Nordström + * + * Copyright (C) 2013 Dmitriy B. + * Copyright (C) 2013 Sergey Lapin + * + */ + +#ifndef SUNXI_NAND_H +#define SUNXI_NAND_H + +#include + +#define NFC_REG_CTL 0x0000 +#define NFC_REG_ST 0x0004 +#define NFC_REG_INT 0x0008 +#define NFC_REG_TIMING_CTL 0x000C +#define NFC_REG_TIMING_CFG 0x0010 +#define NFC_REG_ADDR_LOW 0x0014 +#define NFC_REG_ADDR_HIGH 0x0018 +#define NFC_REG_SECTOR_NUM 0x001C +#define NFC_REG_CNT 0x0020 +#define NFC_REG_CMD 0x0024 +#define NFC_REG_RCMD_SET 0x0028 +#define NFC_REG_WCMD_SET 0x002C +#define NFC_REG_IO_DATA 0x0030 +#define NFC_REG_ECC_CTL 0x0034 +#define NFC_REG_ECC_ST 0x0038 +#define NFC_REG_DEBUG 0x003C +#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) +#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) +#define NFC_REG_SPARE_AREA 0x00A0 +#define NFC_REG_PAT_ID 0x00A4 +#define NFC_RAM0_BASE 0x0400 +#define NFC_RAM1_BASE 0x0800 + +/* define bit use in NFC_CTL */ +#define NFC_EN BIT(0) +#define NFC_RESET BIT(1) +#define NFC_BUS_WIDTH_MSK BIT(2) +#define NFC_BUS_WIDTH_8 (0 << 2) +#define NFC_BUS_WIDTH_16 (1 << 2) +#define NFC_RB_SEL_MSK BIT(3) +#define NFC_RB_SEL(x) ((x) << 3) +#define NFC_CE_SEL_MSK (0x7 << 24) +#define NFC_CE_SEL(x) ((x) << 24) +#define NFC_CE_CTL BIT(6) +#define NFC_PAGE_SHIFT_MSK (0xf << 8) +#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) +#define NFC_PAGE_SIZE(a) ((fls(a) - 11) << 8) +#define NFC_SAM BIT(12) +#define NFC_RAM_METHOD BIT(14) +#define NFC_DEBUG_CTL BIT(31) + +/* define bit use in NFC_ST */ +#define NFC_RB_B2R BIT(0) +#define NFC_CMD_INT_FLAG BIT(1) +#define NFC_DMA_INT_FLAG BIT(2) +#define NFC_CMD_FIFO_STATUS BIT(3) +#define NFC_STA BIT(4) +#define NFC_NATCH_INT_FLAG BIT(5) +#define NFC_RB_STATE(x) BIT((x) + 8) + +/* define bit use in NFC_INT */ +#define NFC_B2R_INT_ENABLE BIT(0) +#define NFC_CMD_INT_ENABLE BIT(1) +#define NFC_DMA_INT_ENABLE BIT(2) +#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ + NFC_CMD_INT_ENABLE | \ + NFC_DMA_INT_ENABLE) + +/* define bit use in NFC_TIMING_CTL */ +#define NFC_TIMING_CTL_EDO BIT(8) + +/* define NFC_TIMING_CFG register layout */ +#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ + (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ + (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ + (((tCAD) & 0x7) << 8)) + +/* define bit use in NFC_CMD */ +#define NFC_CMD_LOW_BYTE_MSK 0xff +#define NFC_CMD_HIGH_BYTE_MSK (0xff << 8) +#define NFC_CMD(x) (x) +#define NFC_ADR_NUM_OFFSET 16 +#define NFC_ADR_NUM_MSK (0x7 << NFC_ADR_NUM_OFFSET) +#define NFC_ADR_NUM(x) (((x) - 1) << NFC_ADR_NUM_OFFSET) +#define NFC_SEND_ADR BIT(19) +#define NFC_ACCESS_DIR BIT(20) +#define NFC_DATA_TRANS BIT(21) +#define NFC_SEND_CMD1 BIT(22) +#define NFC_WAIT_FLAG BIT(23) +#define NFC_SEND_CMD2 BIT(24) +#define NFC_SEQ BIT(25) +#define NFC_DATA_SWAP_METHOD BIT(26) +#define NFC_ROW_AUTO_INC BIT(27) +#define NFC_SEND_CMD3 BIT(28) +#define NFC_SEND_CMD4 BIT(29) +#define NFC_CMD_TYPE_MSK (0x3 << 30) +#define NFC_NORMAL_OP (0 << 30) +#define NFC_ECC_OP (1 << 30) +#define NFC_PAGE_OP (2 << 30) + +/* define bit use in NFC_RCMD_SET */ +#define NFC_READ_CMD_OFFSET 0 +#define NFC_READ_CMD_MSK (0xff << NFC_READ_CMD_OFFSET) +#define NFC_RND_READ_CMD0_OFFSET 8 +#define NFC_RND_READ_CMD0_MSK (0xff << NFC_RND_READ_CMD0_OFFSET) +#define NFC_RND_READ_CMD1_OFFSET 16 +#define NFC_RND_READ_CMD1_MSK (0xff << NFC_RND_READ_CMD1_OFFSET) + +/* define bit use in NFC_WCMD_SET */ +#define NFC_PROGRAM_CMD_MSK 0xff +#define NFC_RND_WRITE_CMD_MSK (0xff << 8) +#define NFC_READ_CMD0_MSK (0xff << 16) +#define NFC_READ_CMD1_MSK (0xff << 24) + +/* define bit use in NFC_ECC_CTL */ +#define NFC_ECC_EN BIT(0) +#define NFC_ECC_PIPELINE BIT(3) +#define NFC_ECC_EXCEPTION BIT(4) +#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) +#define NFC_ECC_BLOCK_512 BIT(5) +#define NFC_RANDOM_EN BIT(9) +#define NFC_RANDOM_DIRECTION BIT(10) +#define NFC_ECC_MODE_MSK (0xf << 12) +#define NFC_ECC_MODE(x) ((x) << 12) +#define NFC_RANDOM_SEED_MSK (0x7fff << 16) +#define NFC_RANDOM_SEED(x) ((x) << 16) + +/* define bit use in NFC_ECC_ST */ +#define NFC_ECC_ERR(x) BIT(x) +#define NFC_ECC_PAT_FOUND(x) BIT((x) + 16) +#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) + +#define NFC_DEFAULT_TIMEOUT_MS 1000 + +#define NFC_SRAM_SIZE 1024 + +#define NFC_MAX_CS 7 + +#endif diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index ab569d8086a..909c846e020 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -13,50 +13,7 @@ #include #include -/* registers */ -#define NFC_REG_CTL 0x00000000 -#define NFC_REG_ST 0x00000004 -#define NFC_REG_ADDR_LOW 0x00000014 -#define NFC_REG_ADDR_HIGH 0x00000018 -#define NFC_REG_CNT 0x00000020 -#define NFC_REG_CMD 0x00000024 -#define NFC_REG_RCMD_SET 0x00000028 -#define NFC_REG_ECC_CTL 0x00000034 -#define NFC_REG_ECC_ST 0x00000038 -#define NFC_REG_SPARE_AREA 0x000000A0 -#define NFC_RAM0_BASE 0x00000400 -#define NFC_RAM1_BASE 0x00000800 - -#define NFC_EN (1 << 0) -#define NFC_RESET (1 << 1) -#define NFC_PAGE_SHIFT_MSK (0xf << 8) -#define NFC_PAGE_SIZE(a) ((fls(a) - 11) << 8) - -#define NFC_ECC_EN (1 << 0) -#define NFC_ECC_EXCEPTION (1 << 4) -#define NFC_ECC_BLOCK_512 (1 << 5) -#define NFC_RANDOM_EN (1 << 9) -#define NFC_RANDOM_DIRECTION (1 << 10) - -#define NFC_ADR_NUM_OFFSET 16 -#define NFC_SEND_ADR (1 << 19) -#define NFC_ACCESS_DIR (1 << 20) -#define NFC_DATA_TRANS (1 << 21) -#define NFC_SEND_CMD1 (1 << 22) -#define NFC_WAIT_FLAG (1 << 23) -#define NFC_SEND_CMD2 (1 << 24) -#define NFC_SEQ (1 << 25) -#define NFC_DATA_SWAP_METHOD (1 << 26) -#define NFC_ROW_AUTO_INC (1 << 27) -#define NFC_SEND_CMD3 (1 << 28) -#define NFC_SEND_CMD4 (1 << 29) -#define NFC_NORMAL_OP (0 << 30) -#define NFC_ECC_OP (1 << 30) -#define NFC_PAGE_OP (2 << 30) - -#define NFC_CMD_INT_FLAG (1 << 1) -#define NFC_DMA_INT_FLAG (1 << 2) -#define NFC_CMD_FIFO_STATUS (1 << 3) +#include "sunxi_nand.h" #define NFC_READ_CMD_OFFSET 0 #define NFC_RND_READ_CMD0_OFFSET 8 From bd9944c74f6974d9a08665783f3b31c6c7c883d4 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:42 +0100 Subject: [PATCH 07/26] mtd: rawnand: sunxi: add per SoC capabilities Introduce per SoC capabilities in sunxi_nand.c This prepares for the H616 support that has quite a lot differences in registers offset and capabilities. Start with the ECC strength table. No functional change. Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 18 ++++++++++++++++-- drivers/mtd/nand/raw/sunxi_nand.h | 10 ++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 20028d539ac..41262b089cb 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -149,6 +149,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * @clk_rate: NAND controller current clock rate * @chips: a list containing all the NAND chips attached to * this NAND controller + * @caps: NAND Controller capabilities */ struct sunxi_nfc { struct nand_hw_control controller; @@ -159,6 +160,7 @@ struct sunxi_nfc { unsigned long assigned_cs; unsigned long clk_rate; struct list_head chips; + const struct sunxi_nfc_caps *caps; }; static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) @@ -1269,6 +1271,9 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct sunxi_nand_hw_ecc *data; struct nand_ecclayout *layout; int nsectors; @@ -1291,7 +1296,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, } /* Add ECC info retrieval from DT */ - for (i = 0; i < ARRAY_SIZE(strengths); i++) { + for (i = 0; i < nfc->caps->nstrengths; i++) { if (ecc->strength <= strengths[i]) { /* * Update ecc->strength value with the actual strength @@ -1302,7 +1307,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, } } - if (i >= ARRAY_SIZE(strengths)) { + if (i >= nfc->caps->nstrengths) { dev_err(mtd->dev, "unsupported strength\n"); ret = -ENOTSUPP; goto err; @@ -1680,6 +1685,10 @@ static int sunxi_nand_probe(struct udevice *dev) if (!nfc->regs) return -EINVAL; + nfc->caps = (const struct sunxi_nfc_caps *)dev_get_driver_data(dev); + if (!nfc->caps) + return -EINVAL; + ret = reset_get_bulk(dev, &rst_bulk); if (!ret) reset_deassert_bulk(&rst_bulk); @@ -1701,9 +1710,14 @@ static int sunxi_nand_probe(struct udevice *dev) return 0; } +static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { + .nstrengths = 9, +}; + static const struct udevice_id sunxi_nand_ids[] = { { .compatible = "allwinner,sun4i-a10-nand", + .data = (unsigned long)&sunxi_nfc_a10_caps, }, { } }; diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 59803ccc9f2..80fbc8df009 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -153,4 +153,14 @@ #define NFC_MAX_CS 7 +/* + * NAND Controller capabilities structure: stores NAND controller capabilities + * for distinction between compatible strings. + * + * @nstrengths: Number of element of ECC strengths array + */ +struct sunxi_nfc_caps { + unsigned int nstrengths; +}; + #endif From 6124050e53d109ba6e73dfbace3b8252570debac Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:43 +0100 Subject: [PATCH 08/26] mtd: rawnand: sunxi: move ECC_ERR_CNT register offset in SoC caps ECC_ERR_CNT register offset moved in H616, so let's make it a SoC cap Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 3 ++- drivers/mtd/nand/raw/sunxi_nand.h | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 41262b089cb..6699a577958 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -736,7 +736,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, return 1; } - ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); + ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, 0))); memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); @@ -1712,6 +1712,7 @@ static int sunxi_nand_probe(struct udevice *dev) static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .nstrengths = 9, + .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, }; static const struct udevice_id sunxi_nand_ids[] = { diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 80fbc8df009..b961f2ef3bf 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -41,7 +41,8 @@ #define NFC_REG_ECC_CTL 0x0034 #define NFC_REG_ECC_ST 0x0038 #define NFC_REG_DEBUG 0x003C -#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) +#define NFC_REG_A10_ECC_ERR_CNT 0x0040 +#define NFC_REG_ECC_ERR_CNT(nfc, x) (((nfc)->caps->reg_ecc_err_cnt + (x)) & ~0x3) #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 #define NFC_REG_PAT_ID 0x00A4 @@ -158,9 +159,11 @@ * for distinction between compatible strings. * * @nstrengths: Number of element of ECC strengths array + * @reg_ecc_err_cnt: ECC error counter register */ struct sunxi_nfc_caps { unsigned int nstrengths; + unsigned int reg_ecc_err_cnt; }; #endif From eb66861acc8f7efc398a7ed5eec3384c23eab16f Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:44 +0100 Subject: [PATCH 09/26] mtd: rawnand: sunxi: move USER_DATA register offset in SoC caps USER_DATA register offset moved in H616, so let's make it a SoC cap Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 7 ++++--- drivers/mtd/nand/raw/sunxi_nand.h | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 6699a577958..942789569e1 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -766,7 +766,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, * Retrieve the corrected OOB bytes. */ sunxi_nfc_user_data_to_buf(readl(nfc->regs + - NFC_REG_USER_DATA(0)), + NFC_REG_USER_DATA(nfc, 0)), oob); /* De-randomize the Bad Block Marker. */ @@ -837,10 +837,10 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, memcpy(user_data, oob, 4); sunxi_nfc_randomize_bbm(mtd, page, user_data); writel(sunxi_nfc_buf_to_user_data(user_data), - nfc->regs + NFC_REG_USER_DATA(0)); + nfc->regs + NFC_REG_USER_DATA(nfc, 0)); } else { writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(0)); + nfc->regs + NFC_REG_USER_DATA(nfc, 0)); } if (data_off + ecc->size != oob_off) @@ -1713,6 +1713,7 @@ static int sunxi_nand_probe(struct udevice *dev) static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .nstrengths = 9, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, + .reg_user_data = NFC_REG_A10_USER_DATA, }; static const struct udevice_id sunxi_nand_ids[] = { diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index b961f2ef3bf..1977d1bd8ea 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -43,7 +43,8 @@ #define NFC_REG_DEBUG 0x003C #define NFC_REG_A10_ECC_ERR_CNT 0x0040 #define NFC_REG_ECC_ERR_CNT(nfc, x) (((nfc)->caps->reg_ecc_err_cnt + (x)) & ~0x3) -#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) +#define NFC_REG_A10_USER_DATA 0x0050 +#define NFC_REG_USER_DATA(nfc, x) ((nfc)->caps->reg_user_data + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 #define NFC_REG_PAT_ID 0x00A4 #define NFC_RAM0_BASE 0x0400 @@ -160,10 +161,12 @@ * * @nstrengths: Number of element of ECC strengths array * @reg_ecc_err_cnt: ECC error counter register + * @reg_user_data: User data register */ struct sunxi_nfc_caps { unsigned int nstrengths; unsigned int reg_ecc_err_cnt; + unsigned int reg_user_data; }; #endif From 2e6852a8418463f79747d40921a3cc579de3a2ef Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:45 +0100 Subject: [PATCH 10/26] mtd: rawnand: sunxi: move ECC_PAT_FOUND register in SoC caps Move ECC_PAT_FOUND register in SoC capabilities structure This register offset moved in H616, it's now its own register (@0x3c, bits 0-31), not shared with NFC_ECC_ST any more (was @0x38 bits 16-31). Push that specificity in caps structure. Reviewed-by: Andre Przywara Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 9 +++++++-- drivers/mtd/nand/raw/sunxi_nand.h | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 942789569e1..07026ced0af 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -698,6 +698,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; u32 status; + u32 pattern_found; int ret; if (*cur_off != data_off) @@ -723,8 +724,9 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, *cur_off = oob_off + ecc->bytes + 4; - status = readl(nfc->regs + NFC_REG_ECC_ST); - if (status & NFC_ECC_PAT_FOUND(0)) { + pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); + pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); + if (pattern_found & NFC_ECC_PAT_FOUND(0)) { u8 pattern = 0xff; if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) @@ -743,6 +745,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); + status = readl(nfc->regs + NFC_REG_ECC_ST); if (status & NFC_ECC_ERR(0)) { /* * Re-read the data with the randomizer disabled to identify @@ -1714,6 +1717,8 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .nstrengths = 9, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, + .reg_pat_found = NFC_REG_ECC_ST, + .pat_found_mask = GENMASK(31, 16), }; static const struct udevice_id sunxi_nand_ids[] = { diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 1977d1bd8ea..35079d37bb1 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -25,6 +25,9 @@ #include +/* non compile-time field get */ +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) + #define NFC_REG_CTL 0x0000 #define NFC_REG_ST 0x0004 #define NFC_REG_INT 0x0008 @@ -146,7 +149,14 @@ /* define bit use in NFC_ECC_ST */ #define NFC_ECC_ERR(x) BIT(x) -#define NFC_ECC_PAT_FOUND(x) BIT((x) + 16) + +/* + * define bit use in NFC_REG_PAT_FOUND + * For A10/A23, NFC_REG_PAT_FOUND == NFC_ECC_ST register + */ +#define NFC_ECC_PAT_FOUND(x) BIT(x) +#define NFC_ECC_PAT_FOUND_MSK(nfc) ((nfc)->caps->pat_found_mask) + #define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) #define NFC_DEFAULT_TIMEOUT_MS 1000 @@ -162,11 +172,15 @@ * @nstrengths: Number of element of ECC strengths array * @reg_ecc_err_cnt: ECC error counter register * @reg_user_data: User data register + * @reg_pat_found: Data Pattern Status Register + * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register */ struct sunxi_nfc_caps { unsigned int nstrengths; unsigned int reg_ecc_err_cnt; unsigned int reg_user_data; + unsigned int reg_pat_found; + unsigned int pat_found_mask; }; #endif From f5178513a4ccbcf4f22a474ac116b58aa5d39564 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:46 +0100 Subject: [PATCH 11/26] mtd: rawnand: sunxi: add has_ecc_block_512 capability Introduce has_ecc_block_512 capability The H616 controller can't handle 512 bytes ECC block size. The NFC_ECC_BLOCK_512 bit disappeared in H6, and NDFC_RANDOM_EN took its place. So, add has_ecc_block_512 capability to only set this bit on SoC having it. On the way, let's drop NFC_ECC_BLOCK_SIZE_MSK which was just a mask for the very same bit. No functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 14 +++++++++++--- drivers/mtd/nand/raw/sunxi_nand.h | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 07026ced0af..d14a12b6662 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -659,11 +659,12 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) u32 ecc_ctl; ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | - NFC_ECC_BLOCK_SIZE_MSK); + ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE); + if (nfc->caps->has_ecc_block_512) + ecc_ctl &= ~NFC_ECC_BLOCK_512; ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; - if (nand->ecc.size == 512) + if (nand->ecc.size == 512 && nfc->caps->has_ecc_block_512) ecc_ctl |= NFC_ECC_BLOCK_512; writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); @@ -1453,6 +1454,8 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); int ret; if (!ecc->size) { @@ -1463,6 +1466,10 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) if (!ecc->size || !ecc->strength) return -EINVAL; + /* If 512B ECC is not supported, switch to 1024 */ + if (ecc->size == 512 && !nfc->caps->has_ecc_block_512) + ecc->size = 1024; + switch (ecc->mode) { case NAND_ECC_SOFT_BCH: break; @@ -1714,6 +1721,7 @@ static int sunxi_nand_probe(struct udevice *dev) } static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { + .has_ecc_block_512 = true, .nstrengths = 9, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 35079d37bb1..b4a05733c81 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -138,7 +138,6 @@ #define NFC_ECC_EN BIT(0) #define NFC_ECC_PIPELINE BIT(3) #define NFC_ECC_EXCEPTION BIT(4) -#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) #define NFC_ECC_BLOCK_512 BIT(5) #define NFC_RANDOM_EN BIT(9) #define NFC_RANDOM_DIRECTION BIT(10) @@ -169,6 +168,7 @@ * NAND Controller capabilities structure: stores NAND controller capabilities * for distinction between compatible strings. * + * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks * @nstrengths: Number of element of ECC strengths array * @reg_ecc_err_cnt: ECC error counter register * @reg_user_data: User data register @@ -176,6 +176,7 @@ * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register */ struct sunxi_nfc_caps { + bool has_ecc_block_512; unsigned int nstrengths; unsigned int reg_ecc_err_cnt; unsigned int reg_user_data; From b4c005d6222bc21b6c25be04399e14cd97ff9577 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:47 +0100 Subject: [PATCH 12/26] mtd: rawnand: sunxi: move NFC_ECC_MODE offset in SoC caps NFC_ECC_MODE register offset moved in H616, so let's make it a SoC cap No functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 6 ++++-- drivers/mtd/nand/raw/sunxi_nand.h | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index d14a12b6662..c5efafa2414 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -659,10 +659,11 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) u32 ecc_ctl; ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE); + ecc_ctl &= ~(NFC_ECC_MODE_MSK(nfc) | NFC_ECC_PIPELINE); if (nfc->caps->has_ecc_block_512) ecc_ctl &= ~NFC_ECC_BLOCK_512; - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(nfc, data->mode) + | NFC_ECC_EXCEPTION; if (nand->ecc.size == 512 && nfc->caps->has_ecc_block_512) ecc_ctl |= NFC_ECC_BLOCK_512; @@ -1727,6 +1728,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .reg_user_data = NFC_REG_A10_USER_DATA, .reg_pat_found = NFC_REG_ECC_ST, .pat_found_mask = GENMASK(31, 16), + .ecc_mode_mask = GENMASK(15, 12), }; static const struct udevice_id sunxi_nand_ids[] = { diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index b4a05733c81..3b155b0c8f3 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -25,8 +25,9 @@ #include -/* non compile-time field get */ +/* non compile-time field get/prep */ #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) #define NFC_REG_CTL 0x0000 #define NFC_REG_ST 0x0004 @@ -141,8 +142,8 @@ #define NFC_ECC_BLOCK_512 BIT(5) #define NFC_RANDOM_EN BIT(9) #define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_MSK (0xf << 12) -#define NFC_ECC_MODE(x) ((x) << 12) +#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)) #define NFC_RANDOM_SEED_MSK (0x7fff << 16) #define NFC_RANDOM_SEED(x) ((x) << 16) @@ -174,6 +175,7 @@ * @reg_user_data: User data register * @reg_pat_found: Data Pattern Status Register * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register + * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register */ struct sunxi_nfc_caps { bool has_ecc_block_512; @@ -182,6 +184,7 @@ struct sunxi_nfc_caps { unsigned int reg_user_data; unsigned int reg_pat_found; unsigned int pat_found_mask; + unsigned int ecc_mode_mask; }; #endif From 9edd503aadac826a279b2b36cd537cfeb6a8296e Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:48 +0100 Subject: [PATCH 13/26] mtd: rawnand: sunxi: introduce reg_pat_id in sunxi_nfc_caps Introduce NDFC Pattern ID Register in capability structure The H6/H616 pattern ID register is not at the same offset as the A10/A23 one, so move its offset into sunxi_nfc_caps. No functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 3 ++- drivers/mtd/nand/raw/sunxi_nand.h | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index c5efafa2414..c032cabb4dc 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -731,7 +731,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (pattern_found & NFC_ECC_PAT_FOUND(0)) { u8 pattern = 0xff; - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) pattern = 0x0; memset(data, pattern, ecc->size); @@ -1727,6 +1727,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, .reg_pat_found = NFC_REG_ECC_ST, + .reg_pat_id = NFC_REG_A10_PAT_ID, .pat_found_mask = GENMASK(31, 16), .ecc_mode_mask = GENMASK(15, 12), }; diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 3b155b0c8f3..e18feb77c93 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -50,7 +50,8 @@ #define NFC_REG_A10_USER_DATA 0x0050 #define NFC_REG_USER_DATA(nfc, x) ((nfc)->caps->reg_user_data + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID 0x00A4 +#define NFC_REG_PAT_ID(nfc) ((nfc)->caps->reg_pat_id) +#define NFC_REG_A10_PAT_ID 0x00A4 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -173,6 +174,7 @@ * @nstrengths: Number of element of ECC strengths array * @reg_ecc_err_cnt: ECC error counter register * @reg_user_data: User data register + * @reg_pat_id: Pattern ID Register * @reg_pat_found: Data Pattern Status Register * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register @@ -182,6 +184,7 @@ struct sunxi_nfc_caps { unsigned int nstrengths; unsigned int reg_ecc_err_cnt; unsigned int reg_user_data; + unsigned int reg_pat_id; unsigned int reg_pat_found; unsigned int pat_found_mask; unsigned int ecc_mode_mask; From 0b0f13d503e854086da5d0bf5f015d5a5b0e974f Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:49 +0100 Subject: [PATCH 14/26] mtd: rawnand: sunxi_spl: add per SoC capabilities Introduce per SoC capabilities in sunxi_nand_spl.c Prepare for the H616 support that has quite a lot of differences in registers offset and capabilities. Start with the 512 bytes ECC capability. No functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 909c846e020..035b224bb43 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -27,6 +27,7 @@ struct nfc_config { int nseeds; bool randomize; bool valid; + const struct sunxi_nfc_caps *caps; }; /* minimal "boot0" style NAND support for Allwinner A20 */ @@ -51,6 +52,10 @@ const uint16_t random_seed[128] = { 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, }; +__maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { + .has_ecc_block_512 = true, +}; + #define DEFAULT_TIMEOUT_US 100000 static int check_value_inner(int offset, int expected_bits, @@ -220,12 +225,16 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, int data_off = i * conf->ecc_size; int oob_off = conf->page_size + (i * oob_chunk_sz); u8 *data = dest + data_off; + u32 ecc512_bit = 0; + + if (conf->caps->has_ecc_block_512 && conf->ecc_size == 512) + ecc512_bit = NFC_ECC_BLOCK_512; /* Clear ECC status and restart ECC engine */ writel(0, SUNXI_NFC_BASE + NFC_REG_ECC_ST); writel((rand_seed << 16) | (conf->ecc_strength << 12) | (conf->randomize ? NFC_RANDOM_EN : 0) | - (conf->ecc_size == 512 ? NFC_ECC_BLOCK_512 : 0) | + ecc512_bit | NFC_ECC_EN | NFC_ECC_EXCEPTION, SUNXI_NFC_BASE + NFC_REG_ECC_CTL); @@ -389,6 +398,8 @@ static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) if (conf->valid) return 0; + conf->caps = &sunxi_nfc_a10_caps; + /* * Modern NANDs are more likely than legacy ones, so we start testing * with 5 address cycles. From d46bdfe086064934c35e406411e0cf32581604f0 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:50 +0100 Subject: [PATCH 15/26] mtd: rawnand: sunxi: move NFC_RANDOM_EN register offset in SoC caps NFC_RANDOM_{EN,DIRECTION} registers offset moved in H616 Let's make it a SoC capability. NFC_RANDOM_DIRECTION also moved, but it's unused, just remove it. No functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 5 +++-- drivers/mtd/nand/raw/sunxi_nand.h | 5 +++-- drivers/mtd/nand/raw/sunxi_nand_spl.c | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index c032cabb4dc..c35dff75de0 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -608,7 +608,7 @@ static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) if (!(nand->options & NAND_NEED_SCRAMBLING)) return; - writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, + writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN(nfc), nfc->regs + NFC_REG_ECC_CTL); } @@ -620,7 +620,7 @@ static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) if (!(nand->options & NAND_NEED_SCRAMBLING)) return; - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN(nfc), nfc->regs + NFC_REG_ECC_CTL); } @@ -1730,6 +1730,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .reg_pat_id = NFC_REG_A10_PAT_ID, .pat_found_mask = GENMASK(31, 16), .ecc_mode_mask = GENMASK(15, 12), + .random_en_mask = BIT(9), }; static const struct udevice_id sunxi_nand_ids[] = { diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index e18feb77c93..2cbfc5c9c62 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -141,8 +141,7 @@ #define NFC_ECC_PIPELINE BIT(3) #define NFC_ECC_EXCEPTION BIT(4) #define NFC_ECC_BLOCK_512 BIT(5) -#define NFC_RANDOM_EN BIT(9) -#define NFC_RANDOM_DIRECTION BIT(10) +#define NFC_RANDOM_EN(nfc) ((nfc)->caps->random_en_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)) #define NFC_RANDOM_SEED_MSK (0x7fff << 16) @@ -178,6 +177,7 @@ * @reg_pat_found: Data Pattern Status Register * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register + * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register */ struct sunxi_nfc_caps { bool has_ecc_block_512; @@ -188,6 +188,7 @@ struct sunxi_nfc_caps { unsigned int reg_pat_found; unsigned int pat_found_mask; unsigned int ecc_mode_mask; + unsigned int random_en_mask; }; #endif diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 035b224bb43..49ff56fb695 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -54,6 +54,7 @@ const uint16_t random_seed[128] = { __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .has_ecc_block_512 = true, + .random_en_mask = BIT(9), }; #define DEFAULT_TIMEOUT_US 100000 @@ -233,7 +234,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, /* Clear ECC status and restart ECC engine */ writel(0, SUNXI_NFC_BASE + NFC_REG_ECC_ST); writel((rand_seed << 16) | (conf->ecc_strength << 12) | - (conf->randomize ? NFC_RANDOM_EN : 0) | + (conf->randomize ? NFC_RANDOM_EN(conf) : 0) | ecc512_bit | NFC_ECC_EN | NFC_ECC_EXCEPTION, SUNXI_NFC_BASE + NFC_REG_ECC_CTL); From 0a80eb814664fe2613aa0241d3c07cdb587eda00 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:51 +0100 Subject: [PATCH 16/26] mtd: rawnand: sunxi: introduce reg_spare_area in sunxi_nfc_caps Introduce NDFC Spare Area Register offset in SoC capabilities The H6/H616 spare area register is not at the same offset as the A10/A23 one, so move its offset into sunxi_nfc_caps. No functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 3 ++- drivers/mtd/nand/raw/sunxi_nand.h | 7 +++++-- drivers/mtd/nand/raw/sunxi_nand_spl.c | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index c35dff75de0..aabfcdfe637 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -328,7 +328,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) ctl |= NFC_RB_SEL(sel->rb.info.nativeid); } - writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); + writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA(nfc)); if (nfc->clk_rate != sunxi_nand->clk_rate) { sunxi_nfc_set_clk_rate(sunxi_nand->clk_rate); @@ -1727,6 +1727,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, .reg_pat_found = NFC_REG_ECC_ST, + .reg_spare_area = NFC_REG_A10_SPARE_AREA, .reg_pat_id = NFC_REG_A10_PAT_ID, .pat_found_mask = GENMASK(31, 16), .ecc_mode_mask = GENMASK(15, 12), diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 2cbfc5c9c62..b5a3800d33d 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -49,8 +49,9 @@ #define NFC_REG_ECC_ERR_CNT(nfc, x) (((nfc)->caps->reg_ecc_err_cnt + (x)) & ~0x3) #define NFC_REG_A10_USER_DATA 0x0050 #define NFC_REG_USER_DATA(nfc, x) ((nfc)->caps->reg_user_data + ((x) * 4)) -#define NFC_REG_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID(nfc) ((nfc)->caps->reg_pat_id) +#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_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -173,6 +174,7 @@ * @nstrengths: Number of element of ECC strengths array * @reg_ecc_err_cnt: ECC error counter register * @reg_user_data: User data register + * @reg_spare_area: Spare Area Register * @reg_pat_id: Pattern ID Register * @reg_pat_found: Data Pattern Status Register * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register @@ -184,6 +186,7 @@ struct sunxi_nfc_caps { unsigned int nstrengths; unsigned int reg_ecc_err_cnt; unsigned int reg_user_data; + unsigned int reg_spare_area; unsigned int reg_pat_id; unsigned int reg_pat_found; unsigned int pat_found_mask; diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 49ff56fb695..7f9ee14ffc3 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -54,6 +54,7 @@ const uint16_t random_seed[128] = { __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .has_ecc_block_512 = true, + .reg_spare_area = NFC_REG_A10_SPARE_AREA, .random_en_mask = BIT(9), }; @@ -151,7 +152,7 @@ static void nand_apply_config(const struct nfc_config *conf) writel(val | NFC_PAGE_SIZE(conf->page_size), SUNXI_NFC_BASE + NFC_REG_CTL); writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_REG_CNT); - writel(conf->page_size, SUNXI_NFC_BASE + NFC_REG_SPARE_AREA); + writel(conf->page_size, SUNXI_NFC_BASE + NFC_REG_SPARE_AREA(conf)); } static int nand_load_page(const struct nfc_config *conf, u32 offs) From 50b459973c3e747d9e5e4b38acb0702724fd8e68 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:52 +0100 Subject: [PATCH 17/26] mtd: rawnand: sunxi_spl: use NFC_ECC_ERR_MSK and NFC_ECC_PAT_FOUND Use defines instead of hardcoded values for NFC_ECC_{ERR_MSK,PAT_FOUND} SPL is using hard coded values for ECC error detection and empty chunk detection. The H6/H616 registers for that have changed, the pattern found is no more in the NFC_REG_ECC_ST register. So, don't presume anymore that pattern_found is in NFC_REG_ECC_ST, and read the pattern_found register to get this information. Apart from an additional register reading, no functional change. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.h | 3 +++ drivers/mtd/nand/raw/sunxi_nand_spl.c | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index b5a3800d33d..25d18eaa0b8 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -150,6 +150,7 @@ /* define bit use in NFC_ECC_ST */ #define NFC_ECC_ERR(x) BIT(x) +#define NFC_ECC_ERR_MSK(nfc) ((nfc)->caps->ecc_err_mask) /* * define bit use in NFC_REG_PAT_FOUND @@ -177,6 +178,7 @@ * @reg_spare_area: Spare Area Register * @reg_pat_id: Pattern ID Register * @reg_pat_found: Data Pattern Status Register + * @ecc_err_mask: ERR_ERR mask in NFC_ECC_ST register * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register @@ -190,6 +192,7 @@ struct sunxi_nfc_caps { unsigned int reg_pat_id; unsigned int reg_pat_found; unsigned int pat_found_mask; + unsigned int ecc_err_mask; unsigned int ecc_mode_mask; unsigned int random_en_mask; }; diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 7f9ee14ffc3..da94285e6b7 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -55,6 +55,9 @@ const uint16_t random_seed[128] = { __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .has_ecc_block_512 = true, .reg_spare_area = NFC_REG_A10_SPARE_AREA, + .reg_pat_found = NFC_REG_ECC_ST, + .pat_found_mask = GENMASK(31, 16), + .ecc_err_mask = GENMASK(15, 0), .random_en_mask = BIT(9), }; @@ -211,7 +214,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, u16 rand_seed = 0; int oob_chunk_sz = ecc_bytes[conf->ecc_strength]; int page = offs / conf->page_size; - u32 ecc_st; + u32 ecc_st, pattern_found; int i; if (offs % conf->page_size || len % conf->ecc_size || @@ -256,15 +259,21 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, ecc_st = readl(SUNXI_NFC_BASE + NFC_REG_ECC_ST); /* ECC error detected. */ - if (ecc_st & 0xffff) + if (ecc_st & NFC_ECC_ERR_MSK(conf)) return -EIO; /* - * Return 1 if the first chunk is empty (needed for - * configuration detection). + * Return 1 if the first chunk is empty (all 00 or ff) + * (needed for configuration detection). */ - if (!i && (ecc_st & 0x10000)) - return 1; + if (!i) { + pattern_found = readl(SUNXI_NFC_BASE + + conf->caps->reg_pat_found); + pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(conf), + pattern_found); + if (pattern_found & NFC_ECC_PAT_FOUND(0)) + return 1; + } /* Retrieve the data from SRAM */ nand_readlcpy((u32 *)data, From 442eb06c92eba87b63dcd7222088a18e951e024d Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:53 +0100 Subject: [PATCH 18/26] mtd: rawnand: sunxi_spl: increase max_oobsize for 2KiB pages Increase max_oobsize to take into account bigger OOB on 2KiB pages Some NAND chip (e.g. Kioxia TC58NVG1S3HTA00) have a 2KiB page size + 128 bytes OOB. In order to detect them, the max_oobsize has to be increased from 64 to 128 bytes. Tested on Kioxia TC58NVG1S3HTA00 NAND chip on Whatsminer H616 board. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index da94285e6b7..9a6286b015d 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -303,7 +303,7 @@ static int nand_max_ecc_strength(struct nfc_config *conf) */ switch (conf->page_size) { case 2048: - max_oobsize = 64; + max_oobsize = 128; break; case 4096: max_oobsize = 256; From 01c5b0ec50b24cda83340426c8487e72c02c6910 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:54 +0100 Subject: [PATCH 19/26] mtd: rawnand: sunxi_spl: use NFC_ECC_MODE and NFC_RANDOM_SEED macros Use generic macros for ECC_MODE and RANDOM_SEED As H6/H616 registers are different, use more generic macros than hard coded values specific to A10-like SoC. No functional changes. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 9a6286b015d..76458c2063e 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -57,6 +57,7 @@ __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .reg_spare_area = NFC_REG_A10_SPARE_AREA, .reg_pat_found = NFC_REG_ECC_ST, .pat_found_mask = GENMASK(31, 16), + .ecc_mode_mask = GENMASK(15, 12), .ecc_err_mask = GENMASK(15, 0), .random_en_mask = BIT(9), }; @@ -237,7 +238,9 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, /* Clear ECC status and restart ECC engine */ writel(0, SUNXI_NFC_BASE + NFC_REG_ECC_ST); - writel((rand_seed << 16) | (conf->ecc_strength << 12) | + + writel(NFC_RANDOM_SEED(rand_seed) | + NFC_ECC_MODE(conf, conf->ecc_strength) | (conf->randomize ? NFC_RANDOM_EN(conf) : 0) | ecc512_bit | NFC_ECC_EN | NFC_ECC_EXCEPTION, From 25cbc335b47c30f2cc5c08e9610b47bccead3c2a Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:55 +0100 Subject: [PATCH 20/26] sunxi: clock: H6: add NAND controller clock registers Add missing NAND controller-related clock registers The NAND controller on H6/H616 uses one clock for its internal logic (NAND0_CLK) and one clock for ECC engine (NAND1_CLK) in addition to AHB and MBUS clocks. As NAND{0,1}_CLKs and MBUS_GATE are missing, add them. The bit locations are from H616/H6 User Manual. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- .../include/asm/arch-sunxi/clock_sun50i_h6.h | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index 45fa4ab6e57..8d1c7c18548 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -24,7 +24,11 @@ #define CCU_H6_APB2_CFG 0x524 #define CCU_H6_MBUS_CFG 0x540 #define CCU_H6_DRAM_CLK_CFG 0x800 +#define CCU_H6_MBUS_GATE 0x804 #define CCU_H6_DRAM_GATE_RESET 0x80c +#define CCU_NAND0_CLK_CFG 0x810 +#define CCU_NAND1_CLK_CFG 0x814 +#define CCU_H6_NAND_GATE_RESET 0x82c #define CCU_MMC0_CLK_CFG 0x830 #define CCU_MMC1_CLK_CFG 0x834 #define CCU_MMC2_CLK_CFG 0x838 @@ -146,6 +150,16 @@ #define RESET_SHIFT (16) #define GATE_SHIFT (0) +/* MBUS gate offsets */ +#define MBUS_GATE_OFFSET_DI 11 +#define MBUS_GATE_OFFSET_G2D 10 +#define MBUS_GATE_OFFSET_CSI 8 +#define MBUS_GATE_OFFSET_NAND 5 +#define MBUS_GATE_OFFSET_TS0 3 +#define MBUS_GATE_OFFSET_VE 2 +#define MBUS_GATE_OFFSET_CE 1 +#define MBUS_GATE_OFFSET_DMA 0 + /* DRAM clock bit field */ #define DRAM_CLK_ENABLE BIT(31) #define DRAM_MOD_RESET BIT(30) @@ -155,6 +169,16 @@ #define DRAM_CLK_M_MASK (0x1f) #define DRAM_CLK_M(m) (((m)-1) << 0) +/* NAND clock bit field */ +#define CCM_NAND_CTRL_M(x) ((x) - 1) +#define CCM_NAND_CTRL_N(x) ((x) << 8) +#define CCM_NAND_CTRL_OSCM24 (0x0 << 24) +#define CCM_NAND_CTRL_PLL6 (0x1 << 24) +#define CCM_NAND_CTRL_PLL_PERIPH2 (0x2 << 24) +#define CCM_NAND_CTRL_PLL6X2 (0x3 << 24) +#define CCM_NAND_CTRL_PLL_PERIPH2X2 (0x4 << 24) +#define CCM_NAND_CTRL_ENABLE (0x1 << 31) + /* MMC clock bit field */ #define CCM_MMC_CTRL_M(x) ((x) - 1) #define CCM_MMC_CTRL_N(x) ((x) << 8) From 4a611a82e5158d5789cb7bbf6feabaadc9d6b796 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:56 +0100 Subject: [PATCH 21/26] clk: sunxi: Add MBUS Master Clock Gating Register Add MBUS Master Clock Gating Register for H6 and H616 For H6/H616, the NAND controller needs the MBUS NAND clock along with CLK_NAND0/1 and CLK_BUS_NAND. The bit locations are from H6/H616 User Manuals. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/clk/sunxi/clk_h6.c | 2 ++ drivers/clk/sunxi/clk_h616.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c index 1b7bd9dea2f..81deb5728e5 100644 --- a/drivers/clk/sunxi/clk_h6.c +++ b/drivers/clk/sunxi/clk_h6.c @@ -20,6 +20,8 @@ static struct ccu_clk_gate h6_gates[] = { [CLK_DE] = GATE(0x600, BIT(31)), [CLK_BUS_DE] = GATE(0x60c, BIT(0)), + [CLK_MBUS_NAND] = GATE(0x804, BIT(5)), + [CLK_NAND0] = GATE(0x810, BIT(31)), [CLK_NAND1] = GATE(0x814, BIT(31)), [CLK_BUS_NAND] = GATE(0x82c, BIT(0)), diff --git a/drivers/clk/sunxi/clk_h616.c b/drivers/clk/sunxi/clk_h616.c index b1e999e18c1..3e7eea25bfe 100644 --- a/drivers/clk/sunxi/clk_h616.c +++ b/drivers/clk/sunxi/clk_h616.c @@ -19,6 +19,8 @@ static struct ccu_clk_gate h616_gates[] = { [CLK_DE] = GATE(0x600, BIT(31)), [CLK_BUS_DE] = GATE(0x60c, BIT(0)), + [CLK_MBUS_NAND] = GATE(0x804, BIT(5)), + [CLK_NAND0] = GATE(0x810, BIT(31)), [CLK_NAND1] = GATE(0x814, BIT(31)), [CLK_BUS_NAND] = GATE(0x82c, BIT(0)), From f163da5e6d26904e5df509169ccc8cc91a0fb295 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:57 +0100 Subject: [PATCH 22/26] mtd: rawnand: sunxi: add support for H6/H616 nand controller Introduce H6/H616 NAND controller support for U-Boot 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. Introduce the basic support, with ECC and scrambling, but without DMA/MDMA. Tested on Whatsminer H616 board (with and without scrambling, ECC) Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/Kconfig | 3 +- drivers/mtd/nand/raw/sunxi_nand.c | 104 ++++++++++++++++++++++++++++-- drivers/mtd/nand/raw/sunxi_nand.h | 32 ++++++++- 3 files changed, 132 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index e0ff28cb21b..4d197089be4 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -472,7 +472,8 @@ config NAND_SANDBOX config NAND_SUNXI bool "Support for NAND on Allwinner SoCs" default ARCH_SUNXI - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I \ + || MACH_SUN50I_H616 || MACH_SUN50I_H6 select SYS_NAND_SELF_INIT select SYS_NAND_U_BOOT_LOCATIONS select SPL_NAND_SUPPORT diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index aabfcdfe637..e4635644747 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -187,6 +187,14 @@ static void sunxi_nfc_set_clk_rate(unsigned long hz) CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m), ccm + CCU_NAND0_CLK_CFG); +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) + setbits_le32(ccm + CCU_H6_NAND_GATE_RESET, + (1 << GATE_SHIFT) | (1 << RESET_SHIFT)); + setbits_le32(ccm + CCU_H6_MBUS_GATE, (1 << MBUS_GATE_OFFSET_NAND)); + writel(CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_PLL6 | + CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m), + ccm + CCU_NAND1_CLK_CFG); +#else /* gate on nand clock */ setbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_NAND0)); #ifdef CONFIG_MACH_SUN9I @@ -194,6 +202,7 @@ static void sunxi_nfc_set_clk_rate(unsigned long hz) #else setbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_DMA)); #endif +#endif } static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, @@ -688,6 +697,53 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) buf[3] = user_data >> 24; } +/* + * On H6/H616 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 int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *data, int data_off, u8 *oob, int oob_off, @@ -715,6 +771,9 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (ret) return ret; + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, 4, 0); + sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, nfc->regs + NFC_REG_CMD); @@ -855,6 +914,9 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, if (ret) return ret; + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, 4, 0); + sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | NFC_ECC_OP, @@ -1275,7 +1337,6 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nfc *nfc, static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); @@ -1302,12 +1363,12 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, /* Add ECC info retrieval from DT */ for (i = 0; i < nfc->caps->nstrengths; i++) { - if (ecc->strength <= strengths[i]) { + if (ecc->strength <= nfc->caps->ecc_strengths[i]) { /* * Update ecc->strength value with the actual strength * that will be used by the ECC engine. */ - ecc->strength = strengths[i]; + ecc->strength = nfc->caps->ecc_strengths[i]; break; } } @@ -1721,9 +1782,22 @@ static int sunxi_nand_probe(struct udevice *dev) return 0; } +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, - .nstrengths = 9, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), + .ecc_strengths = sunxi_ecc_strengths_a10, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, .reg_pat_found = NFC_REG_ECC_ST, @@ -1732,6 +1806,24 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .pat_found_mask = GENMASK(31, 16), .ecc_mode_mask = GENMASK(15, 12), .random_en_mask = BIT(9), + .max_ecc_steps = 16, +}; + +static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6), + .ecc_strengths = sunxi_ecc_strengths_h6, + .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_pat_found = NFC_REG_H6_PAT_FOUND, + .reg_spare_area = NFC_REG_H6_SPARE_AREA, + .reg_pat_id = NFC_REG_H6_PAT_ID, + .pat_found_mask = GENMASK(31, 0), + .ecc_mode_mask = GENMASK(15, 8), + .random_en_mask = BIT(5), + .user_data_len_tab = sunxi_user_data_len_h6, + .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), + .max_ecc_steps = 32, }; static const struct udevice_id sunxi_nand_ids[] = { @@ -1739,6 +1831,10 @@ static const struct udevice_id sunxi_nand_ids[] = { .compatible = "allwinner,sun4i-a10-nand", .data = (unsigned long)&sunxi_nfc_a10_caps, }, + { + .compatible = "allwinner,sun50i-h616-nand-controller", + .data = (unsigned long)&sunxi_nfc_h616_caps, + }, { } }; diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 25d18eaa0b8..6ee3ea14ee1 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -44,15 +44,26 @@ #define NFC_REG_IO_DATA 0x0030 #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_A10_USER_DATA 0x0050 +#define NFC_REG_H6_USER_DATA 0x0080 +#define NFC_REG_H6_USER_DATA_LEN 0x0070 #define NFC_REG_USER_DATA(nfc, x) ((nfc)->caps->reg_user_data + ((x) * 4)) + +/* 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_H6_SPARE_AREA 0x0114 +#define NFC_REG_PAT_ID(nfc) ((nfc)->caps->reg_pat_id) #define NFC_REG_A10_PAT_ID 0x00A4 +#define NFC_REG_H6_PAT_ID 0x0118 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -161,6 +172,9 @@ #define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 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_SRAM_SIZE 1024 @@ -173,8 +187,10 @@ * * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks * @nstrengths: Number of element of ECC strengths array + * @ecc_strengths: available ECC strengths array * @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 @@ -182,12 +198,21 @@ * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register + * @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 + * @max_ecc_steps: Maximum supported steps for ECC, this is also the + * number of user data registers */ struct sunxi_nfc_caps { bool has_ecc_block_512; unsigned int nstrengths; + const u8 *ecc_strengths; 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; @@ -195,6 +220,9 @@ struct sunxi_nfc_caps { unsigned int ecc_err_mask; unsigned int ecc_mode_mask; unsigned int random_en_mask; + const u8 *user_data_len_tab; + unsigned int nuser_data_tab; + unsigned int max_ecc_steps; }; #endif From 7d1de98011519ebbb128f76f368724b62f3bc6eb Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:58 +0100 Subject: [PATCH 23/26] mtd: rawnand: sunxi_spl: add support for H6/H616 nand controller Introduce H6/H616 NAND controller support for SPL 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. For SPL, most of the work was setting the clocks, adding the new capability structure for H616 and supporting the new USER_DATA_LEN registers. Tested on Whatsminer H616 board (with and without scrambling, ECC) Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- board/sunxi/board.c | 8 +++ drivers/mtd/nand/raw/sunxi_nand_spl.c | 89 ++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 85f20ffe085..954a8715075 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -309,11 +309,19 @@ static void nand_clock_setup(void) { void * const ccm = (void *)SUNXI_CCM_BASE; +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) + setbits_le32(ccm + CCU_H6_NAND_GATE_RESET, + (1 << GATE_SHIFT) | (1 << RESET_SHIFT)); + setbits_le32(ccm + CCU_H6_MBUS_GATE, (1 << MBUS_GATE_OFFSET_NAND)); + setbits_le32(ccm + CCU_NAND1_CLK_CFG, CCM_NAND_CTRL_ENABLE | + CCM_NAND_CTRL_N(0) | CCM_NAND_CTRL_M(1)); +#else setbits_le32(ccm + CCU_AHB_GATE0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I || \ defined CONFIG_MACH_SUN9I || defined CONFIG_MACH_SUN50I setbits_le32(ccm + CCU_AHB_RESET0_CFG, (1 << AHB_GATE_OFFSET_NAND0)); +#endif #endif setbits_le32(ccm + CCU_NAND0_CLK_CFG, CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_N(0) | CCM_NAND_CTRL_M(1)); diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 76458c2063e..c69f704497a 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -52,6 +52,10 @@ const uint16_t random_seed[128] = { 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, }; +static const u8 sunxi_user_data_len_h6[] = { + 0, 4, 8, 12, 16, 20, 24, 28, 32 +}; + __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .has_ecc_block_512 = true, .reg_spare_area = NFC_REG_A10_SPARE_AREA, @@ -62,6 +66,18 @@ __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .random_en_mask = BIT(9), }; +__maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { + .reg_user_data_len = NFC_REG_H6_USER_DATA_LEN, + .reg_spare_area = NFC_REG_H6_SPARE_AREA, + .reg_pat_found = NFC_REG_H6_PAT_FOUND, + .pat_found_mask = GENMASK(31, 0), + .ecc_mode_mask = GENMASK(15, 8), + .ecc_err_mask = GENMASK(31, 0), + .user_data_len_tab = sunxi_user_data_len_h6, + .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), + .random_en_mask = BIT(5), +}; + #define DEFAULT_TIMEOUT_US 100000 static int check_value_inner(int offset, int expected_bits, @@ -197,7 +213,61 @@ static int nand_change_column(u16 column) return 0; } -static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116}; +/* + * On H6/H616 the user_data length has to be set in specific registers + * before writing. + */ +static void sunxi_nfc_reset_user_data_len(const struct nfc_config *nfc) +{ + int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY; + + /* not all SoCs have this register */ + if (!NFC_REG_USER_DATA_LEN(nfc, 0)) + return; + + for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step) + writel(0, SUNXI_NFC_BASE + NFC_REG_USER_DATA_LEN(nfc, i)); +} + +static void sunxi_nfc_set_user_data_len(const struct nfc_config *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) { + printf("Unsupported length for user data reg: %d\n", len); + return; + } + + val = readl(SUNXI_NFC_BASE + 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, SUNXI_NFC_BASE + NFC_REG_USER_DATA_LEN(nfc, step)); +} + +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) +static const int ecc_bytes[] = { + 32, 46, 54, 60, 74, 82, 88, 96, 102, 110, 116, 124, 130, 138, 144 +}; +#else +static const int ecc_bytes[] = { + 32, 46, 54, 60, 74, 88, 102, 110, 116 +}; +#endif static void nand_readlcpy(u32 *dest, u32 * __iomem src, size_t len) { @@ -256,8 +326,11 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, * the data. */ nand_change_column(oob_off); - nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); + sunxi_nfc_reset_user_data_len(conf); + sunxi_nfc_set_user_data_len(conf, 4, 0); + + nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); /* Get the ECC status */ ecc_st = readl(SUNXI_NFC_BASE + NFC_REG_ECC_ST); @@ -412,7 +485,10 @@ static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) if (conf->valid) return 0; - conf->caps = &sunxi_nfc_a10_caps; + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) || IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + conf->caps = &sunxi_nfc_h616_caps; + else + conf->caps = &sunxi_nfc_a10_caps; /* * Modern NANDs are more likely than legacy ones, so we start testing @@ -515,12 +591,19 @@ void nand_deselect(void) { void * const ccm = (void *)SUNXI_CCM_BASE; +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) + clrbits_le32(ccm + CCU_H6_NAND_GATE_RESET, + (1 << GATE_SHIFT) | (1 << RESET_SHIFT)); + clrbits_le32(ccm + CCU_H6_MBUS_GATE, (1 << MBUS_GATE_OFFSET_NAND)); + clrbits_le32(ccm + CCU_NAND1_CLK_CFG, CCM_NAND_CTRL_ENABLE); +#else clrbits_le32(ccm + CCU_AHB_GATE0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); #ifdef CONFIG_MACH_SUN9I clrbits_le32(ccm + CCU_AHB_GATE1, (1 << AHB_GATE_OFFSET_DMA)); #else clrbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_DMA)); +#endif #endif clrbits_le32(ccm + CCU_NAND0_CLK_CFG, CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_N(0) | CCM_NAND_CTRL_M(1)); From e41e5ae4b5e7fa8d9d08c5cdfba793632321dbf6 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:44:59 +0100 Subject: [PATCH 24/26] mtd: rawnand: sunxi_spl: Fix cast to pointer from integer warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a cast to pointer from integer warning on ARM64 On 64bits platform, the casts done in {read,write}l() give that kind of warnings: drivers/mtd/nand/raw/sunxi_nand_spl.c: In function ‘check_value_inner’: ./arch/arm/include/asm/io.h:110:43: warning: cast to pointer from \ integer of different size [-Wint-to-pointer-cast] 110 | #define __raw_readl(a) (*(volatile unsigned int *)(a)) | ^ [...] drivers/mtd/nand/raw/sunxi_nand_spl.c:81:27: note: in expansion of \ macro ‘readl’ 81 | int val = readl(offset) & expected_bits; Introduce {read,write}l_nfc inline function to do the right cast and push the base address (SUNXI_NFC_BASE) into those functions, making the code more readable. Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 97 +++++++++++++++------------ 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index c69f704497a..67f7d22ed2c 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -80,11 +80,25 @@ __maybe_unused static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { #define DEFAULT_TIMEOUT_US 100000 +static inline u32 readl_nfc(u32 offset) +{ + void * __iomem base = (void *)(uintptr_t)SUNXI_NFC_BASE; + + return readl(base + offset); +} + +static inline u32 writel_nfc(u32 val, u32 offset) +{ + void * __iomem base = (void *)(uintptr_t)SUNXI_NFC_BASE; + + return writel(val, base + offset); +} + static int check_value_inner(int offset, int expected_bits, int timeout_us, int negation) { do { - int val = readl(offset) & expected_bits; + int val = readl_nfc(offset) & expected_bits; if (negation ? !val : val) return 1; udelay(1); @@ -107,8 +121,8 @@ static inline int check_value_negated(int offset, int unexpected_bits, static int nand_wait_cmd_fifo_empty(void) { - if (!check_value_negated(SUNXI_NFC_BASE + NFC_REG_ST, - NFC_CMD_FIFO_STATUS, DEFAULT_TIMEOUT_US)) { + if (!check_value_negated(NFC_REG_ST, NFC_CMD_FIFO_STATUS, + DEFAULT_TIMEOUT_US)) { printf("nand: timeout waiting for empty cmd FIFO\n"); return -ETIMEDOUT; } @@ -118,7 +132,7 @@ static int nand_wait_cmd_fifo_empty(void) static int nand_wait_int(void) { - if (!check_value(SUNXI_NFC_BASE + NFC_REG_ST, NFC_CMD_INT_FLAG, + if (!check_value(NFC_REG_ST, NFC_CMD_INT_FLAG, DEFAULT_TIMEOUT_US)) { printf("nand: timeout waiting for interruption\n"); return -ETIMEDOUT; @@ -135,8 +149,8 @@ static int nand_exec_cmd(u32 cmd) if (ret) return ret; - writel(NFC_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_REG_ST); - writel(cmd, SUNXI_NFC_BASE + NFC_REG_CMD); + writel_nfc(NFC_CMD_INT_FLAG, NFC_REG_ST); + writel_nfc(cmd, NFC_REG_CMD); return nand_wait_int(); } @@ -147,15 +161,12 @@ void nand_init(void) board_nand_init(); - val = readl(SUNXI_NFC_BASE + NFC_REG_CTL); + val = readl_nfc(NFC_REG_CTL); /* enable and reset CTL */ - writel(val | NFC_EN | NFC_RESET, - SUNXI_NFC_BASE + NFC_REG_CTL); + writel_nfc(val | NFC_EN | NFC_RESET, NFC_REG_CTL); - if (!check_value_negated(SUNXI_NFC_BASE + NFC_REG_CTL, - NFC_RESET, DEFAULT_TIMEOUT_US)) { + if (!check_value_negated(NFC_REG_CTL, NFC_RESET, DEFAULT_TIMEOUT_US)) printf("Couldn't initialize nand\n"); - } /* reset NAND */ nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET); @@ -167,24 +178,23 @@ static void nand_apply_config(const struct nfc_config *conf) nand_wait_cmd_fifo_empty(); - val = readl(SUNXI_NFC_BASE + NFC_REG_CTL); + val = readl_nfc(NFC_REG_CTL); val &= ~NFC_PAGE_SHIFT_MSK; - writel(val | NFC_PAGE_SIZE(conf->page_size), - SUNXI_NFC_BASE + NFC_REG_CTL); - writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_REG_CNT); - writel(conf->page_size, SUNXI_NFC_BASE + NFC_REG_SPARE_AREA(conf)); + writel_nfc(val | NFC_PAGE_SIZE(conf->page_size), NFC_REG_CTL); + writel_nfc(conf->ecc_size, NFC_REG_CNT); + writel_nfc(conf->page_size, NFC_REG_SPARE_AREA(conf)); } static int nand_load_page(const struct nfc_config *conf, u32 offs) { int page = offs / conf->page_size; - writel((NAND_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | - (NAND_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | - (NAND_CMD_READSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_REG_RCMD_SET); - writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_REG_ADDR_LOW); - writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_REG_ADDR_HIGH); + writel_nfc((NAND_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | + (NAND_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | + (NAND_CMD_READSTART << NFC_READ_CMD_OFFSET), + NFC_REG_RCMD_SET); + writel_nfc(((page & 0xFFFF) << 16), NFC_REG_ADDR_LOW); + writel_nfc((page >> 16) & 0xFF, NFC_REG_ADDR_HIGH); return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_NORMAL_OP | NFC_SEND_ADR | NFC_WAIT_FLAG | @@ -195,11 +205,11 @@ static int nand_change_column(u16 column) { int ret; - writel((NAND_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | - (NAND_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | - (NAND_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_REG_RCMD_SET); - writel(column, SUNXI_NFC_BASE + NFC_REG_ADDR_LOW); + writel_nfc((NAND_CMD_RNDOUTSTART << NFC_RND_READ_CMD1_OFFSET) | + (NAND_CMD_RNDOUT << NFC_RND_READ_CMD0_OFFSET) | + (NAND_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), + NFC_REG_RCMD_SET); + writel_nfc(column, NFC_REG_ADDR_LOW); ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_NORMAL_OP | (1 << NFC_ADR_NUM_OFFSET) | NFC_SEND_ADR | @@ -226,7 +236,7 @@ static void sunxi_nfc_reset_user_data_len(const struct nfc_config *nfc) return; for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step) - writel(0, SUNXI_NFC_BASE + NFC_REG_USER_DATA_LEN(nfc, i)); + writel_nfc(0, NFC_REG_USER_DATA_LEN(nfc, i)); } static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc, @@ -252,11 +262,11 @@ static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc, return; } - val = readl(SUNXI_NFC_BASE + NFC_REG_USER_DATA_LEN(nfc, step)); + val = readl_nfc(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, SUNXI_NFC_BASE + NFC_REG_USER_DATA_LEN(nfc, step)); + writel_nfc(val, NFC_REG_USER_DATA_LEN(nfc, step)); } #if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) @@ -307,18 +317,18 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, ecc512_bit = NFC_ECC_BLOCK_512; /* Clear ECC status and restart ECC engine */ - writel(0, SUNXI_NFC_BASE + NFC_REG_ECC_ST); + writel_nfc(0, NFC_REG_ECC_ST); - writel(NFC_RANDOM_SEED(rand_seed) | - NFC_ECC_MODE(conf, conf->ecc_strength) | - (conf->randomize ? NFC_RANDOM_EN(conf) : 0) | - ecc512_bit | - NFC_ECC_EN | NFC_ECC_EXCEPTION, - SUNXI_NFC_BASE + NFC_REG_ECC_CTL); + writel_nfc(NFC_RANDOM_SEED(rand_seed) | + NFC_ECC_MODE(conf, conf->ecc_strength) | + (conf->randomize ? NFC_RANDOM_EN(conf) : 0) | + ecc512_bit | + NFC_ECC_EN | NFC_ECC_EXCEPTION, + NFC_REG_ECC_CTL); /* Move the data in SRAM */ nand_change_column(data_off); - writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_REG_CNT); + writel_nfc(conf->ecc_size, NFC_REG_CNT); nand_exec_cmd(NFC_DATA_TRANS); /* @@ -332,7 +342,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); /* Get the ECC status */ - ecc_st = readl(SUNXI_NFC_BASE + NFC_REG_ECC_ST); + ecc_st = readl_nfc(NFC_REG_ECC_ST); /* ECC error detected. */ if (ecc_st & NFC_ECC_ERR_MSK(conf)) @@ -343,8 +353,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, * (needed for configuration detection). */ if (!i) { - pattern_found = readl(SUNXI_NFC_BASE + - conf->caps->reg_pat_found); + pattern_found = readl_nfc(conf->caps->reg_pat_found); pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(conf), pattern_found); if (pattern_found & NFC_ECC_PAT_FOUND(0)) @@ -357,8 +366,8 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, conf->ecc_size); /* Stop the ECC engine */ - writel(readl(SUNXI_NFC_BASE + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, - SUNXI_NFC_BASE + NFC_REG_ECC_CTL); + writel_nfc(readl_nfc(NFC_REG_ECC_CTL) & ~NFC_ECC_EN, + NFC_REG_ECC_CTL); if (data_off + conf->ecc_size >= len) break; From bd22b7e5d1b0b08de873ce55a2237e22049ddd53 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:45:00 +0100 Subject: [PATCH 25/26] mtd: rawnand: sunxi: fix page size in control register The MACRO NFC_PAGE_SHIFT(x) already deals with removing 10 from nand->page_shift, so it shouldn't be done twice. Fixes: 4ccae81cdadc ("mtd: nand: Add the sunxi NAND controller driver") Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index e4635644747..ef27a4b7a36 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -328,7 +328,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) sel = &sunxi_nand->sels[chip]; ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | - NFC_PAGE_SHIFT(nand->page_shift - 10); + NFC_PAGE_SHIFT(nand->page_shift); if (sel->rb.type == RB_NONE) { nand->dev_ready = NULL; } else { From 800ebf7e94e3a7e375cf915ae21e14514fd81450 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 23 Jan 2026 12:45:01 +0100 Subject: [PATCH 26/26] tools: sunxi-spl-image-builder: support H6/H616 NAND boot The H6/H616 boot ROM doesn't expect a SPL scrambled the same way as older SoCs. It doesn't use a specific seeds table, it expects a maximized ECC (BCH-80), a specific BBM (FF000301) and doesn't work if empty pages are skipped (it needs its specific BBM, even in the padding). So, add a --soc=h6 option to support H6/616 with: - more ECC strengths - specific BBM - default_scrambler_seeds[] with all values - no empty pages skip In Kconfig, select BCH-80 by default for SUNXI_SPL_ECC_STRENGTH to make BROM happy. And in scripts/Makefile.xpl, use --soc=h6 option when building for a SUN50I_GEN_H6 SoC. Tested on Whatsminer H616 board, booting from NAND. Reviewed-by: Miquel Raynal Co-developed-by: James Hilliard Signed-off-by: James Hilliard Signed-off-by: Richard Genoud Signed-off-by: Michael Trimarchi --- drivers/mtd/nand/raw/Kconfig | 1 + scripts/Makefile.xpl | 1 + tools/sunxi-spl-image-builder.c | 92 ++++++++++++++++++++++++++------- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 4d197089be4..306175873fa 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -489,6 +489,7 @@ if NAND_SUNXI config NAND_SUNXI_SPL_ECC_STRENGTH int "Allwinner NAND SPL ECC Strength" + default 80 if SUN50I_GEN_H6 default 64 config NAND_SUNXI_SPL_ECC_SIZE diff --git a/scripts/Makefile.xpl b/scripts/Makefile.xpl index 5e65d7b2498..55aeac1038e 100644 --- a/scripts/Makefile.xpl +++ b/scripts/Makefile.xpl @@ -457,6 +457,7 @@ $(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin FORCE quiet_cmd_sunxi_spl_image_builder = SUNXI_SPL_IMAGE_BUILDER $@ cmd_sunxi_spl_image_builder = $(objtree)/tools/sunxi-spl-image-builder \ + $(if $(CONFIG_SUN50I_GEN_H6),--soc=h6) \ -c $(CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH)/$(CONFIG_NAND_SUNXI_SPL_ECC_SIZE) \ -p $(CONFIG_SYS_NAND_PAGE_SIZE) \ -o $(CONFIG_SYS_NAND_OOBSIZE) \ diff --git a/tools/sunxi-spl-image-builder.c b/tools/sunxi-spl-image-builder.c index a367f117740..8a8ab124a7e 100644 --- a/tools/sunxi-spl-image-builder.c +++ b/tools/sunxi-spl-image-builder.c @@ -11,6 +11,7 @@ #include #include +#include #include #define BCH_PRIMITIVE_POLY 0x5803 @@ -27,6 +28,7 @@ struct image_info { int eraseblock_size; int scramble; int boot0; + int h6; off_t offset; const char *source; const char *dest; @@ -84,18 +86,29 @@ static void scramble(const struct image_info *info, uint16_t state; int i; - /* Boot0 is always scrambled no matter the command line option. */ - if (info->boot0) { + /* + * Bail out earlier if the user didn't ask for scrambling. + * But Boot0 is always scrambled no matter the command line option. + */ + if (!info->boot0 && !info->scramble) + return; + + /* + * On H6, the BROM scrambler seed is no different than the default one + */ + if (info->boot0 && !info->h6) { state = brom_scrambler_seeds[0]; } else { - unsigned seedmod = info->eraseblock_size / info->page_size; + unsigned int seedmod; - /* Bail out earlier if the user didn't ask for scrambling. */ - if (!info->scramble) - return; - - if (seedmod > ARRAY_SIZE(default_scrambler_seeds)) + if (info->h6) { + /* H6 boot0 uses all 128 seeds */ seedmod = ARRAY_SIZE(default_scrambler_seeds); + } else { + seedmod = info->eraseblock_size / info->page_size; + if (seedmod > ARRAY_SIZE(default_scrambler_seeds)) + seedmod = ARRAY_SIZE(default_scrambler_seeds); + } state = default_scrambler_seeds[page % seedmod]; } @@ -137,14 +150,19 @@ static int write_page(const struct image_info *info, uint8_t *buffer, fwrite(buffer, info->page_size + info->oob_size, 1, dst); - for (i = 0; i < info->usable_page_size; i++) { - if (buffer[i] != 0xff) - break; - } + /* + * H6 BROM doesn't support empty pages + */ + if (!info->h6) { + for (i = 0; i < info->usable_page_size; i++) { + if (buffer[i] != 0xff) + break; + } - /* We leave empty pages at 0xff. */ - if (i == info->usable_page_size) - return 0; + /* We leave empty pages at 0xff. */ + if (i == info->usable_page_size) + return 0; + } /* Restore the source pointer to read it again. */ fseek(src, -cnt, SEEK_CUR); @@ -212,6 +230,14 @@ static int write_page(const struct image_info *info, uint8_t *buffer, } memset(ecc, 0, eccbytes); + + if (info->h6) { + /* BBM taken from vendor code: FF 00 03 01 */ + buffer[info->ecc_step_size + 1] = 0; + buffer[info->ecc_step_size + 2] = 3; // NAND_VERSION_0 + buffer[info->ecc_step_size + 3] = 1; // NAND_VERSION_1 + } + swap_bits(buffer, info->ecc_step_size + 4); encode_bch(bch, buffer, info->ecc_step_size + 4, ecc); swap_bits(buffer, info->ecc_step_size + 4); @@ -303,6 +329,8 @@ static void display_help(int status) "-e --eraseblock= Erase block size\n" "-b --boot0 Build a boot0 image.\n" "-s --scramble Scramble data\n" + "-t --soc= Build an image compatible with SoC type \n" + " (possible values: a10, h6. Default: a10)\n" "-a --address= Where the image will be programmed.\n" "\n" "Notes:\n" @@ -313,6 +341,9 @@ static void display_help(int status) " Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n" " Valid ECC step size: 512 and 1024\n" "\n" + "On H6/H616, the only ECC step size supported is 1024, but more ECC\n" + "strengths are supported: 44, 52, 68, 72, 76, 80\n" + "\n" "If you are building a boot0 image, you'll have specify extra options.\n" "These options should be chosen based on the layouts described here:\n" " http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n" @@ -342,7 +373,12 @@ static void display_help(int status) static int check_image_info(struct image_info *info) { - static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + static int ecc_strengths_a10[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + static int ecc_strengths_h6[] = { + 16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80 + }; + int *valid_ecc_strengths; + size_t nstrengths; int eccbytes, eccsteps; unsigned i; @@ -367,12 +403,25 @@ static int check_image_info(struct image_info *info) return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) { + if (info->h6) { + if (info->ecc_step_size != 1024) { + fprintf(stderr, + "H6 SoCs supports only 1024 bytes ECC step\n"); + return -EINVAL; + } + valid_ecc_strengths = ecc_strengths_h6; + nstrengths = ARRAY_SIZE(ecc_strengths_h6); + } else { + valid_ecc_strengths = ecc_strengths_a10; + nstrengths = ARRAY_SIZE(ecc_strengths_a10); + } + + for (i = 0; i < nstrengths; i++) { if (valid_ecc_strengths[i] == info->ecc_strength) break; } - if (i == ARRAY_SIZE(valid_ecc_strengths)) { + if (i == nstrengths) { fprintf(stderr, "Invalid ECC strength argument: %d\n", info->ecc_strength); return -EINVAL; @@ -416,10 +465,11 @@ int main(int argc, char **argv) {"boot0", no_argument, 0, 'b'}, {"scramble", no_argument, 0, 's'}, {"address", required_argument, 0, 'a'}, + {"soc", required_argument, 0, 't'}, {0, 0, 0, 0}, }; - int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh", + int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sht:", long_options, &option_index); if (c == EOF) break; @@ -454,6 +504,10 @@ int main(int argc, char **argv) case 'a': info.offset = strtoull(optarg, NULL, 0); break; + case 't': + if (strcmp("h6", optarg) == 0) + info.h6 = 1; + break; case '?': display_help(-1); break;