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.
This commit is contained in:
Tom Rini 2026-02-03 17:36:07 -06:00
commit 3c72973b7a
14 changed files with 702 additions and 304 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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;
void * const ccm = (void *)SUNXI_CCM_BASE;
setbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
#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
setbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
#endif
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)

View File

@ -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)),

View File

@ -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)),

View File

@ -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

View File

@ -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);
/* gate on nand clock */
setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0));
#ifdef CONFIG_MACH_SUN9I
setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
#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
setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
/* gate on nand clock */
setbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_NAND0));
#ifdef CONFIG_MACH_SUN9I
setbits_le32(ccm + CCU_AHB_GATE1, (1 << AHB_GATE_OFFSET_DMA));
#else
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,
},
{ }
};

View File

@ -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 <b.brezillon.dev@gmail.com>
* Copyright (C) 2015 Roy Spliet <r.spliet@ultimaker.com>
* 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 <yuq825@gmail.com>
*
* https://github.com/hno/Allwinner-Info
* Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
*
* Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
* Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
*
*/
#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

View File

@ -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));
#ifdef CONFIG_MACH_SUN9I
clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
#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->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
clrbits_le32(ccm + CCU_AHB_GATE0,
(CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
#ifdef CONFIG_MACH_SUN9I
clrbits_le32(ccm + CCU_AHB_GATE1, (1 << AHB_GATE_OFFSET_DMA));
#else
clrbits_le32(ccm + CCU_AHB_GATE0, (1 << AHB_GATE_OFFSET_DMA));
#endif
clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
#endif
clrbits_le32(ccm + CCU_NAND0_CLK_CFG, CCM_NAND_CTRL_ENABLE |
CCM_NAND_CTRL_N(0) | CCM_NAND_CTRL_M(1));
}

View File

@ -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) \

View File

@ -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;
unsigned int seedmod;
/* Bail out earlier if the user didn't ask for scrambling. */
if (!info->scramble)
return;
if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
if (info->h6) {
/* H6 boot0 uses all 128 seeds */
seedmod = ARRAY_SIZE(default_scrambler_seeds);
} else {
seedmod = info->eraseblock_size / info->page_size;
if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
seedmod = ARRAY_SIZE(default_scrambler_seeds);
}
state = default_scrambler_seeds[page % seedmod];
}
@ -137,14 +150,19 @@ static int write_page(const struct image_info *info, uint8_t *buffer,
fwrite(buffer, info->page_size + info->oob_size, 1, dst);
for (i = 0; i < info->usable_page_size; i++) {
if (buffer[i] != 0xff)
break;
}
/*
* H6 BROM doesn't support empty pages
*/
if (!info->h6) {
for (i = 0; i < info->usable_page_size; i++) {
if (buffer[i] != 0xff)
break;
}
/* We leave empty pages at 0xff. */
if (i == info->usable_page_size)
return 0;
/* We leave empty pages at 0xff. */
if (i == info->usable_page_size)
return 0;
}
/* Restore the source pointer to read it again. */
fseek(src, -cnt, SEEK_CUR);
@ -212,6 +230,14 @@ static int write_page(const struct image_info *info, uint8_t *buffer,
}
memset(ecc, 0, eccbytes);
if (info->h6) {
/* BBM taken from vendor code: FF 00 03 01 */
buffer[info->ecc_step_size + 1] = 0;
buffer[info->ecc_step_size + 2] = 3; // NAND_VERSION_0
buffer[info->ecc_step_size + 3] = 1; // NAND_VERSION_1
}
swap_bits(buffer, info->ecc_step_size + 4);
encode_bch(bch, buffer, info->ecc_step_size + 4, ecc);
swap_bits(buffer, info->ecc_step_size + 4);
@ -303,6 +329,8 @@ static void display_help(int status)
"-e <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;