diff options
Diffstat (limited to 'drivers/mtd/nand/raw/sunxi_nand.c')
| -rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand.c | 409 |
1 files changed, 338 insertions, 71 deletions
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index f6a8e8ae819d..9dcdc93734cb 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -29,6 +29,12 @@ #include <linux/iopoll.h> #include <linux/reset.h> +/* non compile-time field get/prep */ +#undef field_get +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +#undef field_prep +#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 @@ -45,13 +51,40 @@ #define NFC_REG_A23_IO_DATA 0x0300 #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_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_H6_RDATA_CTL 0x0044 +#define NFC_REG_H6_RDATA_0 0x0048 +#define NFC_REG_H6_RDATA_1 0x004C +#define NFC_REG_A10_USER_DATA 0x0050 +#define NFC_REG_H6_USER_DATA 0x0080 +#define NFC_REG_USER_DATA(nfc, x) (nfc->caps->reg_user_data + ((x) * 4)) +#define NFC_REG_H6_USER_DATA_LEN 0x0070 +/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */ +#define NFC_REG_USER_DATA_LEN_CAPACITY 8 +#define NFC_REG_USER_DATA_LEN(nfc, step) \ + (nfc->caps->reg_user_data_len + \ + ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4) +#define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area) +#define NFC_REG_A10_SPARE_AREA 0x00A0 +#define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id) +#define NFC_REG_A10_PAT_ID 0x00A4 #define NFC_REG_MDMA_ADDR 0x00C0 #define NFC_REG_MDMA_CNT 0x00C4 +#define NFC_REG_H6_EFNAND_STATUS 0x0110 +#define NFC_REG_H6_SPARE_AREA 0x0114 +#define NFC_REG_H6_PAT_ID 0x0118 +#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C +#define NFC_REG_H6_NDMA_MODE_CTL 0x0120 +#define NFC_REG_H6_MDMA_DLBA_REG 0x0200 +#define NFC_REG_H6_MDMA_STA 0x0204 +#define NFC_REG_H6_MDMA_INT_MAS 0x0208 +#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C +#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210 +#define NFC_REG_H6_MDMA_CNT 0x0214 + #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -63,6 +96,7 @@ #define NFC_BUS_WIDTH_16 (1 << 2) #define NFC_RB_SEL_MSK BIT(3) #define NFC_RB_SEL(x) ((x) << 3) +/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */ #define NFC_CE_SEL_MSK GENMASK(26, 24) #define NFC_CE_SEL(x) ((x) << 24) #define NFC_CE_CTL BIT(6) @@ -81,6 +115,9 @@ #define NFC_STA BIT(4) #define NFC_NATCH_INT_FLAG BIT(5) #define NFC_RB_STATE(x) BIT(x + 8) +#define NFC_RB_STATE_MSK GENMASK(11, 8) +#define NDFC_RDATA_STA_1 BIT(12) +#define NDFC_RDATA_STA_0 BIT(13) /* define bit use in NFC_INT */ #define NFC_B2R_INT_ENABLE BIT(0) @@ -92,6 +129,7 @@ /* define bit use in NFC_TIMING_CTL */ #define NFC_TIMING_CTL_EDO BIT(8) +#define NFC_TIMING_CTL_E_EDO BIT(9) /* define NFC_TIMING_CFG register layout */ #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ @@ -99,9 +137,15 @@ (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ (((tCAD) & 0x7) << 8)) +#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC) \ + ((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) | \ + (((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) | \ + (((tWC) & 0x3) << 18)) + /* define bit use in NFC_CMD */ #define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) -#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) +#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) /* 15-10 reserved on H6 */ +#define NFC_CMD_ADR_NUM_MSK GENMASK(9, 8) #define NFC_CMD(x) (x) #define NFC_ADR_NUM_MSK GENMASK(18, 16) #define NFC_ADR_NUM(x) (((x) - 1) << 16) @@ -114,6 +158,7 @@ #define NFC_SEQ BIT(25) #define NFC_DATA_SWAP_METHOD BIT(26) #define NFC_ROW_AUTO_INC BIT(27) +#define NFC_H6_SEND_RND_CMD2 BIT(27) #define NFC_SEND_CMD3 BIT(28) #define NFC_SEND_CMD4 BIT(29) #define NFC_CMD_TYPE_MSK GENMASK(31, 30) @@ -125,6 +170,7 @@ #define NFC_READ_CMD_MSK GENMASK(7, 0) #define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) #define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) +#define NFC_RND_READ_CMD2_MSK GENMASK(31, 24) /* define bit use in NFC_WCMD_SET */ #define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) @@ -138,25 +184,46 @@ #define NFC_ECC_EXCEPTION BIT(4) #define NFC_ECC_BLOCK_SIZE_MSK BIT(5) #define NFC_ECC_BLOCK_512 BIT(5) -#define NFC_RANDOM_EN BIT(9) -#define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_MSK GENMASK(15, 12) -#define NFC_ECC_MODE(x) ((x) << 12) +#define NFC_RANDOM_EN(nfc) (nfc->caps->random_en_mask) +#define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_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)) +/* RANDOM_PAGE_SIZE: 0: ECC block size 1: page size */ +#define NFC_A23_RANDOM_PAGE_SIZE BIT(11) +#define NFC_H6_RANDOM_PAGE_SIZE BIT(7) #define NFC_RANDOM_SEED_MSK GENMASK(30, 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 GENMASK(15, 0) -#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) +#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_MSK(nfc) (nfc->caps->pat_found_mask) + #define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) -#define NFC_DEFAULT_TIMEOUT_MS 1000 +#define NFC_USER_DATA_LEN_MSK(step) \ + (0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4)) -#define NFC_SRAM_SIZE 1024 +#define NFC_DEFAULT_TIMEOUT_MS 1000 #define NFC_MAX_CS 7 +/* + * On A10/A23, this is the size of the NDFC User Data Register, containing the + * mandatory user data bytes following the ECC for each ECC step. + * Thus, for each ECC step, we need the ECC bytes + USER_DATA_SZ. + * Those bits are currently unsused, and kept as default value 0xffffffff. + * + * On H6/H616, this size became configurable, from 0 bytes to 32, via the + * USER_DATA_LEN registers. + */ +#define USER_DATA_SZ 4 + /** * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select * @@ -211,13 +278,57 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * * @has_mdma: Use mbus dma mode, otherwise general dma * through MBUS on A23/A33 needs extra configuration. + * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks + * @has_ecc_clk: If the controller needs an ECC clock. + * @has_mbus_clk: If the controller needs a mbus clock. * @reg_io_data: I/O data register + * @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 + * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register + * @random_dir_mask: RANDOM_DIRECTION mask in NFC_ECC_CTL register + * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register + * @ecc_err_mask: NFC_ECC_ERR mask in NFC_ECC_ST register + * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @dma_maxburst: DMA maxburst + * @ecc_strengths: Available ECC strengths array + * @nstrengths: Size of @ecc_strengths + * @max_ecc_steps: Maximum supported steps for ECC, this is also the + * number of user data registers + * @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 + * @sram_size: Size of the NAND controller SRAM */ struct sunxi_nfc_caps { bool has_mdma; + bool has_ecc_block_512; + bool has_ecc_clk; + bool has_mbus_clk; unsigned int reg_io_data; + 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 random_en_mask; + unsigned int random_dir_mask; + unsigned int ecc_mode_mask; + unsigned int ecc_err_mask; + unsigned int pat_found_mask; unsigned int dma_maxburst; + const u8 *ecc_strengths; + unsigned int nstrengths; + const u8 *user_data_len_tab; + unsigned int nuser_data_tab; + unsigned int max_ecc_steps; + int sram_size; }; /** @@ -228,6 +339,8 @@ struct sunxi_nfc_caps { * @regs: NAND controller registers * @ahb_clk: NAND controller AHB clock * @mod_clk: NAND controller mod clock + * @ecc_clk: NAND controller ECC clock + * @mbus_clk: NAND controller MBUS clock * @reset: NAND controller reset line * @assigned_cs: bitmask describing already assigned CS lines * @clk_rate: NAND controller current clock rate @@ -243,6 +356,8 @@ struct sunxi_nfc { void __iomem *regs; struct clk *ahb_clk; struct clk *mod_clk; + struct clk *ecc_clk; + struct clk *mbus_clk; struct reset_control *reset; unsigned long assigned_cs; unsigned long clk_rate; @@ -431,7 +546,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) if (sel->rb >= 0) ctl |= NFC_RB_SEL(sel->rb); - 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) { clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); @@ -455,7 +570,7 @@ static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len) while (len > offs) { bool poll = false; - cnt = min(len - offs, NFC_SRAM_SIZE); + cnt = min(len - offs, nfc->caps->sram_size); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -493,7 +608,7 @@ static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf, while (len > offs) { bool poll = false; - cnt = min(len - offs, NFC_SRAM_SIZE); + cnt = min(len - offs, nfc->caps->sram_size); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -623,13 +738,12 @@ static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page, bool ecc) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + u32 ecc_ctl; u16 state; if (!(nand->options & NAND_NEED_SCRAMBLING)) return; - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); state = sunxi_nfc_randomizer_state(nand, page, ecc); ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); @@ -642,7 +756,7 @@ static void sunxi_nfc_randomizer_enable(struct nand_chip *nand) 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); } @@ -653,7 +767,7 @@ static void sunxi_nfc_randomizer_disable(struct nand_chip *nand) 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); } @@ -717,20 +831,66 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), - oob); + sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)), oob); /* De-randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) sunxi_nfc_randomize_bbm(nand, page, oob); } +/* + * On H6/H6 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 void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, const u8 *oob, int step, bool bbm, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u8 user_data[4]; + u8 user_data[USER_DATA_SZ]; /* Randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { @@ -740,7 +900,7 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, } writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(step)); + nfc->regs + NFC_REG_USER_DATA(nfc, step)); } static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, @@ -757,7 +917,8 @@ static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, } static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, - int step, u32 status, bool *erased) + int step, u32 status, u32 pattern_found, + bool *erased) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; @@ -768,10 +929,10 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, if (status & NFC_ECC_ERR(step)) return -EBADMSG; - if (status & NFC_ECC_PAT_FOUND(step)) { + if (pattern_found & BIT(step)) { u8 pattern; - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) { pattern = 0x0; } else { pattern = 0xff; @@ -782,12 +943,12 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, memset(data, pattern, ecc->size); if (oob) - memset(oob, pattern, ecc->bytes + 4); + memset(oob, pattern, ecc->bytes + USER_DATA_SZ); return 0; } - tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, step)); return NFC_ECC_ERR_CNT(step, tmp); } @@ -802,6 +963,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; + u32 pattern_found; bool erased; int ret; @@ -817,6 +979,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (ret) return ret; + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, @@ -827,10 +991,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + 4; + *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; + + pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); + pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0, readl(nfc->regs + NFC_REG_ECC_ST), + pattern_found, &erased); if (erased) return 1; @@ -847,11 +1015,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, - false); + nand_change_read_column_op(nand, oob_off, oob, + ecc->bytes + USER_DATA_SZ, false); - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, + ecc->bytes + USER_DATA_SZ, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; @@ -861,7 +1029,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (oob_required) { nand_change_read_column_op(nand, oob_off, NULL, 0, false); - sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4, + sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + USER_DATA_SZ, true, page); sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0, @@ -911,7 +1079,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf unsigned int max_bitflips = 0; int ret, i, raw_mode = 0; struct scatterlist sg; - u32 status, wait; + u32 status, pattern_found, wait; ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -923,6 +1091,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf return ret; sunxi_nfc_hw_ecc_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); @@ -952,17 +1122,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf return ret; status = readl(nfc->regs + NFC_REG_ECC_ST); + pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); + pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); for (i = 0; i < nchunks; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; bool erased; ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL, oob_required ? oob : NULL, - i, status, &erased); + i, status, pattern_found, + &erased); /* ECC errors are handled in the second loop. */ if (ret < 0) @@ -972,7 +1145,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf /* TODO: use DMA to retrieve OOB */ nand_change_read_column_op(nand, mtd->writesize + oob_off, - oob, ecc->bytes + 4, false); + oob, ecc->bytes + USER_DATA_SZ, false); sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, !i, page); @@ -984,10 +1157,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); } - if (status & NFC_ECC_ERR_MSK) { + if (status & NFC_ECC_ERR_MSK(nfc)) { for (i = 0; i < nchunks; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1007,10 +1180,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf /* TODO: use DMA to retrieve OOB */ nand_change_read_column_op(nand, mtd->writesize + oob_off, - oob, ecc->bytes + 4, false); + oob, ecc->bytes + USER_DATA_SZ, false); - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, + ecc->bytes + USER_DATA_SZ, NULL, 0, ecc->strength); if (ret >= 0) @@ -1052,6 +1225,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | @@ -1063,7 +1238,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + 4; + *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; return 0; } @@ -1074,7 +1249,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, { struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); + int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -1107,7 +1282,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1166,7 +1341,7 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = bufpoi + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1220,7 +1395,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); const u8 *data = buf + data_off; const u8 *oob = nand->oob_poi + oob_off; @@ -1258,7 +1433,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); const u8 *data = buf + data_off; const u8 *oob = nand->oob_poi + oob_off; @@ -1296,10 +1471,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, if (ret) goto pio_fallback; + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { - const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); + const u8 *oob = nand->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, i); } nand_prog_page_begin_op(nand, page, 0, NULL, 0); @@ -1567,7 +1744,7 @@ static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, if (section >= ecc->steps) return -ERANGE; - oobregion->offset = section * (ecc->bytes + 4) + 4; + oobregion->offset = section * (ecc->bytes + USER_DATA_SZ) + 4; oobregion->length = ecc->bytes; return 0; @@ -1601,10 +1778,10 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) return -ERANGE; - oobregion->offset = section * (ecc->bytes + 4); + oobregion->offset = section * (ecc->bytes + USER_DATA_SZ); if (section < ecc->steps) - oobregion->length = 4; + oobregion->length = USER_DATA_SZ; else oobregion->length = mtd->oobsize - oobregion->offset; @@ -1620,9 +1797,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, struct nand_ecc_ctrl *ecc, struct device_node *np) { - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + const u8 *strengths = nfc->caps->ecc_strengths; struct mtd_info *mtd = nand_to_mtd(nand); struct nand_device *nanddev = mtd_to_nanddev(mtd); int nsectors; @@ -1638,7 +1815,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, bytes = (mtd->oobsize - 2) / nsectors; /* 4 non-ECC bytes are added before each ECC bytes section */ - bytes -= 4; + bytes -= USER_DATA_SZ; /* and bytes has to be even. */ if (bytes % 2) @@ -1646,7 +1823,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->strength = bytes * 8 / fls(8 * ecc->size); - for (i = 0; i < ARRAY_SIZE(strengths); i++) { + for (i = 0; i < nfc->caps->nstrengths; i++) { if (strengths[i] > ecc->strength) break; } @@ -1667,7 +1844,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, } /* Add ECC info retrieval from DT */ - for (i = 0; i < ARRAY_SIZE(strengths); i++) { + for (i = 0; i < nfc->caps->nstrengths; i++) { if (ecc->strength <= strengths[i]) { /* * Update ecc->strength value with the actual strength @@ -1678,7 +1855,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, } } - if (i >= ARRAY_SIZE(strengths)) { + if (i >= nfc->caps->nstrengths) { dev_err(nfc->dev, "unsupported strength\n"); return -ENOTSUPP; } @@ -1691,7 +1868,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, nsectors = mtd->writesize / ecc->size; - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) + if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) return -EINVAL; ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; @@ -1714,11 +1891,17 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->read_oob_raw = nand_read_oob_std; ecc->write_oob_raw = nand_write_oob_std; - sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION | + sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, i) | NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE | NFC_ECC_EN; - if (ecc->size == 512) - sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; + if (ecc->size == 512) { + if (nfc->caps->has_ecc_block_512) { + sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; + } else { + dev_err(nfc->dev, "512B ECC block not supported\n"); + return -EOPNOTSUPP; + } + } return 0; } @@ -1807,7 +1990,7 @@ static int sunxi_nfc_exec_subop(struct nand_chip *nand, case NAND_OP_DATA_OUT_INSTR: start = nand_subop_get_data_start_off(subop, i); remaining = nand_subop_get_data_len(subop, i); - cnt = min_t(u32, remaining, NFC_SRAM_SIZE); + cnt = min_t(u32, remaining, nfc->caps->sram_size); cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; if (instr->type == NAND_OP_DATA_OUT_INSTR) { @@ -2094,6 +2277,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (irq < 0) return irq; + nfc->caps = of_device_get_match_data(dev); + if (!nfc->caps) + return -EINVAL; + nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb"); if (IS_ERR(nfc->ahb_clk)) { dev_err(dev, "failed to retrieve ahb clk\n"); @@ -2106,6 +2293,22 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return PTR_ERR(nfc->mod_clk); } + if (nfc->caps->has_ecc_clk) { + nfc->ecc_clk = devm_clk_get_enabled(dev, "ecc"); + if (IS_ERR(nfc->ecc_clk)) { + dev_err(dev, "failed to retrieve ecc clk\n"); + return PTR_ERR(nfc->ecc_clk); + } + } + + if (nfc->caps->has_mbus_clk) { + nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus"); + if (IS_ERR(nfc->mbus_clk)) { + dev_err(dev, "failed to retrieve mbus clk\n"); + return PTR_ERR(nfc->mbus_clk); + } + } + nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); if (IS_ERR(nfc->reset)) return PTR_ERR(nfc->reset); @@ -2116,12 +2319,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return ret; } - nfc->caps = of_device_get_match_data(&pdev->dev); - if (!nfc->caps) { - ret = -EINVAL; - goto out_ahb_reset_reassert; - } - ret = sunxi_nfc_rst(nfc); if (ret) goto out_ahb_reset_reassert; @@ -2168,15 +2365,81 @@ static void sunxi_nfc_remove(struct platform_device *pdev) dma_release_channel(nfc->dmac); } +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, .reg_io_data = NFC_REG_A10_IO_DATA, + .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, + .reg_user_data = NFC_REG_A10_USER_DATA, + .reg_spare_area = NFC_REG_A10_SPARE_AREA, + .reg_pat_id = NFC_REG_A10_PAT_ID, + .reg_pat_found = NFC_REG_ECC_ST, + .random_en_mask = BIT(9), + .random_dir_mask = BIT(10), + .ecc_mode_mask = GENMASK(15, 12), + .ecc_err_mask = GENMASK(15, 0), + .pat_found_mask = GENMASK(31, 16), .dma_maxburst = 4, + .ecc_strengths = sunxi_ecc_strengths_a10, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), + .max_ecc_steps = 16, + .sram_size = 1024, }; static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { .has_mdma = true, + .has_ecc_block_512 = true, + .reg_io_data = NFC_REG_A23_IO_DATA, + .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, + .reg_user_data = NFC_REG_A10_USER_DATA, + .reg_spare_area = NFC_REG_A10_SPARE_AREA, + .reg_pat_id = NFC_REG_A10_PAT_ID, + .reg_pat_found = NFC_REG_ECC_ST, + .random_en_mask = BIT(9), + .random_dir_mask = BIT(10), + .ecc_mode_mask = GENMASK(15, 12), + .ecc_err_mask = GENMASK(15, 0), + .pat_found_mask = GENMASK(31, 16), + .dma_maxburst = 8, + .ecc_strengths = sunxi_ecc_strengths_a10, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), + .max_ecc_steps = 16, + .sram_size = 1024, +}; + +static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { + .has_ecc_clk = true, + .has_mbus_clk = true, .reg_io_data = NFC_REG_A23_IO_DATA, + .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_spare_area = NFC_REG_H6_SPARE_AREA, + .reg_pat_id = NFC_REG_H6_PAT_ID, + .reg_pat_found = NFC_REG_H6_PAT_FOUND, + .random_en_mask = BIT(5), + .random_dir_mask = BIT(6), + .ecc_mode_mask = GENMASK(15, 8), + .ecc_err_mask = GENMASK(31, 0), + .pat_found_mask = GENMASK(31, 0), .dma_maxburst = 8, + .ecc_strengths = sunxi_ecc_strengths_h6, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6), + .user_data_len_tab = sunxi_user_data_len_h6, + .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), + .max_ecc_steps = 32, + .sram_size = 8192, }; static const struct of_device_id sunxi_nfc_ids[] = { @@ -2188,6 +2451,10 @@ static const struct of_device_id sunxi_nfc_ids[] = { .compatible = "allwinner,sun8i-a23-nand-controller", .data = &sunxi_nfc_a23_caps, }, + { + .compatible = "allwinner,sun50i-h616-nand-controller", + .data = &sunxi_nfc_h616_caps, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); |
