summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-02-03 17:36:07 -0600
committerTom Rini <[email protected]>2026-02-03 18:13:54 -0600
commit3c72973b7a7fbc3f57b20bf2e2e630ba9d31a686 (patch)
tree59a8328c97c1428d41ab68671956ead21124950d
parentede7198a37019266bfc4a992bdd22292196a97f9 (diff)
parent800ebf7e94e3a7e375cf915ae21e14514fd81450 (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.h1
-rw-r--r--arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h24
-rw-r--r--arch/arm/include/asm/arch-sunxi/clock_sun6i.h1
-rw-r--r--arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h1
-rw-r--r--arch/arm/include/asm/arch-sunxi/clock_sun9i.h2
-rw-r--r--board/sunxi/board.c21
-rw-r--r--drivers/clk/sunxi/clk_h6.c2
-rw-r--r--drivers/clk/sunxi/clk_h616.c2
-rw-r--r--drivers/mtd/nand/raw/Kconfig4
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c306
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.h228
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand_spl.c317
-rw-r--r--scripts/Makefile.xpl1
-rw-r--r--tools/sunxi-spl-image-builder.c92
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;