X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fmtd%2Fnand%2Fvf610_nfc.c;h=f99bdaf94da2be3dc216ff05f9580105a8049163;hb=a0e80c97c9ba1409c2370f6c8b2a4d6a48cdb15c;hp=d98dd28800eb087aa506bc31281aafb0c8fb1aa5;hpb=1d2f74690cace803844bce198a6d5fa1b6cd11f9;p=oweals%2Fu-boot.git diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index d98dd28800..f99bdaf94d 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2014 Freescale Semiconductor, Inc. and others + * Copyright 2009-2015 Freescale Semiconductor, Inc. and others * * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. * Ported to U-Boot by Stefan Agner @@ -12,16 +12,14 @@ * * Based on original driver mpc5121_nfc.c. * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * SPDX-License-Identifier: GPL-2.0+ * * Limitations: * - Untested on MPC5125 and M54418. - * - DMA not used. + * - DMA and pipelining not used. * - 2K pages or less. - * - Only 2K page w. 64+OOB and hardware ECC. + * - HW ECC: Only 2K page with 64+ OOB. + * - HW ECC: Only 24 and 32-bit error correction implemented. */ #include @@ -53,6 +51,7 @@ #define PAGE_2K 0x0800 #define OOB_64 0x0040 +#define OOB_MAX 0x0100 /* * NFC_CMD2[CODE] values. See section: @@ -62,6 +61,7 @@ * Briefly these are bitmasks of controller cycles. */ #define READ_PAGE_CMD_CODE 0x7EE0 +#define READ_ONFI_PARAM_CMD_CODE 0x4860 #define PROGRAM_PAGE_CMD_CODE 0x7FC0 #define ERASE_CMD_CODE 0x4EC0 #define READ_ID_CMD_CODE 0x4804 @@ -71,6 +71,7 @@ /* NFC ECC mode define */ #define ECC_BYPASS 0 #define ECC_45_BYTE 6 +#define ECC_60_BYTE 7 /*** Register Mask and bit definitions */ @@ -125,63 +126,40 @@ #define NFC_TIMEOUT (1000) -/* ECC status placed at end of buffers. */ -#define ECC_SRAM_ADDR ((PAGE_2K+256-8) >> 3) -#define ECC_STATUS_MASK 0x80 -#define ECC_ERR_COUNT 0x3F - /* - * ECC status is stored at NFC_CFG[ECCADD] +4 for little-endian - * and +7 for big-endian SOC. + * ECC status - seems to consume 8 bytes (double word). The documented + * status byte is located in the lowest byte of the second word (which is + * the 4th or 7th byte depending on endianness). + * Calculate an offset to store the ECC status at the end of the buffer. */ -#ifdef CONFIG_VF610 -#define ECC_OFFSET 4 -#else -#define ECC_OFFSET 7 -#endif +#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) + +#define ECC_STATUS 0x4 +#define ECC_STATUS_MASK 0x80 +#define ECC_STATUS_ERR_COUNT 0x3F + +enum vf610_nfc_alt_buf { + ALT_BUF_DATA = 0, + ALT_BUF_ID = 1, + ALT_BUF_STAT = 2, + ALT_BUF_ONFI = 3, +}; struct vf610_nfc { - struct mtd_info *mtd; - struct nand_chip chip; - void __iomem *regs; - uint column; - int spareonly; - int page_sz; - int page; + struct nand_chip chip; + void __iomem *regs; + uint buf_offset; + int write_sz; /* Status and ID are in alternate locations. */ - int alt_buf; -#define ALT_BUF_ID 1 -#define ALT_BUF_STAT 2 - struct clk *clk; + enum vf610_nfc_alt_buf alt_buf; }; -#define mtd_to_nfc(_mtd) \ - (struct vf610_nfc *)((struct nand_chip *)_mtd->priv)->priv +#define mtd_to_nfc(_mtd) nand_get_controller_data(mtd_to_nand(_mtd)) -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; +#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES) +#define ECC_HW_MODE ECC_45_BYTE -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 11, - .len = 4, - .veroffs = 15, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 11, - .len = 4, - .veroffs = 15, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -static struct nand_ecclayout vf610_nfc_ecc45 = { +static struct nand_ecclayout vf610_nfc_ecc = { .eccbytes = 45, .eccpos = {19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -190,9 +168,27 @@ static struct nand_ecclayout vf610_nfc_ecc45 = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, .oobfree = { - {.offset = 8, - .length = 11} } + {.offset = 2, + .length = 17} } +}; +#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES) +#define ECC_HW_MODE ECC_60_BYTE + +static struct nand_ecclayout vf610_nfc_ecc = { + .eccbytes = 60, + .eccpos = { 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63 }, + .oobfree = { + {.offset = 2, + .length = 2} } }; +#endif static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg) { @@ -228,8 +224,12 @@ static inline void vf610_nfc_set_field(struct mtd_info *mtd, u32 reg, static inline void vf610_nfc_memcpy(void *dst, const void *src, size_t n) { /* - * Use this accessor for the interal SRAM buffers. On ARM we can - * treat the SRAM buffer as if its memory, hence use memcpy + * Use this accessor for the internal SRAM buffers. On the ARM + * Freescale Vybrid SoC it's known that the driver can treat + * the SRAM buffer as if it's memory. Other platform might need + * to treat the buffers differently. + * + * For the time being, use memcpy */ memcpy(dst, src, n); } @@ -244,7 +244,7 @@ static inline void vf610_nfc_clear_status(void __iomem *regbase) } /* Wait for complete operation */ -static inline void vf610_nfc_done(struct mtd_info *mtd) +static void vf610_nfc_done(struct mtd_info *mtd) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); uint start; @@ -262,7 +262,7 @@ static inline void vf610_nfc_done(struct mtd_info *mtd) while (!(vf610_nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT)) { if (get_timer(start) > NFC_TIMEOUT) { - printf("Timeout while waiting for !BUSY.\n"); + printf("Timeout while waiting for IDLE.\n"); return; } } @@ -275,11 +275,13 @@ static u8 vf610_nfc_get_id(struct mtd_info *mtd, int col) if (col < 4) { flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS1); - return (flash_id >> (3-col)*8) & 0xff; + flash_id >>= (3 - col) * 8; } else { flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS2); - return flash_id >> 24; + flash_id >>= 24; } + + return flash_id & 0xff; } static u8 vf610_nfc_get_status(struct mtd_info *mtd) @@ -320,8 +322,8 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) { if (column != -1) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); - if (nfc->chip.options | NAND_BUSWIDTH_16) - column = column/2; + if (nfc->chip.options & NAND_BUSWIDTH_16) + column = column / 2; vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK, COL_ADDR_SHIFT, column); } @@ -330,6 +332,13 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) ROW_ADDR_SHIFT, page); } +static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode) +{ + vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ecc_mode); +} + static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size) { __raw_writel(size, regbase + NFC_SECTOR_SIZE); @@ -340,45 +349,66 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, int column, int page) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; - nfc->column = max(column, 0); - nfc->spareonly = 0; - nfc->alt_buf = 0; + nfc->buf_offset = max(column, 0); + nfc->alt_buf = ALT_BUF_DATA; switch (command) { + case NAND_CMD_SEQIN: + /* Use valid column/page from preread... */ + vf610_nfc_addr_cycle(mtd, column, page); + nfc->buf_offset = 0; + + /* + * SEQIN => data => PAGEPROG sequence is done by the controller + * hence we do not need to issue the command here... + */ + return; case NAND_CMD_PAGEPROG: - nfc->page = -1; - vf610_nfc_transfer_size(nfc->regs, nfc->page_sz); + trfr_sz += nfc->write_sz; + vf610_nfc_ecc_mode(mtd, ECC_HW_MODE); + vf610_nfc_transfer_size(nfc->regs, trfr_sz); vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN, command, PROGRAM_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(mtd, column, page); break; case NAND_CMD_RESET: vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE); break; - /* - * NFC does not support sub-page reads and writes, - * so emulate them using full page transfers. - */ + case NAND_CMD_READOOB: - nfc->spareonly = 1; - case NAND_CMD_SEQIN: /* Pre-read for partial writes. */ + trfr_sz += mtd->oobsize; + column = mtd->writesize; + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(mtd, column, page); + vf610_nfc_ecc_mode(mtd, ECC_BYPASS); + break; + case NAND_CMD_READ0: - column = 0; - /* Already read? */ - if (nfc->page == page) - return; - nfc->page = page; - vf610_nfc_transfer_size(nfc->regs, nfc->page_sz); + trfr_sz += mtd->writesize + mtd->oobsize; + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_ecc_mode(mtd, ECC_HW_MODE); vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, NAND_CMD_READSTART, READ_PAGE_CMD_CODE); vf610_nfc_addr_cycle(mtd, column, page); break; + case NAND_CMD_PARAM: + nfc->alt_buf = ALT_BUF_ONFI; + trfr_sz = 3 * sizeof(struct nand_onfi_params); + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_send_command(nfc->regs, NAND_CMD_PARAM, + READ_ONFI_PARAM_CMD_CODE); + vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, column); + vf610_nfc_ecc_mode(mtd, ECC_BYPASS); + break; + case NAND_CMD_ERASE1: - nfc->page = -1; vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_send_commands(nfc->regs, command, NAND_CMD_ERASE2, ERASE_CMD_CODE); @@ -387,83 +417,82 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_READID: nfc->alt_buf = ALT_BUF_ID; + nfc->buf_offset = 0; vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE); + vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, column); break; case NAND_CMD_STATUS: nfc->alt_buf = ALT_BUF_STAT; vf610_nfc_transfer_size(nfc->regs, 0); - vf610_nfc_send_command(nfc->regs, command, - STATUS_READ_CMD_CODE); + vf610_nfc_send_command(nfc->regs, command, STATUS_READ_CMD_CODE); break; default: return; } vf610_nfc_done(mtd); -} - -static inline void vf610_nfc_read_spare(struct mtd_info *mtd, void *buf, - int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - len = min(mtd->oobsize, (uint)len); - if (len > 0) - vf610_nfc_memcpy(buf, nfc->regs + mtd->writesize, len); + nfc->write_sz = 0; } /* Read data from NFC buffers */ static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->column; - uint l; + uint c = nfc->buf_offset; - /* Handle main area */ - if (!nfc->spareonly) { - l = min((uint)len, mtd->writesize - c); - nfc->column += l; - - if (!nfc->alt_buf) - vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, - l); - else - if (nfc->alt_buf & ALT_BUF_ID) - *buf = vf610_nfc_get_id(mtd, c); - else - *buf = vf610_nfc_get_status(mtd); - - buf += l; - len -= l; - } + /* Alternate buffers are only supported through read_byte */ + if (nfc->alt_buf) + return; - /* Handle spare area access */ - if (len) { - nfc->column += len; - vf610_nfc_read_spare(mtd, buf, len); - } + vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); + + nfc->buf_offset += len; } /* Write data to NFC buffers */ -static void vf610_nfc_write_buf(struct mtd_info *mtd, const u_char *buf, +static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->column; + uint c = nfc->buf_offset; uint l; - l = min((uint)len, mtd->writesize + mtd->oobsize - c); - nfc->column += l; + l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); + + nfc->write_sz += l; + nfc->buf_offset += l; } /* Read byte from NFC buffers */ -static u8 vf610_nfc_read_byte(struct mtd_info *mtd) +static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) { + struct vf610_nfc *nfc = mtd_to_nfc(mtd); u8 tmp; - vf610_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + uint c = nfc->buf_offset; + + switch (nfc->alt_buf) { + case ALT_BUF_ID: + tmp = vf610_nfc_get_id(mtd, c); + break; + case ALT_BUF_STAT: + tmp = vf610_nfc_get_status(mtd); + break; +#ifdef __LITTLE_ENDIAN + case ALT_BUF_ONFI: + /* Reverse byte since the controller uses big endianness */ + c = nfc->buf_offset ^ 0x3; + /* fall-through */ +#endif + default: + tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); + break; + } + nfc->buf_offset++; return tmp; } @@ -471,6 +500,7 @@ static u8 vf610_nfc_read_byte(struct mtd_info *mtd) static u16 vf610_nfc_read_word(struct mtd_info *mtd) { u16 tmp; + vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); return tmp; } @@ -490,12 +520,11 @@ static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) #ifdef CONFIG_VF610 u32 tmp = vf610_nfc_read(mtd, NFC_ROW_ADDR); tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); - tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; - if (chip == 0) - tmp |= 1 << ROW_ADDR_CHIP_SEL_SHIFT; - else if (chip == 1) - tmp |= 2 << ROW_ADDR_CHIP_SEL_SHIFT; + if (chip >= 0) { + tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; + tmp |= (1 << chip) << ROW_ADDR_CHIP_SEL_SHIFT; + } vf610_nfc_write(mtd, NFC_ROW_ADDR, tmp); #endif @@ -516,66 +545,78 @@ static inline int count_written_bits(uint8_t *buff, int size, int max_bits) return written_bits; } -static inline int vf610_nfc_correct_data(struct mtd_info *mtd, u_char *dat) +static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *oob, int page) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; u8 ecc_status; u8 ecc_count; - int flip; + int flips; + int flips_threshold = nfc->chip.ecc.strength / 2; + + ecc_status = vf610_nfc_read(mtd, ecc_status_off) & 0xff; + ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; - ecc_status = __raw_readb(nfc->regs + ECC_SRAM_ADDR * 8 + ECC_OFFSET); - ecc_count = ecc_status & ECC_ERR_COUNT; if (!(ecc_status & ECC_STATUS_MASK)) return ecc_count; - /* If 'ecc_count' zero or less then buffer is all 0xff or erased. */ - flip = count_written_bits(dat, nfc->chip.ecc.size, ecc_count); + /* Read OOB without ECC unit enabled */ + vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); + vf610_nfc_read_buf(mtd, oob, mtd->oobsize); - /* ECC failed. */ - if (flip > ecc_count) { - nfc->page = -1; - return -1; - } + /* + * On an erased page, bit count (including OOB) should be zero or + * at least less then half of the ECC strength. + */ + flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold); + flips += count_written_bits(oob, mtd->oobsize, flips_threshold); + + if (unlikely(flips > flips_threshold)) + return -EINVAL; /* Erased page. */ memset(dat, 0xff, nfc->chip.ecc.size); - return 0; + memset(oob, 0xff, mtd->oobsize); + return flips; } - static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int eccsize = chip->ecc.size; int stat; - uint8_t *p = buf; - - - vf610_nfc_read_buf(mtd, p, eccsize); + vf610_nfc_read_buf(mtd, buf, eccsize); if (oob_required) vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - stat = vf610_nfc_correct_data(mtd, p); + stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + return 0; + } else { mtd->ecc_stats.corrected += stat; - - return 0; + return stat; + } } /* * ECC will be calculated automatically */ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + vf610_nfc_write_buf(mtd, buf, mtd->writesize); if (oob_required) vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + /* Always write whole page including OOB due to HW ECC */ + nfc->write_sz = mtd->writesize + mtd->oobsize; + return 0; } @@ -587,7 +628,7 @@ struct vf610_nfc_config { static int vf610_nfc_nand_init(int devnum, void __iomem *addr) { - struct mtd_info *mtd = &nand_info[devnum]; + struct mtd_info *mtd; struct nand_chip *chip; struct vf610_nfc *nfc; int err = 0; @@ -610,19 +651,11 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) chip = &nfc->chip; nfc->regs = addr; - mtd->priv = chip; - chip->priv = nfc; + mtd = nand_to_mtd(chip); + nand_set_controller_data(chip, nfc); - if (cfg.width == 16) { + if (cfg.width == 16) chip->options |= NAND_BUSWIDTH_16; - vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); - } else { - chip->options &= ~NAND_BUSWIDTH_16; - vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); - } - - /* Disable subpage writes as we do not provide ecc->hwctl */ - chip->options |= NAND_NO_SUBPAGE_WRITE; chip->dev_ready = vf610_nfc_dev_ready; chip->cmdfunc = vf610_nfc_command; @@ -632,57 +665,42 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) chip->write_buf = vf610_nfc_write_buf; chip->select_chip = vf610_nfc_select_chip; - /* Bad block options. */ - if (cfg.flash_bbt) - chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_CREATE; - - /* Default to software ECC until flash ID. */ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; + chip->options |= NAND_NO_SUBPAGE_WRITE; - nfc->page_sz = PAGE_2K + OOB_64; - nfc->page_sz += cfg.width == 16 ? 1 : 0; + chip->ecc.size = PAGE_2K; /* Set configuration register. */ + vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); - /* Enable Idle IRQ */ - vf610_nfc_set(mtd, NFC_IRQ_STATUS, IDLE_EN_BIT); - - /* PAGE_CNT = 1 */ + /* Disable virtual pages, only one elementary transfer unit */ vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, CONFIG_PAGE_CNT_SHIFT, 1); - /* Set ECC_STATUS offset */ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_SRAM_ADDR_MASK, - CONFIG_ECC_SRAM_ADDR_SHIFT, ECC_SRAM_ADDR); - /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) { err = -ENXIO; goto error; } - chip->ecc.mode = NAND_ECC_SOFT; /* default */ + if (cfg.width == 16) + vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); - nfc->page_sz = mtd->writesize + mtd->oobsize; + /* Bad block options. */ + if (cfg.flash_bbt) + chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB | + NAND_BBT_CREATE; /* Single buffer only, max 256 OOB minus ECC status */ - if (nfc->page_sz > PAGE_2K + 256 - 8) { - dev_err(nfc->dev, "Unsupported flash size\n"); + if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { + dev_err(nfc->dev, "Unsupported flash page size\n"); err = -ENXIO; goto error; } - nfc->page_sz += cfg.width == 16 ? 1 : 0; if (cfg.hardware_ecc) { if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { @@ -691,7 +709,16 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) goto error; } - chip->ecc.layout = &vf610_nfc_ecc45; + if (chip->ecc.size != mtd->writesize) { + dev_err(nfc->dev, "ecc size: %d\n", chip->ecc.size); + dev_err(nfc->dev, "Step size needs to be page size\n"); + err = -ENXIO; + goto error; + } + + /* Current HW ECC layouts only use 64 bytes of OOB */ + if (mtd->oobsize > 64) + mtd->oobsize = 64; /* propagate ecc.layout to mtd_info */ mtd->ecclayout = chip->ecc.layout; @@ -699,16 +726,23 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) chip->ecc.write_page = vf610_nfc_write_page; chip->ecc.mode = NAND_ECC_HW; - chip->ecc.bytes = 45; chip->ecc.size = PAGE_2K; + chip->ecc.layout = &vf610_nfc_ecc; +#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES) chip->ecc.strength = 24; + chip->ecc.bytes = 45; +#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES) + chip->ecc.strength = 32; + chip->ecc.bytes = 60; +#endif - /* set ECC mode to 45 bytes OOB with 24 bits correction */ + /* Set ECC_STATUS offset */ vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_45_BYTE); + CONFIG_ECC_SRAM_ADDR_MASK, + CONFIG_ECC_SRAM_ADDR_SHIFT, + ECC_SRAM_ADDR >> 3); - /* Enable ECC_STATUS */ + /* Enable ECC status in SRAM */ vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); } @@ -717,7 +751,7 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) if (err) return err; - err = nand_register(devnum); + err = nand_register(devnum, mtd); if (err) return err;