From 4c6de8560cb42a6b6c2d565d41b6801e4b02d4b3 Mon Sep 17 00:00:00 2001 From: Christian Hitz Date: Wed, 12 Oct 2011 09:31:59 +0200 Subject: nand: Merge BCH code from Linux nand driver [backport from linux commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe] This patch merges the BCH ECC algorithm from the 3.0 Linux kernel. This enables U-Boot to support modern NAND flash chips that require more than 1-bit of ECC in software. Signed-off-by: Christian Hitz Cc: Scott Wood Signed-off-by: Scott Wood --- include/linux/mtd/nand.h | 10 ++---- include/linux/mtd/nand_bch.h | 72 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 include/linux/mtd/nand_bch.h (limited to 'include/linux/mtd') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1cdc7ae2793..527d9ae03f8 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -18,13 +18,6 @@ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H -/* XXX U-BOOT XXX */ -#if 0 -#include -#include -#include -#endif - #include "config.h" #include "linux/mtd/compat.h" @@ -132,6 +125,7 @@ typedef enum { NAND_ECC_HW, NAND_ECC_HW_SYNDROME, NAND_ECC_HW_OOB_FIRST, + NAND_ECC_SOFT_BCH, } nand_ecc_modes_t; /* @@ -308,6 +302,7 @@ struct nand_hw_control { * @prepad: padding information for syndrome based ecc generators * @postpad: padding information for syndrome based ecc generators * @layout: ECC layout control struct pointer + * @priv: pointer to private ecc control data * @hwctl: function to control hardware ecc generator. Must only * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware @@ -328,6 +323,7 @@ struct nand_ecc_ctrl { int prepad; int postpad; struct nand_ecclayout *layout; + void *priv; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h new file mode 100644 index 00000000000..d8754dd8c8e --- /dev/null +++ b/include/linux/mtd/nand_bch.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2011 Ivan Djelic + * + * 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. + * + * This file is the header for the NAND BCH ECC implementation. + */ + +#ifndef __MTD_NAND_BCH_H__ +#define __MTD_NAND_BCH_H__ + +struct mtd_info; +struct nand_bch_control; + +#if defined(CONFIG_NAND_ECC_BCH) + +static inline int mtd_nand_has_bch(void) { return 1; } + +/* + * Calculate BCH ecc code + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code); + +/* + * Detect and correct bit errors + */ +int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, + u_char *calc_ecc); +/* + * Initialize BCH encoder/decoder + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, + unsigned int eccbytes, struct nand_ecclayout **ecclayout); +/* + * Release BCH encoder/decoder resources + */ +void nand_bch_free(struct nand_bch_control *nbc); + +#else /* !CONFIG_NAND_ECC_BCH */ + +static inline int mtd_nand_has_bch(void) { return 0; } + +static inline int +nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return -1; +} + +static inline int +nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + return -1; +} + +static inline struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, + unsigned int eccbytes, struct nand_ecclayout **ecclayout) +{ + return NULL; +} + +static inline void nand_bch_free(struct nand_bch_control *nbc) {} + +#endif /* CONFIG_NAND_ECC_BCH */ + +#endif /* __MTD_NAND_BCH_H__ */ -- cgit v1.3.1 From 2a8e0fc8b3dc31a3c571e439fbf04b882c8986be Mon Sep 17 00:00:00 2001 From: Christian Hitz Date: Wed, 12 Oct 2011 09:32:02 +0200 Subject: nand: Merge changes from Linux nand driver [backport from linux commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe] This patch synchronizes the nand driver with the Linux 3.0 state. Signed-off-by: Christian Hitz Cc: Scott Wood [scottwood@freescale.com: minor fixes] Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_base.c | 459 +++++++++++++++++++++++++++++-------------- include/linux/mtd/bbm.h | 23 ++- include/linux/mtd/nand.h | 374 ++++++++++++++++++++--------------- 3 files changed, 541 insertions(+), 315 deletions(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 758b53fa29f..ed2640c3ef0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -115,6 +115,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd->priv; + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + } + + /* Do not allow past end of device */ + if (ofs + len > mtd->size) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure @@ -123,8 +152,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); */ static void nand_release_device(struct mtd_info *mtd) { - struct nand_chip *this = mtd->priv; - this->select_chip(mtd, -1); /* De-select the NAND device */ + struct nand_chip *chip = mtd->priv; + + /* De-select the NAND device */ + chip->select_chip(mtd, -1); } /** @@ -316,6 +347,9 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct nand_chip *chip = mtd->priv; u16 bad; + if (chip->options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; if (getchip) { @@ -333,14 +367,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) bad = cpu_to_le16(chip->read_word(mtd)); if (chip->badblockpos & 0x1) bad >>= 8; - if ((bad & 0xFF) != 0xff) - res = 1; + else + bad &= 0xFF; } else { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - if (chip->read_byte(mtd) != 0xff) - res = 1; + bad = chip->read_byte(mtd); } + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + if (getchip) nand_release_device(mtd); @@ -359,7 +397,10 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; - int block, ret; + int block, ret, i = 0; + + if (chip->options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); @@ -370,17 +411,31 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) if (chip->options & NAND_USE_FLASH_BBT) ret = nand_update_bbt(mtd, ofs); else { - /* We write two bytes, so we dont have to mess with 16 bit - * access - */ nand_get_device(chip, mtd, FL_WRITING); - ofs += mtd->oobsize; - chip->ops.len = chip->ops.ooblen = 2; - chip->ops.datbuf = NULL; - chip->ops.oobbuf = buf; - chip->ops.ooboffs = chip->badblockpos & ~0x01; - ret = nand_do_write_oob(mtd, ofs, &chip->ops); + /* Write to first two pages and to byte 1 and 6 if necessary. + * If we write to more than one location, the first error + * encountered quits the procedure. We write two bytes per + * location, so we dont have to mess with 16 bit access. + */ + do { + chip->ops.len = chip->ops.ooblen = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + + if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) { + chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS + & ~0x01; + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + } + i++; + ofs += mtd->writesize; + } while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) && + i < 2); + nand_release_device(mtd); } if (!ret) @@ -399,6 +454,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + + /* broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; @@ -419,11 +479,6 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, { struct nand_chip *chip = mtd->priv; - if (!(chip->options & NAND_BBT_SCANNED)) { - chip->options |= NAND_BBT_SCANNED; - chip->scan_bbt(mtd); - } - if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); @@ -686,9 +741,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, * * Get the device and lock it for exclusive access */ -static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +static int +nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) { - this->state = new_state; + chip->state = new_state; return 0; } @@ -701,10 +757,10 @@ static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int ne * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs */ -static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { unsigned long timeo; - int state = this->state; + int state = chip->state; u32 time_start; if (state == FL_ERASING) @@ -712,10 +768,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) else timeo = (CONFIG_SYS_HZ * 20) / 1000; - if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) - this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); + if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) + chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); else - this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); time_start = get_timer(0); @@ -725,11 +781,11 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) return 0x01; } - if (this->dev_ready) { - if (this->dev_ready(mtd)) + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) break; } else { - if (this->read_byte(mtd) & NAND_STATUS_READY) + if (chip->read_byte(mtd) & NAND_STATUS_READY) break; } } @@ -739,7 +795,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) ; #endif /* PPCHAMELON_NAND_TIMER_HACK */ - return this->read_byte(mtd); + return (int)chip->read_byte(mtd); } /** @@ -860,6 +916,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + int index = 0; /* Column address wihin the page aligned to ECC size (256bytes). */ start_step = data_offs / chip->ecc.size; @@ -898,26 +955,30 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, } else { /* send the command to read the particular ecc bytes */ /* take care about buswidth alignment in read_buf */ - aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); + index = start_step * chip->ecc.bytes; + + aligned_pos = eccpos[index] & ~(busw - 1); aligned_len = eccfrag_len; - if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) + if (eccpos[index] & (busw - 1)) aligned_len++; - if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) + if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) aligned_len++; - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + aligned_pos, -1); chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); } for (i = 0; i < eccfrag_len; i++) - chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; p = bufpoi + data_col_addr; for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { int stat; - stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat == -1) + stat = chip->ecc.correct(mtd, p, + &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat < 0) mtd->ecc_stats.failed++; else mtd->ecc_stats.corrected += stat; @@ -1142,6 +1203,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -1187,7 +1251,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Transfer not aligned data */ if (!aligned) { - if (!NAND_SUBPAGE_READ(chip) && !oob) + if (!NAND_SUBPAGE_READ(chip) && !oob && + !(mtd->ecc_stats.failed - stats.failed)) chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -1195,18 +1260,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf += bytes; if (unlikely(oob)) { - /* Raw mode does data:oob:data:oob */ - if (ops->mode != MTD_OOB_RAW) { - int toread = min(oobreadlen, - chip->ecc.layout->oobavail); - if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); - oobreadlen -= toread; - } - } else - buf = nand_transfer_oob(chip, - buf, ops, mtd->oobsize); + + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } } if (!(chip->options & NAND_NO_READRDY)) { @@ -1793,13 +1854,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * nand_fill_oob - [Internal] Transfer client buffer to oob * @chip: nand chip structure * @oob: oob data buffer + * @len: oob data write length * @ops: oob ops structure */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) { - size_t len = ops->ooblen; - switch (ops->mode) { case MTD_OOB_PLACE: @@ -1838,7 +1898,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, return NULL; } -#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 +#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) /** * nand_do_write_ops - [Internal] NAND write with ECC @@ -1854,6 +1914,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int chipnr, realpage, page, blockmask, column; struct nand_chip *chip = mtd->priv; uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; @@ -1890,6 +1955,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); + /* Don't allow multipage oob writes with offset */ + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) + return -EINVAL; + while (1) { WATCHDOG_RESET(); @@ -1907,8 +1976,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, wbuf = chip->buffers->databuf; } - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(chip, oob, len, ops); + oobwritelen -= len; + } ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); @@ -2043,7 +2115,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->pagebuf = -1; memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops); + nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); @@ -2166,27 +2238,10 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, __func__, (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)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - } - /* Length must align on block boundary */ - if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Length not block aligned\n"); - return -EINVAL; - } - - /* Do not allow erase past end of device */ - if ((instr->len + instr->addr) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Erase past end of device\n"); - return -EINVAL; - } - - instr->fail_addr = 0xffffffff; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_ERASING); @@ -2371,7 +2426,8 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) struct nand_chip *chip = mtd->priv; int ret; - if ((ret = nand_block_isbad(mtd, ofs))) { + ret = nand_block_isbad(mtd, ofs); + if (ret) { /* If it was bad already, return success and do nothing. */ if (ret > 0) return 0; @@ -2444,6 +2500,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int i; int val; + /* try ONFI for unknow chip or LP */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') @@ -2486,7 +2543,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, if (!mtd->name) mtd->name = p->model; - mtd->writesize = le32_to_cpu(p->byte_per_page); mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); @@ -2495,6 +2551,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, if (le16_to_cpu(p->features) & 1) *busw = NAND_BUSWIDTH_16; + chip->options &= ~NAND_CHIPOPTIONS_MSK; + chip->options |= (NAND_NO_READRDY | + NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + return 1; } #else @@ -2506,41 +2566,6 @@ static inline int nand_flash_detect_onfi(struct mtd_info *mtd, } #endif -static void nand_flash_detect_non_onfi(struct mtd_info *mtd, - struct nand_chip *chip, - const struct nand_flash_dev *type, - int *busw) -{ - /* Newer devices have all the information in additional id bytes */ - if (!type->pagesize) { - int extid; - /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = chip->read_byte(mtd); - /* The 4th id byte is the important one */ - extid = chip->read_byte(mtd); - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; - - } else { - /* - * Old devices have chip data hardcoded in the device id table - */ - mtd->erasesize = type->erasesize; - mtd->writesize = type->pagesize; - mtd->oobsize = mtd->writesize / 32; - *busw = type->options & NAND_BUSWIDTH_16; - } -} - /* * Get the flash and manufacturer id and lookup if the type is supported */ @@ -2550,8 +2575,9 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, int *maf_id, int *dev_id, const struct nand_flash_dev *type) { - int ret, maf_idx; - int tmp_id, tmp_manf; + int i, maf_idx; + u8 id_data[8]; + int ret; /* Select the device */ chip->select_chip(mtd, 0); @@ -2577,15 +2603,13 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read manufacturer and device IDs */ - - tmp_manf = chip->read_byte(mtd); - tmp_id = chip->read_byte(mtd); + for (i = 0; i < 2; i++) + id_data[i] = chip->read_byte(mtd); - if (tmp_manf != *maf_id || tmp_id != *dev_id) { + if (id_data[0] != *maf_id || id_data[1] != *dev_id) { printk(KERN_INFO "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, *dev_id, tmp_manf, tmp_id); + *maf_id, *dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } @@ -2596,30 +2620,121 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (*dev_id == type->id) break; - if (!type->name) { - /* supress warning if there is no nand */ - if (*maf_id != 0x00 && *maf_id != 0xff && - *dev_id != 0x00 && *dev_id != 0xff) - printk(KERN_INFO "%s: unknown NAND device: " - "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - __func__, *maf_id, *dev_id); - return ERR_PTR(-ENODEV); + chip->onfi_version = 0; + if (!type->name || !type->pagesize) { + /* Check is chip is ONFI compliant */ + ret = nand_flash_detect_onfi(mtd, chip, &busw); + if (ret) + goto ident_done; } + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read entire ID string */ + + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); + + if (!type->name) + return ERR_PTR(-ENODEV); + if (!mtd->name) mtd->name = type->name; chip->chipsize = (uint64_t)type->chipsize << 20; - chip->onfi_version = 0; - ret = nand_flash_detect_onfi(mtd, chip, &busw); - if (!ret) - nand_flash_detect_non_onfi(mtd, chip, type, &busw); + if (!type->pagesize && chip->init_size) { + /* set the pagesize, oobsize, erasesize by the driver*/ + busw = chip->init_size(mtd, chip, id_data); + } else if (!type->pagesize) { + int extid; + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = id_data[2]; + /* The 4th id byte is the important one */ + extid = id_data[3]; + + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New style (6 byte ID): Samsung K9GBG08U0M (p.40) + * + * Check for wraparound + Samsung ID + nonzero 6th byte + * to decide what to do. + */ + if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && + id_data[0] == NAND_MFR_SAMSUNG && + (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (extid & 0x03) { + case 1: + mtd->oobsize = 128; + break; + case 2: + mtd->oobsize = 218; + break; + case 3: + mtd->oobsize = 400; + break; + default: + mtd->oobsize = 436; + break; + } + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } + } else { + /* + * Old devices have chip data hardcoded in the device id table + */ + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + busw = type->options & NAND_BUSWIDTH_16; + /* + * Check for Spansion/AMD ID + repeating 5th, 6th byte since + * some Spansion chips have erasesize that conflicts with size + * listed in nand_ids table + * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) + */ + if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && + id_data[5] == 0x00 && id_data[6] == 0x00 && + id_data[7] == 0x00 && mtd->writesize == 512) { + mtd->erasesize = 128 * 1024; + mtd->erasesize <<= ((id_data[3] & 0x03) << 1); + } + } /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; chip->options |= type->options & NAND_CHIPOPTIONS_MSK; + /* Check if chip is a not a samsung device. Do not clear the + * options for chips which are not having an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; +ident_done: + /* * Set chip as a default. Board drivers can override it, if necessary */ @@ -2654,18 +2769,48 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, ffs(mtd->erasesize) - 1; if (chip->chipsize & 0xffffffff) chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; - else - chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31; + else { + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); + chip->chip_shift += 32 - 1; + } + + chip->badblockbits = 8; /* Set the bad block position */ - chip->badblockpos = mtd->writesize > 512 ? - NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) + chip->badblockpos = NAND_LARGE_BADBLOCK_POS; + else + chip->badblockpos = NAND_SMALL_BADBLOCK_POS; - /* Check if chip is a not a samsung device. Do not clear the - * options for chips which are not having an extended id. + /* + * Bad block marker is stored in the last page of each block + * on Samsung and Hynix MLC devices; stored in first two pages + * of each block on Micron devices with 2KiB pages and on + * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan + * only the first page. */ - if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) - chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (*maf_id == NAND_MFR_SAMSUNG || + *maf_id == NAND_MFR_HYNIX)) + chip->options |= NAND_BBT_SCANLASTPAGE; + else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (*maf_id == NAND_MFR_SAMSUNG || + *maf_id == NAND_MFR_HYNIX || + *maf_id == NAND_MFR_TOSHIBA || + *maf_id == NAND_MFR_AMD)) || + (mtd->writesize == 2048 && + *maf_id == NAND_MFR_MICRON)) + chip->options |= NAND_BBT_SCAN2NDPAGE; + + /* + * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6 + */ + if (!(busw & NAND_BUSWIDTH_16) && + *maf_id == NAND_MFR_STMICRO && + mtd->writesize == 2048) { + chip->options |= NAND_BBT_SCANBYTE1AND6; + chip->badblockpos = 0; + } /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) @@ -2677,9 +2822,15 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; + /* TODO onfi flash name */ MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, - nand_manuf_ids[maf_idx].name, type->name); + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, + nand_manuf_ids[maf_idx].name, +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + chip->onfi_version ? chip->onfi_params.model : type->name); +#else + type->name); +#endif return type; } @@ -2865,7 +3016,8 @@ int nand_scan_tail(struct mtd_info *mtd) 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; + if (!chip->ecc.size) + chip->ecc.size = 256; chip->ecc.bytes = 3; break; @@ -2973,7 +3125,8 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -2992,9 +3145,10 @@ int nand_scan_tail(struct mtd_info *mtd) /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) - chip->options |= NAND_BBT_SCANNED; + return 0; - return 0; + /* Build bad block table */ + return chip->scan_bbt(mtd); } /** @@ -3039,4 +3193,9 @@ void nand_release(struct mtd_info *mtd) kfree(chip->bbt); if (!(chip->options & NAND_OWN_BUFFERS)) kfree(chip->buffers); + + /* Free bad block descriptor memory */ + if (chip->badblock_pattern && chip->badblock_pattern->options + & NAND_BBT_DYNAMICSTRUCT) + kfree(chip->badblock_pattern); } diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 7db25465df1..2fb36177ab9 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -11,8 +11,19 @@ * Thomas Gleixner * * 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. + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #ifndef __LINUX_MTD_BBM_H #define __LINUX_MTD_BBM_H @@ -76,7 +87,7 @@ struct nand_bbt_descr { #define NAND_BBT_PERCHIP 0x00000080 /* bbt has a version counter at offset veroffs */ #define NAND_BBT_VERSION 0x00000100 -/* Create a bbt if none axists */ +/* Create a bbt if none exists */ #define NAND_BBT_CREATE 0x00000200 /* Search good / bad pattern through all pages of a block */ #define NAND_BBT_SCANALLPAGES 0x00000400 @@ -88,6 +99,12 @@ struct nand_bbt_descr { #define NAND_BBT_SAVECONTENT 0x00002000 /* Search good / bad pattern on the first and the second page */ #define NAND_BBT_SCAN2NDPAGE 0x00004000 +/* Search good / bad pattern on the last page of the eraseblock */ +#define NAND_BBT_SCANLASTPAGE 0x00008000 +/* Chip stores bad block marker on BOTH 1st and 6th bytes of OOB */ +#define NAND_BBT_SCANBYTE1AND6 0x00100000 +/* The nand_bbt_descr was created dynamicaly and must be freed */ +#define NAND_BBT_DYNAMICSTRUCT 0x00200000 /* The maximum number of blocks to scan for a bbt */ #define NAND_BBT_SCAN_MAXBLOCKS 4 diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 527d9ae03f8..99668d552e2 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1,9 +1,9 @@ /* * linux/include/linux/mtd/nand.h * - * Copyright (c) 2000 David Woodhouse - * Steven J. Hill - * Thomas Gleixner + * Copyright © 2000-2010 David Woodhouse + * Steven J. Hill + * Thomas Gleixner * * 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 @@ -36,17 +36,18 @@ extern int nand_scan_ident(struct mtd_info *mtd, int max_chips, extern int nand_scan_tail(struct mtd_info *mtd); /* Free resources held by the NAND device */ -extern void nand_release (struct mtd_info *mtd); +extern void nand_release(struct mtd_info *mtd); /* Internal helper for board drivers which need to override command function */ extern void nand_wait_ready(struct mtd_info *mtd); -/* This constant declares the max. oobsize / page, which +/* + * This constant declares the max. oobsize / page, which * is supported now. If you add a chip with bigger oobsize/page * adjust this accordingly. */ -#define NAND_MAX_OOBSIZE 218 -#define NAND_MAX_PAGESIZE 4096 +#define NAND_MAX_OOBSIZE 576 +#define NAND_MAX_PAGESIZE 8192 /* * Constants for hardware specific CLE/ALE/NCE function @@ -79,10 +80,14 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_SEQIN 0x80 #define NAND_CMD_RNDIN 0x85 #define NAND_CMD_READID 0x90 -#define NAND_CMD_PARAM 0xec #define NAND_CMD_ERASE2 0xd0 +#define NAND_CMD_PARAM 0xec #define NAND_CMD_RESET 0xff +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 + /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 #define NAND_CMD_RNDOUTSTART 0xE0 @@ -142,9 +147,10 @@ typedef enum { #define NAND_GET_DEVICE 0x80 -/* Option constants for bizarre disfunctionality and real -* features -*/ +/* + * Option constants for bizarre disfunctionality and real + * features. + */ /* Chip can not auto increment pages */ #define NAND_NO_AUTOINCR 0x00000001 /* Buswitdh is 16 bit */ @@ -155,23 +161,36 @@ typedef enum { #define NAND_CACHEPRG 0x00000008 /* Chip has copy back function */ #define NAND_COPYBACK 0x00000010 -/* AND Chip which has 4 banks and a confusing page / block - * assignment. See Renesas datasheet for further information */ +/* + * AND Chip which has 4 banks and a confusing page / block + * assignment. See Renesas datasheet for further information. + */ #define NAND_IS_AND 0x00000020 -/* Chip has a array of 4 pages which can be read without - * additional ready /busy waits */ +/* + * Chip has a array of 4 pages which can be read without + * additional ready /busy waits. + */ #define NAND_4PAGE_ARRAY 0x00000040 -/* Chip requires that BBT is periodically rewritten to prevent +/* + * Chip requires that BBT is periodically rewritten to prevent * bits from adjacent blocks from 'leaking' in altering data. - * This happens with the Renesas AG-AND chips, possibly others. */ + * This happens with the Renesas AG-AND chips, possibly others. + */ #define BBT_AUTO_REFRESH 0x00000080 -/* Chip does not require ready check on read. True +/* + * Chip does not require ready check on read. True * for all large page devices, as they do not support - * autoincrement.*/ + * autoincrement. + */ #define NAND_NO_READRDY 0x00000100 /* Chip does not allow subpage writes */ #define NAND_NO_SUBPAGE_WRITE 0x00000200 +/* Device is one of 'new' xD cards that expose fake nand command set */ +#define NAND_BROKEN_XD 0x00000400 + +/* Device behaves just like nand, but is readonly */ +#define NAND_ROM 0x00000800 /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ @@ -190,17 +209,29 @@ typedef enum { #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) /* Non chip related options */ -/* Use a flash based bad block table. This option is passed to the - * default bad block table function. */ +/* + * Use a flash based bad block table. OOB identifier is saved in OOB area. + * This option is passed to the default bad block table function. + */ #define NAND_USE_FLASH_BBT 0x00010000 /* This option skips the bbt scan during initialization. */ #define NAND_SKIP_BBTSCAN 0x00020000 -/* This option is defined if the board driver allocates its own buffers - (e.g. because it needs them DMA-coherent */ +/* + * This option is defined if the board driver allocates its own buffers + * (e.g. because it needs them DMA-coherent). + */ #define NAND_OWN_BUFFERS 0x00040000 +/* Chip may not exist, so silence any errors in scan */ +#define NAND_SCAN_SILENT_NODEV 0x00080000 +/* + * If passed additionally to NAND_USE_FLASH_BBT then BBT code will not touch + * the OOB area. + */ +#define NAND_USE_FLASH_BBT_NO_OOB 0x00800000 +/* Create an empty BBT with no vendor information if the BBT is available */ +#define NAND_CREATE_EMPTY_BBT 0x01000000 + /* Options set by nand scan */ -/* bbt has already been read */ -#define NAND_BBT_SCANNED 0x40000000 /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 @@ -275,13 +306,13 @@ struct nand_onfi_params { #define ONFI_CRC_BASE 0x4F4E - /** * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * @lock: protection lock * @active: the mtd device which holds the controller currently - * @wq: wait queue to sleep on if a NAND operation is in progress - * used instead of the per chip wait queue when a hw controller is available + * @wq: wait queue to sleep on if a NAND operation is in + * progress used instead of the per chip wait queue + * when a hw controller is available. */ struct nand_hw_control { /* XXX U-BOOT XXX */ @@ -302,58 +333,50 @@ struct nand_hw_control { * @prepad: padding information for syndrome based ecc generators * @postpad: padding information for syndrome based ecc generators * @layout: ECC layout control struct pointer - * @priv: pointer to private ecc control data + * @priv: pointer to private ecc control data * @hwctl: function to control hardware ecc generator. Must only * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware * @correct: function for ecc correction, matching to ecc generator (sw/hw) * @read_page_raw: function to read a raw page without ECC * @write_page_raw: function to write a raw page without ECC - * @read_page: function to read a page according to the ecc generator requirements - * @write_page: function to write a page according to the ecc generator requirements + * @read_page: function to read a page according to the ecc generator + * requirements. + * @read_subpage: function to read parts of the page covered by ECC. + * @write_page: function to write a page according to the ecc generator + * requirements. * @read_oob: function to read chip OOB data * @write_oob: function to write chip OOB data */ struct nand_ecc_ctrl { - nand_ecc_modes_t mode; - int steps; - int size; - int bytes; - int total; - int prepad; - int postpad; + nand_ecc_modes_t mode; + int steps; + int size; + int bytes; + int total; + int prepad; + int postpad; struct nand_ecclayout *layout; - void *priv; - void (*hwctl)(struct mtd_info *mtd, int mode); - int (*calculate)(struct mtd_info *mtd, - const uint8_t *dat, - uint8_t *ecc_code); - int (*correct)(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, - uint8_t *calc_ecc); - int (*read_page_raw)(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page); - void (*write_page_raw)(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf); - int (*read_page)(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page); - int (*read_subpage)(struct mtd_info *mtd, - struct nand_chip *chip, - uint32_t offs, uint32_t len, - uint8_t *buf); - void (*write_page)(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf); - int (*read_oob)(struct mtd_info *mtd, - struct nand_chip *chip, - int page, - int sndcmd); - int (*write_oob)(struct mtd_info *mtd, - struct nand_chip *chip, - int page); + void *priv; + void (*hwctl)(struct mtd_info *mtd, int mode); + int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code); + int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, + uint8_t *calc_ecc); + int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page); + void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf); + int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page); + int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offs, uint32_t len, uint8_t *buf); + void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf); + int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, + int sndcmd); + int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, + int page); }; /** @@ -373,125 +396,150 @@ struct nand_buffers { /** * struct nand_chip - NAND Private Flash Chip Data - * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device - * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device + * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the + * flash device + * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the + * flash device. * @read_byte: [REPLACEABLE] read one byte from the chip * @read_word: [REPLACEABLE] read one word from the chip * @write_buf: [REPLACEABLE] write data from the buffer to the chip * @read_buf: [REPLACEABLE] read data from the chip into the buffer - * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data + * @verify_buf: [REPLACEABLE] verify buffer contents against the chip + * data. * @select_chip: [REPLACEABLE] select chip nr * @block_bad: [REPLACEABLE] check, if the block is bad * @block_markbad: [REPLACEABLE] mark the block bad - * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling + * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling * ALE/CLE/nCE. Also used to write command and address - * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line - * If set to NULL no access to ready/busy is available and the ready/busy information - * is read from the chip status register - * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip - * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready + * @init_size: [BOARDSPECIFIC] hardwarespecific function for setting + * mtd->oobsize, mtd->writesize and so on. + * @id_data contains the 8 bytes values of NAND_CMD_READID. + * Return with the bus width. + * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing + * device ready/busy line. If set to NULL no access to + * ready/busy is available and the ready/busy information + * is read from the chip status register. + * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing + * commands to the chip. + * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on + * ready. * @ecc: [BOARDSPECIFIC] ecc control ctructure * @buffers: buffer structure for read/write * @hwcontrol: platform-specific hardware control structure * @ops: oob operation operands - * @erase_cmd: [INTERN] erase command write function, selectable due to AND support + * @erase_cmd: [INTERN] erase command write function, selectable due + * to AND support. * @scan_bbt: [REPLACEABLE] function to scan bad block table - * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) - * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress + * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring + * data from array to read regs (tR). * @state: [INTERN] the current state of the NAND device * @oob_poi: poison value buffer - * @page_shift: [INTERN] number of address bits in a page (column address bits) + * @page_shift: [INTERN] number of address bits in a page (column + * address bits). * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry * @chip_shift: [INTERN] number of address bits in one chip - * @datbuf: [INTERN] internal buffer for one page + oob - * @oobbuf: [INTERN] oob buffer for one eraseblock - * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized - * @data_poi: [INTERN] pointer to a data buffer - * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about - * special functionality. See the defines for further explanation - * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @options: [BOARDSPECIFIC] various chip options. They can partly + * be set to inform nand_scan about special functionality. + * See the defines for further explanation. + * @badblockpos: [INTERN] position of the bad block marker in the oob + * area. + * @badblockbits: [INTERN] number of bits to left-shift the bad block + * number * @cellinfo: [INTERN] MLC/multichip data from chip ident * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 - * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf + * @pagebuf: [INTERN] holds the pagenumber which is currently in + * data_buf. * @subpagesize: [INTERN] holds the subpagesize + * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), + * non 0 if ONFI supported. + * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is + * supported, 0 otherwise. * @ecclayout: [REPLACEABLE] the default ecc placement scheme * @bbt: [INTERN] bad block table pointer - * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup + * @bbt_td: [REPLACEABLE] bad block table descriptor for flash + * lookup. * @bbt_md: [REPLACEABLE] bad block table mirror descriptor - * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan - * @controller: [REPLACEABLE] a pointer to a hardware controller structure - * which is shared among multiple independend devices + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial + * bad block scan. + * @controller: [REPLACEABLE] a pointer to a hardware controller + * structure which is shared among multiple independend + * devices. * @priv: [OPTIONAL] pointer to private chip date - * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks - * (determine if errors are correctable) + * @errstat: [OPTIONAL] hardware specific function to perform + * additional error status checks (determine if errors are + * correctable). * @write_page: [REPLACEABLE] High-level page write function */ struct nand_chip { - void __iomem *IO_ADDR_R; - void __iomem *IO_ADDR_W; - - uint8_t (*read_byte)(struct mtd_info *mtd); - u16 (*read_word)(struct mtd_info *mtd); - void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); - void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); - int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); - void (*select_chip)(struct mtd_info *mtd, int chip); - int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); - int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); - void (*cmd_ctrl)(struct mtd_info *mtd, int dat, - unsigned int ctrl); - int (*dev_ready)(struct mtd_info *mtd); - void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); - int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); - void (*erase_cmd)(struct mtd_info *mtd, int page); - int (*scan_bbt)(struct mtd_info *mtd); - int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); - int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw); - - int chip_delay; - unsigned int options; - - int page_shift; - int phys_erase_shift; - int bbt_erase_shift; - int chip_shift; - int numchips; - uint64_t chipsize; - int pagemask; - int pagebuf; - int subpagesize; - uint8_t cellinfo; - int badblockpos; - int onfi_version; + void __iomem *IO_ADDR_R; + void __iomem *IO_ADDR_W; + + uint8_t (*read_byte)(struct mtd_info *mtd); + u16 (*read_word)(struct mtd_info *mtd); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); + int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); + int (*init_size)(struct mtd_info *mtd, struct nand_chip *this, + u8 *id_data); + int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, + int page_addr); + int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); + void (*erase_cmd)(struct mtd_info *mtd, int page); + int (*scan_bbt)(struct mtd_info *mtd); + int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, + int status, int page); + int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw); + + int chip_delay; + unsigned int options; + + int page_shift; + int phys_erase_shift; + int bbt_erase_shift; + int chip_shift; + int numchips; + uint64_t chipsize; + int pagemask; + int pagebuf; + int subpagesize; + uint8_t cellinfo; + int badblockpos; + int badblockbits; + + int onfi_version; #ifdef CONFIG_SYS_NAND_ONFI_DETECTION struct nand_onfi_params onfi_params; #endif - int state; + int state; - uint8_t *oob_poi; - struct nand_hw_control *controller; - struct nand_ecclayout *ecclayout; + uint8_t *oob_poi; + struct nand_hw_control *controller; + struct nand_ecclayout *ecclayout; struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; - struct nand_hw_control hwcontrol; struct mtd_oob_ops ops; - uint8_t *bbt; - struct nand_bbt_descr *bbt_td; - struct nand_bbt_descr *bbt_md; + uint8_t *bbt; + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; - struct nand_bbt_descr *badblock_pattern; + struct nand_bbt_descr *badblock_pattern; - void *priv; + void *priv; }; /* @@ -535,7 +583,7 @@ struct nand_flash_dev { */ struct nand_manufacturers { int id; - char * name; + char *name; }; extern const struct nand_flash_dev nand_flash_ids[]; @@ -548,7 +596,7 @@ extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, uint8_t * buf); + size_t *retlen, uint8_t *buf); /* * Constants for oob configuration @@ -569,17 +617,20 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, * @priv: hardware controller specific settings */ struct platform_nand_chip { - int nr_chips; - int chip_offset; - int nr_partitions; - struct mtd_partition *partitions; - struct nand_ecclayout *ecclayout; - int chip_delay; - unsigned int options; - const char **part_probe_types; - void *priv; + int nr_chips; + int chip_offset; + int nr_partitions; + struct mtd_partition *partitions; + struct nand_ecclayout *ecclayout; + int chip_delay; + unsigned int options; + const char **part_probe_types; + void *priv; }; +/* Keep gcc happy */ +struct platform_device; + /** * struct platform_nand_ctrl - controller level device structure * @hwcontrol: platform specific hardware control structure @@ -592,12 +643,11 @@ struct platform_nand_chip { * All fields are optional and depend on the hardware driver requirements */ struct platform_nand_ctrl { - void (*hwcontrol)(struct mtd_info *mtd, int cmd); - int (*dev_ready)(struct mtd_info *mtd); - void (*select_chip)(struct mtd_info *mtd, int chip); - void (*cmd_ctrl)(struct mtd_info *mtd, int dat, - unsigned int ctrl); - void *priv; + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); + void (*select_chip)(struct mtd_info *mtd, int chip); + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); + void *priv; }; /** @@ -606,8 +656,8 @@ struct platform_nand_ctrl { * @ctrl: controller level device structure */ struct platform_nand_data { - struct platform_nand_chip chip; - struct platform_nand_ctrl ctrl; + struct platform_nand_chip chip; + struct platform_nand_ctrl ctrl; }; /* Some helpers to access the data structures */ -- cgit v1.3.1 From ff8a8a718341e42af8642f01ce630d4a77c58f82 Mon Sep 17 00:00:00 2001 From: Christian Hitz Date: Wed, 12 Oct 2011 09:32:04 +0200 Subject: nand: Merge changes to BBT from Linux nand driver [backport from linux commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe] This patch synchronizes the nand driver with the Linux 3.0 state. Signed-off-by: Christian Hitz Cc: Scott Wood Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_bbt.c | 427 ++++++++++++++++++++++++++++++++++---------- include/linux/mtd/bbm.h | 2 + 2 files changed, 330 insertions(+), 99 deletions(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index ded652be345..2b730e09c97 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -13,27 +13,36 @@ * Description: * * When nand_scan_bbt is called, then it tries to find the bad block table - * depending on the options in the bbt descriptor(s). If a bbt is found - * then the contents are read and the memory based bbt is created. If a - * mirrored bbt is selected then the mirror is searched too and the - * versions are compared. If the mirror has a greater version number - * than the mirror bbt is used to build the memory based bbt. + * depending on the options in the BBT descriptor(s). If no flash based BBT + * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory + * marked good / bad blocks. This information is used to create a memory BBT. + * Once a new bad block is discovered then the "factory" information is updated + * on the device. + * If a flash based BBT is specified then the function first tries to find the + * BBT on flash. If a BBT is found then the contents are read and the memory + * based BBT is created. If a mirrored BBT is selected then the mirror is + * searched too and the versions are compared. If the mirror has a greater + * version number than the mirror BBT is used to build the memory based BBT. * If the tables are not versioned, then we "or" the bad block information. - * If one of the bbt's is out of date or does not exist it is (re)created. - * If no bbt exists at all then the device is scanned for factory marked + * If one of the BBTs is out of date or does not exist it is (re)created. + * If no BBT exists at all then the device is scanned for factory marked * good / bad blocks and the bad block tables are created. * - * For manufacturer created bbts like the one found on M-SYS DOC devices - * the bbt is searched and read but never created + * For manufacturer created BBTs like the one found on M-SYS DOC devices + * the BBT is searched and read but never created * - * The autogenerated bad block table is located in the last good blocks + * The auto generated bad block table is located in the last good blocks * of the device. The table is mirrored, so it can be updated eventually. - * The table is marked in the oob area with an ident pattern and a version - * number which indicates which of both tables is more up to date. + * The table is marked in the OOB area with an ident pattern and a version + * number which indicates which of both tables is more up to date. If the NAND + * controller needs the complete OOB area for the ECC information then the + * option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern + * and the version byte into the data area and the OOB area will remain + * untouched. * * The table uses 2 bits per block - * 11b: block is good - * 00b: block is factory marked bad + * 11b: block is good + * 00b: block is factory marked bad * 01b, 10b: block is marked bad due to wear * * The memory bad block table uses the following scheme: @@ -55,9 +64,21 @@ #include #include #include +#include +#include #include +static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) +{ + int ret; + + ret = memcmp(buf, td->pattern, td->len); + if (!ret) + return ret; + return -1; +} + /** * check_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search @@ -76,6 +97,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc int i, end = 0; uint8_t *p = buf; + if (td->options & NAND_BBT_NO_OOB) + return check_pattern_no_oob(buf, td); + end = paglen + td->offs; if (td->options & NAND_BBT_SCANEMPTY) { for (i = 0; i < end; i++) { @@ -91,6 +115,28 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc return -1; } + /* Check both positions 1 and 6 for pattern? */ + if (td->options & NAND_BBT_SCANBYTE1AND6) { + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += NAND_SMALL_BADBLOCK_POS - td->offs; + /* Check region between positions 1 and 6 */ + for (i = 0; i < NAND_SMALL_BADBLOCK_POS - td->offs - td->len; + i++) { + if (*p++ != 0xff) + return -1; + } + } + else { + p += NAND_SMALL_BADBLOCK_POS - td->offs; + } + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + } + if (td->options & NAND_BBT_SCANEMPTY) { p += td->len; end += td->len; @@ -122,36 +168,74 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) if (p[td->offs + i] != td->pattern[i]) return -1; } + /* Need to check location 1 AND 6? */ + if (td->options & NAND_BBT_SCANBYTE1AND6) { + for (i = 0; i < td->len; i++) { + if (p[NAND_SMALL_BADBLOCK_POS + i] != td->pattern[i]) + return -1; + } + } return 0; } +/** + * add_marker_len - compute the length of the marker in data area + * @td: BBT descriptor used for computation + * + * The length will be 0 if the markeris located in OOB area. + */ +static u32 add_marker_len(struct nand_bbt_descr *td) +{ + u32 len; + + if (!(td->options & NAND_BBT_NO_OOB)) + return 0; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + return len; +} + /** * read_bbt - [GENERIC] Read the bad block table starting from page * @mtd: MTD device structure * @buf: temporary buffer * @page: the starting page * @num: the number of bbt descriptors to read - * @bits: number of bits per block + * @td: the bbt describtion table * @offs: offset in the memory table - * @reserved_block_code: Pattern to identify reserved blocks * * Read the bad block table starting from page. * */ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, - int bits, int offs, int reserved_block_code) + struct nand_bbt_descr *td, int offs) { int res, i, j, act = 0; struct nand_chip *this = mtd->priv; size_t retlen, len, totlen; loff_t from; + int bits = td->options & NAND_BBT_NRBITS_MSK; uint8_t msk = (uint8_t) ((1 << bits) - 1); + u32 marker_len; + int reserved_block_code = td->reserved_block_code; totlen = (num * bits) >> 3; + marker_len = add_marker_len(td); from = ((loff_t) page) << this->page_shift; while (totlen) { len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); + if (marker_len) { + /* + * In case the BBT marker is not in the OOB area it + * will be just in the first page. + */ + len -= marker_len; + from += marker_len; + marker_len = 0; + } res = mtd->read(mtd, from, len, &retlen, buf); if (res < 0) { if (retlen != len) { @@ -170,9 +254,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, continue; if (reserved_block_code && (tmp == reserved_block_code)) { printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", - (loff_t)((offs << 2) + - (act >> 1)) << - this->bbt_erase_shift); + (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); mtd->ecc_stats.bbtblocks++; continue; @@ -180,8 +262,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, /* 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%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << - this->bbt_erase_shift); + (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); @@ -211,42 +292,86 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc { struct nand_chip *this = mtd->priv; int res = 0, i; - int bits; - bits = td->options & NAND_BBT_NRBITS_MSK; if (td->options & NAND_BBT_PERCHIP) { int offs = 0; for (i = 0; i < this->numchips; i++) { if (chip == -1 || chip == i) - res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); + res = read_bbt(mtd, buf, td->pages[i], + this->chipsize >> this->bbt_erase_shift, + td, offs); if (res) return res; offs += this->chipsize >> (this->bbt_erase_shift + 2); } } else { - res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); + res = read_bbt(mtd, buf, td->pages[0], + mtd->size >> this->bbt_erase_shift, td, 0); if (res) return res; } return 0; } +/* + * BBT marker is in the first page, no OOB. + */ +static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + struct nand_bbt_descr *td) +{ + size_t retlen; + size_t len; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + + return mtd->read(mtd, offs, len, &retlen, buf); +} + /* * Scan read raw data from flash */ -static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len) { struct mtd_oob_ops ops; + int res; ops.mode = MTD_OOB_RAW; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; - ops.oobbuf = buf; - ops.datbuf = buf; - ops.len = len; - return mtd->read_oob(mtd, offs, &ops); + + while (len > 0) { + if (len <= mtd->writesize) { + ops.oobbuf = buf + len; + ops.datbuf = buf; + ops.len = len; + return mtd->read_oob(mtd, offs, &ops); + } else { + ops.oobbuf = buf + mtd->writesize; + ops.datbuf = buf; + ops.len = mtd->writesize; + res = mtd->read_oob(mtd, offs, &ops); + + if (res) + return res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + } + return 0; +} + +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len, struct nand_bbt_descr *td) +{ + if (td->options & NAND_BBT_NO_OOB) + return scan_read_raw_data(mtd, buf, offs, td); + else + return scan_read_raw_oob(mtd, buf, offs, len); } /* @@ -267,6 +392,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, return mtd->write_oob(mtd, offs, &ops); } +static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + u32 ver_offs = td->veroffs; + + if (!(td->options & NAND_BBT_NO_OOB)) + ver_offs += mtd->writesize; + return ver_offs; +} + /** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * @mtd: MTD device structure @@ -285,18 +419,18 @@ 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, (loff_t)td->pages[0] << - this->page_shift, mtd->writesize); - td->version[0] = buf[mtd->writesize + td->veroffs]; + scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, + mtd->writesize, td); + td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - scan_read_raw(mtd, buf, (loff_t)md->pages[0] << - this->page_shift, mtd->writesize); - md->version[0] = buf[mtd->writesize + md->veroffs]; + scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, + mtd->writesize, td); + md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); } @@ -312,7 +446,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, { int ret, j; - ret = scan_read_raw(mtd, buf, offs, readlen); + ret = scan_read_raw_oob(mtd, buf, offs, readlen); if (ret) return ret; @@ -376,16 +510,14 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, loff_t from; size_t readlen; - MTDDEBUG (MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n"); if (bd->options & NAND_BBT_SCANALLPAGES) len = 1 << (this->bbt_erase_shift - this->page_shift); - else { - if (bd->options & NAND_BBT_SCAN2NDPAGE) - len = 2; - else - len = 1; - } + else if (bd->options & NAND_BBT_SCAN2NDPAGE) + len = 2; + else + len = 1; if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ @@ -415,9 +547,14 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, from = (loff_t)startblock << (this->bbt_erase_shift - 1); } + if (this->options & NAND_BBT_SCANLASTPAGE) + from += mtd->erasesize - (mtd->writesize * len); + for (i = startblock; i < numblocks;) { int ret; + BUG_ON(bd->options & NAND_BBT_NO_OOB); + if (bd->options & NAND_BBT_SCANALLPAGES) ret = scan_block_full(mtd, bd, from, buf, readlen, scanlen, len); @@ -429,7 +566,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); - MTDDEBUG (MTD_DEBUG_LEVEL0, + MTDDEBUG(MTD_DEBUG_LEVEL0, "Bad eraseblock %d at 0x%012llx\n", i >> 1, (unsigned long long)from); mtd->ecc_stats.badblocks++; @@ -497,11 +634,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr loff_t offs = (loff_t)actblock << this->bbt_erase_shift; /* Read first page */ - scan_read_raw(mtd, buf, offs, mtd->writesize); + scan_read_raw(mtd, buf, offs, mtd->writesize, td); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { - td->version[i] = buf[mtd->writesize + td->veroffs]; + offs = bbt_get_ver_offs(mtd, td); + td->version[i] = buf[offs]; } break; } @@ -685,12 +823,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); ooboffs = len + (pageoffs * mtd->oobsize); + } else if (td->options & NAND_BBT_NO_OOB) { + ooboffs = 0; + offs = td->len; + /* the version byte */ + if (td->options & NAND_BBT_VERSION) + offs++; + /* Calc length */ + len = (size_t) (numblocks >> sft); + len += offs; + /* Make it page aligned ! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len); + /* Pattern is located at the begin of first page */ + memcpy(buf, td->pattern, td->len); } else { /* Calc length */ len = (size_t) (numblocks >> sft); /* Make it page aligned ! */ - len = (len + (mtd->writesize - 1)) & - ~(mtd->writesize - 1); + len = ALIGN(len, mtd->writesize); /* Preset the buffer with 0xff */ memset(buf, 0xff, len + (len >> this->page_shift)* mtd->oobsize); @@ -724,13 +876,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (res < 0) goto outerr; - res = scan_write_bbt(mtd, to, len, buf, &buf[len]); + res = scan_write_bbt(mtd, to, len, buf, + td->options & NAND_BBT_NO_OOB ? NULL : + &buf[len]); if (res < 0) goto outerr; - printk(KERN_DEBUG "Bad block table written to 0x%012llx, " - "version 0x%02X\n", (unsigned long long)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; @@ -791,7 +944,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc rd2 = NULL; /* Per chip or per device ? */ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; - /* Mirrored table avilable ? */ + /* Mirrored table available ? */ if (md) { if (td->pages[i] == -1 && md->pages[i] == -1) { writeops = 0x03; @@ -845,7 +998,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc continue; /* Create the table in memory by scanning the chip(s) */ - create_bbt(mtd, buf, bd, chipsel); + if (!(this->options & NAND_CREATE_EMPTY_BBT)) + create_bbt(mtd, buf, bd, chipsel); td->version[i] = 1; if (md) @@ -910,8 +1064,7 @@ 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, (loff_t)block << - (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); continue; } update = 0; @@ -932,11 +1085,58 @@ 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, (loff_t)(block - 2) << - (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); } } +/** + * verify_bbt_descr - verify the bad block description + * @mtd: MTD device structure + * @bd: the table to verify + * + * This functions performs a few sanity checks on the bad block description + * table. + */ +static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + u32 pattern_len; + u32 bits; + u32 table_size; + + if (!bd) + return; + + pattern_len = bd->len; + bits = bd->options & NAND_BBT_NRBITS_MSK; + + BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) && + !(this->options & NAND_USE_FLASH_BBT)); + BUG_ON(!bits); + + if (bd->options & NAND_BBT_VERSION) + pattern_len++; + + if (bd->options & NAND_BBT_NO_OOB) { + BUG_ON(!(this->options & NAND_USE_FLASH_BBT)); + BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB)); + BUG_ON(bd->offs); + if (bd->options & NAND_BBT_VERSION) + BUG_ON(bd->veroffs != bd->len); + BUG_ON(bd->options & NAND_BBT_SAVECONTENT); + } + + if (bd->options & NAND_BBT_PERCHIP) + table_size = this->chipsize >> this->bbt_erase_shift; + else + table_size = mtd->size >> this->bbt_erase_shift; + table_size >>= 3; + table_size *= bits; + if (bd->options & NAND_BBT_NO_OOB) + table_size += pattern_len; + BUG_ON(table_size > (1 << this->bbt_erase_shift)); +} + /** * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) * @mtd: MTD device structure @@ -978,6 +1178,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } return res; } + verify_bbt_descr(mtd, td); + verify_bbt_descr(mtd, md); /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); @@ -1073,34 +1275,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; -static struct nand_bbt_descr smallpage_memorybased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 5, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 2, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr smallpage_flashbased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 5, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr largepage_flashbased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 0, - .len = 2, - .pattern = scan_ff_pattern -}; - static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; static struct nand_bbt_descr agand_flashbased = { @@ -1135,6 +1309,59 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern }; +static struct nand_bbt_descr bbt_main_no_bbt_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_no_bbt_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \ + NAND_BBT_SCANBYTE1AND6) +/** + * nand_create_default_bbt_descr - [Internal] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for + * + * This function allocates and initializes a nand_bbt_descr for BBM detection + * based on the properties of "this". The new descriptor is stored in + * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when + * passed to this function. + * + */ +static int nand_create_default_bbt_descr(struct nand_chip *this) +{ + struct nand_bbt_descr *bd; + if (this->badblock_pattern) { + printk(KERN_WARNING "BBT descr already allocated; not replacing.\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) { + printk(KERN_ERR "nand_create_default_bbt_descr: Out of memory\n"); + return -ENOMEM; + } + bd->options = this->options & BBT_SCAN_OPTIONS; + bd->offs = this->badblockpos; + bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + this->badblock_pattern = bd; + return 0; +} + /** * nand_default_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure @@ -1168,20 +1395,22 @@ int nand_default_bbt(struct mtd_info *mtd) if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern descriptors */ if (!this->bbt_td) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; + if (this->options & NAND_USE_FLASH_BBT_NO_OOB) { + this->bbt_td = &bbt_main_no_bbt_descr; + this->bbt_md = &bbt_mirror_no_bbt_descr; + } else { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } } } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->writesize > 512) ? - &largepage_memorybased : &smallpage_memorybased; - } } + + if (!this->badblock_pattern) + nand_create_default_bbt_descr(this); + return nand_scan_bbt(mtd, this->badblock_pattern); } @@ -1202,8 +1431,8 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) block = (int)(offs >> (this->bbt_erase_shift - 1)); res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; - MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: " - "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1); + MTDDEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); switch ((int)res) { case 0x00: diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 2fb36177ab9..8cbcdae1143 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -105,6 +105,8 @@ struct nand_bbt_descr { #define NAND_BBT_SCANBYTE1AND6 0x00100000 /* The nand_bbt_descr was created dynamicaly and must be freed */ #define NAND_BBT_DYNAMICSTRUCT 0x00200000 +/* The bad block table does not OOB for marker */ +#define NAND_BBT_NO_OOB 0x00400000 /* The maximum number of blocks to scan for a bbt */ #define NAND_BBT_SCAN_MAXBLOCKS 4 -- cgit v1.3.1 From 4fee6c2f295f932b8febdc7ce8731ba045695fa5 Mon Sep 17 00:00:00 2001 From: Christian Hitz Date: Wed, 12 Oct 2011 09:32:06 +0200 Subject: nand: make 1-bit software ECC configurable The software ECC algorithm is not necessary when hardware ECC is available and can be left out for a smaller image size. Enable with CONFIG_MTD_ECC_SOFT. Signed-off-by: Christian Hitz Cc: Scott Wood Signed-off-by: Scott Wood --- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_base.c | 4 ++++ include/linux/mtd/nand_ecc.h | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1d1b6286510..998fc73497c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -39,7 +39,7 @@ COBJS-y += nand_bbt.o COBJS-y += nand_ids.o COBJS-y += nand_util.o endif -COBJS-y += nand_ecc.o +COBJS-$(CONFIG_MTD_ECC_SOFT) += nand_ecc.o COBJS-y += nand_base.o COBJS-$(CONFIG_NAND_ECC_BCH) += nand_bch.o diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8ab83036f59..12b960fdb90 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3028,6 +3028,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.mode = NAND_ECC_SOFT; case NAND_ECC_SOFT: + if (!mtd_nand_has_ecc_soft()) { + printk(KERN_WARNING "CONFIG_MTD_ECC_SOFT not enabled\n"); + return -EINVAL; + } chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 090da505425..9715a53d86d 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -15,6 +15,10 @@ struct mtd_info; +#if defined(CONFIG_MTD_ECC_SOFT) + +static inline int mtd_nand_has_ecc_soft(void) { return 1; } + /* * Calculate 3 byte ECC code for 256 byte block */ @@ -25,4 +29,25 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code */ int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); +#else + +static inline int mtd_nand_has_ecc_soft(void) { return 0; } + +static inline int +nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + return -1; +} + +static inline int +nand_correct_data(struct mtd_info *mtd, + u_char *dat, + u_char *read_ecc, + u_char *calc_ecc) +{ + return -1; +} + +#endif + #endif /* __MTD_NAND_ECC_H__ */ -- cgit v1.3.1