diff options
author | Minkyu Kang <mk7.kang@samsung.com> | 2010-01-15 22:41:58 +0900 |
---|---|---|
committer | Minkyu Kang <mk7.kang@samsung.com> | 2010-01-15 22:41:58 +0900 |
commit | e598dfc22c8789991d165714bec53b2390fc999d (patch) | |
tree | cf3c704c4b00e06605bdfe23384b8af97022efa6 /drivers/mtd | |
parent | e7ae13a57b4cbaa2cd3da8ffca614853d9d84230 (diff) | |
parent | 1c2a8e359ebbec0dbef62f5b54c72f9cd72ccd59 (diff) | |
download | u-boot-imx-e598dfc22c8789991d165714bec53b2390fc999d.zip u-boot-imx-e598dfc22c8789991d165714bec53b2390fc999d.tar.gz u-boot-imx-e598dfc22c8789991d165714bec53b2390fc999d.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-arm
Conflicts:
cpu/arm_cortexa8/s5pc1xx/cache.c
include/asm-arm/arch-s5pc1xx/sys_proto.h
include/sja1000.h
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/davinci_nand.c | 250 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_elbc_nand.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 147 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 41 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_util.c | 10 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c2410_nand.c | 31 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 742 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 14 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_uboot.c | 4 | ||||
-rw-r--r-- | drivers/mtd/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash.c | 5 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash_internal.h | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/stmicro.c | 3 | ||||
-rw-r--r-- | drivers/mtd/spi/winbond.c | 332 |
14 files changed, 1356 insertions, 229 deletions
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index eabaf3e..bfc2acf 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -59,14 +59,111 @@ static emif_registers *const emif_regs = (void *) DAVINCI_ASYNC_EMIF_CNTRL_BASE; +/* + * Exploit the little endianness of the ARM to do multi-byte transfers + * per device read. This can perform over twice as quickly as individual + * byte transfers when buffer alignment is conducive. + * + * NOTE: This only works if the NAND is not connected to the 2 LSBs of + * the address bus. On Davinci EVM platforms this has always been true. + */ +static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + const u32 *nand = chip->IO_ADDR_R; + + /* Make sure that buf is 32 bit aligned */ + if (((int)buf & 0x3) != 0) { + if (((int)buf & 0x1) != 0) { + if (len) { + *buf = readb(nand); + buf += 1; + len--; + } + } + + if (((int)buf & 0x3) != 0) { + if (len >= 2) { + *(u16 *)buf = readw(nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + *(u32 *)buf = readl(nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + *(u16 *)buf = readw(nand); + buf += 2; + len -= 2; + } + + if (len) + *buf = readb(nand); + } +} + +static void nand_davinci_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + const u32 *nand = chip->IO_ADDR_W; + + /* Make sure that buf is 32 bit aligned */ + if (((int)buf & 0x3) != 0) { + if (((int)buf & 0x1) != 0) { + if (len) { + writeb(*buf, nand); + buf += 1; + len--; + } + } + + if (((int)buf & 0x3) != 0) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + writel(*(u32 *)buf, nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + + if (len) + writeb(*buf, nand); + } +} + static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *this = mtd->priv; u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W; - IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); - if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); + if ( ctrl & NAND_CLE ) IO_ADDR_W |= MASK_CLE; if ( ctrl & NAND_ALE ) @@ -75,33 +172,28 @@ static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int c } if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); + writeb(cmd, IO_ADDR_W); } #ifdef CONFIG_SYS_NAND_HW_ECC static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) { - int dummy; + u_int32_t val; - dummy = emif_regs->NANDF1ECC; + (void)readl(&(emif_regs->NANDFECC[CONFIG_SYS_NAND_CS - 2])); - /* FIXME: only chipselect 0 is supported for now */ - emif_regs->NANDFCR |= 1 << 8; + val = readl(&emif_regs->NANDFCR); + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS); + writel(val, &emif_regs->NANDFCR); } static u_int32_t nand_davinci_readecc(struct mtd_info *mtd, u_int32_t region) { u_int32_t ecc = 0; - if (region == 1) - ecc = emif_regs->NANDF1ECC; - else if (region == 2) - ecc = emif_regs->NANDF2ECC; - else if (region == 3) - ecc = emif_regs->NANDF3ECC; - else if (region == 4) - ecc = emif_regs->NANDF4ECC; + ecc = readl(&(emif_regs->NANDFECC[region - 1])); return(ecc); } @@ -182,13 +274,7 @@ static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, u_char * #ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = { -/* - * TI uses a different layout for 4K page deviecs. Since the - * eccpos filed can hold only a limited number of entries, adding - * support for 4K page will result in compilation warnings - * 4K Support will be added later - */ -#ifdef CONFIG_SYS_NAND_PAGE_2K +#if defined(CONFIG_SYS_NAND_PAGE_2K) .eccbytes = 40, .eccpos = { 24, 25, 26, 27, 28, @@ -200,6 +286,21 @@ static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = { .oobfree = { {.offset = 2, .length = 22, }, }, +#elif defined(CONFIG_SYS_NAND_PAGE_4K) + .eccbytes = 80, + .eccpos = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 46, }, + }, #endif }; @@ -214,8 +315,12 @@ static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode) * Start a new ECC calculation for reading or writing 512 bytes * of data. */ - val = (emif_regs->NANDFCR & ~(3 << 4)) | (1 << 12); - emif_regs->NANDFCR = val; + val = readl(&emif_regs->NANDFCR); + val &= ~DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK; + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_4BIT_ECC_SEL(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_4BIT_ECC_START; + writel(val, &emif_regs->NANDFCR); break; case NAND_ECC_READSYN: val = emif_regs->NAND4BITECC1; @@ -239,59 +344,55 @@ static int nand_davinci_4bit_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) { - unsigned int hw_4ecc[4] = { 0, 0, 0, 0 }; - unsigned int const1 = 0, const2 = 0; - unsigned char count1 = 0; + unsigned int hw_4ecc[4]; + unsigned int i; nand_davinci_4bit_readecc(mtd, hw_4ecc); /*Convert 10 bit ecc value to 8 bit */ - for (count1 = 0; count1 < 2; count1++) { - const2 = count1 * 5; - const1 = count1 * 2; + for (i = 0; i < 2; i++) { + unsigned int hw_ecc_low = hw_4ecc[i * 2]; + unsigned int hw_ecc_hi = hw_4ecc[(i * 2) + 1]; /* Take first 8 bits from val1 (count1=0) or val5 (count1=1) */ - ecc_code[const2] = hw_4ecc[const1] & 0xFF; + *ecc_code++ = hw_ecc_low & 0xFF; /* * Take 2 bits as LSB bits from val1 (count1=0) or val5 * (count1=1) and 6 bits from val2 (count1=0) or * val5 (count1=1) */ - ecc_code[const2 + 1] = - ((hw_4ecc[const1] >> 8) & 0x3) | ((hw_4ecc[const1] >> 14) & - 0xFC); + *ecc_code++ = + ((hw_ecc_low >> 8) & 0x3) | ((hw_ecc_low >> 14) & 0xFC); /* * Take 4 bits from val2 (count1=0) or val5 (count1=1) and * 4 bits from val3 (count1=0) or val6 (count1=1) */ - ecc_code[const2 + 2] = - ((hw_4ecc[const1] >> 22) & 0xF) | - ((hw_4ecc[const1 + 1] << 4) & 0xF0); + *ecc_code++ = + ((hw_ecc_low >> 22) & 0xF) | ((hw_ecc_hi << 4) & 0xF0); /* * Take 6 bits from val3(count1=0) or val6 (count1=1) and * 2 bits from val4 (count1=0) or val7 (count1=1) */ - ecc_code[const2 + 3] = - ((hw_4ecc[const1 + 1] >> 4) & 0x3F) | - ((hw_4ecc[const1 + 1] >> 10) & 0xC0); + *ecc_code++ = + ((hw_ecc_hi >> 4) & 0x3F) | ((hw_ecc_hi >> 10) & 0xC0); /* Take 8 bits from val4 (count1=0) or val7 (count1=1) */ - ecc_code[const2 + 4] = (hw_4ecc[const1 + 1] >> 18) & 0xFF; + *ecc_code++ = (hw_ecc_hi >> 18) & 0xFF; } + return 0; } - static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { - unsigned short ecc_10bit[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int i; - unsigned int hw_4ecc[4] = { 0, 0, 0, 0 }, iserror = 0; - unsigned short *pspare = NULL, *pspare1 = NULL; + unsigned int hw_4ecc[4]; + unsigned int iserror; + unsigned short *ecc16; unsigned int numerrors, erroraddress, errorvalue; u32 val; @@ -308,44 +409,41 @@ static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat, return 0; /* Convert 8 bit in to 10 bit */ - pspare = (unsigned short *)&read_ecc[2]; - pspare1 = (unsigned short *)&read_ecc[0]; + ecc16 = (unsigned short *)&read_ecc[0]; - /* Take 10 bits from 0th and 1st bytes */ - ecc_10bit[0] = (*pspare1) & 0x3FF; + /* + * Write the parity values in the NAND Flash 4-bit ECC Load register. + * Write each parity value one at a time starting from 4bit_ecc_val8 + * to 4bit_ecc_val1. + */ - /* Take 6 bits from 1st byte and 4 bits from 2nd byte */ - ecc_10bit[1] = (((*pspare1) >> 10) & 0x3F) - | (((pspare[0]) << 6) & 0x3C0); + /*Take 2 bits from 8th byte and 8 bits from 9th byte */ + writel(((ecc16[4]) >> 6) & 0x3FF, &emif_regs->NAND4BITECCLOAD); - /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */ - ecc_10bit[2] = ((pspare[0]) >> 4) & 0x3FF; + /* Take 4 bits from 7th byte and 6 bits from 8th byte */ + writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0), + &emif_regs->NAND4BITECCLOAD); - /*Take 2 bits from 3rd byte and 8 bits from 4th byte */ - ecc_10bit[3] = (((pspare[0]) >> 14) & 0x3) - | ((((pspare[1])) << 2) & 0x3FC); + /* Take 6 bits from 6th byte and 4 bits from 7th byte */ + writel((ecc16[3] >> 2) & 0x3FF, &emif_regs->NAND4BITECCLOAD); /* Take 8 bits from 5th byte and 2 bits from 6th byte */ - ecc_10bit[4] = ((pspare[1]) >> 8) - | ((((pspare[2])) << 8) & 0x300); + writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300), + &emif_regs->NAND4BITECCLOAD); - /* Take 6 bits from 6th byte and 4 bits from 7th byte */ - ecc_10bit[5] = (pspare[2] >> 2) & 0x3FF; + /*Take 2 bits from 3rd byte and 8 bits from 4th byte */ + writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC), + &emif_regs->NAND4BITECCLOAD); - /* Take 4 bits from 7th byte and 6 bits from 8th byte */ - ecc_10bit[6] = (((pspare[2]) >> 12) & 0xF) - | ((((pspare[3])) << 4) & 0x3F0); + /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */ + writel(((ecc16[1]) >> 4) & 0x3FF, &emif_regs->NAND4BITECCLOAD); - /*Take 2 bits from 8th byte and 8 bits from 9th byte */ - ecc_10bit[7] = ((pspare[3]) >> 6) & 0x3FF; + /* Take 6 bits from 1st byte and 4 bits from 2nd byte */ + writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0), + &emif_regs->NAND4BITECCLOAD); - /* - * Write the parity values in the NAND Flash 4-bit ECC Load register. - * Write each parity value one at a time starting from 4bit_ecc_val8 - * to 4bit_ecc_val1. - */ - for (i = 7; i >= 0; i--) - emif_regs->NAND4BITECCLOAD = ecc_10bit[i]; + /* Take 10 bits from 0th and 1st bytes */ + writel((ecc16[0]) & 0x3FF, &emif_regs->NAND4BITECCLOAD); /* * Perform a dummy read to the EMIF Revision Code and Status register. @@ -362,8 +460,7 @@ static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat, */ nand_davinci_4bit_readecc(mtd, hw_4ecc); - if (hw_4ecc[0] == ECC_STATE_NO_ERR && hw_4ecc[1] == ECC_STATE_NO_ERR && - hw_4ecc[2] == ECC_STATE_NO_ERR && hw_4ecc[3] == ECC_STATE_NO_ERR) + if (!(hw_4ecc[0] | hw_4ecc[1] | hw_4ecc[2] | hw_4ecc[3])) return 0; /* @@ -510,6 +607,9 @@ void davinci_nand_init(struct nand_chip *nand) /* Set address of hardware control function */ nand->cmd_ctrl = nand_davinci_hwcontrol; + nand->read_buf = nand_davinci_read_buf; + nand->write_buf = nand_davinci_write_buf; + nand->dev_ready = nand_davinci_dev_ready; nand_flash_init(); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 50cb4aa..146e9bf 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -766,9 +766,6 @@ int board_nand_init(struct nand_chip *nand) nand->waitfunc = fsl_elbc_wait; /* set up nand options */ - /* redirect the pointer of bbt pattern to RAM */ - bbt_main_descr.pattern = bbt_pattern; - bbt_mirror_descr.pattern = mirror_pattern; nand->bbt_td = &bbt_main_descr; nand->bbt_md = &bbt_mirror_descr; @@ -815,7 +812,6 @@ int board_nand_init(struct nand_chip *nand) /* Large-page-specific setup */ if (or & OR_FCM_PGS) { priv->page_size = 1; - largepage_memorybased.pattern = scan_ff_pattern; nand->badblock_pattern = &largepage_memorybased; /* adjust ecc setup if needed */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 426bb95..7171bdd 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -893,6 +893,9 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read + * + * Not for syndrome calculating ecc controllers, which use a special oob layout */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) @@ -903,10 +906,53 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * We need a special oob layout and handling even when OOB isn't used. + */ +static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + + return 0; +} + +/** * nand_read_page_swecc - [REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) @@ -946,9 +992,9 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function * @mtd: mtd info structure * @chip: nand chip info structure - * @dataofs offset of requested data within the page - * @readlen data length - * @buf: buffer to store read data + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data */ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) { @@ -1015,7 +1061,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 int stat; stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat < 0) + if (stat == -1) mtd->ecc_stats.failed++; else mtd->ecc_stats.corrected += stat; @@ -1028,6 +1074,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * Not for syndrome calculating ecc controllers which need a special oob layout */ @@ -1059,7 +1106,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat == -1) + if (stat < 0) mtd->ecc_stats.failed++; else mtd->ecc_stats.corrected += stat; @@ -1072,6 +1119,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * Hardware ECC for large page chips, require OOB to be read first. * For this ECC mode, the write_page method is re-used from ECC_HW. @@ -1120,6 +1168,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. @@ -1677,6 +1726,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * + * Not for syndrome calculating ecc controllers, which use a special oob layout */ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) @@ -1686,6 +1737,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_write_page_raw_syndrome - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * + * We need a special oob layout and handling even when ECC isn't checked. + */ +static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); +} +/** * nand_write_page_swecc - [REPLACABLE] software ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure @@ -2211,13 +2300,15 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt) { - int page, len, status, pages_per_block, ret, chipnr; + int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; - int rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS]={0}; + loff_t rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS] = {0}; unsigned int bbt_masked_page = 0xffffffff; + loff_t len; - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", - (unsigned int) instr->addr, (unsigned int) instr->len); + MTDDEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, " + "len = %llu\n", (unsigned long long) instr->addr, + (unsigned long long) instr->len); /* Start address must align on block boundary */ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { @@ -2313,7 +2404,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; - instr->fail_addr = (page << chip->page_shift); + instr->fail_addr = ((loff_t)page << chip->page_shift); goto erase_exit; } @@ -2323,7 +2414,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, */ if (bbt_masked_page != 0xffffffff && (page & BBT_PAGE_MASK) == bbt_masked_page) - rewrite_bbt[chipnr] = (page << chip->page_shift); + rewrite_bbt[chipnr] = + ((loff_t)page << chip->page_shift); /* Increment page address and decrement length */ len -= (1 << chip->phys_erase_shift); @@ -2370,8 +2462,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, continue; /* update the BBT for chip */ MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], - chip->bbt_td->pages[chipnr]); + "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } @@ -2566,7 +2658,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!mtd->name) mtd->name = type->name; - chip->chipsize = type->chipsize << 20; + chip->chipsize = (uint64_t)type->chipsize << 20; /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { @@ -2624,7 +2716,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; - chip->chip_shift = ffs(chip->chipsize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31; /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? @@ -2722,7 +2817,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure - * @maxchips: Number of chips to scan for * * This is the second phase of the normal nand_scan() function. It * fills out all the uninitialized function pointers with the defaults @@ -2761,7 +2855,6 @@ int nand_scan_tail(struct mtd_info *mtd) default: printk(KERN_WARNING "No oob scheme defined for " "oobsize %d\n", mtd->oobsize); -/* BUG(); */ } } @@ -2772,10 +2865,6 @@ int nand_scan_tail(struct mtd_info *mtd) * check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw; switch (chip->ecc.mode) { case NAND_ECC_HW_OOB_FIRST: @@ -2795,6 +2884,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_hwecc; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_std; if (!chip->ecc.write_oob) @@ -2816,6 +2909,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_syndrome; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw_syndrome; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw_syndrome; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_syndrome; if (!chip->ecc.write_oob) @@ -2834,6 +2931,8 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_swecc; chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = 256; @@ -2846,6 +2945,8 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_raw; chip->ecc.write_page = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; @@ -2862,7 +2963,8 @@ int nand_scan_tail(struct mtd_info *mtd) * the out of band area */ chip->ecc.layout->oobavail = 0; - for (i = 0; chip->ecc.layout->oobfree[i].length; i++) + for (i = 0; chip->ecc.layout->oobfree[i].length + && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; mtd->oobavail = chip->ecc.layout->oobavail; @@ -2890,6 +2992,7 @@ int nand_scan_tail(struct mtd_info *mtd) break; case 4: case 8: + case 16: mtd->subpage_sft = 2; break; } diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index d68a315f..2fe68ab 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -182,16 +182,19 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { - printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", + (loff_t)((offs << 2) + + (act >> 1)) << + this->bbt_erase_shift); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); mtd->ecc_stats.bbtblocks++; continue; } /* Leave it for now, if its matured we can move this * message to MTD_DEBUG_LEVEL0 */ - printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) << + this->bbt_erase_shift); /* Factory marked bad or worn out ? */ if (tmp == 0) this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); @@ -295,8 +298,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, - mtd->writesize); + scan_read_raw(mtd, buf, (loff_t)td->pages[0] << + this->page_shift, mtd->writesize); td->version[0] = buf[mtd->writesize + td->veroffs]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); @@ -304,8 +307,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, - mtd->writesize); + scan_read_raw(mtd, buf, (loff_t)md->pages[0] << + this->page_shift, mtd->writesize); md->version[0] = buf[mtd->writesize + md->veroffs]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); @@ -422,7 +425,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; - from = startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << (this->bbt_erase_shift - 1); } for (i = startblock; i < numblocks;) { @@ -440,8 +443,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); MTDDEBUG (MTD_DEBUG_LEVEL0, - "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); + "Bad eraseblock %d at 0x%012llx\n", + i >> 1, (unsigned long long)from); mtd->ecc_stats.badblocks++; } @@ -507,7 +510,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr for (block = 0; block < td->maxblocks; block++) { int actblock = startblock + dir * block; - loff_t offs = actblock << this->bbt_erase_shift; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; /* Read first page */ scan_read_raw(mtd, buf, offs, mtd->writesize); @@ -731,7 +734,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; - einfo.addr = (unsigned long)to; + einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); if (res < 0) @@ -741,8 +744,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (res < 0) goto outerr; - printk(KERN_DEBUG "Bad block table written to 0x%08x, version " - "0x%02X\n", (unsigned int)to, td->version[chip]); + printk(KERN_DEBUG "Bad block table written to 0x%012llx, " + "version 0x%02X\n", (unsigned long long)to, + td->version[chip]); /* Mark it as used */ td->pages[chip] = page; @@ -922,7 +926,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) newval = oldval | (0x2 << (block & 0x06)); this->bbt[(block >> 3)] = newval; if ((oldval != newval) && td->reserved_block_code) - nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)block << + (this->bbt_erase_shift - 1)); continue; } update = 0; @@ -943,7 +948,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) new ones have been marked, then we need to update the stored bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 2) << + (this->bbt_erase_shift - 1)); } } @@ -1039,7 +1045,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) if (!this->bbt || !td) return -EINVAL; - len = mtd->size >> (this->bbt_erase_shift + 2); /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index bec9277..29c42f7 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -41,10 +41,6 @@ #include <nand.h> #include <jffs2/jffs2.h> -#if !defined(CONFIG_SYS_64BIT_VSPRINTF) -#warning Please define CONFIG_SYS_64BIT_VSPRINTF for correct output! -#endif - typedef struct erase_info erase_info_t; typedef struct mtd_info mtd_info_t; @@ -452,7 +448,7 @@ static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset, len_incl_bad += block_len; offset += block_len; - if ((offset + len_incl_bad) >= nand->size) + if (offset >= nand->size) break; } @@ -490,7 +486,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, len_incl_bad = get_len_incl_bad (nand, offset, *length); - if ((offset + len_incl_bad) >= nand->size) { + if ((offset + len_incl_bad) > nand->size) { printf ("Attempt to write outside the flash area\n"); return -EINVAL; } @@ -562,7 +558,7 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, len_incl_bad = get_len_incl_bad (nand, offset, *length); - if ((offset + len_incl_bad) >= nand->size) { + if ((offset + len_incl_bad) > nand->size) { printf ("Attempt to read outside the flash area\n"); return -EINVAL; } diff --git a/drivers/mtd/nand/s3c2410_nand.c b/drivers/mtd/nand/s3c2410_nand.c index 3f15d2d..a27d47e 100644 --- a/drivers/mtd/nand/s3c2410_nand.c +++ b/drivers/mtd/nand/s3c2410_nand.c @@ -36,6 +36,21 @@ #define S3C2410_ADDR_NALE 4 #define S3C2410_ADDR_NCLE 8 +#ifdef CONFIG_NAND_SPL + +/* in the early stage of NAND flash booting, printf() is not available */ +#define printf(fmt, args...) + +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); +} +#endif + static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *chip = mtd->priv; @@ -83,9 +98,10 @@ void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { - ecc_code[0] = NFECC0; - ecc_code[1] = NFECC1; - ecc_code[2] = NFECC2; + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + ecc_code[0] = readb(&nand->NFECC); + ecc_code[1] = readb(&nand->NFECC + 1); + ecc_code[2] = readb(&nand->NFECC + 2); debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", mtd , ecc_code[0], ecc_code[1], ecc_code[2]); @@ -130,8 +146,13 @@ int board_nand_init(struct nand_chip *nand) /* initialize nand_chip data structure */ nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA; + nand->select_chip = NULL; + /* read_buf and write_buf are default */ /* read_byte and write_byte are default */ +#ifdef CONFIG_NAND_SPL + nand->read_buf = nand_read_buf; +#endif /* hwcontrol always must be implemented */ nand->cmd_ctrl = s3c2410_hwcontrol; @@ -142,7 +163,9 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.hwctl = s3c2410_nand_enable_hwecc; nand->ecc.calculate = s3c2410_nand_calculate_ecc; nand->ecc.correct = s3c2410_nand_correct_data; - nand->ecc.mode = NAND_ECC_HW3_512; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; #else nand->ecc.mode = NAND_ECC_SOFT; #endif diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 368fa6e..f9273ab 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -9,6 +9,11 @@ * auto-placement support, read-while load support, various fixes * Copyright (C) Nokia Corporation, 2007 * + * Rohit Hagargundgi <h.rohit at samsung.com>, + * Amul Kumar Saha <amul.saha@samsung.com>: + * Flex-OneNAND support + * Copyright (C) Samsung Electronics, 2009 + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -24,7 +29,7 @@ #include <malloc.h> /* It should access 16-bit instead of 8-bit */ -static inline void *memcpy_16(void *dst, const void *src, unsigned int len) +static void *memcpy_16(void *dst, const void *src, unsigned int len) { void *ret = dst; short *d = dst; @@ -37,6 +42,27 @@ static inline void *memcpy_16(void *dst, const void *src, unsigned int len) } /** + * onenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes + */ +static struct nand_ecclayout onenand_oob_128 = { + .eccbytes = 64, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105 + }, + .oobfree = { + {2, 4}, {18, 4}, {34, 4}, {50, 4}, + {66, 4}, {82, 4}, {98, 4}, {114, 4} + } +}; + +/** * onenand_oob_64 - oob info for large (2KB) page */ static struct nand_ecclayout onenand_oob_64 = { @@ -74,6 +100,14 @@ static const unsigned char ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ }; /** @@ -180,6 +214,85 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) } /** + * flexonenand_block - Return block number for flash address + * @param this - OneNAND device structure + * @param addr - Address for which block number is needed + */ +static unsigned int flexonenand_block(struct onenand_chip *this, loff_t addr) +{ + unsigned int boundary, blk, die = 0; + + if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + blk += die ? this->density_mask : 0; + return blk; +} + +unsigned int onenand_block(struct onenand_chip *this, loff_t addr) +{ + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_block(this, addr); +} + +/** + * flexonenand_addr - Return address of the block + * @this: OneNAND device structure + * @block: Block number on Flex-OneNAND + * + * Return address of the block + */ +static loff_t flexonenand_addr(struct onenand_chip *this, int block) +{ + loff_t ofs = 0; + int die = 0, boundary; + + if (ONENAND_IS_DDP(this) && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + + boundary = this->boundary[die]; + ofs += (loff_t) block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (loff_t) (block - boundary - 1) + << (this->erase_shift - 1); + return ofs; +} + +loff_t onenand_addr(struct onenand_chip *this, int block) +{ + if (!FLEXONENAND(this)) + return (loff_t) block << this->erase_shift; + return flexonenand_addr(this, block); +} + +/** + * flexonenand_region - [Flex-OneNAND] Return erase region of addr + * @param mtd MTD device structure + * @param addr address whose erase region needs to be identified + */ +int flexonenand_region(struct mtd_info *mtd, loff_t addr) +{ + int i; + + for (i = 0; i < mtd->numeraseregions; i++) + if (addr < mtd->eraseregions[i].offset) + break; + return i - 1; +} + +/** * onenand_get_density - [DEFAULT] Get OneNAND density * @param dev_id OneNAND device ID * @@ -205,10 +318,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - int value, readcmd = 0; + int value; int block, page; + /* Now we use page size operation */ - int sectors = 4, count = 4; + int sectors = 0, count = 0; /* Address translation */ switch (cmd) { @@ -220,15 +334,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, page = -1; break; + case FLEXONENAND_CMD_PI_ACCESS: + /* addr contains die index */ + block = addr * this->density_mask; + page = -1; + break; + case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: - block = (int)(addr >> this->erase_shift); + block = onenand_block(this, addr); page = -1; break; + case FLEXONENAND_CMD_READ_PI: + cmd = ONENAND_CMD_READ; + block = addr * this->density_mask; + page = 0; + break; + default: - block = (int)(addr >> this->erase_shift); - page = (int)(addr >> this->page_shift); + block = onenand_block(this, addr); + page = (int) (addr + - onenand_addr(this, block)) >> this->page_shift; page &= this->page_mask; break; } @@ -240,8 +367,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_MLC(this)) + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); return 0; } @@ -252,7 +382,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - /* Write 'DFS, FBA' of Flash */ + /* Select DataRAM for DDP */ value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); @@ -262,10 +392,14 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, int dataram; switch (cmd) { + case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); - readcmd = 1; + if (ONENAND_IS_MLC(this)) + dataram = ONENAND_SET_BUFFERRAM0(this); + else + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); + break; default: @@ -292,6 +426,29 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, } /** + * onenand_read_ecc - return ecc status + * @param this onenand chip structure + */ +static int onenand_read_ecc(struct onenand_chip *this) +{ + int ecc, i; + + if (!FLEXONENAND(this)) + return this->read_word(this->base + ONENAND_REG_ECC_STATUS); + + for (i = 0; i < 4; i++) { + ecc = this->read_word(this->base + + ((ONENAND_REG_ECC_STATUS + i) << 1)); + if (likely(!ecc)) + continue; + if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) + return ONENAND_ECC_2BIT_ALL; + } + + return 0; +} + +/** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure * @param state state to select the max. timeout value @@ -305,7 +462,7 @@ static int onenand_wait(struct mtd_info *mtd, int state) struct onenand_chip *this = mtd->priv; unsigned int flags = ONENAND_INT_MASTER; unsigned int interrupt = 0; - unsigned int ctrl, ecc; + unsigned int ctrl; while (1) { interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); @@ -315,6 +472,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + if (interrupt & ONENAND_INT_READ) { + int ecc = onenand_read_ecc(this); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk("onenand_wait: ECC error = 0x%04x\n", ecc); + return -EBADMSG; + } + } + if (ctrl & ONENAND_CTRL_ERROR) { printk("onenand_wait: controller error = 0x%04x\n", ctrl); if (ctrl & ONENAND_CTRL_LOCK) @@ -324,14 +489,6 @@ static int onenand_wait(struct mtd_info *mtd, int state) return -EIO; } - if (interrupt & ONENAND_INT_READ) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_wait: ECC error = 0x%04x\n", ecc); - return -EBADMSG; - } - } return 0; } @@ -499,7 +656,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) if (found && ONENAND_IS_DDP(this)) { /* Select DataRAM for DDP */ - int block = (int) (addr >> this->erase_shift); + int block = onenand_block(this, addr); int value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } @@ -632,6 +789,45 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, } /** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait + * + * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has + * lower page address and MSB page has higher page address in paired pages. + * If power off occurs during MSB page program, the paired LSB page data can + * become corrupt. LSB page recovery read is a way to read LSB page though page + * data are corrupted. When uncorrectable error occurs as a result of LSB page + * read after power up, issue LSB page recovery read. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + int i; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR) + return status; + + /* check if address lies in MLC region */ + i = flexonenand_region(mtd, addr); + if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) + return status; + + printk("onenand_recover_lsb:" + "Attempting to recover from uncorrectable read\n"); + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from @@ -673,6 +869,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; /* Read-while-load method */ + /* Note: We can't use this feature in MLC */ /* Do first load to bufferRAM */ if (read < len) { @@ -680,6 +877,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, this->main_buf = buf; this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; @@ -694,7 +893,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, while (!ret) { /* If there is more to load then start next load */ from += thislen; - if (read + thislen < len) { + if (!ONENAND_IS_MLC(this) && read + thislen < len) { this->main_buf = buf + thislen; this->command(mtd, ONENAND_CMD_READ, from, writesize); /* @@ -728,6 +927,16 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, oobcolumn = 0; } + if (ONENAND_IS_MLC(this) && (read + thislen < len)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } + /* See if we are done */ read += thislen; if (read == len) @@ -735,16 +944,19 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, /* Set up for next read from bufferRAM */ if (unlikely(boundary)) this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); - ONENAND_SET_NEXT_BUFFERRAM(this); + if (!ONENAND_IS_MLC(this)) + ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, writesize, len - read); column = 0; - /* Now wait for load */ - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); - if (ret == -EBADMSG) - ret = 0; + if (!ONENAND_IS_MLC(this)) { + /* Now wait for load */ + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } } /* @@ -781,7 +993,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; - int ret = 0; + int ret = 0, readcmd; from += ops->ooboffs; @@ -812,16 +1024,21 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { thislen = oobsize - column; thislen = min_t(int, thislen, len); this->spare_buf = buf; - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; @@ -945,9 +1162,12 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) + int ecc = onenand_read_ecc(this); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" + ", controller = 0x%04x\n", ecc, ctrl); return ONENAND_BBT_READ_ERROR; + } } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); @@ -976,12 +1196,14 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; - int ret = 0; + int ret = 0, readcmd; size_t len = ops->ooblen; u_char *buf = ops->oobbuf; MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + /* Initialize return value */ ops->oobretlen = 0; @@ -1002,11 +1224,14 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, thislen = min_t(int, thislen, len); this->spare_buf = buf; - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->bbt_wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + if (ret) break; @@ -1044,9 +1269,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to { struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; - int status, i; + int status, i, readcmd; - this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + this->command(mtd, readcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) @@ -1291,7 +1518,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; - int written = 0; + int written = 0, oobcmd; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; @@ -1333,6 +1560,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, oobbuf = this->oob_buf; + oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); @@ -1348,7 +1577,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + if (ONENAND_IS_MLC(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, 0, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { @@ -1475,34 +1711,54 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) { struct onenand_chip *this = mtd->priv; unsigned int block_size; - loff_t addr; - int len; - int ret = 0; - - MTDDEBUG (MTD_DEBUG_LEVEL3, - "onenand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); + loff_t addr = instr->addr; + unsigned int len = instr->len; + int ret = 0, i; + struct mtd_erase_region_info *region = NULL; + unsigned int region_end = 0; - block_size = (1 << this->erase_shift); + MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", + (unsigned int) addr, len); - /* Start address must align on block boundary */ - if (unlikely(instr->addr & (block_size - 1))) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_erase: Unaligned address\n"); + /* Do not allow erase past end of device */ + if (unlikely((len + addr) > mtd->size)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + "Erase past end of device\n"); return -EINVAL; } - /* Length must align on block boundary */ - if (unlikely(instr->len & (block_size - 1))) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_erase: Length not block aligned\n"); - return -EINVAL; + if (FLEXONENAND(this)) { + /* Find the eraseregion of this address */ + i = flexonenand_region(mtd, addr); + region = &mtd->eraseregions[i]; + + block_size = region->erasesize; + region_end = region->offset + + region->erasesize * region->numblocks; + + /* Start address within region must align on block boundary. + * Erase region's start offset is always block start address. + */ + if (unlikely((addr - region->offset) & (block_size - 1))) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + " Unaligned address\n"); + return -EINVAL; + } + } else { + block_size = 1 << this->erase_shift; + + /* Start address must align on block boundary */ + if (unlikely(addr & (block_size - 1))) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + "Unaligned address\n"); + return -EINVAL; + } } - /* Do not allow erase past end of device */ - if (unlikely((instr->len + instr->addr) > mtd->size)) { + /* Length must align on block boundary */ + if (unlikely(len & (block_size - 1))) { MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_erase: Erase past end of device\n"); + "onenand_erase: Length not block aligned\n"); return -EINVAL; } @@ -1512,9 +1768,6 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) onenand_get_device(mtd, FL_ERASING); /* Loop throught the pages */ - len = instr->len; - addr = instr->addr; - instr->state = MTD_ERASING; while (len) { @@ -1541,14 +1794,7 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) else MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " "Failed erase, block %d\n", - (unsigned)(addr >> this->erase_shift)); - if (ret == -EPERM) - printk("onenand_erase: " - "Device is write protected!!!\n"); - else - printk("onenand_erase: " - "Failed erase, block %d\n", - (unsigned)(addr >> this->erase_shift)); + onenand_block(this, addr)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; @@ -1557,6 +1803,23 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) len -= block_size; addr += block_size; + + if (addr == region_end) { + if (!len) + break; + region++; + + block_size = region->erasesize; + region_end = region->offset + + region->erasesize * region->numblocks; + + if (len & (block_size - 1)) { + /* This has been checked at MTD + * partitioning level. */ + printk("onenand_erase: Unaligned address\n"); + goto erase_exit; + } + } } instr->state = MTD_ERASE_DONE; @@ -1634,7 +1897,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) int block; /* Get block number */ - block = ((int) ofs) >> bbm->bbt_erase_shift; + block = onenand_block(this, ofs); if (bbm->bbt) bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); @@ -1682,8 +1945,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int int start, end, block, value, status; int wp_status_mask; - start = ofs >> this->erase_shift; - end = len >> this->erase_shift; + start = onenand_block(this, ofs); + end = onenand_block(this, ofs + len); if (cmd == ONENAND_CMD_LOCK) wp_status_mask = ONENAND_WP_LS; @@ -1718,7 +1981,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int } /* Block lock scheme */ - for (block = start; block < start + end; block++) { + for (block = start; block < end; block++) { /* Set block address */ value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); @@ -1831,7 +2094,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; loff_t ofs = 0; - size_t len = this->chipsize; + size_t len = mtd->size; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ @@ -1847,14 +2110,12 @@ static void onenand_unlock_all(struct mtd_info *mtd) & ONENAND_CTRL_ONGO) continue; - return; - /* Check lock status */ if (onenand_check_lock_status(this)) return; /* Workaround for all block unlock in DDP */ - if (ONENAND_IS_DDP(this)) { + if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { /* All blocks on another chip */ ofs = this->chipsize >> 1; len = this->chipsize >> 1; @@ -1906,6 +2167,14 @@ static void onenand_check_features(struct mtd_info *mtd) break; } + if (ONENAND_IS_MLC(this)) + this->options &= ~ONENAND_HAS_2PLANE; + + if (FLEXONENAND(this)) { + this->options &= ~ONENAND_HAS_CONT_LOCK; + this->options |= ONENAND_HAS_UNLOCK_ALL; + } + if (this->options & ONENAND_HAS_CONT_LOCK) printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); if (this->options & ONENAND_HAS_UNLOCK_ALL) @@ -1922,16 +2191,18 @@ static void onenand_check_features(struct mtd_info *mtd) */ char *onenand_print_device_info(int device, int version) { - int vcc, demuxed, ddp, density; + int vcc, demuxed, ddp, density, flexonenand; char *dev_info = malloc(80); char *p = dev_info; vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; - density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - p += sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)", + density = onenand_get_density(device); + flexonenand = device & DEVICE_IS_FLEXONENAND; + p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)", demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", device); @@ -1957,7 +2228,7 @@ static int onenand_check_maf(int manuf) char *name; int i; - for (i = 0; size; i++) + for (i = 0; i < size; i++) if (manuf == onenand_manuf_ids[i].id) break; @@ -1974,6 +2245,265 @@ static int onenand_check_maf(int manuf) } /** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info - onenand info structure +* +* Fill up boundary[] field in onenand_chip +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int die, bdry; + int ret, syscfg, locked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) + locked = 0; + else + locked = 1; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + ret = this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info + * boundary[], diesize[], mtd->size, mtd->erasesize, + * mtd->eraseregions + * @param mtd - MTD device structure + */ +static void flexonenand_get_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, i, eraseshift, density; + int blksperdie, maxbdry; + loff_t ofs; + + density = onenand_get_density(this->device_id); + blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + maxbdry = blksperdie - 1; + eraseshift = this->erase_shift - 1; + + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = 0; + ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += + this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = maxbdry ^ + this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + /* Expose MLC erase size except when all blocks are SLC */ + mtd->erasesize = 1 << this->erase_shift; + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x," + " numblocks: %04u]\n", mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (loff_t) (blksperdie << this->erase_shift); + this->diesize[die] -= (loff_t) (this->boundary[die] + 1) + << (this->erase_shift - 1); + mtd->size += this->diesize[die]; + } +} + +/** + * flexonenand_check_blocks_erased - Check if blocks are erased + * @param mtd_info - mtd info structure + * @param start - first erase block to check + * @param end - last erase block to check + * + * Converting an unerased block from MLC to SLC + * causes byte values to change. Since both data and its ECC + * have changed, reads on the block give uncorrectable error. + * This might lead to the block being detected as bad. + * + * Avoid this by ensuring that the block to be converted is + * erased. + */ +static int flexonenand_check_blocks_erased(struct mtd_info *mtd, + int start, int end) +{ + struct onenand_chip *this = mtd->priv; + int i, ret; + int block; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooboffs = 0, + .ooblen = mtd->oobsize, + .datbuf = NULL, + .oobbuf = this->oob_buf, + }; + loff_t addr; + + printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); + + for (block = start; block <= end; block++) { + addr = flexonenand_addr(this, block); + if (onenand_block_isbad_nolock(mtd, addr, 0)) + continue; + + /* + * Since main area write results in ECC write to spare, + * it is sufficient to check only ECC bytes for change. + */ + ret = onenand_read_oob_nolock(mtd, addr, &ops); + if (ret) + return ret; + + for (i = 0; i < mtd->oobsize; i++) + if (this->oob_buf[i] != 0xff) + break; + + if (i != mtd->oobsize) { + printk(KERN_WARNING "Block %d not erased.\n", block); + return 1; + } + } + + return 0; +} + +/** + * flexonenand_set_boundary - Writes the SLC boundary + * @param mtd - mtd info structure + */ +int flexonenand_set_boundary(struct mtd_info *mtd, int die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie, old, new, thisboundary; + loff_t addr; + + if (die >= this->dies) + return -EINVAL; + + if (boundary == this->boundary[die]) + return 0; + + density = onenand_get_density(this->device_id); + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + + if (boundary >= blksperdie) { + printk("flexonenand_set_boundary:" + "Invalid boundary value. " + "Boundary not changed.\n"); + return -EINVAL; + } + + /* Check if converting blocks are erased */ + old = this->boundary[die] + (die * this->density_mask); + new = boundary + (die * this->density_mask); + ret = flexonenand_check_blocks_erased(mtd, min(old, new) + + 1, max(old, new)); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); + return ret; + } + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + /* Check is boundary is locked */ + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + thisboundary = this->read_word(this->base + ONENAND_DATARAM); + if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { + printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); + goto out; + } + + printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", + die, boundary, lock ? "(Locked)" : "(Unlocked)"); + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + addr = die ? this->diesize[0] : 0; + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + ret = this->wait(mtd, FL_ERASING); + if (ret) { + printk("flexonenand_set_boundary:" + "Failed PI erase for Die %d\n", die); + goto out; + } + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk("flexonenand_set_boundary:" + "Failed PI write for Die %d\n", die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); + ret = this->wait(mtd, FL_WRITING); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + flexonenand_get_size(mtd); + + return ret; +} + +/** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure * @@ -2016,48 +2546,69 @@ static int onenand_probe(struct mtd_info *mtd) maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); /* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) return -ENXIO; - /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */ - if (dev_id & (1 << 9)) { - printk("Not yet support Flex-OneNAND\n"); - return -ENXIO; - } - /* Flash device information */ mtd->name = onenand_print_device_info(dev_id, ver_id); this->device_id = dev_id; this->version_id = ver_id; density = onenand_get_density(dev_id); + if (FLEXONENAND(this)) { + this->dies = ONENAND_IS_DDP(this) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1)); + if (!mtd->eraseregions) + return -ENOMEM; + } + + /* + * For Flex-OneNAND, chipsize represents maximum possible device size. + * mtd->size represents the actual device size. + */ this->chipsize = (16 << density) << 20; - /* Set density mask. it is used for DDP */ - if (ONENAND_IS_DDP(this)) - this->density_mask = (1 << (density + 6)); - else - this->density_mask = 0; /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_MLC(this)) + mtd->writesize <<= 1; + mtd->oobsize = mtd->writesize >> 5; /* Pagers per block is always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; + /* + * Flex-OneNAND SLC area has 64 pages per block. + * Flex-OneNAND MLC area has 128 pages per block. + * Expose MLC erase size to find erase_shift and page_mask. + */ + if (FLEXONENAND(this)) + mtd->erasesize <<= 1; this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; this->ppb_shift = (this->erase_shift - this->page_shift); this->page_mask = (mtd->erasesize / mtd->writesize) - 1; + /* Set density mask. it is used for DDP */ + if (ONENAND_IS_DDP(this)) + this->density_mask = this->chipsize >> (this->erase_shift + 1); /* It's real page size */ this->writesize = mtd->writesize; /* REVIST: Multichip handling */ - mtd->size = this->chipsize; + if (FLEXONENAND(this)) + flexonenand_get_size(mtd); + else + mtd->size = this->chipsize; /* Check OneNAND features */ onenand_check_features(mtd); @@ -2149,6 +2700,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) * Allow subpage writes up to oobsize. */ switch (mtd->oobsize) { + case 128: + this->ecclayout = &onenand_oob_128; + mtd->subpage_sft = 0; + break; + case 64: this->ecclayout = &onenand_oob_64; mtd->subpage_sft = 2; diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index d538f95..1354877 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -69,6 +69,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf, loff_t from; size_t readlen, ooblen; struct mtd_oob_ops ops; + int rgn; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -82,7 +83,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf, /* Note that numblocks is 2 * (real numblocks) here; * see i += 2 below as it makses shifting and masking less painful */ - numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); startblock = 0; from = 0; @@ -115,7 +116,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf, } } i += 2; - from += (1 << bbm->bbt_erase_shift); + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, from); + from += mtd->eraseregions[rgn].erasesize; + } else + from += (1 << bbm->bbt_erase_shift); } return 0; @@ -152,7 +158,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) uint8_t res; /* Get block number * 2 */ - block = (int)(offs >> (bbm->bbt_erase_shift - 1)); + block = (int) (onenand_block(this, offs) << 1); res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; MTDDEBUG (MTD_DEBUG_LEVEL2, @@ -191,7 +197,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct bbm_info *bbm = this->bbm; int len, ret = 0; - len = mtd->size >> (this->erase_shift + 2); + len = this->chipsize >> (this->erase_shift + 2); /* Allocate memory (2bit per block) */ bbm->bbt = malloc(len); if (!bbm->bbt) { diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c index 9823b5b..c642016 100644 --- a/drivers/mtd/onenand/onenand_uboot.c +++ b/drivers/mtd/onenand/onenand_uboot.c @@ -40,8 +40,10 @@ void onenand_init(void) onenand_scan(&onenand_mtd, 1); + if (onenand_chip.device_id & DEVICE_IS_FLEXONENAND) + puts("Flex-"); puts("OneNAND: "); - print_size(onenand_mtd.size, "\n"); + print_size(onenand_chip.chipsize, "\n"); #ifdef CONFIG_MTD_DEVICE /* diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index e3e0292..4f11b36 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o +COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o COBJS := $(COBJS-y) diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 25346a4..612f819 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -140,6 +140,11 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, flash = spi_flash_probe_macronix(spi, idcode); break; #endif +#ifdef CONFIG_SPI_FLASH_WINBOND + case 0xef: + flash = spi_flash_probe_winbond(spi, idcode); + break; +#endif #ifdef CONFIG_SPI_FLASH_STMICRO case 0x20: flash = spi_flash_probe_stmicro(spi, idcode); diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 0612383..08546fb 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -49,3 +49,4 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode); diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index 9b910c1..ae0d047 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -281,7 +281,8 @@ int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len) ret = 0; for (actual = 0; actual < len; actual++) { - cmd[1] = (offset / sector_size) + actual; + cmd[1] = offset >> 16; + offset += sector_size; ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0); if (ret < 0) { diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c new file mode 100644 index 0000000..b8da923 --- /dev/null +++ b/drivers/mtd/spi/winbond.c @@ -0,0 +1,332 @@ +/* + * Copyright 2008, Network Appliance Inc. + * Author: Jason McMullan <mcmullan <at> netapp.com> + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* M25Pxx-specific commands */ +#define CMD_W25_WREN 0x06 /* Write Enable */ +#define CMD_W25_WRDI 0x04 /* Write Disable */ +#define CMD_W25_RDSR 0x05 /* Read Status Register */ +#define CMD_W25_WRSR 0x01 /* Write Status Register */ +#define CMD_W25_READ 0x03 /* Read Data Bytes */ +#define CMD_W25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_W25_PP 0x02 /* Page Program */ +#define CMD_W25_SE 0x20 /* Sector (4K) Erase */ +#define CMD_W25_BE 0xd8 /* Block (64K) Erase */ +#define CMD_W25_CE 0xc7 /* Chip Erase */ +#define CMD_W25_DP 0xb9 /* Deep Power-down */ +#define CMD_W25_RES 0xab /* Release from DP, and Read Signature */ + +#define WINBOND_ID_W25X16 0x3015 +#define WINBOND_ID_W25X32 0x3016 +#define WINBOND_ID_W25X64 0x3017 + +#define WINBOND_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct winbond_spi_flash_params { + uint16_t id; + /* Log2 of page size in power-of-two mode */ + uint8_t l2_page_size; + uint16_t pages_per_sector; + uint16_t sectors_per_block; + uint8_t nr_blocks; + const char *name; +}; + +/* spi_flash needs to be first so upper layers can free() it */ +struct winbond_spi_flash { + struct spi_flash flash; + const struct winbond_spi_flash_params *params; +}; + +static inline struct winbond_spi_flash * +to_winbond_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct winbond_spi_flash, flash); +} + +static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { + { + .id = WINBOND_ID_W25X16, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 32, + .name = "W25X16", + }, + { + .id = WINBOND_ID_W25X32, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 64, + .name = "W25X32", + }, + { + .id = WINBOND_ID_W25X64, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 128, + .name = "W25X64", + }, +}; + +static int winbond_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 cmd[4] = { CMD_W25_RDSR, 0xff, 0xff, 0xff }; + + ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) { + debug("SF: Failed to get status for cmd %02x: %d\n", cmd, ret); + return -1; + } + + if ((status & WINBOND_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & WINBOND_SR_WIP) == 0) + return 0; + + debug("SF: Timed out on command %02x: %d\n", cmd, ret); + /* Timed out */ + return -1; +} + +/* + * Assemble the address part of a command for Winbond devices in + * non-power-of-two page size mode. + */ +static void winbond_build_address(struct winbond_spi_flash *stm, u8 *cmd, u32 offset) +{ + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + + /* + * The "extra" space per page is the power-of-two page size + * divided by 32. + */ + page_shift = stm->params->l2_page_size; + page_size = (1 << page_shift); + page_addr = offset / page_size; + byte_addr = offset % page_size; + + cmd[0] = page_addr >> (16 - page_shift); + cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[2] = byte_addr; +} + +static int winbond_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + winbond_build_address(stm, cmd + 1, offset); + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int winbond_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_shift = stm->params->l2_page_size; + page_size = (1 << page_shift); + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_W25_PP; + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[3] = byte_addr; + debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, + cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + goto out; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Winbond Page Program failed\n"); + goto out; + } + + ret = winbond_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: Winbond page programming timed out\n"); + goto out; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: Winbond: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +int winbond_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); + unsigned long sector_size; + unsigned int page_shift; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + page_shift = stm->params->l2_page_size; + sector_size = (1 << page_shift) * stm->params->pages_per_sector; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_W25_SE; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual++) { + winbond_build_address(stm, &cmd[1], offset + actual * sector_size); + printf("Erase: %02x %02x %02x %02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + goto out; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: Winbond sector erase failed\n"); + goto out; + } + + ret = winbond_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: Winbond sector erase timed out\n"); + goto out; + } + } + + debug("SF: Winbond: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode) +{ + const struct winbond_spi_flash_params *params; + unsigned long page_size; + struct winbond_spi_flash *stm; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) { + params = &winbond_spi_flash_table[i]; + if (params->id == ((idcode[1] << 8) | idcode[2])) + break; + } + + if (i == ARRAY_SIZE(winbond_spi_flash_table)) { + debug("SF: Unsupported Winbond ID %02x%02x\n", + idcode[1], idcode[2]); + return NULL; + } + + stm = malloc(sizeof(struct winbond_spi_flash)); + if (!stm) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + stm->params = params; + stm->flash.spi = spi; + stm->flash.name = params->name; + + /* Assuming power-of-two page size initially. */ + page_size = 1 << params->l2_page_size; + + stm->flash.write = winbond_write; + stm->flash.erase = winbond_erase; + stm->flash.read = winbond_read_fast; + stm->flash.size = page_size * params->pages_per_sector + * params->sectors_per_block + * params->nr_blocks; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, page_size, stm->flash.size); + + return &stm->flash; +} |