X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=kernel%2Fdrivers%2Fmtd%2Fnand%2Fnand_base.c;fp=kernel%2Fdrivers%2Fmtd%2Fnand%2Fnand_base.c;h=3ff583f165cdfce7138f3f02d771fcb9d8f99e5e;hb=e09b41010ba33a20a87472ee821fa407a5b8da36;hp=c2e1232cd45cc847197c3960ddbd3cd38584dd55;hpb=f93b97fd65072de626c074dbe099a1fff05ce060;p=kvmfornfv.git diff --git a/kernel/drivers/mtd/nand/nand_base.c b/kernel/drivers/mtd/nand/nand_base.c index c2e1232cd..3ff583f16 100644 --- a/kernel/drivers/mtd/nand/nand_base.c +++ b/kernel/drivers/mtd/nand/nand_base.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand.c - * * Overview: * This is the generic MTD driver for NAND flash devices. It should be * capable of working with almost all NAND chips currently available. @@ -48,6 +46,7 @@ #include #include #include +#include /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { @@ -544,23 +543,32 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) } } -/* Wait for the ready pin, after a command. The timeout is caught later. */ +/** + * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * + * Wait for the ready pin after a command, and warn if a timeout occurs. + */ void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - unsigned long timeo = jiffies + msecs_to_jiffies(20); + unsigned long timeo = 400; - /* 400ms timeout */ if (in_interrupt() || oops_in_progress) - return panic_nand_wait_ready(mtd, 400); + return panic_nand_wait_ready(mtd, timeo); led_trigger_event(nand_led_trigger, LED_FULL); /* Wait until command is processed or timeout occurs */ + timeo = jiffies + msecs_to_jiffies(timeo); do { if (chip->dev_ready(mtd)) - break; - touch_softlockup_watchdog(); + goto out; + cond_resched(); } while (time_before(jiffies, timeo)); + + pr_warn_ratelimited( + "timeout while waiting for chip to become ready\n"); +out: led_trigger_event(nand_led_trigger, LED_OFF); } EXPORT_SYMBOL_GPL(nand_wait_ready); @@ -886,15 +894,13 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: MTD device structure * @chip: NAND chip structure * - * Wait for command done. This applies to erase and program only. Erase can - * take up to 400ms and program up to 20ms according to general NAND and - * SmartMedia specs. + * Wait for command done. This applies to erase and program only. */ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - int status, state = chip->state; - unsigned long timeo = (state == FL_ERASING ? 400 : 20); + int status; + unsigned long timeo = 400; led_trigger_event(nand_led_trigger, LED_FULL); @@ -910,7 +916,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) panic_nand_wait(mtd, chip, timeo); else { timeo = jiffies + msecs_to_jiffies(timeo); - while (time_before(jiffies, timeo)) { + do { if (chip->dev_ready) { if (chip->dev_ready(mtd)) break; @@ -919,7 +925,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) break; } cond_resched(); - } + } while (time_before(jiffies, timeo)); } led_trigger_event(nand_led_trigger, LED_OFF); @@ -1101,6 +1107,134 @@ out: } EXPORT_SYMBOL(nand_lock); +/** + * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data + * @buf: buffer to test + * @len: buffer length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a buffer contains only 0xff, which means the underlying region + * has been erased and is ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region is not erased. + * Note: The logic of this function has been extracted from the memweight + * implementation, except that nand_check_erased_buf function exit before + * testing the whole buffer if the number of bitflips exceed the + * bitflips_threshold value. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. + */ +static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) +{ + const unsigned char *bitmap = buf; + int bitflips = 0; + int weight; + + for (; len && ((uintptr_t)bitmap) % sizeof(long); + len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len >= sizeof(long); + len -= sizeof(long), bitmap += sizeof(long)) { + weight = hweight_long(*((unsigned long *)bitmap)); + bitflips += BITS_PER_LONG - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len > 0; len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + return bitflips; +} + +/** + * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only + * 0xff data + * @data: data buffer to test + * @datalen: data length + * @ecc: ECC buffer + * @ecclen: ECC length + * @extraoob: extra OOB buffer + * @extraooblen: extra OOB length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a data buffer and its associated ECC and OOB data contains only + * 0xff pattern, which means the underlying region has been erased and is + * ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region as not erased. + * + * Note: + * 1/ ECC algorithms are working on pre-defined block sizes which are usually + * different from the NAND page size. When fixing bitflips, ECC engines will + * report the number of errors per chunk, and the NAND core infrastructure + * expect you to return the maximum number of bitflips for the whole page. + * This is why you should always use this function on a single chunk and + * not on the whole page. After checking each chunk you should update your + * max_bitflips value accordingly. + * 2/ When checking for bitflips in erased pages you should not only check + * the payload data but also their associated ECC data, because a user might + * have programmed almost all bits to 1 but a few. In this case, we + * shouldn't consider the chunk as erased, and checking ECC bytes prevent + * this case. + * 3/ The extraoob argument is optional, and should be used if some of your OOB + * data are protected by the ECC engine. + * It could also be used if you support subpages and want to attach some + * extra OOB data to an ECC chunk. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. In case of success, the passed buffers are filled with 0xff. + */ +int nand_check_erased_ecc_chunk(void *data, int datalen, + void *ecc, int ecclen, + void *extraoob, int extraooblen, + int bitflips_threshold) +{ + int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; + + data_bitflips = nand_check_erased_buf(data, datalen, + bitflips_threshold); + if (data_bitflips < 0) + return data_bitflips; + + bitflips_threshold -= data_bitflips; + + ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); + if (ecc_bitflips < 0) + return ecc_bitflips; + + bitflips_threshold -= ecc_bitflips; + + extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, + bitflips_threshold); + if (extraoob_bitflips < 0) + return extraoob_bitflips; + + if (data_bitflips) + memset(data, 0xff, datalen); + + if (ecc_bitflips) + memset(ecc, 0xff, ecclen); + + if (extraoob_bitflips) + memset(extraoob, 0xff, extraooblen); + + return data_bitflips + ecc_bitflips + extraoob_bitflips; +} +EXPORT_SYMBOL(nand_check_erased_ecc_chunk); + /** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure @@ -2028,11 +2162,12 @@ out: * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { chip->write_buf(mtd, buf, mtd->writesize); if (oob_required) @@ -2047,12 +2182,14 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write * * We need a special oob layout and handling even when ECC isn't checked. */ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2089,9 +2226,11 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write */ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2107,7 +2246,7 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - return chip->ecc.write_page_raw(mtd, chip, buf, 1); + return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); } /** @@ -2116,9 +2255,11 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write */ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2150,11 +2291,12 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @data_len: data length * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write */ static int nand_write_subpage_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, const uint8_t *buf, - int oob_required) + int oob_required, int page) { uint8_t *oob_buf = chip->oob_poi; uint8_t *ecc_calc = chip->buffers->ecccalc; @@ -2209,13 +2351,15 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static int nand_write_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2279,12 +2423,13 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required); + oob_required, page); else if (subpage) status = chip->ecc.write_subpage(mtd, chip, offset, data_len, - buf, oob_required); + buf, oob_required, page); else - status = chip->ecc.write_page(mtd, chip, buf, oob_required); + status = chip->ecc.write_page(mtd, chip, buf, oob_required, + page); if (status < 0) return status; @@ -2928,9 +3073,6 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; - /* clear the sub feature parameters */ - memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN); - chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) *subfeature_param++ = chip->read_byte(mtd); @@ -2968,7 +3110,7 @@ static void nand_resume(struct mtd_info *mtd) */ static void nand_shutdown(struct mtd_info *mtd) { - nand_get_device(mtd, FL_SHUTDOWN); + nand_get_device(mtd, FL_PM_SUSPENDED); } /* Set default functions */ @@ -3689,7 +3831,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (find_full_id_nand(mtd, chip, type, id_data, &busw)) goto ident_done; } else if (*dev_id == type->dev_id) { - break; + break; } } @@ -3712,10 +3854,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->chipsize = (uint64_t)type->chipsize << 20; - 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) { + if (!type->pagesize) { /* Decode parameters from extended ID */ nand_decode_ext_id(mtd, chip, id_data, &busw); } else { @@ -3798,6 +3937,39 @@ ident_done: return type; } +static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, + struct device_node *dn) +{ + int ecc_mode, ecc_strength, ecc_step; + + if (of_get_nand_bus_width(dn) == 16) + chip->options |= NAND_BUSWIDTH_16; + + if (of_get_nand_on_flash_bbt(dn)) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + ecc_mode = of_get_nand_ecc_mode(dn); + ecc_strength = of_get_nand_ecc_strength(dn); + ecc_step = of_get_nand_ecc_step_size(dn); + + if ((ecc_step >= 0 && !(ecc_strength >= 0)) || + (!(ecc_step >= 0) && ecc_strength >= 0)) { + pr_err("must set both strength and step size in DT\n"); + return -EINVAL; + } + + if (ecc_mode >= 0) + chip->ecc.mode = ecc_mode; + + if (ecc_strength >= 0) + chip->ecc.strength = ecc_strength; + + if (ecc_step > 0) + chip->ecc.size = ecc_step; + + return 0; +} + /** * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -3815,6 +3987,16 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; + int ret; + + if (chip->flash_node) { + ret = nand_dt_init(mtd, chip, chip->flash_node); + if (ret) + return ret; + } + + if (!mtd->name && mtd->dev.parent) + mtd->name = dev_name(mtd->dev.parent); /* Set the default functions */ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);