diff options
author | Wolfgang Denk <wd@denx.de> | 2012-02-11 22:14:56 +0100 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2012-02-11 22:14:56 +0100 |
commit | f57a94c2b566a647003c7885a450ba0f28dbff34 (patch) | |
tree | 4ed0e63aaaad760e9494c1cb83e405e2ac7c07ed | |
parent | 6e4c1da86e77576476b566977305ddd82f9e6b8c (diff) | |
parent | 0990dc61787ec03b0ae7579a51e5eb661112f13f (diff) | |
download | u-boot-imx-f57a94c2b566a647003c7885a450ba0f28dbff34.zip u-boot-imx-f57a94c2b566a647003c7885a450ba0f28dbff34.tar.gz u-boot-imx-f57a94c2b566a647003c7885a450ba0f28dbff34.tar.bz2 |
Merge branch 'master' of /home/wd/git/u-boot/custodians
* 'master' of /home/wd/git/u-boot/custodians:
nand/fsl_elbc: Convert to self-init
nand: Introduce CONFIG_SYS_NAND_SELF_INIT
nand_spl: store ecc data on the stack
mtd/nand: Add ONFI support for FSL NAND controller
nand: make 1-bit software ECC configurable
nand: Sanitize ONFI strings.
nand: Merge changes to BBT from Linux nand driver
nand: Merge changes from Linux nand driver
nand: cleanup whitespace
nand: Add more NAND types from Linux nand driver
nand: Merge BCH code from Linux nand driver
NAND: Remove additional (CONFIG_SYS)_NAND_MAX_CHIPS
NAND: remove NAND_MAX_CHIPS definitions
nand_spl_simple: store ecc data on the stack
66 files changed, 3085 insertions, 693 deletions
diff --git a/doc/README.nand b/doc/README.nand index 023740e..04a87c9 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -120,6 +120,68 @@ Configuration Options: CONFIG_SYS_NAND_MAX_CHIPS The maximum number of NAND chips per device to be supported. + CONFIG_SYS_NAND_SELF_INIT + Traditionally, glue code in drivers/mtd/nand/nand.c has driven + the initialization process -- it provides the mtd and nand + structs, calls a board init function for a specific device, + calls nand_scan(), and registers with mtd. + + This arrangement does not provide drivers with the flexibility to + run code between nand_scan_ident() and nand_scan_tail(), or other + deviations from the "normal" flow. + + If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/nand.c + will make one call to board_nand_init(), with no arguments. That + function is responsible for calling a driver init function for + each NAND device on the board, that performs all initialization + tasks except setting mtd->name, and registering with the rest of + U-Boot. Those last tasks are accomplished by calling nand_register() + on the new mtd device. + + Example of new init to be added to the end of an existing driver + init: + + /* + * devnum is the device number to be used in nand commands + * and in mtd->name. Must be less than + * CONFIG_SYS_NAND_MAX_DEVICE. + */ + mtd = &nand_info[devnum]; + + /* chip is struct nand_chip, and is now provided by the driver. */ + mtd->priv = &chip; + + /* + * Fill in appropriate values if this driver uses these fields, + * or uses the standard read_byte/write_buf/etc. functions from + * nand_base.c that use these fields. + */ + chip.IO_ADDR_R = ...; + chip.IO_ADDR_W = ...; + + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL)) + error out + + /* + * Insert here any code you wish to run after the chip has been + * identified, but before any other I/O is done. + */ + + if (nand_scan_tail(mtd)) + error out + + if (nand_register(devnum)) + error out + + In addition to providing more flexibility to the driver, it reduces + the difference between a U-Boot driver and its Linux counterpart. + nand_init() is now reduced to calling board_nand_init() once, and + printing a size summary. This should also make it easier to + transition to delayed NAND initialization. + + Please convert your driver even if you don't need the extra + flexibility, so that one day we can eliminate the old mechanism. + NOTE: ===== diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 36ee454..998fc73 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -39,8 +39,9 @@ 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 COBJS-$(CONFIG_NAND_ATMEL) += atmel_nand.o COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4d1e527..9076ad4 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -22,6 +22,7 @@ #include <common.h> #include <malloc.h> +#include <nand.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -57,7 +58,6 @@ struct fsl_elbc_ctrl; /* mtd information per set */ struct fsl_elbc_mtd { - struct mtd_info mtd; struct nand_chip chip; struct fsl_elbc_ctrl *ctrl; @@ -340,18 +340,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* READID must read all 5 possible bytes while CEB is active */ case NAND_CMD_READID: - vdbg("fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + case NAND_CMD_PARAM: + vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command); out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) | (FIR_OP_RBW << FIR_OP2_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); - /* 5 bytes for manuf, device and exts */ - out_be32(&lbc->fbcr, 5); - ctrl->read_bytes = 5; + out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&lbc->fbcr, 256); + ctrl->read_bytes = 256; ctrl->use_mdr = 1; - ctrl->mdr = 0; - + ctrl->mdr = column; set_addr(mtd, 0, 0, 0); fsl_elbc_run_command(mtd); return; @@ -683,10 +686,13 @@ static void fsl_elbc_ctrl_init(void) elbc_ctrl->addr = NULL; } -int board_nand_init(struct nand_chip *nand) +static int fsl_elbc_chip_init(int devnum, u8 *addr) { + struct mtd_info *mtd = &nand_info[devnum]; + struct nand_chip *nand; struct fsl_elbc_mtd *priv; uint32_t br = 0, or = 0; + int ret; if (!elbc_ctrl) { fsl_elbc_ctrl_init(); @@ -699,19 +705,19 @@ int board_nand_init(struct nand_chip *nand) return -ENOMEM; priv->ctrl = elbc_ctrl; - priv->vbase = nand->IO_ADDR_R; + priv->vbase = addr; /* Find which chip select it is connected to. It'd be nice * if we could pass more than one datum to the NAND driver... */ for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { - phys_addr_t base_addr = virt_to_phys(nand->IO_ADDR_R); + phys_addr_t phys_addr = virt_to_phys(addr); br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br); or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or); if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM && - (br & or & BR_BA) == BR_PHYS_ADDR(base_addr)) + (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr)) break; } @@ -721,6 +727,9 @@ int board_nand_init(struct nand_chip *nand) return -ENODEV; } + nand = &priv->chip; + mtd->priv = nand; + elbc_ctrl->chips[priv->bank] = priv; /* fill in nand_chip structure */ @@ -791,5 +800,32 @@ int board_nand_init(struct nand_chip *nand) } } + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_register(devnum); + if (ret) + return ret; + return 0; } + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = + CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + fsl_elbc_chip_init(i, (u8 *)base_address[i]); +} diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c index d987f4c..4cf4c1c 100644 --- a/drivers/mtd/nand/nand.c +++ b/drivers/mtd/nand/nand.c @@ -23,6 +23,7 @@ #include <common.h> #include <nand.h> +#include <errno.h> #ifndef CONFIG_SYS_NAND_BASE_LIST #define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } @@ -31,63 +32,84 @@ DECLARE_GLOBAL_DATA_PTR; int nand_curr_device = -1; + + nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; +#ifndef CONFIG_SYS_NAND_SELF_INIT static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; +#endif + +static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8]; + +static unsigned long total_nand_size; /* in kiB */ + +/* Register an initialized NAND mtd device with the U-Boot NAND command. */ +int nand_register(int devnum) +{ + struct mtd_info *mtd; + + if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE) + return -EINVAL; + + mtd = &nand_info[devnum]; + + sprintf(dev_name[devnum], "nand%d", devnum); + mtd->name = dev_name[devnum]; + +#ifdef CONFIG_MTD_DEVICE + /* + * Add MTD device so that we can reference it later + * via the mtdcore infrastructure (e.g. ubi). + */ + add_mtd_device(mtd); +#endif + + total_nand_size += mtd->size / 1024; -static const char default_nand_name[] = "nand"; -static __attribute__((unused)) char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8]; + if (nand_curr_device == -1) + nand_curr_device = devnum; + + return 0; +} -static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand, - ulong base_addr) +#ifndef CONFIG_SYS_NAND_SELF_INIT +static void nand_init_chip(int i) { + struct mtd_info *mtd = &nand_info[i]; + struct nand_chip *nand = &nand_chip[i]; + ulong base_addr = base_address[i]; int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; - static int __attribute__((unused)) i = 0; if (maxchips < 1) maxchips = 1; - mtd->priv = nand; + mtd->priv = nand; nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; - if (board_nand_init(nand) == 0) { - if (nand_scan(mtd, maxchips) == 0) { - if (!mtd->name) - mtd->name = (char *)default_nand_name; -#ifdef CONFIG_NEEDS_MANUAL_RELOC - else - mtd->name += gd->reloc_off; -#endif -#ifdef CONFIG_MTD_DEVICE - /* - * Add MTD device so that we can reference it later - * via the mtdcore infrastructure (e.g. ubi). - */ - sprintf(dev_name[i], "nand%d", i); - mtd->name = dev_name[i++]; - add_mtd_device(mtd); -#endif - } else - mtd->name = NULL; - } else { - mtd->name = NULL; - mtd->size = 0; - } + if (board_nand_init(nand)) + return; + if (nand_scan(mtd, maxchips)) + return; + + nand_register(i); } +#endif void nand_init(void) { +#ifdef CONFIG_SYS_NAND_SELF_INIT + board_nand_init(); +#else int i; - unsigned int size = 0; - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { - nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]); - size += nand_info[i].size / 1024; - if (nand_curr_device == -1) - nand_curr_device = i; - } - printf("%u MiB\n", size / 1024); + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + nand_init_chip(i); +#endif + + printf("%lu MiB\n", total_nand_size / 1024); #ifdef CONFIG_SYS_NAND_SELECT_DEVICE /* diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 27f6c77..12b960f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -43,6 +43,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> #ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> @@ -70,7 +71,7 @@ static struct nand_ecclayout nand_oob_8 = { {.offset = 3, .length = 2}, {.offset = 6, - .length = 2}} + .length = 2} } }; static struct nand_ecclayout nand_oob_16 = { @@ -78,7 +79,7 @@ static struct nand_ecclayout nand_oob_16 = { .eccpos = {0, 1, 2, 3, 6, 7}, .oobfree = { {.offset = 8, - . length = 8}} + . length = 8} } }; static struct nand_ecclayout nand_oob_64 = { @@ -89,24 +90,23 @@ static struct nand_ecclayout nand_oob_64 = { 56, 57, 58, 59, 60, 61, 62, 63}, .oobfree = { {.offset = 2, - .length = 38}} + .length = 38} } }; static struct nand_ecclayout nand_oob_128 = { .eccbytes = 48, .eccpos = { - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, + 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 = 78}} + .length = 78} } }; - static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); @@ -115,16 +115,47 @@ 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 * * Deselect, release chip lock and wake up anyone waiting on the device */ -static void nand_release_device (struct mtd_info *mtd) +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); } /** @@ -768,8 +824,9 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * * 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) +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; @@ -850,7 +907,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @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) +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -858,6 +916,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 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; @@ -896,26 +955,30 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 } 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; @@ -1082,7 +1145,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { - switch(ops->mode) { + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_RAW: @@ -1094,7 +1157,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; - for(; free->length && len; free++, len -= bytes) { + for (; free->length && len; free++, len -= bytes) { /* Read request not from offset 0 ? */ if (unlikely(roffs)) { if (roffs >= free->length) { @@ -1140,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; @@ -1155,7 +1221,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; - while(1) { + while (1) { WATCHDOG_RESET(); bytes = min(mtd->writesize - col, readlen); @@ -1173,18 +1239,20 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, - bufpoi, page); + bufpoi, page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) - ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); + ret = chip->ecc.read_subpage(mtd, chip, + col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - page); + page); if (ret < 0) break; /* 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); } @@ -1192,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)) { @@ -1259,11 +1323,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** - * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read @@ -1456,8 +1520,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, int len; uint8_t *buf = ops->oobbuf; - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", - (unsigned long long)from, readlen); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)from, readlen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; @@ -1465,8 +1529,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt to start read outside oob\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " + "outside oob\n", __func__); return -EINVAL; } @@ -1474,8 +1538,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " + "of device\n", __func__); return -EINVAL; } @@ -1486,7 +1550,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - while(1) { + while (1) { WATCHDOG_RESET(); sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); @@ -1550,14 +1614,14 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " + "beyond end of device\n", __func__); return -EINVAL; } nand_get_device(chip, mtd, FL_READING); - switch(ops->mode) { + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: case MTD_OOB_RAW: @@ -1572,7 +1636,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, else ret = nand_do_read_ops(mtd, from, ops); - out: +out: nand_release_device(mtd); return ret; } @@ -1601,8 +1665,9 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * * 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) +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; @@ -1789,14 +1854,13 @@ 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) { + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_RAW: @@ -1808,7 +1872,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, uint32_t boffs = 0, woffs = ops->ooboffs; size_t bytes = 0; - for(; free->length && len; free++, len -= bytes) { + for (; free->length && len; free++, len -= bytes) { /* Write request not from offset 0 ? */ if (unlikely(woffs)) { if (woffs >= free->length) { @@ -1834,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 @@ -1850,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; @@ -1886,7 +1955,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); - while(1) { + /* Don't allow multipage oob writes with offset */ + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) + return -EINVAL; + + while (1) { WATCHDOG_RESET(); int bytes = mtd->writesize; @@ -1903,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)); @@ -1985,8 +2061,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)ops->ooblen); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; @@ -1995,24 +2071,24 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt to write past end of page\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " + "past end of page\n", __func__); return -EINVAL; } if (unlikely(ops->ooboffs >= len)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt to start write outside oob\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " + "write outside oob\n", __func__); return -EINVAL; } - /* Do not allow reads past end of device */ + /* Do not allow write past end of device */ if (unlikely(to >= mtd->size || ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt write beyond end of device\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); return -EINVAL; } @@ -2039,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); @@ -2067,14 +2143,14 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); return -EINVAL; } nand_get_device(chip, mtd, FL_WRITING); - switch(ops->mode) { + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: case MTD_OOB_RAW: @@ -2089,7 +2165,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, else ret = nand_do_write_ops(mtd, to, ops); - out: +out: nand_release_device(mtd); return ret; } @@ -2158,31 +2234,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, unsigned int bbt_masked_page = 0xffffffff; loff_t 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)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); - return -EINVAL; - } + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); - /* 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"); + if (check_offs_len(mtd, instr->addr, instr->len)) 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); @@ -2199,8 +2258,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Device is write protected!!!\n"); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2226,8 +2285,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, */ if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { - printk(KERN_WARNING "nand_erase: attempt to erase a " - "bad block at page 0x%08x\n", page); + printk(KERN_WARNING "%s: attempt to erase a bad block " + "at page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2254,10 +2313,11 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " - "Failed erase, page 0x%08x\n", page); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " + "page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; - instr->fail_addr = ((loff_t)page << chip->page_shift); + instr->fail_addr = + ((loff_t)page << chip->page_shift); goto erase_exit; } @@ -2292,7 +2352,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, } instr->state = MTD_ERASE_DONE; - erase_exit: +erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; @@ -2314,9 +2374,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (!rewrite_bbt[chipnr]) continue; /* update the BBT for chip */ - MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], - chip->bbt_td->pages[chipnr]); + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " + "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, + rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } @@ -2334,7 +2394,7 @@ static void nand_sync(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_SYNCING); @@ -2366,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; @@ -2416,10 +2477,29 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) } #ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* + * sanitize ONFI strings so we can safely print them + */ +static void sanitize_string(char *s, size_t len) +{ + ssize_t i; + + /* null terminate */ + s[len - 1] = 0; + + /* remove non printable chars */ + for (i = 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + + /* remove trailing spaces */ + strim(s); +} + static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) { int i; - while (len--) { crc ^= *p++ << 8; for (i = 0; i < 8; i++) @@ -2432,14 +2512,14 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise */ -static int nand_flash_detect_onfi(struct mtd_info *mtd, - struct nand_chip *chip, +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int *busw) { struct nand_onfi_params *p = &chip->onfi_params; 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') @@ -2450,7 +2530,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, for (i = 0; i < 3; i++) { chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == - le16_to_cpu(p->crc)) { + le16_to_cpu(p->crc)) { printk(KERN_INFO "ONFI param page %d valid\n", i); break; } @@ -2475,14 +2555,15 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, chip->onfi_version = 0; if (!chip->onfi_version) { - printk(KERN_INFO "%s: unsupported ONFI " - "version: %d\n", __func__, val); + printk(KERN_INFO "%s: unsupported ONFI version: %d\n", + __func__, val); return 0; } + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); 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); @@ -2491,6 +2572,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, 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 @@ -2502,41 +2587,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 */ @@ -2546,8 +2596,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); @@ -2573,15 +2624,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 */ + for (i = 0; i < 2; i++) + id_data[i] = chip->read_byte(mtd); - tmp_manf = chip->read_byte(mtd); - tmp_id = 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); } @@ -2592,30 +2641,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 */ @@ -2650,18 +2790,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) @@ -2673,9 +2843,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; } @@ -2704,7 +2880,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, nand_set_defaults(chip, busw); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table); + type = nand_get_flash_type(mtd, chip, busw, + &nand_maf_id, &nand_dev_id, table); if (IS_ERR(type)) { #ifndef CONFIG_SYS_NAND_QUIET_TEST @@ -2763,7 +2940,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one */ - if (!chip->ecc.layout) { + if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: chip->ecc.layout = &nand_oob_8; @@ -2796,7 +2973,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* Similar to NAND_ECC_HW, but a separate read_page handle */ if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { - printk(KERN_WARNING "No ECC functions supplied, " + printk(KERN_WARNING "No ECC functions supplied; " "Hardware ECC not possible\n"); BUG(); } @@ -2825,7 +3002,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page == nand_read_page_hwecc || !chip->ecc.write_page || chip->ecc.write_page == nand_write_page_hwecc)) { - printk(KERN_WARNING "No ECC functions supplied, " + printk(KERN_WARNING "No ECC functions supplied; " "Hardware ECC not possible\n"); BUG(); } @@ -2851,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; @@ -2860,10 +3041,44 @@ 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; + case NAND_ECC_SOFT_BCH: + if (!mtd_nand_has_bch()) { + printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n"); + return -EINVAL; + } + chip->ecc.calculate = nand_bch_calculate_ecc; + chip->ecc.correct = nand_bch_correct_data; + 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; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. + * Otherwise, default to 4 bits for large page devices + */ + if (!chip->ecc.size && (mtd->oobsize >= 64)) { + chip->ecc.size = 512; + chip->ecc.bytes = 7; + } + chip->ecc.priv = nand_bch_init(mtd, + chip->ecc.size, + chip->ecc.bytes, + &chip->ecc.layout); + if (!chip->ecc.priv) + printk(KERN_WARNING "BCH ECC initialization failed!\n"); + + break; + case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); @@ -2899,7 +3114,7 @@ int nand_scan_tail(struct mtd_info *mtd) * mode */ chip->ecc.steps = mtd->writesize / chip->ecc.size; - if(chip->ecc.steps * chip->ecc.size != mtd->writesize) { + if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { printk(KERN_WARNING "Invalid ecc parameters\n"); BUG(); } @@ -2911,7 +3126,7 @@ int nand_scan_tail(struct mtd_info *mtd) */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { - switch(chip->ecc.steps) { + switch (chip->ecc.steps) { case 2: mtd->subpage_sft = 1; break; @@ -2935,7 +3150,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; @@ -2954,9 +3170,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); } /** @@ -2989,6 +3206,9 @@ void nand_release(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + if (chip->ecc.mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + #ifdef CONFIG_MTD_PARTITIONS /* Deregister partitions */ del_mtd_partitions(mtd); @@ -2998,4 +3218,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/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index ded652b..2b730e0 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 <linux/mtd/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/bitops.h> #include <asm/errno.h> +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,20 +292,21 @@ 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; } @@ -232,21 +314,64 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } /* + * 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,12 +1085,59 @@ 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 * @bd: descriptor for the good/bad block search pattern @@ -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/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c new file mode 100644 index 0000000..7835fce --- /dev/null +++ b/drivers/mtd/nand/nand_bch.c @@ -0,0 +1,236 @@ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file 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 file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <common.h> +/*#include <asm/io.h>*/ +#include <linux/types.h> + +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/bch.h> +#include <malloc.h> + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @ecclayout: private ecc layout for this BCH configuration + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + struct nand_ecclayout ecclayout; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + + return 0; +} + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n", + __func__, errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -1; + } + return count; +} + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * @eccsize: ecc block size in bytes + * @eccbytes: ecc length in bytes + * @ecclayout: output default layout + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, + struct nand_ecclayout **ecclayout) +{ + unsigned int m, t, eccsteps, i; + struct nand_ecclayout *layout; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* if no ecc placement scheme was provided, build one */ + if (!*ecclayout) { + + /* handle large page devices only */ + if (mtd->oobsize < 64) { + printk(KERN_WARNING "must provide an oob scheme for " + "oobsize %d\n", mtd->oobsize); + goto fail; + } + + layout = &nbc->ecclayout; + layout->eccbytes = eccsteps*eccbytes; + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + printk(KERN_WARNING "no suitable oob scheme available " + "for oobsize %d eccbytes %u\n", mtd->oobsize, + eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + + *ecclayout = layout; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 8d7ea76..3953549 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -76,9 +76,13 @@ const struct nand_flash_dev nand_flash_ids[] = { /*512 Megabit */ {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS}, {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS}, {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16}, {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16}, /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, @@ -86,6 +90,7 @@ const struct nand_flash_dev nand_flash_ids[] = { {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16}, /* 2 Gigabit */ {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, @@ -111,6 +116,36 @@ const struct nand_flash_dev nand_flash_ids[] = { {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + /* 32 Gigabit */ + {"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS}, + {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, + {"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16}, + {"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16}, + + /* 64 Gigabit */ + {"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS}, + {"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS}, + {"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16}, + {"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16}, + + /* 128 Gigabit */ + {"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS}, + {"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS}, + {"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16}, + {"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16}, + + /* 256 Gigabit */ + {"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS}, + {"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS}, + {"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16}, + {"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16}, + + /* 512 Gigabit */ + {"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS}, + {"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS}, + {"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16}, + {"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16}, + /* * Renesas AND 1 Gigabit. Those chips do not support extended id and * have a strange page/block layout ! The chosen minimum erasesize is diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c index 7eb08a3..4a4d02f 100644 --- a/drivers/mtd/nand/nand_spl_simple.c +++ b/drivers/mtd/nand/nand_spl_simple.c @@ -27,6 +27,11 @@ static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; static nand_info_t mtd; static struct nand_chip nand_chip; +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + #if (CONFIG_SYS_NAND_PAGE_SIZE <= 512) /* * NAND command for small page NAND devices (512) @@ -145,29 +150,21 @@ static int nand_is_bad_block(int block) static int nand_read_page(int block, int page, uchar *dst) { struct nand_chip *this = mtd.priv; - u_char *ecc_calc; - u_char *ecc_code; - u_char *oob_data; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; int i; int eccsize = CONFIG_SYS_NAND_ECCSIZE; int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + int eccsteps = ECCSTEPS; uint8_t *p = dst; - /* - * No malloc available for now, just use some temporary locations - * in SDRAM - */ - ecc_calc = (u_char *)(CONFIG_SYS_SDRAM_BASE + 0x10000); - ecc_code = ecc_calc + 0x100; - oob_data = ecc_calc + 0x200; - nand_command(block, page, 0, NAND_CMD_READOOB); this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); nand_command(block, page, 0, NAND_CMD_READ0); /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < CONFIG_SYS_NAND_ECCTOTAL; i++) + for (i = 0; i < ECCTOTAL; i++) ecc_code[i] = oob_data[nand_ecc_pos[i]]; @@ -184,24 +181,17 @@ static int nand_read_page(int block, int page, uchar *dst) static int nand_read_page(int block, int page, void *dst) { struct nand_chip *this = mtd.priv; - u_char *ecc_calc; - u_char *ecc_code; - u_char *oob_data; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; int i; int eccsize = CONFIG_SYS_NAND_ECCSIZE; int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + int eccsteps = ECCSTEPS; uint8_t *p = dst; nand_command(block, page, 0, NAND_CMD_READ0); - /* No malloc available for now, just use some temporary locations - * in SDRAM - */ - ecc_calc = (u_char *)(CONFIG_SYS_SDRAM_BASE + 0x10000); - ecc_code = ecc_calc + 0x100; - oob_data = ecc_calc + 0x200; - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { if (this->ecc.mode != NAND_ECC_SOFT) this->ecc.hwctl(&mtd, NAND_ECC_READ); @@ -211,10 +201,10 @@ static int nand_read_page(int block, int page, void *dst) this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < CONFIG_SYS_NAND_ECCTOTAL; i++) + for (i = 0; i < ECCTOTAL; i++) ecc_code[i] = oob_data[nand_ecc_pos[i]]; - eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + eccsteps = ECCSTEPS; p = dst; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { diff --git a/include/configs/P1_P2_RDB.h b/include/configs/P1_P2_RDB.h index 00fa74d..cee788a 100644 --- a/include/configs/P1_P2_RDB.h +++ b/include/configs/P1_P2_RDB.h @@ -273,11 +273,10 @@ extern unsigned long get_board_sys_clk(unsigned long dummy); #endif #endif +#define CONFIG_CMD_NAND #define CONFIG_SYS_NAND_BASE_LIST {CONFIG_SYS_NAND_BASE} #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_MTD_NAND_VERIFY_WRITE -#define CONFIG_CMD_NAND 1 #define CONFIG_NAND_FSL_ELBC 1 #define CONFIG_SYS_NAND_BLOCK_SIZE (16 * 1024) diff --git a/include/configs/PMC440.h b/include/configs/PMC440.h index ed47a87..b820954 100644 --- a/include/configs/PMC440.h +++ b/include/configs/PMC440.h @@ -200,9 +200,7 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 16 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS {0, 1, 2, 3, 6, 7} #endif diff --git a/include/configs/SIMPC8313.h b/include/configs/SIMPC8313.h index 77be360..0976077 100644 --- a/include/configs/SIMPC8313.h +++ b/include/configs/SIMPC8313.h @@ -144,10 +144,9 @@ #endif #define CONFIG_SYS_FPGA_BASE 0xFF000000 +#define CONFIG_CMD_NAND #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_MTD_NAND_VERIFY_WRITE -#define CONFIG_CMD_NAND 1 #define CONFIG_NAND_FSL_ELBC 1 #define CONFIG_SYS_NAND_BR_PRELIM (CONFIG_SYS_NAND_BASE \ diff --git a/include/configs/VCMA9.h b/include/configs/VCMA9.h index a370c15..5cc8ece 100644 --- a/include/configs/VCMA9.h +++ b/include/configs/VCMA9.h @@ -237,7 +237,6 @@ #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #define CONFIG_S3C24XX_CUSTOM_NAND_TIMING #define CONFIG_S3C24XX_TACLS 1 diff --git a/include/configs/acadia.h b/include/configs/acadia.h index 5573dc7..8c447ca 100644 --- a/include/configs/acadia.h +++ b/include/configs/acadia.h @@ -185,9 +185,7 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 16 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS {0, 1, 2, 3, 6, 7} #ifdef CONFIG_ENV_IS_IN_NAND diff --git a/include/configs/am3517_crane.h b/include/configs/am3517_crane.h index 0a0c261..b0dd2f0 100644 --- a/include/configs/am3517_crane.h +++ b/include/configs/am3517_crane.h @@ -359,10 +359,6 @@ 10, 11, 12, 13} #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/am3517_evm.h b/include/configs/am3517_evm.h index d44eeec..f797f3f 100644 --- a/include/configs/am3517_evm.h +++ b/include/configs/am3517_evm.h @@ -360,10 +360,6 @@ 10, 11, 12, 13} #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/aria.h b/include/configs/aria.h index cf2e7d4..c9f0076 100644 --- a/include/configs/aria.h +++ b/include/configs/aria.h @@ -247,13 +247,9 @@ */ #define CONFIG_CMD_NAND /* enable NAND support */ #define CONFIG_JFFS2_NAND /* with JFFS2 on it */ - - #define CONFIG_NAND_MPC5121_NFC #define CONFIG_SYS_NAND_BASE 0x40000000 - #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS CONFIG_SYS_MAX_NAND_DEVICE /* * Configuration parameters for MPC5121 NAND driver diff --git a/include/configs/at91sam9m10g45ek.h b/include/configs/at91sam9m10g45ek.h index 5ef6bd2..6a02188 100644 --- a/include/configs/at91sam9m10g45ek.h +++ b/include/configs/at91sam9m10g45ek.h @@ -128,7 +128,6 @@ /* NAND flash */ #ifdef CONFIG_CMD_NAND -#define CONFIG_NAND_MAX_CHIPS 1 #define CONFIG_NAND_ATMEL #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE ATMEL_BASE_CS3 diff --git a/include/configs/bamboo.h b/include/configs/bamboo.h index 7b66fc0..506a558 100644 --- a/include/configs/bamboo.h +++ b/include/configs/bamboo.h @@ -179,9 +179,7 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 16 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS {0, 1, 2, 3, 6, 7} #ifdef CONFIG_ENV_IS_IN_NAND diff --git a/include/configs/bf526-ezbrd.h b/include/configs/bf526-ezbrd.h index 1945c03..0031093 100644 --- a/include/configs/bf526-ezbrd.h +++ b/include/configs/bf526-ezbrd.h @@ -70,7 +70,6 @@ #define CONFIG_DRIVER_NAND_BFIN #define CONFIG_SYS_NAND_BASE 0 /* not actually used */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_CMD_NAND #endif diff --git a/include/configs/bf527-ad7160-eval.h b/include/configs/bf527-ad7160-eval.h index 9c35f2d..fa05103 100644 --- a/include/configs/bf527-ad7160-eval.h +++ b/include/configs/bf527-ad7160-eval.h @@ -69,7 +69,6 @@ #define CONFIG_DRIVER_NAND_BFIN #define CONFIG_SYS_NAND_BASE 0 /* not actually used */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #endif diff --git a/include/configs/bf527-ezkit.h b/include/configs/bf527-ezkit.h index 1256e81..d80eac2 100644 --- a/include/configs/bf527-ezkit.h +++ b/include/configs/bf527-ezkit.h @@ -69,7 +69,6 @@ #define CONFIG_DRIVER_NAND_BFIN #define CONFIG_SYS_NAND_BASE 0 /* not actually used */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #endif diff --git a/include/configs/bf548-ezkit.h b/include/configs/bf548-ezkit.h index 3eadcef..89adfef 100644 --- a/include/configs/bf548-ezkit.h +++ b/include/configs/bf548-ezkit.h @@ -131,7 +131,6 @@ #define CONFIG_DRIVER_NAND_BFIN #define CONFIG_SYS_NAND_BASE 0 /* not actually used */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 /* diff --git a/include/configs/cam_enc_4xx.h b/include/configs/cam_enc_4xx.h index a21d448..5d9672f 100644 --- a/include/configs/cam_enc_4xx.h +++ b/include/configs/cam_enc_4xx.h @@ -90,7 +90,6 @@ #define CONFIG_SYS_NAND_BASE_LIST { 0x02000000, } /* socket has two chipselects, nCE0 gated by address BIT(14) */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_MAX_CHIPS 1 /* SPI support */ #define CONFIG_SPI @@ -236,9 +235,6 @@ #define CONFIG_SYS_NAND_ECCBYTES 10 #define CONFIG_SYS_NAND_OOBSIZE 64 #define CONFIG_SYS_NAND_5_ADDR_CYCLE -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (40) /* * RBL searches from Block n (n = 1..24) diff --git a/include/configs/canyonlands.h b/include/configs/canyonlands.h index 8c03582..acb127c 100644 --- a/include/configs/canyonlands.h +++ b/include/configs/canyonlands.h @@ -197,9 +197,7 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 64 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS {40, 41, 42, 43, 44, 45, 46, 47, \ 48, 49, 50, 51, 52, 53, 54, 55, \ 56, 57, 58, 59, 60, 61, 62, 63} diff --git a/include/configs/cm-bf527.h b/include/configs/cm-bf527.h index 4f2b904..b15a1eb 100644 --- a/include/configs/cm-bf527.h +++ b/include/configs/cm-bf527.h @@ -68,7 +68,6 @@ #define CONFIG_BFIN_NFC_CTL_VAL 0x0033 #define CONFIG_SYS_NAND_BASE 0 /* not actually used */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_CMD_NAND #endif diff --git a/include/configs/cpu9260.h b/include/configs/cpu9260.h index 8674a35..8b6a687 100644 --- a/include/configs/cpu9260.h +++ b/include/configs/cpu9260.h @@ -295,7 +295,6 @@ /* NAND flash */ #define CONFIG_NAND_ATMEL -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x40000000 #define CONFIG_SYS_NAND_DBW_8 1 diff --git a/include/configs/da830evm.h b/include/configs/da830evm.h index 6ac25d2..e8c0212 100644 --- a/include/configs/da830evm.h +++ b/include/configs/da830evm.h @@ -115,7 +115,6 @@ #define CONFIG_SYS_CLE_MASK 0x10 #define CONFIG_SYS_ALE_MASK 0x8 #define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */ -#define NAND_MAX_CHIPS 1 #endif #ifdef CONFIG_USE_NOR diff --git a/include/configs/da850evm.h b/include/configs/da850evm.h index fcbbace..220890d 100644 --- a/include/configs/da850evm.h +++ b/include/configs/da850evm.h @@ -182,7 +182,6 @@ #define CONFIG_SYS_ALE_MASK 0x8 #undef CONFIG_SYS_NAND_HW_ECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */ -#define NAND_MAX_CHIPS 1 #endif /* diff --git a/include/configs/davinci_dm355leopard.h b/include/configs/davinci_dm355leopard.h index dfa0a00..dc5b408 100644 --- a/include/configs/davinci_dm355leopard.h +++ b/include/configs/davinci_dm355leopard.h @@ -69,7 +69,6 @@ #define CONFIG_SYS_NAND_BASE_LIST { 0x02000000, } #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_MAX_CHIPS 1 /* U-Boot command configuration */ #include <config_cmd_default.h> diff --git a/include/configs/devkit8000.h b/include/configs/devkit8000.h index 758326b..2b6a6ee 100644 --- a/include/configs/devkit8000.h +++ b/include/configs/devkit8000.h @@ -343,11 +343,6 @@ #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) - #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/ea20.h b/include/configs/ea20.h index 74fec3f..cc0f5b0 100644 --- a/include/configs/ea20.h +++ b/include/configs/ea20.h @@ -197,7 +197,6 @@ #define CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST #define CONFIG_SYS_NAND_USE_FLASH_BBT #define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */ -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_64BIT_VSPRINTF /* needed for nand_util.c */ #endif diff --git a/include/configs/eb_cpux9k2.h b/include/configs/eb_cpux9k2.h index b08de4a..9046cf0 100644 --- a/include/configs/eb_cpux9k2.h +++ b/include/configs/eb_cpux9k2.h @@ -270,7 +270,6 @@ /* NAND */ -#define CONFIG_SYS_NAND_MAX_CHIPS 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x40000000 #define CONFIG_SYS_NAND_DBW_8 1 diff --git a/include/configs/enbw_cmc.h b/include/configs/enbw_cmc.h index c427dc7..83aec79 100644 --- a/include/configs/enbw_cmc.h +++ b/include/configs/enbw_cmc.h @@ -115,7 +115,6 @@ #define CONFIG_SYS_ALE_MASK 0x8 #undef CONFIG_SYS_NAND_HW_ECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */ -#define NAND_MAX_CHIPS 1 #define MTDIDS_DEFAULT "nor0=physmap-flash.0,nand0=davinci_nand.1" #define MTDPARTS_DEFAULT \ diff --git a/include/configs/hawkboard.h b/include/configs/hawkboard.h index 12acb27..fa21494 100644 --- a/include/configs/hawkboard.h +++ b/include/configs/hawkboard.h @@ -114,7 +114,6 @@ /* Max number of NAND devices */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE_LIST { 0x62000000, } -#define NAND_MAX_CHIPS 1 /* Block 0--not used by bootcode */ #define CONFIG_ENV_OFFSET 0x0 @@ -138,11 +137,8 @@ #define CONFIG_SYS_NAND_BAD_BLOCK_POS 0 #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 10 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 64 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) + #endif /* CONFIG_SYS_USE_NAND */ /* diff --git a/include/configs/kilauea.h b/include/configs/kilauea.h index e66aadf..621dbb8 100644 --- a/include/configs/kilauea.h +++ b/include/configs/kilauea.h @@ -210,9 +210,7 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 16 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS {0, 1, 2, 3, 6, 7} #ifdef CONFIG_ENV_IS_IN_NAND diff --git a/include/configs/km/km_arm.h b/include/configs/km/km_arm.h index 700124c..9c8d222 100644 --- a/include/configs/km/km_arm.h +++ b/include/configs/km/km_arm.h @@ -133,7 +133,6 @@ * NAND Flash configuration */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define BOOTFLASH_START 0x0 diff --git a/include/configs/m28evk.h b/include/configs/m28evk.h index b891b12..4efff09 100644 --- a/include/configs/m28evk.h +++ b/include/configs/m28evk.h @@ -154,7 +154,6 @@ #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x60000000 #define CONFIG_SYS_NAND_5_ADDR_CYCLE -#define NAND_MAX_CHIPS 8 /* Environment is in NAND */ #define CONFIG_ENV_IS_IN_NAND diff --git a/include/configs/mecp5123.h b/include/configs/mecp5123.h index ed9282b..f5765b0 100644 --- a/include/configs/mecp5123.h +++ b/include/configs/mecp5123.h @@ -178,9 +178,7 @@ #define CONFIG_CMD_NAND #define CONFIG_NAND_MPC5121_NFC #define CONFIG_SYS_NAND_BASE 0x40000000 - #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS CONFIG_SYS_MAX_NAND_DEVICE /* * Configuration parameters for MPC5121 NAND driver diff --git a/include/configs/mpc5121ads.h b/include/configs/mpc5121ads.h index c3d3afd..01df8b1 100644 --- a/include/configs/mpc5121ads.h +++ b/include/configs/mpc5121ads.h @@ -242,7 +242,6 @@ #define CONFIG_SYS_NAND_BASE 0x40000000 #define CONFIG_SYS_MAX_NAND_DEVICE 2 -#define NAND_MAX_CHIPS CONFIG_SYS_MAX_NAND_DEVICE #define CONFIG_SYS_NAND_SELECT_DEVICE /* driver supports mutipl. chips */ /* diff --git a/include/configs/mv-common.h b/include/configs/mv-common.h index 3f5fcc6..1a63791 100644 --- a/include/configs/mv-common.h +++ b/include/configs/mv-common.h @@ -132,7 +132,6 @@ */ #ifdef CONFIG_CMD_NAND #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_64BIT_VSPRINTF /* needed for nand_util.c */ #endif diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 91af8a0..4c7a686 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -418,10 +418,6 @@ 10, 11, 12, 13} #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/omap3_evm.h b/include/configs/omap3_evm.h index 2ce3959..1fcb7af 100644 --- a/include/configs/omap3_evm.h +++ b/include/configs/omap3_evm.h @@ -121,10 +121,6 @@ 10, 11, 12, 13} #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/omap3_evm_quick_nand.h b/include/configs/omap3_evm_quick_nand.h index 2f879c0..362fa1d 100644 --- a/include/configs/omap3_evm_quick_nand.h +++ b/include/configs/omap3_evm_quick_nand.h @@ -91,10 +91,6 @@ 10, 11, 12, 13} #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/pdm360ng.h b/include/configs/pdm360ng.h index f0154e0..8afc3c0 100644 --- a/include/configs/pdm360ng.h +++ b/include/configs/pdm360ng.h @@ -225,9 +225,7 @@ #define CONFIG_CMD_NAND /* enable NAND support */ #define CONFIG_NAND_MPC5121_NFC #define CONFIG_SYS_NAND_BASE 0x40000000 - #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS CONFIG_SYS_MAX_NAND_DEVICE #define CONFIG_SYS_NAND_SELECT_DEVICE /* driver supports mutipl. chips */ /* diff --git a/include/configs/pm9261.h b/include/configs/pm9261.h index 1e80316..eba5616 100644 --- a/include/configs/pm9261.h +++ b/include/configs/pm9261.h @@ -233,7 +233,6 @@ /* NAND flash */ #define CONFIG_NAND_ATMEL -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x40000000 #define CONFIG_SYS_NAND_DBW_8 1 diff --git a/include/configs/pm9263.h b/include/configs/pm9263.h index 32c5962..bf31c13 100644 --- a/include/configs/pm9263.h +++ b/include/configs/pm9263.h @@ -253,7 +253,6 @@ /* NAND flash */ #ifdef CONFIG_CMD_NAND #define CONFIG_NAND_ATMEL -#define CONFIG_SYS_NAND_MAX_CHIPS 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x40000000 #define CONFIG_SYS_NAND_DBW_8 1 diff --git a/include/configs/pm9g45.h b/include/configs/pm9g45.h index eec9153..4779878 100644 --- a/include/configs/pm9g45.h +++ b/include/configs/pm9g45.h @@ -115,7 +115,6 @@ /* NAND flash */ #ifdef CONFIG_CMD_NAND -#define CONFIG_NAND_MAX_CHIPS 1 #define CONFIG_NAND_ATMEL #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x40000000 diff --git a/include/configs/qi_lb60.h b/include/configs/qi_lb60.h index f989595..ebd8223 100644 --- a/include/configs/qi_lb60.h +++ b/include/configs/qi_lb60.h @@ -99,10 +99,6 @@ #define CONFIG_SYS_NAND_ECC_POS (6 * NANONOTE_NAND_SIZE) #define CONFIG_SYS_NAND_ECCSIZE 512 #define CONFIG_SYS_NAND_ECCBYTES 9 -#define CONFIG_SYS_NAND_ECCSTEPS \ - (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL \ - (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS \ {12, 13, 14, 15, 16, 17, 18, 19,\ 20, 21, 22, 23, 24, 25, 26, 27, \ @@ -117,7 +113,6 @@ #define CONFIG_SYS_NAND_OOBSIZE 128 #define CONFIG_SYS_NAND_BASE 0xB8000000 #define CONFIG_SYS_ONENAND_BASE CONFIG_SYS_NAND_BASE -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_SELECT_DEVICE 1 /* nand driver supports mutipl.*/ #define CONFIG_NAND_SPL_TEXT_BASE 0x80000000 diff --git a/include/configs/sequoia.h b/include/configs/sequoia.h index a406ca0..8e6954e 100644 --- a/include/configs/sequoia.h +++ b/include/configs/sequoia.h @@ -205,9 +205,7 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) #define CONFIG_SYS_NAND_OOBSIZE 16 -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) #define CONFIG_SYS_NAND_ECCPOS {0, 1, 2, 3, 6, 7} #ifdef CONFIG_ENV_IS_IN_NAND diff --git a/include/configs/smdk2410.h b/include/configs/smdk2410.h index 77c0a08..7315984 100644 --- a/include/configs/smdk2410.h +++ b/include/configs/smdk2410.h @@ -211,7 +211,6 @@ #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define NAND_MAX_CHIPS 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #endif diff --git a/include/configs/smdk6400.h b/include/configs/smdk6400.h index 4d0b7b2..a2b9441 100644 --- a/include/configs/smdk6400.h +++ b/include/configs/smdk6400.h @@ -262,12 +262,8 @@ #define CONFIG_SYS_NAND_ECCSIZE CONFIG_SYS_NAND_PAGE_SIZE /* Number of ECC bytes per OOB - S3C6400 calculates 4 bytes ECC in 1-bit mode */ #define CONFIG_SYS_NAND_ECCBYTES 4 -/* Number of ECC-blocks per NAND page */ -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) /* Size of a single OOB region */ #define CONFIG_SYS_NAND_OOBSIZE 64 -/* Number of ECC bytes per page */ -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * CONFIG_SYS_NAND_ECCSTEPS) /* ECC byte positions */ #define CONFIG_SYS_NAND_ECCPOS {40, 41, 42, 43, 44, 45, 46, 47, \ 48, 49, 50, 51, 52, 53, 54, 55, \ diff --git a/include/configs/tam3517-common.h b/include/configs/tam3517-common.h index 817d468..f4963ac 100644 --- a/include/configs/tam3517-common.h +++ b/include/configs/tam3517-common.h @@ -278,11 +278,6 @@ #define CONFIG_SYS_NAND_ECCSIZE 256 #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_SYS_NAND_ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ - CONFIG_SYS_NAND_ECCSTEPS) - #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 diff --git a/include/configs/tnetv107x_evm.h b/include/configs/tnetv107x_evm.h index 4bced0c..7c3f334 100644 --- a/include/configs/tnetv107x_evm.h +++ b/include/configs/tnetv107x_evm.h @@ -90,7 +90,6 @@ #define CONFIG_CMD_MTDPARTS #define CONFIG_MTD_DEVICE #define CONFIG_JFFS2_NAND -#define NAND_MAX_CHIPS 1 #define CONFIG_ENV_OFFSET 0x180000 /* diff --git a/include/configs/tt01.h b/include/configs/tt01.h index a553712..7e293c6 100644 --- a/include/configs/tt01.h +++ b/include/configs/tt01.h @@ -231,7 +231,6 @@ #define CONFIG_NAND_MXC #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_MAX_CHIPS 1 /* * actually this is nothing someone wants to configure! diff --git a/include/linux/bch.h b/include/linux/bch.h new file mode 100644 index 0000000..295b4ef --- /dev/null +++ b/include/linux/bch.h @@ -0,0 +1,79 @@ +/* + * Generic binary BCH encoding/decoding library + * + * 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 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. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic <ivan.djelic@parrot.com> + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. +*/ +#ifndef _BCH_H +#define _BCH_H + +#include <linux/types.h> + +/** + * struct bch_control - BCH control structure + * @m: Galois field order + * @n: maximum codeword size in bits (= 2^m-1) + * @t: error correction capability in bits + * @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t) + * @ecc_bytes: ecc max size (m*t bits) in bytes + * @a_pow_tab: Galois field GF(2^m) exponentiation lookup table + * @a_log_tab: Galois field GF(2^m) log lookup table + * @mod8_tab: remainder generator polynomial lookup tables + * @ecc_buf: ecc parity words buffer + * @ecc_buf2: ecc parity words buffer + * @xi_tab: GF(2^m) base for solving degree 2 polynomial roots + * @syn: syndrome buffer + * @cache: log-based polynomial representation buffer + * @elp: error locator polynomial + * @poly_2t: temporary polynomials of degree 2t + */ +struct bch_control { + unsigned int m; + unsigned int n; + unsigned int t; + unsigned int ecc_bits; + unsigned int ecc_bytes; +/* private: */ + uint16_t *a_pow_tab; + uint16_t *a_log_tab; + uint32_t *mod8_tab; + uint32_t *ecc_buf; + uint32_t *ecc_buf2; + unsigned int *xi_tab; + unsigned int *syn; + int *cache; + struct gf_poly *elp; + struct gf_poly *poly_2t[4]; +}; + +struct bch_control *init_bch(int m, int t, unsigned int prim_poly); + +void free_bch(struct bch_control *bch); + +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc); + +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc); + +#endif /* _BCH_H */ diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 7db2546..8cbcdae 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -11,8 +11,19 @@ * Thomas Gleixner <tglx@linuxtronix.de> * * 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,14 @@ 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 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 diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1cdc7ae..99668d5 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 <dwmw2@infradead.org> - * Steven J. Hill <sjhill@realitydiluted.com> - * Thomas Gleixner <tglx@linutronix.de> + * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> + * Steven J. Hill <sjhill@realitydiluted.com> + * Thomas Gleixner <tglx@linutronix.de> * * 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 @@ -18,13 +18,6 @@ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H -/* XXX U-BOOT XXX */ -#if 0 -#include <linux/wait.h> -#include <linux/spinlock.h> -#include <linux/mtd/mtd.h> -#endif - #include "config.h" #include "linux/mtd/compat.h" @@ -43,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 @@ -86,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 @@ -132,6 +130,7 @@ typedef enum { NAND_ECC_HW, NAND_ECC_HW_SYNDROME, NAND_ECC_HW_OOB_FIRST, + NAND_ECC_SOFT_BCH, } nand_ecc_modes_t; /* @@ -148,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 */ @@ -161,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 \ @@ -196,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 @@ -281,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 */ @@ -308,56 +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 * @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 (*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); }; /** @@ -377,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; }; /* @@ -539,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[]; @@ -552,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 @@ -573,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 @@ -596,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; }; /** @@ -610,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 */ diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h new file mode 100644 index 0000000..d8754dd --- /dev/null +++ b/include/linux/mtd/nand_bch.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + * + * 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__ */ diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 090da50..9715a53 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__ */ diff --git a/include/linux/string.h b/include/linux/string.h index 6239039..9a8cbc2 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -47,6 +47,10 @@ extern char * strchr(const char *,int); #ifndef __HAVE_ARCH_STRRCHR extern char * strrchr(const char *,int); #endif +extern char * skip_spaces(const char *); + +extern char *strim(char *); + #ifndef __HAVE_ARCH_STRSTR extern char * strstr(const char *,const char *); #endif diff --git a/include/nand.h b/include/nand.h index d444ddc..8b3a1a7 100644 --- a/include/nand.h +++ b/include/nand.h @@ -24,13 +24,29 @@ #ifndef _NAND_H_ #define _NAND_H_ +#include <config.h> + +/* + * All boards using a given driver must convert to self-init + * at the same time, so do it here. When all drivers are + * converted, this will go away. + */ +#if defined(CONFIG_NAND_FSL_ELBC) +#define CONFIG_SYS_NAND_SELF_INIT +#endif + extern void nand_init(void); #include <linux/mtd/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> +#ifdef CONFIG_SYS_NAND_SELF_INIT +void board_nand_init(void); +int nand_register(int devnum); +#else extern int board_nand_init(struct nand_chip *nand); +#endif typedef struct mtd_info nand_info_t; diff --git a/lib/Makefile b/lib/Makefile index 35ba7ff..e6e6ec6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,6 +27,7 @@ LIB = $(obj)libgeneric.o ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_ADDR_MAP) += addr_map.o +COBJS-$(CONFIG_BCH) += bch.o COBJS-$(CONFIG_BZIP2) += bzlib.o COBJS-$(CONFIG_BZIP2) += bzlib_crctable.o COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o diff --git a/lib/bch.c b/lib/bch.c new file mode 100644 index 0000000..7f4ca92 --- /dev/null +++ b/lib/bch.c @@ -0,0 +1,1358 @@ +/* + * Generic binary BCH encoding/decoding library + * + * 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 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. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic <ivan.djelic@parrot.com> + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. + * + * Call init_bch to get a pointer to a newly allocated bch_control structure for + * the given m (Galois field order), t (error correction capability) and + * (optional) primitive polynomial parameters. + * + * Call encode_bch to compute and store ecc parity bytes to a given buffer. + * Call decode_bch to detect and locate errors in received data. + * + * On systems supporting hw BCH features, intermediate results may be provided + * to decode_bch in order to skip certain steps. See decode_bch() documentation + * for details. + * + * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of + * parameters m and t; thus allowing extra compiler optimizations and providing + * better (up to 2x) encoding performance. Using this option makes sense when + * (m,t) are fixed and known in advance, e.g. when using BCH error correction + * on a particular NAND flash device. + * + * Algorithmic details: + * + * Encoding is performed by processing 32 input bits in parallel, using 4 + * remainder lookup tables. + * + * The final stage of decoding involves the following internal steps: + * a. Syndrome computation + * b. Error locator polynomial computation using Berlekamp-Massey algorithm + * c. Error locator root finding (by far the most expensive step) + * + * In this implementation, step c is not performed using the usual Chien search. + * Instead, an alternative approach described in [1] is used. It consists in + * factoring the error locator polynomial using the Berlekamp Trace algorithm + * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial + * solving techniques [2] are used. The resulting algorithm, called BTZ, yields + * much better performance than Chien search for usual (m,t) values (typically + * m >= 13, t < 32, see [1]). + * + * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields + * of characteristic 2, in: Western European Workshop on Research in Cryptology + * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. + * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over + * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. + */ + +#include <common.h> +#include <ubi_uboot.h> + +#include <linux/bitops.h> +#include <asm/byteorder.h> +#include <linux/bch.h> + +#if defined(CONFIG_BCH_CONST_PARAMS) +#define GF_M(_p) (CONFIG_BCH_CONST_M) +#define GF_T(_p) (CONFIG_BCH_CONST_T) +#define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) +#else +#define GF_M(_p) ((_p)->m) +#define GF_T(_p) ((_p)->t) +#define GF_N(_p) ((_p)->n) +#endif + +#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) +#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) + +#ifndef dbg +#define dbg(_fmt, args...) do {} while (0) +#endif + +/* + * represent a polynomial over GF(2^m) + */ +struct gf_poly { + unsigned int deg; /* polynomial degree */ + unsigned int c[0]; /* polynomial terms */ +}; + +/* given its degree, compute a polynomial size in bytes */ +#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) + +/* polynomial of degree 1 */ +struct gf_poly_deg1 { + struct gf_poly poly; + unsigned int c[2]; +}; + +/* + * same as encode_bch(), but process input data one byte at a time + */ +static void encode_bch_unaligned(struct bch_control *bch, + const unsigned char *data, unsigned int len, + uint32_t *ecc) +{ + int i; + const uint32_t *p; + const int l = BCH_ECC_WORDS(bch)-1; + + while (len--) { + p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); + + for (i = 0; i < l; i++) + ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); + + ecc[l] = (ecc[l] << 8)^(*p); + } +} + +/* + * convert ecc bytes to aligned, zero-padded 32-bit ecc words + */ +static void load_ecc8(struct bch_control *bch, uint32_t *dst, + const uint8_t *src) +{ + uint8_t pad[4] = {0, 0, 0, 0}; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++, src += 4) + dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; + + memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); + dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; +} + +/* + * convert 32-bit ecc words to ecc bytes + */ +static void store_ecc8(struct bch_control *bch, uint8_t *dst, + const uint32_t *src) +{ + uint8_t pad[4]; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++) { + *dst++ = (src[i] >> 24); + *dst++ = (src[i] >> 16) & 0xff; + *dst++ = (src[i] >> 8) & 0xff; + *dst++ = (src[i] >> 0) & 0xff; + } + pad[0] = (src[nwords] >> 24); + pad[1] = (src[nwords] >> 16) & 0xff; + pad[2] = (src[nwords] >> 8) & 0xff; + pad[3] = (src[nwords] >> 0) & 0xff; + memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); +} + +/** + * encode_bch - calculate BCH ecc parity of data + * @bch: BCH control structure + * @data: data to encode + * @len: data length in bytes + * @ecc: ecc parity data, must be initialized by caller + * + * The @ecc parity array is used both as input and output parameter, in order to + * allow incremental computations. It should be of the size indicated by member + * @ecc_bytes of @bch, and should be initialized to 0 before the first call. + * + * The exact number of computed ecc parity bits is given by member @ecc_bits of + * @bch; it may be less than m*t for large values of t. + */ +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc) +{ + const unsigned int l = BCH_ECC_WORDS(bch)-1; + unsigned int i, mlen; + unsigned long m; + uint32_t w, r[l+1]; + const uint32_t * const tab0 = bch->mod8_tab; + const uint32_t * const tab1 = tab0 + 256*(l+1); + const uint32_t * const tab2 = tab1 + 256*(l+1); + const uint32_t * const tab3 = tab2 + 256*(l+1); + const uint32_t *pdata, *p0, *p1, *p2, *p3; + + if (ecc) { + /* load ecc parity bytes into internal 32-bit buffer */ + load_ecc8(bch, bch->ecc_buf, ecc); + } else { + memset(bch->ecc_buf, 0, sizeof(r)); + } + + /* process first unaligned data bytes */ + m = ((unsigned long)data) & 3; + if (m) { + mlen = (len < (4-m)) ? len : 4-m; + encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); + data += mlen; + len -= mlen; + } + + /* process 32-bit aligned data words */ + pdata = (uint32_t *)data; + mlen = len/4; + data += 4*mlen; + len -= 4*mlen; + memcpy(r, bch->ecc_buf, sizeof(r)); + + /* + * split each 32-bit word into 4 polynomials of weight 8 as follows: + * + * 31 ...24 23 ...16 15 ... 8 7 ... 0 + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt + * tttttttt mod g = r0 (precomputed) + * zzzzzzzz 00000000 mod g = r1 (precomputed) + * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) + * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 + */ + while (mlen--) { + /* input data is read in big-endian format */ + w = r[0]^cpu_to_be32(*pdata++); + p0 = tab0 + (l+1)*((w >> 0) & 0xff); + p1 = tab1 + (l+1)*((w >> 8) & 0xff); + p2 = tab2 + (l+1)*((w >> 16) & 0xff); + p3 = tab3 + (l+1)*((w >> 24) & 0xff); + + for (i = 0; i < l; i++) + r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; + + r[l] = p0[l]^p1[l]^p2[l]^p3[l]; + } + memcpy(bch->ecc_buf, r, sizeof(r)); + + /* process last unaligned bytes */ + if (len) + encode_bch_unaligned(bch, data, len, bch->ecc_buf); + + /* store ecc parity bytes into original parity buffer */ + if (ecc) + store_ecc8(bch, ecc, bch->ecc_buf); +} + +static inline int modulo(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + while (v >= n) { + v -= n; + v = (v & n) + (v >> GF_M(bch)); + } + return v; +} + +/* + * shorter and faster modulo function, only works when v < 2N. + */ +static inline int mod_s(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + return (v < n) ? v : v-n; +} + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly)-1; +} + +static inline int parity(unsigned int x) +{ + /* + * public domain code snippet, lifted from + * http://www-graphics.stanford.edu/~seander/bithacks.html + */ + x ^= x >> 1; + x ^= x >> 2; + x = (x & 0x11111111U) * 0x11111111U; + return (x >> 28) & 1; +} + +/* Galois field basic operations: multiply, divide, inverse, etc. */ + +static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) +{ + return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; +} + +static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + GF_N(bch)-bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) +{ + return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; +} + +static inline unsigned int a_pow(struct bch_control *bch, int i) +{ + return bch->a_pow_tab[modulo(bch, i)]; +} + +static inline int a_log(struct bch_control *bch, unsigned int x) +{ + return bch->a_log_tab[x]; +} + +static inline int a_ilog(struct bch_control *bch, unsigned int x) +{ + return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); +} + +/* + * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t + */ +static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, + unsigned int *syn) +{ + int i, j, s; + unsigned int m; + uint32_t poly; + const int t = GF_T(bch); + + s = bch->ecc_bits; + + /* make sure extra bits in last ecc word are cleared */ + m = ((unsigned int)s) & 31; + if (m) + ecc[s/32] &= ~((1u << (32-m))-1); + memset(syn, 0, 2*t*sizeof(*syn)); + + /* compute v(a^j) for j=1 .. 2t-1 */ + do { + poly = *ecc++; + s -= 32; + while (poly) { + i = deg(poly); + for (j = 0; j < 2*t; j += 2) + syn[j] ^= a_pow(bch, (j+1)*(i+s)); + + poly ^= (1 << i); + } + } while (s > 0); + + /* v(a^(2j)) = v(a^j)^2 */ + for (j = 0; j < t; j++) + syn[2*j+1] = gf_sqr(bch, syn[j]); +} + +static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) +{ + memcpy(dst, src, GF_POLY_SZ(src->deg)); +} + +static int compute_error_locator_polynomial(struct bch_control *bch, + const unsigned int *syn) +{ + const unsigned int t = GF_T(bch); + const unsigned int n = GF_N(bch); + unsigned int i, j, tmp, l, pd = 1, d = syn[0]; + struct gf_poly *elp = bch->elp; + struct gf_poly *pelp = bch->poly_2t[0]; + struct gf_poly *elp_copy = bch->poly_2t[1]; + int k, pp = -1; + + memset(pelp, 0, GF_POLY_SZ(2*t)); + memset(elp, 0, GF_POLY_SZ(2*t)); + + pelp->deg = 0; + pelp->c[0] = 1; + elp->deg = 0; + elp->c[0] = 1; + + /* use simplified binary Berlekamp-Massey algorithm */ + for (i = 0; (i < t) && (elp->deg <= t); i++) { + if (d) { + k = 2*i-pp; + gf_poly_copy(elp_copy, elp); + /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ + tmp = a_log(bch, d)+n-a_log(bch, pd); + for (j = 0; j <= pelp->deg; j++) { + if (pelp->c[j]) { + l = a_log(bch, pelp->c[j]); + elp->c[j+k] ^= a_pow(bch, tmp+l); + } + } + /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ + tmp = pelp->deg+k; + if (tmp > elp->deg) { + elp->deg = tmp; + gf_poly_copy(pelp, elp_copy); + pd = d; + pp = 2*i; + } + } + /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ + if (i < t-1) { + d = syn[2*i+2]; + for (j = 1; j <= elp->deg; j++) + d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); + } + } + dbg("elp=%s\n", gf_poly_str(elp)); + return (elp->deg > t) ? -1 : (int)elp->deg; +} + +/* + * solve a m x m linear system in GF(2) with an expected number of solutions, + * and return the number of found solutions + */ +static int solve_linear_system(struct bch_control *bch, unsigned int *rows, + unsigned int *sol, int nsol) +{ + const int m = GF_M(bch); + unsigned int tmp, mask; + int rem, c, r, p, k, param[m]; + + k = 0; + mask = 1 << m; + + /* Gaussian elimination */ + for (c = 0; c < m; c++) { + rem = 0; + p = c-k; + /* find suitable row for elimination */ + for (r = p; r < m; r++) { + if (rows[r] & mask) { + if (r != p) { + tmp = rows[r]; + rows[r] = rows[p]; + rows[p] = tmp; + } + rem = r+1; + break; + } + } + if (rem) { + /* perform elimination on remaining rows */ + tmp = rows[p]; + for (r = rem; r < m; r++) { + if (rows[r] & mask) + rows[r] ^= tmp; + } + } else { + /* elimination not needed, store defective row index */ + param[k++] = c; + } + mask >>= 1; + } + /* rewrite system, inserting fake parameter rows */ + if (k > 0) { + p = k; + for (r = m-1; r >= 0; r--) { + if ((r > m-1-k) && rows[r]) + /* system has no solution */ + return 0; + + rows[r] = (p && (r == param[p-1])) ? + p--, 1u << (m-r) : rows[r-p]; + } + } + + if (nsol != (1 << k)) + /* unexpected number of solutions */ + return 0; + + for (p = 0; p < nsol; p++) { + /* set parameters for p-th solution */ + for (c = 0; c < k; c++) + rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); + + /* compute unique solution */ + tmp = 0; + for (r = m-1; r >= 0; r--) { + mask = rows[r] & (tmp|1); + tmp |= parity(mask) << (m-r); + } + sol[p] = tmp >> 1; + } + return nsol; +} + +/* + * this function builds and solves a linear system for finding roots of a degree + * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). + */ +static int find_affine4_roots(struct bch_control *bch, unsigned int a, + unsigned int b, unsigned int c, + unsigned int *roots) +{ + int i, j, k; + const int m = GF_M(bch); + unsigned int mask = 0xff, t, rows[16] = {0,}; + + j = a_log(bch, b); + k = a_log(bch, a); + rows[0] = c; + + /* buid linear system to solve X^4+aX^2+bX+c = 0 */ + for (i = 0; i < m; i++) { + rows[i+1] = bch->a_pow_tab[4*i]^ + (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ + (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); + j++; + k += 2; + } + /* + * transpose 16x16 matrix before passing it to linear solver + * warning: this code assumes m < 16 + */ + for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { + for (k = 0; k < 16; k = (k+j+1) & ~j) { + t = ((rows[k] >> j)^rows[k+j]) & mask; + rows[k] ^= (t << j); + rows[k+j] ^= t; + } + } + return solve_linear_system(bch, rows, roots, 4); +} + +/* + * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) + */ +static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0; + + if (poly->c[0]) + /* poly[X] = bX+c with c!=0, root=c/b */ + roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ + bch->a_log_tab[poly->c[1]]); + return n; +} + +/* + * compute roots of a degree 2 polynomial over GF(2^m) + */ +static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0, i, l0, l1, l2; + unsigned int u, v, r; + + if (poly->c[0] && poly->c[1]) { + + l0 = bch->a_log_tab[poly->c[0]]; + l1 = bch->a_log_tab[poly->c[1]]; + l2 = bch->a_log_tab[poly->c[2]]; + + /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ + u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); + /* + * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): + * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = + * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) + * i.e. r and r+1 are roots iff Tr(u)=0 + */ + r = 0; + v = u; + while (v) { + i = deg(v); + r ^= bch->xi_tab[i]; + v ^= (1 << i); + } + /* verify root */ + if ((gf_sqr(bch, r)^r) == u) { + /* reverse z=a/bX transformation and compute log(1/r) */ + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r]+l2); + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r^1]+l2); + } + } + return n; +} + +/* + * compute roots of a degree 3 polynomial over GF(2^m) + */ +static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, n = 0; + unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; + + if (poly->c[0]) { + /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ + e3 = poly->c[3]; + c2 = gf_div(bch, poly->c[0], e3); + b2 = gf_div(bch, poly->c[1], e3); + a2 = gf_div(bch, poly->c[2], e3); + + /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ + c = gf_mul(bch, a2, c2); /* c = a2c2 */ + b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ + a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ + + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a, b, c, tmp) == 4) { + /* remove a2 from final list of roots */ + for (i = 0; i < 4; i++) { + if (tmp[i] != a2) + roots[n++] = a_ilog(bch, tmp[i]); + } + } + } + return n; +} + +/* + * compute roots of a degree 4 polynomial over GF(2^m) + */ +static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, l, n = 0; + unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; + + if (poly->c[0] == 0) + return 0; + + /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ + e4 = poly->c[4]; + d = gf_div(bch, poly->c[0], e4); + c = gf_div(bch, poly->c[1], e4); + b = gf_div(bch, poly->c[2], e4); + a = gf_div(bch, poly->c[3], e4); + + /* use Y=1/X transformation to get an affine polynomial */ + if (a) { + /* first, eliminate cX by using z=X+e with ae^2+c=0 */ + if (c) { + /* compute e such that e^2 = c/a */ + f = gf_div(bch, c, a); + l = a_log(bch, f); + l += (l & 1) ? GF_N(bch) : 0; + e = a_pow(bch, l/2); + /* + * use transformation z=X+e: + * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d + * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d + * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d + * z^4 + az^3 + b'z^2 + d' + */ + d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; + b = gf_mul(bch, a, e)^b; + } + /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ + if (d == 0) + /* assume all roots have multiplicity 1 */ + return 0; + + c2 = gf_inv(bch, d); + b2 = gf_div(bch, a, d); + a2 = gf_div(bch, b, d); + } else { + /* polynomial is already affine */ + c2 = d; + b2 = c; + a2 = b; + } + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { + for (i = 0; i < 4; i++) { + /* post-process roots (reverse transformations) */ + f = a ? gf_inv(bch, roots[i]) : roots[i]; + roots[i] = a_ilog(bch, f^e); + } + n = 4; + } + return n; +} + +/* + * build monic, log-based representation of a polynomial + */ +static void gf_poly_logrep(struct bch_control *bch, + const struct gf_poly *a, int *rep) +{ + int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); + + /* represent 0 values with -1; warning, rep[d] is not set to 1 */ + for (i = 0; i < d; i++) + rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; +} + +/* + * compute polynomial Euclidean division remainder in GF(2^m)[X] + */ +static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, int *rep) +{ + int la, p, m; + unsigned int i, j, *c = a->c; + const unsigned int d = b->deg; + + if (a->deg < d) + return; + + /* reuse or compute log representation of denominator */ + if (!rep) { + rep = bch->cache; + gf_poly_logrep(bch, b, rep); + } + + for (j = a->deg; j >= d; j--) { + if (c[j]) { + la = a_log(bch, c[j]); + p = j-d; + for (i = 0; i < d; i++, p++) { + m = rep[i]; + if (m >= 0) + c[p] ^= bch->a_pow_tab[mod_s(bch, + m+la)]; + } + } + } + a->deg = d-1; + while (!c[a->deg] && a->deg) + a->deg--; +} + +/* + * compute polynomial Euclidean division quotient in GF(2^m)[X] + */ +static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, struct gf_poly *q) +{ + if (a->deg >= b->deg) { + q->deg = a->deg-b->deg; + /* compute a mod b (modifies a) */ + gf_poly_mod(bch, a, b, NULL); + /* quotient is stored in upper part of polynomial a */ + memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); + } else { + q->deg = 0; + q->c[0] = 0; + } +} + +/* + * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] + */ +static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, + struct gf_poly *b) +{ + struct gf_poly *tmp; + + dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); + + if (a->deg < b->deg) { + tmp = b; + b = a; + a = tmp; + } + + while (b->deg > 0) { + gf_poly_mod(bch, a, b, NULL); + tmp = b; + b = a; + a = tmp; + } + + dbg("%s\n", gf_poly_str(a)); + + return a; +} + +/* + * Given a polynomial f and an integer k, compute Tr(a^kX) mod f + * This is used in Berlekamp Trace algorithm for splitting polynomials + */ +static void compute_trace_bk_mod(struct bch_control *bch, int k, + const struct gf_poly *f, struct gf_poly *z, + struct gf_poly *out) +{ + const int m = GF_M(bch); + int i, j; + + /* z contains z^2j mod f */ + z->deg = 1; + z->c[0] = 0; + z->c[1] = bch->a_pow_tab[k]; + + out->deg = 0; + memset(out, 0, GF_POLY_SZ(f->deg)); + + /* compute f log representation only once */ + gf_poly_logrep(bch, f, bch->cache); + + for (i = 0; i < m; i++) { + /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ + for (j = z->deg; j >= 0; j--) { + out->c[j] ^= z->c[j]; + z->c[2*j] = gf_sqr(bch, z->c[j]); + z->c[2*j+1] = 0; + } + if (z->deg > out->deg) + out->deg = z->deg; + + if (i < m-1) { + z->deg *= 2; + /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ + gf_poly_mod(bch, z, f, bch->cache); + } + } + while (!out->c[out->deg] && out->deg) + out->deg--; + + dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); +} + +/* + * factor a polynomial using Berlekamp Trace algorithm (BTA) + */ +static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, + struct gf_poly **g, struct gf_poly **h) +{ + struct gf_poly *f2 = bch->poly_2t[0]; + struct gf_poly *q = bch->poly_2t[1]; + struct gf_poly *tk = bch->poly_2t[2]; + struct gf_poly *z = bch->poly_2t[3]; + struct gf_poly *gcd; + + dbg("factoring %s...\n", gf_poly_str(f)); + + *g = f; + *h = NULL; + + /* tk = Tr(a^k.X) mod f */ + compute_trace_bk_mod(bch, k, f, z, tk); + + if (tk->deg > 0) { + /* compute g = gcd(f, tk) (destructive operation) */ + gf_poly_copy(f2, f); + gcd = gf_poly_gcd(bch, f2, tk); + if (gcd->deg < f->deg) { + /* compute h=f/gcd(f,tk); this will modify f and q */ + gf_poly_div(bch, f, gcd, q); + /* store g and h in-place (clobbering f) */ + *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; + gf_poly_copy(*g, gcd); + gf_poly_copy(*h, q); + } + } +} + +/* + * find roots of a polynomial, using BTZ algorithm; see the beginning of this + * file for details + */ +static int find_poly_roots(struct bch_control *bch, unsigned int k, + struct gf_poly *poly, unsigned int *roots) +{ + int cnt; + struct gf_poly *f1, *f2; + + switch (poly->deg) { + /* handle low degree polynomials with ad hoc techniques */ + case 1: + cnt = find_poly_deg1_roots(bch, poly, roots); + break; + case 2: + cnt = find_poly_deg2_roots(bch, poly, roots); + break; + case 3: + cnt = find_poly_deg3_roots(bch, poly, roots); + break; + case 4: + cnt = find_poly_deg4_roots(bch, poly, roots); + break; + default: + /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ + cnt = 0; + if (poly->deg && (k <= GF_M(bch))) { + factor_polynomial(bch, k, poly, &f1, &f2); + if (f1) + cnt += find_poly_roots(bch, k+1, f1, roots); + if (f2) + cnt += find_poly_roots(bch, k+1, f2, roots+cnt); + } + break; + } + return cnt; +} + +#if defined(USE_CHIEN_SEARCH) +/* + * exhaustive root search (Chien) implementation - not used, included only for + * reference/comparison tests + */ +static int chien_search(struct bch_control *bch, unsigned int len, + struct gf_poly *p, unsigned int *roots) +{ + int m; + unsigned int i, j, syn, syn0, count = 0; + const unsigned int k = 8*len+bch->ecc_bits; + + /* use a log-based representation of polynomial */ + gf_poly_logrep(bch, p, bch->cache); + bch->cache[p->deg] = 0; + syn0 = gf_div(bch, p->c[0], p->c[p->deg]); + + for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { + /* compute elp(a^i) */ + for (j = 1, syn = syn0; j <= p->deg; j++) { + m = bch->cache[j]; + if (m >= 0) + syn ^= a_pow(bch, m+j*i); + } + if (syn == 0) { + roots[count++] = GF_N(bch)-i; + if (count == p->deg) + break; + } + } + return (count == p->deg) ? count : 0; +} +#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) +#endif /* USE_CHIEN_SEARCH */ + +/** + * decode_bch - decode received codeword and find bit error locations + * @bch: BCH control structure + * @data: received data, ignored if @calc_ecc is provided + * @len: data length in bytes, must always be provided + * @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc + * @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data + * @syn: hw computed syndrome data (if NULL, syndrome is calculated) + * @errloc: output array of error locations + * + * Returns: + * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if + * invalid parameters were provided + * + * Depending on the available hw BCH support and the need to compute @calc_ecc + * separately (using encode_bch()), this function should be called with one of + * the following parameter configurations - + * + * by providing @data and @recv_ecc only: + * decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc) + * + * by providing @recv_ecc and @calc_ecc: + * decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc) + * + * by providing ecc = recv_ecc XOR calc_ecc: + * decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc) + * + * by providing syndrome results @syn: + * decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc) + * + * Once decode_bch() has successfully returned with a positive value, error + * locations returned in array @errloc should be interpreted as follows - + * + * if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for + * data correction) + * + * if (errloc[n] < 8*len), then n-th error is located in data and can be + * corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8); + * + * Note that this function does not perform any data correction by itself, it + * merely indicates error locations. + */ +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc) +{ + const unsigned int ecc_words = BCH_ECC_WORDS(bch); + unsigned int nbits; + int i, err, nroots; + uint32_t sum; + + /* sanity check: make sure data length can be handled */ + if (8*len > (bch->n-bch->ecc_bits)) + return -EINVAL; + + /* if caller does not provide syndromes, compute them */ + if (!syn) { + if (!calc_ecc) { + /* compute received data ecc into an internal buffer */ + if (!data || !recv_ecc) + return -EINVAL; + encode_bch(bch, data, len, NULL); + } else { + /* load provided calculated ecc */ + load_ecc8(bch, bch->ecc_buf, calc_ecc); + } + /* load received ecc or assume it was XORed in calc_ecc */ + if (recv_ecc) { + load_ecc8(bch, bch->ecc_buf2, recv_ecc); + /* XOR received and calculated ecc */ + for (i = 0, sum = 0; i < (int)ecc_words; i++) { + bch->ecc_buf[i] ^= bch->ecc_buf2[i]; + sum |= bch->ecc_buf[i]; + } + if (!sum) + /* no error found */ + return 0; + } + compute_syndromes(bch, bch->ecc_buf, bch->syn); + syn = bch->syn; + } + + err = compute_error_locator_polynomial(bch, syn); + if (err > 0) { + nroots = find_poly_roots(bch, 1, bch->elp, errloc); + if (err != nroots) + err = -1; + } + if (err > 0) { + /* post-process raw error locations for easier correction */ + nbits = (len*8)+bch->ecc_bits; + for (i = 0; i < err; i++) { + if (errloc[i] >= nbits) { + err = -1; + break; + } + errloc[i] = nbits-1-errloc[i]; + errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); + } + } + return (err >= 0) ? err : -EBADMSG; +} + +/* + * generate Galois field lookup tables + */ +static int build_gf_tables(struct bch_control *bch, unsigned int poly) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + + /* primitive polynomial must be of degree m */ + if (k != (1u << GF_M(bch))) + return -1; + + for (i = 0; i < GF_N(bch); i++) { + bch->a_pow_tab[i] = x; + bch->a_log_tab[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ + return -1; + x <<= 1; + if (x & k) + x ^= poly; + } + bch->a_pow_tab[GF_N(bch)] = 1; + bch->a_log_tab[0] = 0; + + return 0; +} + +/* + * compute generator polynomial remainder tables for fast encoding + */ +static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) +{ + int i, j, b, d; + uint32_t data, hi, lo, *tab; + const int l = BCH_ECC_WORDS(bch); + const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); + const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); + + memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); + + for (i = 0; i < 256; i++) { + /* p(X)=i is a small polynomial of weight <= 8 */ + for (b = 0; b < 4; b++) { + /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ + tab = bch->mod8_tab + (b*256+i)*l; + data = i << (8*b); + while (data) { + d = deg(data); + /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ + data ^= g[0] >> (31-d); + for (j = 0; j < ecclen; j++) { + hi = (d < 31) ? g[j] << (d+1) : 0; + lo = (j+1 < plen) ? + g[j+1] >> (31-d) : 0; + tab[j] ^= hi|lo; + } + } + } + } +} + +/* + * build a base for factoring degree 2 polynomials + */ +static int build_deg2_base(struct bch_control *bch) +{ + const int m = GF_M(bch); + int i, j, r; + unsigned int sum, x, y, remaining, ak = 0, xi[m]; + + /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ + for (i = 0; i < m; i++) { + for (j = 0, sum = 0; j < m; j++) + sum ^= a_pow(bch, i*(1 << j)); + + if (sum) { + ak = bch->a_pow_tab[i]; + break; + } + } + /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ + remaining = m; + memset(xi, 0, sizeof(xi)); + + for (x = 0; (x <= GF_N(bch)) && remaining; x++) { + y = gf_sqr(bch, x)^x; + for (i = 0; i < 2; i++) { + r = a_log(bch, y); + if (y && (r < m) && !xi[r]) { + bch->xi_tab[r] = x; + xi[r] = 1; + remaining--; + dbg("x%d = %x\n", r, x); + break; + } + y ^= ak; + } + } + /* should not happen but check anyway */ + return remaining ? -1 : 0; +} + +static void *bch_alloc(size_t size, int *err) +{ + void *ptr; + + ptr = kmalloc(size, GFP_KERNEL); + if (ptr == NULL) + *err = 1; + return ptr; +} + +/* + * compute generator polynomial for given (m,t) parameters. + */ +static uint32_t *compute_generator_polynomial(struct bch_control *bch) +{ + const unsigned int m = GF_M(bch); + const unsigned int t = GF_T(bch); + int n, err = 0; + unsigned int i, j, nbits, r, word, *roots; + struct gf_poly *g; + uint32_t *genpoly; + + g = bch_alloc(GF_POLY_SZ(m*t), &err); + roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); + genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); + + if (err) { + kfree(genpoly); + genpoly = NULL; + goto finish; + } + + /* enumerate all roots of g(X) */ + memset(roots , 0, (bch->n+1)*sizeof(*roots)); + for (i = 0; i < t; i++) { + for (j = 0, r = 2*i+1; j < m; j++) { + roots[r] = 1; + r = mod_s(bch, 2*r); + } + } + /* build generator polynomial g(X) */ + g->deg = 0; + g->c[0] = 1; + for (i = 0; i < GF_N(bch); i++) { + if (roots[i]) { + /* multiply g(X) by (X+root) */ + r = bch->a_pow_tab[i]; + g->c[g->deg+1] = 1; + for (j = g->deg; j > 0; j--) + g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; + + g->c[0] = gf_mul(bch, g->c[0], r); + g->deg++; + } + } + /* store left-justified binary representation of g(X) */ + n = g->deg+1; + i = 0; + + while (n > 0) { + nbits = (n > 32) ? 32 : n; + for (j = 0, word = 0; j < nbits; j++) { + if (g->c[n-1-j]) + word |= 1u << (31-j); + } + genpoly[i++] = word; + n -= nbits; + } + bch->ecc_bits = g->deg; + +finish: + kfree(g); + kfree(roots); + + return genpoly; +} + +/** + * init_bch - initialize a BCH encoder/decoder + * @m: Galois field order, should be in the range 5-15 + * @t: maximum error correction capability, in bits + * @prim_poly: user-provided primitive polynomial (or 0 to use default) + * + * Returns: + * a newly allocated BCH control structure if successful, NULL otherwise + * + * This initialization can take some time, as lookup tables are built for fast + * encoding/decoding; make sure not to call this function from a time critical + * path. Usually, init_bch() should be called on module/driver init and + * free_bch() should be called to release memory on exit. + * + * You may provide your own primitive polynomial of degree @m in argument + * @prim_poly, or let init_bch() use its default polynomial. + * + * Once init_bch() has successfully returned a pointer to a newly allocated + * BCH control structure, ecc length in bytes is given by member @ecc_bytes of + * the structure. + */ +struct bch_control *init_bch(int m, int t, unsigned int prim_poly) +{ + int err = 0; + unsigned int i, words; + uint32_t *genpoly; + struct bch_control *bch = NULL; + + const int min_m = 5; + const int max_m = 15; + + /* default primitive polynomials */ + static const unsigned int prim_poly_tab[] = { + 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, + 0x402b, 0x8003, + }; + +#if defined(CONFIG_BCH_CONST_PARAMS) + if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { + printk(KERN_ERR "bch encoder/decoder was configured to support " + "parameters m=%d, t=%d only!\n", + CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); + goto fail; + } +#endif + if ((m < min_m) || (m > max_m)) + /* + * values of m greater than 15 are not currently supported; + * supporting m > 15 would require changing table base type + * (uint16_t) and a small patch in matrix transposition + */ + goto fail; + + /* sanity checks */ + if ((t < 1) || (m*t >= ((1 << m)-1))) + /* invalid t value */ + goto fail; + + /* select a primitive polynomial for generating GF(2^m) */ + if (prim_poly == 0) + prim_poly = prim_poly_tab[m-min_m]; + + bch = kzalloc(sizeof(*bch), GFP_KERNEL); + if (bch == NULL) + goto fail; + + bch->m = m; + bch->t = t; + bch->n = (1 << m)-1; + words = DIV_ROUND_UP(m*t, 32); + bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); + bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); + bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); + bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); + bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); + bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); + bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); + bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); + bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); + bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); + + if (err) + goto fail; + + err = build_gf_tables(bch, prim_poly); + if (err) + goto fail; + + /* use generator polynomial for computing encoding tables */ + genpoly = compute_generator_polynomial(bch); + if (genpoly == NULL) + goto fail; + + build_mod8_tables(bch, genpoly); + kfree(genpoly); + + err = build_deg2_base(bch); + if (err) + goto fail; + + return bch; + +fail: + free_bch(bch); + return NULL; +} + +/** + * free_bch - free the BCH control structure + * @bch: BCH control structure to release + */ +void free_bch(struct bch_control *bch) +{ + unsigned int i; + + if (bch) { + kfree(bch->a_pow_tab); + kfree(bch->a_log_tab); + kfree(bch->mod8_tab); + kfree(bch->ecc_buf); + kfree(bch->ecc_buf2); + kfree(bch->xi_tab); + kfree(bch->syn); + kfree(bch->cache); + kfree(bch->elp); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + kfree(bch->poly_2t[i]); + + kfree(bch); + } +} diff --git a/lib/string.c b/lib/string.c index 2c4f0ec..c3ad055 100644 --- a/lib/string.c +++ b/lib/string.c @@ -214,6 +214,45 @@ char * strrchr(const char * s, int c) } #endif + +/** + * skip_spaces - Removes leading whitespace from @str. + * @str: The string to be stripped. + * + * Returns a pointer to the first non-whitespace character in @str. + */ +char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} + +/** + * strim - Removes leading and trailing whitespace from @s. + * @s: The string to be stripped. + * + * Note that the first trailing whitespace is replaced with a %NUL-terminator + * in the given string @s. Returns a pointer to the first non-whitespace + * character in @s. + */ +char *strim(char *s) +{ + size_t size; + char *end; + + s = skip_spaces(s); + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + return s; +} #ifndef __HAVE_ARCH_STRLEN /** * strlen - Find the length of a string diff --git a/nand_spl/nand_boot.c b/nand_spl/nand_boot.c index 2326962..bfaabd3 100644 --- a/nand_spl/nand_boot.c +++ b/nand_spl/nand_boot.c @@ -24,6 +24,11 @@ static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + #if (CONFIG_SYS_NAND_PAGE_SIZE <= 512) /* * NAND command for small page NAND devices (512) @@ -139,29 +144,21 @@ static int nand_is_bad_block(struct mtd_info *mtd, int block) static int nand_read_page(struct mtd_info *mtd, int block, int page, uchar *dst) { struct nand_chip *this = mtd->priv; - u_char *ecc_calc; - u_char *ecc_code; - u_char *oob_data; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; int i; int eccsize = CONFIG_SYS_NAND_ECCSIZE; int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + int eccsteps = ECCSTEPS; uint8_t *p = dst; - /* - * No malloc available for now, just use some temporary locations - * in SDRAM - */ - ecc_calc = (u_char *)(CONFIG_SYS_SDRAM_BASE + 0x10000); - ecc_code = ecc_calc + 0x100; - oob_data = ecc_calc + 0x200; - nand_command(mtd, block, page, 0, NAND_CMD_READOOB); this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); nand_command(mtd, block, page, 0, NAND_CMD_READ0); /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < CONFIG_SYS_NAND_ECCTOTAL; i++) + for (i = 0; i < ECCTOTAL; i++) ecc_code[i] = oob_data[nand_ecc_pos[i]]; @@ -178,24 +175,17 @@ static int nand_read_page(struct mtd_info *mtd, int block, int page, uchar *dst) static int nand_read_page(struct mtd_info *mtd, int block, int page, uchar *dst) { struct nand_chip *this = mtd->priv; - u_char *ecc_calc; - u_char *ecc_code; - u_char *oob_data; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; int i; int eccsize = CONFIG_SYS_NAND_ECCSIZE; int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + int eccsteps = ECCSTEPS; uint8_t *p = dst; nand_command(mtd, block, page, 0, NAND_CMD_READ0); - /* No malloc available for now, just use some temporary locations - * in SDRAM - */ - ecc_calc = (u_char *)(CONFIG_SYS_SDRAM_BASE + 0x10000); - ecc_code = ecc_calc + 0x100; - oob_data = ecc_calc + 0x200; - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { this->ecc.hwctl(mtd, NAND_ECC_READ); this->read_buf(mtd, p, eccsize); @@ -204,10 +194,10 @@ static int nand_read_page(struct mtd_info *mtd, int block, int page, uchar *dst) this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < CONFIG_SYS_NAND_ECCTOTAL; i++) + for (i = 0; i < ECCTOTAL; i++) ecc_code[i] = oob_data[nand_ecc_pos[i]]; - eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + eccsteps = ECCSTEPS; p = dst; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { |