Introduce H6/H616 NAND controller support for U-Boot The H616 NAND controller has the same base as A10/A23, with some differences: - MDMA is based on chained buffers - its ECC supports up to 80bit per 1024bytes - some registers layouts are a bit different, mainly due do the stronger ECC. - it uses USER_DATA_LEN registers along USER_DATA registers. - it needs a specific clock for ECC and MBUS.
Introduce the basic support, with ECC and scrambling, but without DMA/MDMA. Tested on Whatsminer H616 board (with and without scrambling, ECC) Signed-off-by: Richard Genoud <[email protected]> --- drivers/mtd/nand/raw/Kconfig | 3 +- drivers/mtd/nand/raw/sunxi_nand.c | 112 ++++++++++++++++++++++++++++-- drivers/mtd/nand/raw/sunxi_nand.h | 32 ++++++++- 3 files changed, 139 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 754b99bf3eb6..e4c4d9bcbf63 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -467,7 +467,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 select SYS_NAND_SELF_INIT select SYS_NAND_U_BOOT_LOCATIONS select SPL_NAND_SUPPORT diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 58c895095ce9..c9133cdc8844 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -170,8 +170,14 @@ static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) static void sunxi_nfc_set_clk_rate(unsigned long hz) { +#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) + void * const ccm = (void *)SUNXI_CCM_BASE; + void * const nand0_clk_cfg = ccm + CCU_NAND0_CLK_CFG; +#else struct sunxi_ccm_reg *const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + u32 *nand0_clk_cfg = &ccm->nand0_clk_cfg; +#endif int div_m, div_n; div_m = (clock_get_pll6() + hz - 1) / hz; @@ -186,8 +192,16 @@ 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); + 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)); #ifdef CONFIG_MACH_SUN9I @@ -195,6 +209,7 @@ static void sunxi_nfc_set_clk_rate(unsigned long hz) #else setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); #endif +#endif } static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, @@ -689,6 +704,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, @@ -716,6 +778,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); @@ -856,6 +921,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, @@ -1276,7 +1344,6 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nfc *nfc, static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); @@ -1303,12 +1370,12 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, /* Add ECC info retrieval from DT */ for (i = 0; i < nfc->caps->nstrengths; i++) { - if (ecc->strength <= strengths[i]) { + if (ecc->strength <= nfc->caps->ecc_strengths[i]) { /* * Update ecc->strength value with the actual strength * that will be used by the ECC engine. */ - ecc->strength = strengths[i]; + ecc->strength = nfc->caps->ecc_strengths[i]; break; } } @@ -1722,9 +1789,22 @@ static int sunxi_nand_probe(struct udevice *dev) return 0; } +static const u8 sunxi_ecc_strengths_a10[] = { + 16, 24, 28, 32, 40, 48, 56, 60, 64 +}; + +static const u8 sunxi_ecc_strengths_h6[] = { + 16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80 +}; + +static const u8 sunxi_user_data_len_h6[] = { + 0, 4, 8, 12, 16, 20, 24, 28, 32 +}; + static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .has_ecc_block_512 = true, - .nstrengths = 9, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), + .ecc_strengths = sunxi_ecc_strengths_a10, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, .reg_pat_found = NFC_REG_ECC_ST, @@ -1733,6 +1813,24 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .pat_found_mask = GENMASK(31, 16), .ecc_mode_mask = GENMASK(15, 12), .random_en_mask = BIT(9), + .max_ecc_steps = 16, +}; + +static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6), + .ecc_strengths = sunxi_ecc_strengths_h6, + .reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT, + .reg_user_data = NFC_REG_H6_USER_DATA, + .reg_user_data_len = NFC_REG_H6_USER_DATA_LEN, + .reg_pat_found = NFC_REG_H6_PAT_FOUND, + .reg_spare_area = NFC_REG_H6_SPARE_AREA, + .reg_pat_id = NFC_REG_H6_PAT_ID, + .pat_found_mask = GENMASK(31, 0), + .ecc_mode_mask = GENMASK(15, 8), + .random_en_mask = BIT(5), + .user_data_len_tab = sunxi_user_data_len_h6, + .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), + .max_ecc_steps = 32, }; static const struct udevice_id sunxi_nand_ids[] = { @@ -1740,6 +1838,10 @@ static const struct udevice_id sunxi_nand_ids[] = { .compatible = "allwinner,sun4i-a10-nand", .data = (unsigned long)&sunxi_nfc_a10_caps, }, + { + .compatible = "allwinner,sun50i-h616-nand-controller", + .data = (unsigned long)&sunxi_nfc_h616_caps, + }, { } }; diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 52200468d343..966b743e2613 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -44,15 +44,26 @@ #define NFC_REG_IO_DATA 0x0030 #define NFC_REG_ECC_CTL 0x0034 #define NFC_REG_ECC_ST 0x0038 -#define NFC_REG_DEBUG 0x003C +#define NFC_REG_H6_PAT_FOUND 0x003C #define NFC_REG_A10_ECC_ERR_CNT 0x0040 +#define NFC_REG_H6_ECC_ERR_CNT 0x0050 #define NFC_REG_ECC_ERR_CNT(nfc, x) (((nfc)->caps->reg_ecc_err_cnt + (x)) & ~0x3) #define NFC_REG_A10_USER_DATA 0x0050 +#define NFC_REG_H6_USER_DATA 0x0080 +#define NFC_REG_H6_USER_DATA_LEN 0x0070 #define NFC_REG_USER_DATA(nfc, x) ((nfc)->caps->reg_user_data + ((x) * 4)) + +/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */ +#define NFC_REG_USER_DATA_LEN_CAPACITY 8 +#define NFC_REG_USER_DATA_LEN(nfc, step) \ + ((nfc)->caps->reg_user_data_len + \ + ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4) #define NFC_REG_SPARE_AREA(nfc) ((nfc)->caps->reg_spare_area) #define NFC_REG_A10_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID(nfc) ((nfc)->caps->reg_pat_id) +#define NFC_REG_H6_SPARE_AREA 0x0114 +#define NFC_REG_PAT_ID(nfc) ((nfc)->caps->reg_pat_id) #define NFC_REG_A10_PAT_ID 0x00A4 +#define NFC_REG_H6_PAT_ID 0x0118 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -162,6 +173,9 @@ #define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) +#define NFC_USER_DATA_LEN_MSK(step) \ + (0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4)) + #define NFC_DEFAULT_TIMEOUT_MS 1000 #define NFC_SRAM_SIZE 1024 @@ -174,8 +188,10 @@ * * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks * @nstrengths: Number of element of ECC strengths array + * @ecc_strengths: available ECC strengths array * @reg_ecc_err_cnt: ECC error counter register * @reg_user_data: User data register + * @reg_user_data_len: User data length register * @reg_spare_area: Spare Area Register * @reg_pat_id: Pattern ID Register * @reg_pat_found: Data Pattern Status Register @@ -183,12 +199,21 @@ * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register + * @user_data_len_tab: Table of lenghts supported by USER_DATA_LEN register + * The table index is the value to set in NFC_USER_DATA_LEN + * registers, and the corresponding value is the number of + * bytes to write + * @nuser_data_tab: Size of @user_data_len_tab + * @max_ecc_steps: Maximum supported steps for ECC, this is also the + * number of user data registers */ struct sunxi_nfc_caps { bool has_ecc_block_512; unsigned int nstrengths; + const u8 *ecc_strengths; unsigned int reg_ecc_err_cnt; unsigned int reg_user_data; + unsigned int reg_user_data_len; unsigned int reg_spare_area; unsigned int reg_pat_id; unsigned int reg_pat_found; @@ -196,6 +221,9 @@ struct sunxi_nfc_caps { unsigned int ecc_err_mask; unsigned int ecc_mode_mask; unsigned int random_en_mask; + const u8 *user_data_len_tab; + unsigned int nuser_data_tab; + unsigned int max_ecc_steps; }; #endif

