mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 20:56:12 +02:00
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.
This commit is contained in:
commit
3c72973b7a
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
228
drivers/mtd/nand/raw/sunxi_nand.h
Normal file
228
drivers/mtd/nand/raw/sunxi_nand.h
Normal 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
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user