diff options
| author | Tom Rini <[email protected]> | 2026-02-03 17:36:07 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-02-03 18:13:54 -0600 |
| commit | 3c72973b7a7fbc3f57b20bf2e2e630ba9d31a686 (patch) | |
| tree | 59a8328c97c1428d41ab68671956ead21124950d | |
| parent | ede7198a37019266bfc4a992bdd22292196a97f9 (diff) | |
| parent | 800ebf7e94e3a7e375cf915ae21e14514fd81450 (diff) | |
Merge branch 'u-boot-nand-03022026' of https://source.denx.de/u-boot/custodians/u-boot-nand-flash
CI: https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/pipelines/29183
This series provides a comprehensive cleanup of the Allwinner (sunxi)
NAND controller drivers and introduces full support for the H6 and H616
SoCs in both the main U-Boot driver and the SPL.
The series successfully deduplicates register maps between sunxi_nand.c
and sunxi_nand_spl.c while migrating to a capability-based architecture.
This approach allows the driver to handle the H616's specific
requirements—such as shifted register offsets for ECC/OOB, the removal
of 512B ECC block support, and mandatory MBUS clock gating—without
breaking compatibility for legacy A10/A23 devices.
| -rw-r--r-- | arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 1 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 24 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 1 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h | 1 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-sunxi/clock_sun9i.h | 2 | ||||
| -rw-r--r-- | board/sunxi/board.c | 21 | ||||
| -rw-r--r-- | drivers/clk/sunxi/clk_h6.c | 2 | ||||
| -rw-r--r-- | drivers/clk/sunxi/clk_h616.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand.c | 306 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand.h | 228 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand_spl.c | 317 | ||||
| -rw-r--r-- | scripts/Makefile.xpl | 1 | ||||
| -rw-r--r-- | tools/sunxi-spl-image-builder.c | 92 |
14 files changed, 700 insertions, 302 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_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) 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 <linux/bitops.h> #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..954a8715075 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -307,15 +307,24 @@ 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; - - setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); + 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->ahb_reset0_cfg, (1 << AHB_GATE_OFFSET_NAND0)); + setbits_le32(ccm + CCU_AHB_RESET0_CFG, (1 << AHB_GATE_OFFSET_NAND0)); +#endif #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/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)), diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index e0ff28cb21b..306175873fa 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 @@ -488,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/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 1ce09b56b80..ef27a4b7a36 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -46,128 +46,7 @@ #include <asm/gpio.h> #include <asm/arch/clock.h> -#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 @@ -270,8 +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 - * @complete: a completion object used to wait for NAND - * controller events + * @caps: NAND Controller capabilities */ struct sunxi_nfc { struct nand_hw_control controller; @@ -282,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) @@ -291,8 +170,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; @@ -307,14 +185,23 @@ 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); +#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->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 #endif } @@ -441,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 { @@ -450,7 +337,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); @@ -730,7 +617,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); } @@ -742,7 +629,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); } @@ -781,11 +668,13 @@ 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_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; + 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(nfc, 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); @@ -808,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, @@ -820,6 +756,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) @@ -834,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); @@ -845,11 +785,12 @@ 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))) + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) pattern = 0x0; memset(data, pattern, ecc->size); @@ -858,13 +799,14 @@ 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); 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 @@ -888,7 +830,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. */ @@ -959,10 +901,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) @@ -972,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, @@ -1392,7 +1337,9 @@ 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); struct sunxi_nand_hw_ecc *data; struct nand_ecclayout *layout; int nsectors; @@ -1415,18 +1362,18 @@ 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++) { - if (ecc->strength <= strengths[i]) { + for (i = 0; i < nfc->caps->nstrengths; 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; } } - if (i >= ARRAY_SIZE(strengths)) { + if (i >= nfc->caps->nstrengths) { dev_err(mtd->dev, "unsupported strength\n"); ret = -ENOTSUPP; goto err; @@ -1569,6 +1516,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) { @@ -1579,6 +1528,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; @@ -1804,6 +1757,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); @@ -1825,9 +1782,58 @@ 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 = 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, + .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), + .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[] = { { .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 new file mode 100644 index 00000000000..6ee3ea14ee1 --- /dev/null +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -0,0 +1,228 @@ +/* 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 <[email protected]> + * Copyright (C) 2015 Roy Spliet <[email protected]> + * Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com> + * Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com> + * Derived from: + * https://github.com/yuq/sunxi-nfc-mtd + * Copyright (C) 2013 Qiang Yu <[email protected]> + * + * https://github.com/hno/Allwinner-Info + * Copyright (C) 2013 Henrik Nordström <Henrik Nordström> + * + * Copyright (C) 2013 Dmitriy B. <[email protected]> + * Copyright (C) 2013 Sergey Lapin <[email protected]> + * + */ + +#ifndef SUNXI_NAND_H +#define SUNXI_NAND_H + +#include <linux/bitops.h> + +/* 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 +#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_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_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 + +/* 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_512 BIT(5) +#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) +#define NFC_RANDOM_SEED(x) ((x) << 16) + +/* 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 + * 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_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 + +#define NFC_MAX_CS 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 + * @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 + * @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 + * @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; + unsigned int pat_found_mask; + 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 diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 4f1e2d9a577..67f7d22ed2c 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -13,74 +13,11 @@ #include <linux/delay.h> #include <linux/mtd/rawnand.h> -/* 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_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_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_ADDR_NUM_OFFSET 16 -#define NFC_SEND_ADDR (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_RAW_CMD (0 << 30) -#define NFC_ECC_CMD (1 << 30) -#define NFC_PAGE_CMD (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) +#include "sunxi_nand.h" #define NFC_READ_CMD_OFFSET 0 -#define NFC_RANDOM_READ_CMD0_OFFSET 8 -#define NFC_RANDOM_READ_CMD1_OFFSET 16 - -#define NFC_CMD_RNDOUTSTART 0xE0 -#define NFC_CMD_RNDOUT 0x05 -#define NFC_CMD_READSTART 0x30 +#define NFC_RND_READ_CMD0_OFFSET 8 +#define NFC_RND_READ_CMD1_OFFSET 16 struct nfc_config { int page_size; @@ -90,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 */ @@ -114,13 +52,53 @@ 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, + .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), +}; + +__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 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); @@ -143,7 +121,7 @@ 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, + 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; @@ -154,7 +132,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(NFC_REG_ST, NFC_CMD_INT_FLAG, DEFAULT_TIMEOUT_US)) { printf("nand: timeout waiting for interruption\n"); return -ETIMEDOUT; @@ -171,8 +149,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(NFC_CMD_INT_FLAG, NFC_REG_ST); + writel_nfc(cmd, NFC_REG_CMD); return nand_wait_int(); } @@ -183,15 +161,12 @@ void nand_init(void) board_nand_init(); - val = readl(SUNXI_NFC_BASE + NFC_CTL); + val = readl_nfc(NFC_REG_CTL); /* enable and reset CTL */ - writel(val | NFC_CTL_EN | NFC_CTL_RESET, - SUNXI_NFC_BASE + NFC_CTL); + writel_nfc(val | NFC_EN | NFC_RESET, NFC_REG_CTL); - if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, - NFC_CTL_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); @@ -203,43 +178,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_nfc(NFC_REG_CTL); + val &= ~NFC_PAGE_SHIFT_MSK; + 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((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RANDOM_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); + 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_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) | - (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_RCMD_SET); - writel(column, SUNXI_NFC_BASE + NFC_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_RAW_CMD | - (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | - NFC_CMD_RNDOUT); + ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_NORMAL_OP | + (1 << NFC_ADR_NUM_OFFSET) | NFC_SEND_ADR | + NAND_CMD_RNDOUT); if (ret) return ret; @@ -249,7 +223,70 @@ 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_nfc(0, 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_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_nfc(val, 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) +{ + /* 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) @@ -258,7 +295,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 || @@ -274,18 +311,24 @@ 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_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) | - NFC_ECC_EN | NFC_ECC_EXCEPTION, - SUNXI_NFC_BASE + NFC_ECC_CTL); + writel_nfc(0, NFC_REG_ECC_ST); + + 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_CNT); + writel_nfc(conf->ecc_size, NFC_REG_CNT); nand_exec_cmd(NFC_DATA_TRANS); /* @@ -293,29 +336,38 @@ 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); + 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_ECC_ST); + ecc_st = readl_nfc(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_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)) + 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 */ - writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN, - SUNXI_NFC_BASE + NFC_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; @@ -336,7 +388,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; @@ -442,6 +494,11 @@ static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) if (conf->valid) return 0; + 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 * with 5 address cycles. @@ -541,14 +598,22 @@ 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)); +#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->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 #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)); } 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 <linux/bch.h> #include <getopt.h> +#include <string.h> #include <version.h> #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; - - /* Bail out earlier if the user didn't ask for scrambling. */ - if (!info->scramble) - return; + unsigned int seedmod; - 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 <size> --eraseblock=<size> Erase block size\n" "-b --boot0 Build a boot0 image.\n" "-s --scramble Scramble data\n" + "-t --soc=<soc> Build an image compatible with SoC type <soc>\n" + " (possible values: a10, h6. Default: a10)\n" "-a <offset> --address=<offset> 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; |
