From 857975ee46641f46de6592d1ffd57f8ba9d0e45f Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:20:54 +0200 Subject: [PATCH 01/33] mtd: nand: Take nand_ecc_ctrl initialization out of nand_scan_tail Take ECC initialization code portion out of nand_scan_tail so that we can re-use this implementation. This commit only moves some code around and makes no functional changes. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/nand_base.c | 91 ++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ceb68ca8277a..e25ae3672ee9 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3928,42 +3928,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; } -/** - * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * - * This is the second phase of the normal nand_scan() function. It fills out - * all the uninitialized function pointers with the defaults and scans for a - * bad block table if appropriate. +/* + * Initialize ECC struct: + * - fill ECC struct with default function/values when these ones are undefined + * - fill ECC infos based on MTD device */ -int nand_scan_tail(struct mtd_info *mtd) +static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { int i; - struct nand_chip *chip = mtd->priv; - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_buffers *nbuf; - - /* New bad blocks should be marked in OOB, flash-based BBT, or both */ - BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && - !(chip->bbt_options & NAND_BBT_USE_FLASH)); - - if (!(chip->options & NAND_OWN_BUFFERS)) { - nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize - + mtd->oobsize * 3, GFP_KERNEL); - if (!nbuf) - return -ENOMEM; - nbuf->ecccalc = (uint8_t *)(nbuf + 1); - nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; - nbuf->databuf = nbuf->ecccode + mtd->oobsize; - chip->buffers = nbuf; - } else { - if (!chip->buffers) - return -ENOMEM; - } - - /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one. @@ -3989,9 +3962,6 @@ int nand_scan_tail(struct mtd_info *mtd) } } - if (!chip->write_page) - chip->write_page = nand_write_page; - /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC @@ -4161,6 +4131,57 @@ int nand_scan_tail(struct mtd_info *mtd) } ecc->total = ecc->steps * ecc->bytes; + return 0; +} + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int ret; + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + + if (!(chip->options & NAND_OWN_BUFFERS)) { + nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + + mtd->oobsize * 3, GFP_KERNEL); + if (!nbuf) + return -ENOMEM; + nbuf->ecccalc = (uint8_t *)(nbuf + 1); + nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; + nbuf->databuf = nbuf->ecccode + mtd->oobsize; + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + if (!chip->write_page) + chip->write_page = nand_write_page; + + /* Initialize ECC struct */ + ret = nand_ecc_ctrl_init(mtd, ecc); + if (ret) { + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); + + return ret; + } + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) { From d62e80af7b299ef1cf692389f04937506ec1201e Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 15:01:15 +0200 Subject: [PATCH 02/33] mtd: nand: Add support for NAND partitions Add support for NAND partitions, and indirectly for per partition ECC config, and also per partiton random seed support for the upcoming randomizer support. This is necessary to be able to use different ECC / randomizer settings for the parts of the NAND which are read directly by a bootrom (which has a fixed ECC / random seed setting) and the generic data part of the NAND for which we often want a stronger ECC and / or random seed. Provide helper functions to add/delete/allocate nand partitions. NAND core code now make use of the partition specific nand_ecc_ctrl struct (if available) when doing read/write operations. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/Kconfig | 4 + drivers/mtd/nand/Makefile | 2 + drivers/mtd/nand/nand_base.c | 712 ++++++++++++++++++++++++++++------- drivers/mtd/nand/nand_bch.c | 16 +- drivers/mtd/nand/nand_ecc.c | 4 +- include/linux/mtd/nand.h | 38 ++ 6 files changed, 635 insertions(+), 141 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5b2806a7e5f7..abf4daab732d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -22,6 +22,10 @@ menuconfig MTD_NAND if MTD_NAND +config MTD_OF_NAND_PARTS + tristate + default n + config MTD_NAND_BCH tristate select BCH diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1f897ec3c242..3eb86d3c7963 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -54,4 +54,6 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ +obj-$(CONFIG_MTD_OF_NAND_PARTS) += ofnandpart.o + nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e25ae3672ee9..725529acb206 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1133,26 +1133,26 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; + int eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; - for (steps = chip->ecc.steps; steps > 0; steps--) { + for (steps = chip->cur_ecc->steps; steps > 0; steps--) { chip->read_buf(mtd, buf, eccsize); buf += eccsize; - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->read_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } chip->read_buf(mtd, oob, eccbytes); oob += eccbytes; - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->read_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -1174,30 +1174,31 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + chip->cur_ecc->read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; - eccsteps = chip->ecc.steps; + eccsteps = chip->cur_ecc->steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1222,7 +1223,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int page) { int start_step, end_step, num_steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *p; int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; @@ -1231,16 +1232,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ - start_step = data_offs / chip->ecc.size; - end_step = (data_offs + readlen - 1) / chip->ecc.size; + start_step = data_offs / chip->cur_ecc->size; + end_step = (data_offs + readlen - 1) / chip->cur_ecc->size; num_steps = end_step - start_step + 1; - index = start_step * chip->ecc.bytes; + index = start_step * chip->cur_ecc->bytes; /* Data size aligned to ECC ecc.size */ - datafrag_len = num_steps * chip->ecc.size; - eccfrag_len = num_steps * chip->ecc.bytes; + datafrag_len = num_steps * chip->cur_ecc->size; + eccfrag_len = num_steps * chip->cur_ecc->bytes; - data_col_addr = start_step * chip->ecc.size; + data_col_addr = start_step * chip->cur_ecc->size; /* If we read not a page aligned data */ if (data_col_addr != 0) chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); @@ -1249,8 +1250,9 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, p, datafrag_len); /* Calculate ECC */ - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) - chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + for (i = 0; i < eccfrag_len; + i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) + chip->cur_ecc->calculate(mtd, p, &chip->buffers->ecccalc[i]); /* * The performance is faster if we position offsets according to @@ -1274,7 +1276,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, aligned_len = eccfrag_len; if (eccpos[index] & (busw - 1)) aligned_len++; - if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) + if (eccpos[index + (num_steps * chip->cur_ecc->bytes)] & + (busw - 1)) aligned_len++; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, @@ -1286,11 +1289,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, 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) { + for (i = 0; i < eccfrag_len; + i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) { int stat; - stat = chip->ecc.correct(mtd, p, - &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + stat = chip->cur_ecc->correct(mtd, p, + &chip->buffers->ecccode[i], + &chip->buffers->ecccalc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1314,32 +1319,33 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; - eccsteps = chip->ecc.steps; + eccsteps = chip->cur_ecc->steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1367,12 +1373,12 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; unsigned int max_bitflips = 0; @@ -1381,17 +1387,17 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1416,9 +1422,9 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; @@ -1426,17 +1432,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->read_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN); chip->read_buf(mtd, oob, eccbytes); - stat = chip->ecc.correct(mtd, p, oob, NULL); + stat = chip->cur_ecc->correct(mtd, p, oob, NULL); if (stat < 0) { mtd->ecc_stats.failed++; @@ -1447,9 +1453,9 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, oob += eccbytes; - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->read_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -1479,7 +1485,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, return oob + len; case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_oobfree *free = chip->cur_ecc->layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; @@ -1599,17 +1605,21 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, * the read methods return max bitflips per ecc step. */ if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, - oob_required, - page); + ret = chip->cur_ecc->read_page_raw(mtd, chip, + bufpoi, + oob_required, + page); else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && !oob) - ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi, - page); + ret = chip->cur_ecc->read_subpage(mtd, chip, + col, bytes, + bufpoi, + page); else - ret = chip->ecc.read_page(mtd, chip, bufpoi, - oob_required, page); + ret = chip->cur_ecc->read_page(mtd, chip, + bufpoi, + oob_required, + page); if (ret < 0) { if (use_bufpoi) /* Invalidate page cache */ @@ -1744,6 +1754,39 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, return ret; } +/** + * nand_part_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 + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read. + */ +static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret; + + from += part->offset; + nand_get_device(part->master, FL_READING); + if (part->ecc) + chip->cur_ecc = part->ecc; + ops.len = len; + ops.datbuf = buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_read_ops(part->master, from, &ops); + *retlen = ops.retlen; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function * @mtd: mtd info structure @@ -1769,13 +1812,14 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { int length = mtd->oobsize; - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size; + int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad + + chip->cur_ecc->postpad; + int eccsize = chip->cur_ecc->size; uint8_t *bufpoi = chip->oob_poi; int i, toread, sndrnd = 0, pos; - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); - for (i = 0; i < chip->ecc.steps; i++) { + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page); + for (i = 0; i < chip->cur_ecc->steps; i++) { if (sndrnd) { pos = eccsize + i * (eccsize + chunk); if (mtd->writesize > 512) @@ -1828,9 +1872,10 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad + + chip->cur_ecc->postpad; + int eccsize = chip->cur_ecc->size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->cur_ecc->steps; const uint8_t *bufpoi = chip->oob_poi; /* @@ -1838,7 +1883,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, * or * data-pad-ecc-pad-data-pad .... ecc-pad-oob */ - if (!chip->ecc.prepad && !chip->ecc.postpad) { + if (!chip->cur_ecc->prepad && !chip->cur_ecc->postpad) { pos = steps * (eccsize + chunk); steps = 0; } else @@ -1902,7 +1947,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; if (ops->mode == MTD_OPS_AUTO_OOB) - len = chip->ecc.layout->oobavail; + len = chip->cur_ecc->layout->oobavail; else len = mtd->oobsize; @@ -1930,9 +1975,9 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - ret = chip->ecc.read_oob_raw(mtd, chip, page); + ret = chip->cur_ecc->read_oob_raw(mtd, chip, page); else - ret = chip->ecc.read_oob(mtd, chip, page); + ret = chip->cur_ecc->read_oob(mtd, chip, page); if (ret < 0) break; @@ -2020,6 +2065,56 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, return ret; } +/** + * nand_part_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data. + */ +static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + from += part->offset; + nand_get_device(part->master, FL_READING); + if (part->ecc) + chip->cur_ecc = part->ecc; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_read_oob(part->master, from, ops); + else + ret = nand_do_read_ops(part->master, from, ops); + +out: + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_write_page_raw - [INTERN] raw page write function @@ -2053,26 +2148,26 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; + int eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; - for (steps = chip->ecc.steps; steps > 0; steps--) { + for (steps = chip->cur_ecc->steps; steps > 0; steps--) { chip->write_buf(mtd, buf, eccsize); buf += eccsize; - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->write_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->write_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -2092,21 +2187,21 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; /* Software ECC calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - return chip->ecc.write_page_raw(mtd, chip, buf, 1); + return chip->cur_ecc->write_page_raw(mtd, chip, buf, 1); } /** @@ -2119,20 +2214,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); chip->write_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); } - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -2157,10 +2252,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, { uint8_t *oob_buf = chip->oob_poi; uint8_t *ecc_calc = chip->buffers->ecccalc; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int ecc_steps = chip->ecc.steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; + int ecc_size = chip->cur_ecc->size; + int ecc_bytes = chip->cur_ecc->bytes; + int ecc_steps = chip->cur_ecc->steps; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint32_t start_step = offset / ecc_size; uint32_t end_step = (offset + data_len - 1) / ecc_size; int oob_bytes = mtd->oobsize / ecc_steps; @@ -2168,7 +2263,7 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, for (step = 0; step < ecc_steps; step++) { /* configure controller for WRITE access */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ chip->write_buf(mtd, buf, ecc_size); @@ -2177,7 +2272,7 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, if ((step < start_step) || (step > end_step)) memset(ecc_calc, 0xff, ecc_bytes); else - chip->ecc.calculate(mtd, buf, ecc_calc); + chip->cur_ecc->calculate(mtd, buf, ecc_calc); /* mask OOB of un-touched subpages by padding 0xFF */ /* if oob_required, preserve OOB metadata of written subpage */ @@ -2192,7 +2287,7 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, /* copy calculated ECC for whole page to chip->buffer->oob */ /* this include masked-value(0xFF) for unwritten subpages */ ecc_calc = chip->buffers->ecccalc; - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; /* write OOB buffer to NAND device */ @@ -2216,29 +2311,29 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); chip->write_buf(mtd, p, eccsize); - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->write_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } - chip->ecc.calculate(mtd, p, oob); + chip->cur_ecc->calculate(mtd, p, oob); chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->write_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -2269,7 +2364,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, int status, subpage; if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - chip->ecc.write_subpage) + chip->cur_ecc->write_subpage) subpage = offset || (data_len < mtd->writesize); else subpage = 0; @@ -2277,13 +2372,15 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required); + status = chip->cur_ecc->write_page_raw(mtd, chip, buf, + oob_required); else if (subpage) - status = chip->ecc.write_subpage(mtd, chip, offset, data_len, - buf, oob_required); + status = chip->cur_ecc->write_subpage(mtd, chip, offset, + data_len, buf, + oob_required); else - status = chip->ecc.write_page(mtd, chip, buf, oob_required); + status = chip->cur_ecc->write_page(mtd, chip, buf, + oob_required); if (status < 0) return status; @@ -2342,7 +2439,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, return oob + len; case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_oobfree *free = chip->cur_ecc->layout->oobfree; uint32_t boffs = 0, woffs = ops->ooboffs; size_t bytes = 0; @@ -2537,6 +2634,46 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } +/** + * panic_nand_part_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret; + + to += part->offset; + /* Wait for the device to get ready */ + panic_nand_wait(part->master, chip, 400); + + /* Grab the device */ + panic_nand_get_device(chip, part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + ret = nand_do_write_ops(part->master, to, &ops); + + chip->cur_ecc = &chip->ecc; + *retlen = ops.retlen; + return ret; +} + /** * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure @@ -2564,6 +2701,39 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } +/** + * nand_part_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. + */ +static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret; + + to += part->offset; + nand_get_device(part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_write_ops(part->master, to, &ops); + *retlen = ops.retlen; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure @@ -2582,7 +2752,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, __func__, (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OPS_AUTO_OOB) - len = chip->ecc.layout->oobavail; + len = chip->cur_ecc->layout->oobavail; else len = mtd->oobsize; @@ -2636,9 +2806,11 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); if (ops->mode == MTD_OPS_RAW) - status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + status = chip->cur_ecc->write_oob_raw(mtd, chip, + page & chip->pagemask); else - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + status = chip->cur_ecc->write_oob(mtd, chip, + page & chip->pagemask); chip->select_chip(mtd, -1); @@ -2692,6 +2864,54 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, return ret; } +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + to += part->offset; + nand_get_device(part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(part->master, to, ops); + else + ret = nand_do_write_ops(part->master, to, ops); + +out: + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * single_erase - [GENERIC] NAND standard block erase command function * @mtd: MTD device structure @@ -2721,6 +2941,29 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) return nand_erase_nand(mtd, instr, 0); } +/** + * nand_part_erase - [MTD Interface] erase partition block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks. + */ +static int nand_part_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct nand_part *part = to_nand_part(mtd); + int ret; + + instr->addr += part->offset; + ret = nand_erase_nand(part->master, instr, 0); + if (ret) { + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } + + return ret; +} + /** * nand_erase_nand - [INTERN] erase block(s) * @mtd: MTD device structure @@ -2862,6 +3105,18 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) return nand_block_checkbad(mtd, offs, 1, 0); } +/** + * nand_part_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_part_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_part *part = to_nand_part(mtd); + + return nand_block_checkbad(part->master, part->offset + offs, 1, 0); +} + /** * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad * @mtd: MTD device structure @@ -2882,6 +3137,33 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return nand_block_markbad_lowlevel(mtd, ofs); } +/** + * nand_part_block_markbad - [MTD Interface] Mark block at the given offset as + * bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_part_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_part *part = to_nand_part(mtd); + int ret; + + ofs += part->offset; + ret = nand_block_isbad(part->master, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + ret = nand_block_markbad_lowlevel(part->master, ofs); + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + /** * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand * @mtd: MTD device structure @@ -4134,6 +4416,169 @@ static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) return 0; } +/** + * nand_add_partition - [NAND Interface] Add a NAND partition to a NAND device + * @master: MTD device structure representing the NAND device + * @part: NAND partition to add to the NAND device + * + * Adds a NAND partition to a NAND device. + * The NAND partition cannot overlap with another existing partition. + * + * Returns zero in case of success and a negative error code in case of failure. + */ +int nand_add_partition(struct mtd_info *master, struct nand_part *part) +{ + struct nand_chip *chip = master->priv; + struct mtd_info *mtd = &part->mtd; + struct nand_ecc_ctrl *ecc = part->ecc; + struct nand_part *pos; + bool inserted = false; + int ret; + + /* set up the MTD object for this partition */ + mtd->type = master->type; + mtd->flags = master->flags & ~mtd->flags; + mtd->writesize = master->writesize; + mtd->writebufsize = master->writebufsize; + mtd->oobsize = master->oobsize; + mtd->oobavail = master->oobavail; + mtd->subpage_sft = master->subpage_sft; + mtd->erasesize = master->erasesize; + + mtd->priv = chip; + mtd->owner = master->owner; + mtd->backing_dev_info = master->backing_dev_info; + + mtd->dev.parent = master->dev.parent; + + if (ecc) { + ret = nand_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + } else { + ecc = &chip->ecc; + } + + mtd->_erase = nand_part_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = nand_part_read; + mtd->_write = nand_part_write; + mtd->_panic_write = panic_nand_part_write; + mtd->_read_oob = nand_part_read_oob; + mtd->_write_oob = nand_part_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_block_isbad = nand_part_block_isbad; + mtd->_block_markbad = nand_part_block_markbad; + + /* propagate ecc info to mtd_info */ + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = mtd->ecc_strength; + + part->master = master; + + mutex_lock(&chip->part_lock); + list_for_each_entry(pos, &chip->partitions, node) { + if (part->offset >= pos->offset + pos->mtd.size) { + continue; + } else if (part->offset + mtd->size > pos->offset) { + ret = -EINVAL; + goto out; + } + + list_add(&part->node, pos->node.prev); + inserted = true; + break; + } + + if (!inserted) + list_add_tail(&part->node, &chip->partitions); + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + list_del(&part->node); + goto out; + } + + if (master->_block_isbad) { + uint64_t offs = 0; + + while (offs < mtd->size) { + if (mtd_block_isreserved(master, offs + part->offset)) + mtd->ecc_stats.bbtblocks++; + else if (mtd_block_isbad(master, offs + part->offset)) + mtd->ecc_stats.badblocks++; + offs += mtd->erasesize; + } + } + +out: + mutex_unlock(&chip->part_lock); + return ret; +} +EXPORT_SYMBOL(nand_add_partition); + +/** + * nand_del_partition - [NAND Interface] Delete a NAND part from a NAND dev + * @part: NAND partition to delete + * + * Deletes a NAND partition from a NAND device. + */ +void nand_del_partition(struct nand_part *part) +{ + struct nand_chip *chip = part->mtd.priv; + + mutex_lock(&chip->part_lock); + mtd_device_unregister(&part->mtd); + list_del(&part->node); + mutex_unlock(&chip->part_lock); + + if (part->ecc && part->ecc->mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)part->ecc->priv); + + if (part->release) + part->release(part); +} +EXPORT_SYMBOL(nand_del_partition); + +/* + * NAND part release function. Used by nandpart_alloc as its release function. + */ +static void nandpart_release(struct nand_part *part) +{ + kfree(part); +} + +/** + * nandpart_alloc - [NAND Interface] Allocate a NAND part struct + * + * Allocate a NAND partition and assign the nandpart release function. + * This nand_part struct must be filled before passing it to the + * nand_add_partition function. + */ +struct nand_part *nandpart_alloc(void) +{ + struct nand_part *part = kzalloc(sizeof(*part), GFP_KERNEL); + if (!part) + return ERR_PTR(-ENOMEM); + part->release = nandpart_release; + + return part; +} +EXPORT_SYMBOL(nandpart_alloc); + /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -4182,6 +4627,11 @@ int nand_scan_tail(struct mtd_info *mtd) return ret; } + INIT_LIST_HEAD(&chip->partitions); + mutex_init(&chip->part_lock); + + chip->cur_ecc = &chip->ecc; + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) { diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index 3803e0bba23b..b82b976894b3 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -53,14 +53,14 @@ 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; + struct nand_bch_control *nbc = chip->cur_ecc->priv; unsigned int i; - memset(code, 0, chip->ecc.bytes); - encode_bch(nbc->bch, buf, chip->ecc.size, code); + memset(code, 0, chip->cur_ecc->bytes); + encode_bch(nbc->bch, buf, chip->cur_ecc->size, code); /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) + for (i = 0; i < chip->cur_ecc->bytes; i++) code[i] ^= nbc->eccmask[i]; return 0; @@ -80,15 +80,15 @@ 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; + struct nand_bch_control *nbc = chip->cur_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); + count = decode_bch(nbc->bch, NULL, chip->cur_ecc->size, read_ecc, + calc_ecc, NULL, errloc); if (count > 0) { for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) + if (errloc[i] < (chip->cur_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 */ diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 97c4c0216c90..f35c418bf0e2 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -424,7 +424,7 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, unsigned char *code) { __nand_calculate_ecc(buf, - ((struct nand_chip *)mtd->priv)->ecc.size, code); + ((struct nand_chip *)mtd->priv)->cur_ecc->size, code); return 0; } @@ -524,7 +524,7 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { return __nand_correct_data(buf, read_ecc, calc_ecc, - ((struct nand_chip *)mtd->priv)->ecc.size); + ((struct nand_chip *)mtd->priv)->cur_ecc->size); } EXPORT_SYMBOL(nand_correct_data); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f25e2bdd188c..fea5bd32710e 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -713,6 +713,7 @@ struct nand_chip { struct nand_hw_control *controller; struct nand_ecc_ctrl ecc; + struct nand_ecc_ctrl *cur_ecc; struct nand_buffers *buffers; struct nand_hw_control hwcontrol; @@ -722,9 +723,46 @@ struct nand_chip { struct nand_bbt_descr *badblock_pattern; + struct list_head partitions; + struct mutex part_lock; + void *priv; }; +/** + * struct nand_part - NAND partition structure + * @node: list node used to attach the partition to its NAND dev + * @mtd: MTD partiton info + * @master: MTD device representing the NAND chip + * @offset: partition offset + * @ecc: partition specific ECC struct + * @release: function used to release this nand_part struct + * + * NAND partitions work as standard MTD partitions except it can override + * NAND chip ECC handling. + * This is particularly useful for SoCs that need specific ECC configs to boot + * from NAND while these ECC configs do not fit the NAND chip ECC requirements. + */ +struct nand_part { + struct list_head node; + struct mtd_info mtd; + struct mtd_info *master; + uint64_t offset; + struct nand_ecc_ctrl *ecc; + void (*release)(struct nand_part *part); +}; + +static inline struct nand_part *to_nand_part(struct mtd_info *mtd) +{ + return container_of(mtd, struct nand_part, mtd); +} + +int nand_add_partition(struct mtd_info *master, struct nand_part *part); + +void nand_del_partition(struct nand_part *part); + +struct nand_part *nandpart_alloc(void); + /* * NAND Flash Manufacturer ID Codes */ From 16c43aef004e0f0f12f5243eb4e5b3ca152fb976 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:31:42 +0200 Subject: [PATCH 03/33] mtd: nand: Add DT NAND partition parser Add a of_nandpart_parse function to help parsing NAND partitions from DT. This function should be called from NAND controller drivers just after the nand_scan_tail in place of mtd_device_parse_register. The caller can specify a parser function to retrieve HW specific informations from the DT. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/ofnandpart.c | 104 ++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 17 ++++++ 2 files changed, 121 insertions(+) create mode 100644 drivers/mtd/nand/ofnandpart.c diff --git a/drivers/mtd/nand/ofnandpart.c b/drivers/mtd/nand/ofnandpart.c new file mode 100644 index 000000000000..293daee6d4af --- /dev/null +++ b/drivers/mtd/nand/ofnandpart.c @@ -0,0 +1,104 @@ +/* + * NAND Flash partitions described by the OF (or flattened) device tree + * + * Copyright © 2014 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +static inline bool node_has_compatible(struct device_node *pp) +{ + return of_get_property(pp, "compatible", NULL); +} + +int ofnandpart_parse(struct mtd_info *master, + const struct ofnandpart_data *data) +{ + struct device_node *node; + const char *partname; + struct device_node *pp; + int i; + + if (!data) + return 0; + + node = data->node; + if (!node) + return 0; + + i = 0; + for_each_child_of_node(node, pp) { + const __be32 *reg; + int len; + int a_cells, s_cells; + uint64_t offset, size; + uint32_t mask_flags = 0; + struct nand_part *part; + + if (node_has_compatible(pp)) + continue; + + reg = of_get_property(pp, "reg", &len); + if (!reg) + continue; + + a_cells = of_n_addr_cells(pp); + s_cells = of_n_size_cells(pp); + offset = of_read_number(reg, a_cells); + size = of_read_number(reg + a_cells, s_cells); + + partname = of_get_property(pp, "label", &len); + if (!partname) + partname = of_get_property(pp, "name", &len); + + if (of_get_property(pp, "read-only", &len)) + mask_flags |= MTD_WRITEABLE; + + if (of_get_property(pp, "lock", &len)) + mask_flags |= MTD_POWERUP_LOCK; + + if (data->parse) + part = data->parse(data->priv, master, pp); + else + part = nandpart_alloc(); + + if (IS_ERR(part)) + continue; + + part->offset = offset; + part->master = master; + part->mtd.name = partname; + part->mtd.size = size; + part->mtd.flags = mask_flags; + + if (nand_add_partition(master, part)) { + if (part->release) + part->release(part); + continue; + } + + i++; + } + + if (!i) { + of_node_put(pp); + pr_err("No valid partition found on %s\n", node->full_name); + } + + return i; +} +EXPORT_SYMBOL(ofnandpart_parse); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parser for NAND flash partitioning information in device tree"); +MODULE_AUTHOR("Boris BREZILLON"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index fea5bd32710e..eecd9501452d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1017,6 +1017,23 @@ static inline int jedec_feature(struct nand_chip *chip) : 0; } +/** + * struct ofnandpart_data - struct used to retrieve NAND partitions from a DT + * node + * @parse: driver specific parser function + * @priv: driver private data + * @node: OF node containing NAND partitions + */ +struct ofnandpart_data { + struct nand_part *(*parse)(void *priv, struct mtd_info *master, + struct device_node *pp); + void *priv; + struct device_node *node; +}; + +int ofnandpart_parse(struct mtd_info *master, + const struct ofnandpart_data *data); + /* * struct nand_sdr_timings - SDR NAND chip timings * From a3118c1b004560e9e9e75d89360bebf4df079a66 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:45:40 +0200 Subject: [PATCH 04/33] mtd: nand: Add page status table (pst) Page status table is an byte array storing pages status. It defines 3 status: - unknown: the page has not been read yet and we do not know its current state - empty: the page contains only FFs - filled: the page has been filled with data Care must be taken: an empty page does not mean it can be written, because it might have already been written with only FFs. These page status are useful to check wether the controller should try to correct errors (using ECC) or a derandomize data (using a randomizer block). Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 21 +++++ 2 files changed, 175 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 725529acb206..718e1b7c6f6d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1100,6 +1100,138 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) } EXPORT_SYMBOL(nand_lock); +/** + * nand_page_is_empty - check wether a NAND page contains only FFs + * @mtd: mtd info + * @data: data buffer + * @oob: oob buffer + * + * Reads the data stored in the databuf buffer and check if it contains only + * FFs. + * + * Return true if it does else return false. + */ +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob) +{ + u8 *buf; + int length; + u32 pattern = 0xffffffff; + int bitflips = 0; + int cnt; + + buf = data; + length = mtd->writesize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * BITS_PER_BYTE; i++) { + if (!(buf[i / BITS_PER_BYTE] & + (1 << (i % BITS_PER_BYTE)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + buf = oob; + length = mtd->oobsize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * BITS_PER_BYTE; i++) { + if (!(buf[i / BITS_PER_BYTE] & + (1 << (i % BITS_PER_BYTE)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + return true; +} +EXPORT_SYMBOL(nand_page_is_empty); + +/** + * nand_page_get_status - retrieve page status from the page status table (pst) + * @mtd: mtd info + * @page: page you want to get status on + * + * Return the page status. + */ +int nand_page_get_status(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + u8 shift = (page % 4) * 2; + uint64_t offset = page / 4; + int ret = NAND_PAGE_STATUS_UNKNOWN; + + if (chip->pst) + ret = (chip->pst[offset] >> shift) & 0x3; + + return ret; +} +EXPORT_SYMBOL(nand_page_get_status); + +/** + * nand_page_set_status - assign page status from in the page status table + * @mtd: mtd info + * @page: page you want to get status on + * @status: new status to assign + */ +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status) +{ + struct nand_chip *chip = mtd->priv; + u8 shift; + uint64_t offset; + + if (!chip->pst) + return; + + shift = (page % 4) * 2; + offset = page / 4; + chip->pst[offset] &= ~(0x3 << shift); + chip->pst[offset] |= (status & 0x3) << shift; +} +EXPORT_SYMBOL(nand_page_set_status); + +/** + * nand_pst_create - create a page status table + * @mtd: mtd info + * + * Allocate a page status table and assign it to the mtd device. + * + * Returns 0 in case of success or -ERRNO in case of error. + */ +int nand_pst_create(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->pst) + return 0; + + chip->pst = kzalloc(mtd->size >> + (chip->page_shift + mtd->subpage_sft + 2), + GFP_KERNEL); + if (!chip->pst) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(nand_pst_create); + /** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure @@ -2538,6 +2670,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *wbuf = buf; int use_bufpoi; int part_pagewr = (column || writelen < (mtd->writesize - 1)); + int subpage; if (part_pagewr) use_bufpoi = 1; @@ -2573,6 +2706,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (ret) break; + for (subpage = column / chip->subpagesize; + subpage < (column + writelen) / chip->subpagesize; + subpage++) + nand_page_set_status(mtd, + (page << mtd->subpage_sft) + + subpage, + NAND_PAGE_FILLED); + writelen -= bytes; if (!writelen) break; @@ -2978,6 +3119,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; loff_t len; + int i; pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)instr->addr, @@ -3050,6 +3192,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, goto erase_exit; } + for (i = 0; i < pages_per_block; i++) { + int subpage; + for (subpage = 0; + subpage < 1 << mtd->subpage_sft; + subpage++) { + nand_page_set_status(mtd, + ((page + i) << mtd->subpage_sft) + + subpage, + NAND_PAGE_EMPTY); + } + } + /* Increment page address and decrement length */ len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index eecd9501452d..0709db0dd9c6 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -523,6 +523,24 @@ struct nand_ecc_ctrl { int page); }; +/* + * Constants for page status + */ +enum nand_page_status { + NAND_PAGE_STATUS_UNKNOWN, + NAND_PAGE_EMPTY, + NAND_PAGE_FILLED, +}; + +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob); + +int nand_page_get_status(struct mtd_info *mtd, int page); + +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status); + +int nand_pst_create(struct mtd_info *mtd); + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. @@ -633,6 +651,7 @@ struct nand_buffers { * @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial * bad block scan. + * @pst: [INTERN] page status table * @controller: [REPLACEABLE] a pointer to a hardware controller * structure which is shared among multiple independent * devices. @@ -723,6 +742,8 @@ struct nand_chip { struct nand_bbt_descr *badblock_pattern; + uint8_t *pst; + struct list_head partitions; struct mutex part_lock; From d19db5ecf529bde206041cc6dc1ec4b9bee4b97b Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:46:26 +0200 Subject: [PATCH 05/33] mtd: nand: Introduce a randomizer layer in the NAND framework This patch introduce a new layer in the NAND framework to support both HW and SW randomizers. This randomization is required on some MLC/TLC NAND chips which do not support large islands of same patterns. The randomizer layer defines a nand_rnd_ctrl struct which is intended to be used by NAND core functions or NAND drivers to randomize/derandomize data stored on NAND chips. The implementation can implement any of these functions: - config: prepare a random transfer to/from the NAND chip - write_buf: randomize and write data to the NAND chip - read_buf: read and derandomize data from the NAND chip read/write_buf functions are always called after a config call. The config call specify the page, the column within the page and the action that will take place after the config (either read or write). If column is set to -1, the randomizer is disabled. If page is set to -1, we keep working on the same page. The randomizer layer provides helper functions that choose wether the randomizer or the chip read/write_buf should be used. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/nand_base.c | 278 ++++++++++++++++++++++++++++------- include/linux/mtd/nand.h | 98 ++++++++++++ 2 files changed, 321 insertions(+), 55 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 718e1b7c6f6d..a6ae05ded408 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1100,6 +1100,62 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) } EXPORT_SYMBOL(nand_lock); +/** + * nand_rnd_is_activ - check wether a region of a NAND page requires NAND + * randomizer to be disabled + * @mtd: mtd info + * @page: NAND page + * @column: offset within the page + * @len: len of the region + * + * Returns 1 if the randomizer should be enabled, 0 if not, or -ERR in case of + * error. + * + * In case of success len will contain the size of the region: + * - if the requested region fits in a NAND random region len will not change + * - else len will be replaced by the available length within the NAND random + * region + */ +int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len) +{ + struct nand_chip *chip = mtd->priv; + struct nand_rnd_layout *layout = chip->cur_rnd->layout; + struct nand_rndfree *range; + int ret = 1; + int tmp; + int i; + + if (!len || *len < 0 || column < 0 || + column + *len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + if (layout) { + for (i = 0; i < layout->nranges; i++) { + range = &layout->ranges[i]; + if (column + *len <= range->offset) { + break; + } else if (column >= range->offset + range->length) { + continue; + } else if (column < range->offset) { + tmp = range->offset - column; + if (*len > tmp) + *len = tmp; + break; + } else { + tmp = range->offset + range->length - column; + if (*len > tmp) + *len = tmp; + ret = 0; + break; + } + + } + } + + return ret; +} +EXPORT_SYMBOL(nand_rnd_is_activ); + /** * nand_page_is_empty - check wether a NAND page contains only FFs * @mtd: mtd info @@ -1245,9 +1301,14 @@ EXPORT_SYMBOL(nand_pst_create); static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - chip->read_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, 0, NAND_RND_READ); + nand_rnd_read_buf(mtd, buf, mtd->writesize); + if (oob_required) { + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + return 0; } @@ -1269,28 +1330,40 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; + int column = 0; for (steps = chip->cur_ecc->steps; steps > 0; steps--) { - chip->read_buf(mtd, buf, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, buf, eccsize); buf += eccsize; + column += eccsize; if (chip->cur_ecc->prepad) { - chip->read_buf(mtd, oob, chip->cur_ecc->prepad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad); oob += chip->cur_ecc->prepad; + column += chip->cur_ecc->prepad; } - chip->read_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes; if (chip->cur_ecc->postpad) { - chip->read_buf(mtd, oob, chip->cur_ecc->postpad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad); oob += chip->cur_ecc->postpad; + column += chip->cur_ecc->postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); + if (size) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, size); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; } @@ -1379,7 +1452,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); p = bufpoi + data_col_addr; - chip->read_buf(mtd, p, datafrag_len); + nand_rnd_config(mtd, -1, data_col_addr, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, datafrag_len); /* Calculate ECC */ for (i = 0; i < eccfrag_len; @@ -1398,7 +1472,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, } if (gaps) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); } else { /* * Send the command to read the particular ECC bytes take care @@ -1414,7 +1489,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); - chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + nand_rnd_config(mtd, -1, mtd->writesize + aligned_pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); } for (i = 0; i < eccfrag_len; i++) @@ -1435,6 +1511,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } @@ -1459,13 +1536,17 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); + column += eccsize; } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; @@ -1485,6 +1566,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } @@ -1513,11 +1595,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; unsigned int max_bitflips = 0; + int column = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + column = 0; for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; @@ -1526,7 +1611,8 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, int stat; chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL); @@ -1537,6 +1623,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } @@ -1560,20 +1647,27 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *p = buf; uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); + column += eccsize; if (chip->cur_ecc->prepad) { - chip->read_buf(mtd, oob, chip->cur_ecc->prepad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad); oob += chip->cur_ecc->prepad; } chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, eccbytes); + column += eccbytes; + stat = chip->cur_ecc->correct(mtd, p, oob, NULL); if (stat < 0) { @@ -1586,29 +1680,36 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, oob += eccbytes; if (chip->cur_ecc->postpad) { - chip->read_buf(mtd, oob, chip->cur_ecc->postpad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad); + column += chip->cur_ecc->postpad; oob += chip->cur_ecc->postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); + if (i) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, i); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return max_bitflips; } /** * nand_transfer_oob - [INTERN] Transfer oob to client buffer - * @chip: nand chip structure + * @mtd: mtd structure * @oob: oob destination address * @ops: oob ops structure * @len: size of oob to transfer */ -static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, +static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { + struct nand_chip *chip = mtd->priv; + switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -1736,6 +1837,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, * Now read the page into the buffer. Absent an error, * the read methods return max bitflips per ecc step. */ + nand_rnd_config(mtd, page, -1, NAND_RND_READ); if (unlikely(ops->mode == MTD_OPS_RAW)) ret = chip->cur_ecc->read_page_raw(mtd, chip, bufpoi, @@ -1752,6 +1854,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, bufpoi, oob_required, page); + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + if (ret < 0) { if (use_bufpoi) /* Invalidate page cache */ @@ -1779,8 +1883,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int toread = min(oobreadlen, max_oobsize); if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); + oob = nand_transfer_oob(mtd, oob, ops, + toread); oobreadlen -= toread; } } @@ -1908,12 +2012,15 @@ static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len, nand_get_device(part->master, FL_READING); if (part->ecc) chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; ops.mode = MTD_OPS_PLACE_OOB; ret = nand_do_read_ops(part->master, from, &ops); *retlen = ops.retlen; + chip->cur_rnd = &chip->rnd; chip->cur_ecc = &chip->ecc; nand_release_device(part->master); return ret; @@ -1929,7 +2036,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; } @@ -1948,7 +2057,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->cur_ecc->postpad; int eccsize = chip->cur_ecc->size; uint8_t *bufpoi = chip->oob_poi; - int i, toread, sndrnd = 0, pos; + int i, toread, sndrnd = 0, pos = eccsize; chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page); for (i = 0; i < chip->cur_ecc->steps; i++) { @@ -1961,12 +2070,17 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } else sndrnd = 1; toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); + nand_rnd_config(mtd, page, pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, bufpoi, toread); bufpoi += toread; length -= toread; } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); + if (length > 0) { + pos = mtd->writesize + mtd->oobsize - length; + nand_rnd_config(mtd, page, pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, bufpoi, length); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; } @@ -1985,7 +2099,9 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int length = mtd->oobsize; chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, length); + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); @@ -2041,12 +2157,18 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, } else sndcmd = 1; len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); + nand_rnd_config(mtd, page, pos, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, bufpoi, len); bufpoi += len; length -= len; } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); + if (length > 0) { + pos = mtd->writesize + mtd->oobsize - length; + nand_rnd_config(mtd, page, pos, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, bufpoi, length); + } + + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); @@ -2115,7 +2237,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, break; len = min(len, readlen); - buf = nand_transfer_oob(chip, buf, ops, len); + buf = nand_transfer_oob(mtd, buf, ops, len); if (chip->options & NAND_NEED_READRDY) { /* Apply delay or wait for ready/busy pin */ @@ -2225,6 +2347,8 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, nand_get_device(part->master, FL_READING); if (part->ecc) chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -2242,6 +2366,7 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, ret = nand_do_read_ops(part->master, from, ops); out: + chip->cur_rnd = &chip->rnd; chip->cur_ecc = &chip->ecc; nand_release_device(part->master); return ret; @@ -2260,9 +2385,11 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - chip->write_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_write_buf(mtd, buf, mtd->writesize); + if (oob_required) { + nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize); + } return 0; } @@ -2284,28 +2411,39 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; + int column = 0; for (steps = chip->cur_ecc->steps; steps > 0; steps--) { - chip->write_buf(mtd, buf, eccsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, eccsize); buf += eccsize; + column += eccsize; if (chip->cur_ecc->prepad) { - chip->write_buf(mtd, oob, chip->cur_ecc->prepad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad); oob += chip->cur_ecc->prepad; + column += chip->cur_ecc->prepad; } - chip->write_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes; if (chip->cur_ecc->postpad) { - chip->write_buf(mtd, oob, chip->cur_ecc->postpad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad); oob += chip->cur_ecc->postpad; + column += chip->cur_ecc->postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); + if (size) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, size); + } return 0; } @@ -2352,17 +2490,21 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; uint32_t *eccpos = chip->cur_ecc->layout->eccpos; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, p, eccsize); chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); + column += eccsize; } for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -2398,7 +2540,9 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, buf, ecc_size); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, ecc_size); + offset += ecc_size; /* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) @@ -2423,7 +2567,8 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->oob_poi[eccpos[i]] = ecc_calc[i]; /* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -2448,31 +2593,42 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, int eccsteps = chip->cur_ecc->steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + int column = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, p, eccsize); + column += eccsize; if (chip->cur_ecc->prepad) { - chip->write_buf(mtd, oob, chip->cur_ecc->prepad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad); oob += chip->cur_ecc->prepad; + column += chip->cur_ecc->prepad; } chip->cur_ecc->calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes; if (chip->cur_ecc->postpad) { - chip->write_buf(mtd, oob, chip->cur_ecc->postpad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad); oob += chip->cur_ecc->postpad; + column += chip->cur_ecc->postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); + if (i) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, i); + } return 0; } @@ -2503,6 +2659,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + nand_rnd_config(mtd, page, 0, NAND_RND_WRITE); if (unlikely(raw)) status = chip->cur_ecc->write_page_raw(mtd, chip, buf, oob_required); @@ -2513,6 +2670,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else status = chip->cur_ecc->write_page(mtd, chip, buf, oob_required); + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); if (status < 0) return status; @@ -2802,6 +2960,8 @@ static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, panic_nand_get_device(chip, part->master, FL_WRITING); if (part->ecc) chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; ops.len = len; ops.datbuf = (uint8_t *)buf; @@ -2810,6 +2970,7 @@ static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, ret = nand_do_write_ops(part->master, to, &ops); + chip->cur_rnd = &chip->rnd; chip->cur_ecc = &chip->ecc; *retlen = ops.retlen; return ret; @@ -2864,12 +3025,15 @@ static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, nand_get_device(part->master, FL_WRITING); if (part->ecc) chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; ops.mode = MTD_OPS_PLACE_OOB; ret = nand_do_write_ops(part->master, to, &ops); *retlen = ops.retlen; + chip->cur_rnd = &chip->rnd; chip->cur_ecc = &chip->ecc; nand_release_device(part->master); return ret; @@ -3031,6 +3195,8 @@ static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, nand_get_device(part->master, FL_WRITING); if (part->ecc) chip->cur_ecc = part->ecc; + if (part->rnd) + chip->cur_rnd = part->rnd; switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -3048,6 +3214,7 @@ static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, ret = nand_do_write_ops(part->master, to, ops); out: + chip->cur_rnd = &chip->rnd; chip->cur_ecc = &chip->ecc; nand_release_device(part->master); return ret; @@ -4785,6 +4952,7 @@ int nand_scan_tail(struct mtd_info *mtd) mutex_init(&chip->part_lock); chip->cur_ecc = &chip->ecc; + chip->cur_rnd = &chip->rnd; /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0709db0dd9c6..468a7cecf8c8 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -541,6 +541,64 @@ void nand_page_set_status(struct mtd_info *mtd, int page, int nand_pst_create(struct mtd_info *mtd); +/* + * Constants for randomizer modes + */ +typedef enum { + NAND_RND_NONE, + NAND_RND_SOFT, + NAND_RND_HW, +} nand_rnd_modes_t; + +/* + * Constants for randomizer actions + */ +enum nand_rnd_action { + NAND_RND_NO_ACTION, + NAND_RND_READ, + NAND_RND_WRITE, +}; + +/** + * struct nand_rndfree - Structure defining a NAND page region where the + * randomizer should be disabled + * @offset: range offset + * @length: range length + */ +struct nand_rndfree { + u32 offset; + u32 length; +}; + +/** + * struct nand_rnd_layout - Structure defining rndfree regions + * @nranges: number of ranges + * @ranges: array defining the rndfree regions + */ +struct nand_rnd_layout { + int nranges; + struct nand_rndfree ranges[0]; +}; + +/** + * struct nand_rnd_ctrl - Randomizer Control structure + * @mode: Randomizer mode + * @config: function to prepare the randomizer (i.e.: set the appropriate + * seed/init value). + * @read_buf: function that read from the NAND and descramble the retrieved + * data. + * @write_buf: function that scramble data before writing it to the NAND. + */ +struct nand_rnd_ctrl { + nand_rnd_modes_t mode; + struct nand_rnd_layout *layout; + void *priv; + int (*config)(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action); + 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); +}; + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. @@ -736,6 +794,9 @@ struct nand_chip { struct nand_buffers *buffers; struct nand_hw_control hwcontrol; + struct nand_rnd_ctrl rnd; + struct nand_rnd_ctrl *cur_rnd; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; @@ -757,6 +818,7 @@ struct nand_chip { * @master: MTD device representing the NAND chip * @offset: partition offset * @ecc: partition specific ECC struct + * @rnd: partition specific randomizer struct * @release: function used to release this nand_part struct * * NAND partitions work as standard MTD partitions except it can override @@ -770,6 +832,7 @@ struct nand_part { struct mtd_info *master; uint64_t offset; struct nand_ecc_ctrl *ecc; + struct nand_rnd_ctrl *rnd; void (*release)(struct nand_part *part); }; @@ -906,6 +969,41 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf); +static inline int nand_rnd_config(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->cur_rnd && chip->cur_rnd->config) + return chip->cur_rnd->config(mtd, page, column, action); + + return 0; +} + +static inline void nand_rnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->cur_rnd && chip->cur_rnd->read_buf) + chip->cur_rnd->write_buf(mtd, buf, len); + else + chip->write_buf(mtd, buf, len); +} + +static inline void nand_rnd_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->cur_rnd && chip->cur_rnd->read_buf) + chip->cur_rnd->read_buf(mtd, buf, len); + else + chip->read_buf(mtd, buf, len); +} + +int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len); + /** * struct platform_nand_chip - chip level device structure * @nr_chips: max. number of chips to scan for From ac9306fb95e445c1a428f714d4bb5aa460a96df5 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:47:04 +0200 Subject: [PATCH 06/33] of: mtd: Add NAND randomizer mode retrieval Add a of_get_nand_rnd_mode() helper function. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/of/of_mtd.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/of_mtd.h | 6 ++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index b7361ed70537..4e42c26d91c3 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -83,6 +83,41 @@ int of_get_nand_ecc_strength(struct device_node *np) } EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength); +/** + * It maps 'enum nand_rnd_modes_t' found in include/linux/mtd/nand.h + * into the device tree binding of 'nand-rnd', so that MTD + * device driver can get nand rnd from device tree. + */ +static const char *nand_rnd_modes[] = { + [NAND_RND_NONE] = "none", + [NAND_RND_SOFT] = "soft", + [NAND_RND_HW] = "hw", +}; + +/** + * of_get_nand_rnd_mode - Get nand randomizer mode for given device_node + * @np: Pointer to the given device_node + * + * The function gets randomizer mode string from property 'nand-rnd-mode', + * and return its index in nand_rnd_modes table, or errno in error case. + */ +int of_get_nand_rnd_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "nand-rnd-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(nand_rnd_modes); i++) + if (!strcasecmp(pm, nand_rnd_modes[i])) + return i; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(of_get_nand_rnd_mode); + /** * of_get_nand_bus_width - Get nand bus witdh for given device_node * @np: Pointer to the given device_node diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index e266caa36402..1059472915d2 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -15,6 +15,7 @@ int of_get_nand_ecc_mode(struct device_node *np); int of_get_nand_ecc_step_size(struct device_node *np); int of_get_nand_ecc_strength(struct device_node *np); +int of_get_nand_rnd_mode(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); bool of_get_nand_on_flash_bbt(struct device_node *np); @@ -35,6 +36,11 @@ static inline int of_get_nand_ecc_strength(struct device_node *np) return -ENOSYS; } +static inline int of_get_nand_rnd_mode(struct device_node *np) +{ + return -ENOSYS; +} + static inline int of_get_nand_bus_width(struct device_node *np) { return -ENOSYS; From 32c8ca6ef9e8db9ca4d73cdeb817b105b5015ce8 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 24 Feb 2014 16:28:32 +0100 Subject: [PATCH 07/33] mtd: nand: Add manufacturer specific init code infrastructure Add new fields in nand_manufacturers and nand_chip struct to provide manufacturer specific handling like read retries. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/nand_base.c | 7 +++++++ include/linux/mtd/nand.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a6ae05ded408..462984a16166 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4378,6 +4378,13 @@ static 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; + if (nand_manuf_ids[maf_idx].init) { + int err; + err = nand_manuf_ids[maf_idx].init(mtd, id_data); + if (err) + return ERR_PTR(err); + } + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", *maf_id, *dev_id); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 468a7cecf8c8..a6227768111d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -753,6 +753,9 @@ struct nand_chip { int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, int feature_addr, uint8_t *subfeature_para); int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); + void (*manuf_cleanup)(struct mtd_info *mtd); + + void *manuf_priv; int chip_delay; unsigned int options; @@ -955,6 +958,7 @@ struct nand_flash_dev { struct nand_manufacturers { int id; char *name; + int (*init)(struct mtd_info *mtd, const uint8_t *id); }; extern struct nand_flash_dev nand_flash_ids[]; From abc57e9f26f39024ecb4e53b078d26016db047ca Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 24 Feb 2014 16:30:22 +0100 Subject: [PATCH 08/33] mtd: nand: Add hynix specific initializer Add an hynix initiliazer to manage read retries on h27uxgt8t2a chip. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_hynix.c | 159 ++++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_ids.c | 3 +- include/linux/mtd/nand.h | 2 + 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 drivers/mtd/nand/nand_hynix.c diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 3eb86d3c7963..4f2139e6e3e4 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MTD_NAND) += nand.o obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o -obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o nand_hynix.o obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c new file mode 100644 index 000000000000..0d051bf5e2f0 --- /dev/null +++ b/drivers/mtd/nand/nand_hynix.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2014 Boris BREZILLON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include +#include +#include + +static u8 h27ucg8t2a_read_retry_regs[] = { + 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf +}; + +struct hynix_read_retry { + u8 *regs; + u8 values[64]; +}; + +struct hynix_nand { + struct hynix_read_retry read_retry; +}; + +int nand_setup_read_retry_hynix(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd->priv; + struct hynix_nand *hynix = chip->manuf_priv; + int offset = retry_mode * 8; + int status; + int i; + + chip->cmdfunc(mtd, 0x36, -1, -1); + for (i = 0; i < 8; i++) { + int column = hynix->read_retry.regs[i]; + column |= column << 8; + chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); + chip->write_byte(mtd, hynix->read_retry.values[offset + i]); + } + chip->cmdfunc(mtd, 0x16, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static void h27ucg8t2a_cleanup(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + kfree(chip->manuf_priv); +} + +static int h27ucg8t2a_init(struct mtd_info *mtd, const uint8_t *id) +{ + struct nand_chip *chip = mtd->priv; + struct hynix_nand *hynix; + u8 * buf = NULL; + int i, j; + int ret; + + buf = kzalloc(1024, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x36, 0xff, -1); + chip->write_byte(mtd, 0x40); + chip->cmdfunc(mtd, NAND_CMD_NONE, 0xcc, -1); + chip->write_byte(mtd, 0x4d); + chip->cmdfunc(mtd, 0x16, -1, -1); + chip->cmdfunc(mtd, 0x17, -1, -1); + chip->cmdfunc(mtd, 0x04, -1, -1); + chip->cmdfunc(mtd, 0x19, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, 0x200); + + chip->read_buf(mtd, buf, 2); + if (buf[0] != 0x8 || buf[1] != 0x8) { + ret = -EINVAL; + goto leave; + } + chip->read_buf(mtd, buf, 1024); + + ret = 0; + for (j = 0; j < 8; j++) { + for (i = 0; i < 64; i++) { + u8 *tmp = buf + (128 * j); + if ((tmp[i] | tmp[i + 64]) != 0xff) { + ret = -EINVAL; + goto leave; + } + } + } + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x38, -1, -1); + chip->select_chip(mtd, -1); + + if (!ret) { + hynix = kzalloc(sizeof(*hynix), GFP_KERNEL); + if (!hynix) { + ret = -ENOMEM; + goto leave; + } + + hynix->read_retry.regs = h27ucg8t2a_read_retry_regs; + memcpy(hynix->read_retry.values, buf, 64); + chip->manuf_priv = hynix; + chip->setup_read_retry = nand_setup_read_retry_hynix; + chip->read_retries = 8; + chip->manuf_cleanup = h27ucg8t2a_cleanup; + } + +leave: + kfree(buf); + + return ret; +} + +struct hynix_nand_initializer { + u8 id[6]; + int (*init)(struct mtd_info *mtd, const uint8_t *id); +}; + +struct hynix_nand_initializer initializers[] = { + { + .id = {NAND_MFR_HYNIX, 0xde, 0x94, 0xda, 0x74, 0xc4}, + .init = h27ucg8t2a_init, + }, +}; + +int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(initializers); i++) { + struct hynix_nand_initializer *initializer = &initializers[i]; + if (memcmp(id, initializer->id, sizeof(initializer->id))) + continue; + + return initializer->init(mtd, id); + } + + return 0; +} +EXPORT_SYMBOL(hynix_nand_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Boris BREZILLON "); +MODULE_DESCRIPTION("Hynix NAND specific code"); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 7124400d903b..c1730ae6b938 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -161,6 +161,7 @@ struct nand_flash_dev nand_flash_ids[] = { {NULL} }; + /* Manufacturer IDs */ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_TOSHIBA, "Toshiba"}, @@ -169,7 +170,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_NATIONAL, "National"}, {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, - {NAND_MFR_HYNIX, "Hynix"}, + {NAND_MFR_HYNIX, "Hynix", hynix_nand_init}, {NAND_MFR_MICRON, "Micron"}, {NAND_MFR_AMD, "AMD/Spansion"}, {NAND_MFR_MACRONIX, "Macronix"}, diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index a6227768111d..00124236f9b3 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -964,6 +964,8 @@ struct nand_manufacturers { extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; +int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id); + extern int nand_default_bbt(struct mtd_info *mtd); extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs); From 4e49d02090179ce11430b1d548f5d9144bcb34f3 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Thu, 1 Jan 2015 16:44:45 +0100 Subject: [PATCH 09/33] mtd: nand: Fix NAND_* options to use unique values. NAND_BUSWIDTH_AUTO (64b37b2a6) and NAND_USE_BOUNCE_BUFFER (66507c7bc) are the same value. Change the later introduced NAND_USE_BOUNCE_BUFFER to a different value. Signed-off-by: Michal Suchanek Signed-off-by: Hans de Goede --- include/linux/mtd/nand.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 00124236f9b3..f6a506d81a8d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -177,18 +177,18 @@ typedef enum { #define NAND_OWN_BUFFERS 0x00020000 /* Chip may not exist, so silence any errors in scan */ #define NAND_SCAN_SILENT_NODEV 0x00040000 -/* - * This option could be defined by controller drivers to protect against - * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers - */ -#define NAND_USE_BOUNCE_BUFFER 0x00080000 /* * Autodetect nand buswidth with readid/onfi. * This suppose the driver will configure the hardware in 8 bits mode * when calling nand_scan_ident, and update its configuration * before calling nand_scan_tail. */ -#define NAND_BUSWIDTH_AUTO 0x00080000 +#define NAND_BUSWIDTH_AUTO 0x00080000 +/* + * This option could be defined by controller drivers to protect against + * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers + */ +#define NAND_USE_BOUNCE_BUFFER 0x00100000 /* Options set by nand scan */ /* Nand scan has allocated controller struct */ From fa6b5cad4af53cb9f732db31a2dc28ff7774b075 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 25 May 2015 12:57:48 +0200 Subject: [PATCH 10/33] mtd: nand: nand_get_flash_type: Print detected ECC strength and size Print the detected ECC strength and size from nand_get_flash_type(). Signed-off-by: Hans de Goede --- drivers/mtd/nand/nand_base.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 462984a16166..eff3c67941ed 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4398,9 +4398,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, type->name); - pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", + pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, " + "OOB size: %d, ECC strength %d size %d\n", (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", - mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); + mtd->erasesize >> 10, mtd->writesize, mtd->oobsize, + chip->ecc_strength_ds, chip->ecc_step_ds); return type; } From 74f4355bc589e22560ae5127b4e614ec16024f71 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Thu, 1 Jan 2015 00:57:46 +0100 Subject: [PATCH 11/33] mtd: nand: print full chip ID Full chip ID is printed so user has data to paste from syslog in case of chip misidentification. Signed-off-by: Michal Suchanek Signed-off-by: Hans de Goede --- drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index eff3c67941ed..d8f5ec82cb6b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4208,7 +4208,7 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type) } static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, - struct nand_flash_dev *type, u8 *id_data, int *busw) + struct nand_flash_dev *type, const u8 *id_data, int *busw) { if (!strncmp(type->id, id_data, type->id_len)) { mtd->writesize = type->pagesize; @@ -4233,6 +4233,21 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, return false; } +/* + * Print full detail of chip ID read from chip. + */ +static void print_nand_chip_info(int maf_id, int dev_id, u8 id_data[8]) +{ + u8 delim[8] = { [0 ... 7] = ',' }; + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", maf_id, dev_id); + delim[7] = ' '; + delim[nand_id_len(id_data, 8) - 1] = ';'; + /* This sucks. Kernel seems to insert newline after every other printk so format in one go. */ + pr_info("chip id data: 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c 0x%02x%c\n", + id_data[0], delim[0], id_data[1], delim[1], id_data[2], delim[2], id_data[3], delim[3], + id_data[4], delim[4], id_data[5], delim[5], id_data[6], delim[6], id_data[7], delim[7]); +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ @@ -4346,8 +4361,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * Check, if buswidth is correct. Hardware drivers should set * chip correct! */ - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - *maf_id, *dev_id); + print_nand_chip_info(*maf_id, *dev_id, id_data); pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name); pr_warn("bus width %d instead %d bit\n", (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, @@ -4385,8 +4399,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, return ERR_PTR(err); } - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - *maf_id, *dev_id); + print_nand_chip_info(*maf_id, *dev_id, id_data); if (chip->onfi_version) pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, From 1d25128a705db921a230255c2a2d74f836af5edf Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 21 Oct 2014 14:37:15 +0200 Subject: [PATCH 12/33] mtd: nand: sunxi: Add NAND partition support Add NAND partition support to the sunxi_nand driver. Signed-off-by: Boris Brezillon Signed-off-by: Hans de Goede --- drivers/mtd/nand/Kconfig | 1 + drivers/mtd/nand/sunxi_nand.c | 73 ++++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index abf4daab732d..08f1ec28bfad 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -533,6 +533,7 @@ config MTD_NAND_XWAY config MTD_NAND_SUNXI tristate "Support for NAND on Allwinner SoCs" depends on ARCH_SUNXI + select MTD_OF_NAND_PARTS help Enables support for NAND Flash chips on Allwinner SoCs. diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 6f93b2990d25..c3e04732ff02 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -201,6 +201,23 @@ struct sunxi_nand_hw_ecc { struct nand_ecclayout layout; }; +/* + * sunxi NAND partition structure: stores NAND partitions information + * + * @part: base paritition structure + * @ecc: per-partition ECC info + */ +struct sunxi_nand_part { + struct nand_part part; + struct nand_ecc_ctrl ecc; +}; + +static inline struct sunxi_nand_part * +to_sunxi_nand_part(struct nand_part *part) +{ + return container_of(part, struct sunxi_nand_part, part); +} + /* * NAND chip structure: stores NAND chip device related information * @@ -521,7 +538,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, int oob_required, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct nand_ecclayout *layout = ecc->layout; struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; @@ -607,7 +624,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, const uint8_t *buf, int oob_required) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct nand_ecclayout *layout = ecc->layout; struct sunxi_nand_hw_ecc *data = ecc->priv; int offset; @@ -681,7 +698,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; uint8_t *oob = chip->oob_poi; @@ -749,7 +766,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, int oob_required) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); - struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct sunxi_nand_hw_ecc *data = ecc->priv; uint8_t *oob = chip->oob_poi; int offset = 0; @@ -1099,8 +1116,13 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, ecc->strength = nand->ecc_strength_ds; } - if (!ecc->size || !ecc->strength) - return -EINVAL; + if (!ecc->size || !ecc->strength) { + if (ecc == &nand->ecc) + return -EINVAL; + + ecc->size = nand->ecc.size; + ecc->strength = nand->ecc.strength; + } ecc->mode = NAND_ECC_HW; @@ -1135,12 +1157,39 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, return 0; } +static void sunxi_nand_part_release(struct nand_part *part) +{ + kfree(to_sunxi_nand_part(part)); +} + +struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master, + struct device_node *pp) +{ + struct sunxi_nand_part *part; + int ret; + + part = kzalloc(sizeof(*part), GFP_KERNEL); + part->part.release = sunxi_nand_part_release; + + ret = sunxi_nand_ecc_init(master, &part->ecc, pp); + if (ret) + goto err; + + part->part.ecc = &part->ecc; + + return &part->part; + +err: + kfree(part); + return ERR_PTR(ret); +} + static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, struct device_node *np) { const struct nand_sdr_timings *timings; struct sunxi_nand_chip *chip; - struct mtd_part_parser_data ppdata; + struct ofnandpart_data ppdata; struct mtd_info *mtd; struct nand_chip *nand; int nsels; @@ -1269,8 +1318,14 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, return ret; } - ppdata.of_node = np; - ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + ppdata.node = np; + ppdata.parse = sunxi_ofnandpart_parse; + ret = ofnandpart_parse(mtd, &ppdata); + if (!ret) + ret = mtd_device_register(mtd, NULL, 0); + else if (ret > 0) + ret = 0; + if (ret) { dev_err(dev, "failed to register mtd device: %d\n", ret); nand_release(mtd); From 759945f4c04502c3a1dedfb7ed0dd2009a729f4b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 21 Oct 2014 14:40:42 +0200 Subject: [PATCH 13/33] mtd: nand: sunxi: Add HW randomizer support Add support for the HW randomizer available on the sunxi nand controller. Signed-off-by: Boris Brezillon Signed-off-by: Hans de Goede --- drivers/mtd/nand/sunxi_nand.c | 603 +++++++++++++++++++++++++++++++++- 1 file changed, 585 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index c3e04732ff02..2f6ab39f1c1d 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -206,10 +206,12 @@ struct sunxi_nand_hw_ecc { * * @part: base paritition structure * @ecc: per-partition ECC info + * @rnd: per-partition randomizer info */ struct sunxi_nand_part { struct nand_part part; struct nand_ecc_ctrl ecc; + struct nand_rnd_ctrl rnd; }; static inline struct sunxi_nand_part * @@ -218,6 +220,29 @@ to_sunxi_nand_part(struct nand_part *part) return container_of(part, struct sunxi_nand_part, part); } +/* + * sunxi NAND randomizer structure: stores NAND randomizer information + * + * @page: current page + * @column: current column + * @nseeds: seed table size + * @seeds: seed table + * @subseeds: pre computed sub seeds + * @step: step function + * @left: number of remaining bytes in the page + * @state: current randomizer state + */ +struct sunxi_nand_hw_rnd { + int page; + int column; + int nseeds; + u16 *seeds; + u16 *subseeds; + u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left); + int left; + u16 state; +}; + /* * NAND chip structure: stores NAND chip device related information * @@ -233,6 +258,7 @@ struct sunxi_nand_chip { struct list_head node; struct nand_chip nand; struct mtd_info mtd; + void *buffer; unsigned long clk_rate; int selected; int nsels; @@ -489,6 +515,185 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, } } +static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count) +{ + state &= 0x7fff; + count *= 8; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count) +{ + state &= 0x7fff; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; + u16 state; + + if (page < 0 && column < 0) { + rnd->page = -1; + rnd->column = -1; + return 0; + } + + if (column < 0) + column = 0; + if (page < 0) + page = rnd->page; + + if (page < 0) + return -EINVAL; + + if (page != rnd->page && action == NAND_RND_READ) { + int status; + + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) + status = NAND_PAGE_EMPTY; + else + status = NAND_PAGE_FILLED; + + nand_page_set_status(mtd, page, status); + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1); + } + } + + state = rnd->seeds[page % rnd->nseeds]; + rnd->page = page; + rnd->column = column; + + if (rnd->step) { + rnd->state = rnd->step(mtd, state, column, &rnd->left); + } else { + rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096); + rnd->left = mtd->oobsize + mtd->writesize - column; + } + + return 0; +} + +static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; + u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + int cnt; + int offs = 0; + int rndactiv; + + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN); + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + if (rnd->page < 0) { + sunxi_nfc_write_buf(mtd, buf, len); + return; + } + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column, + &cnt); + if (rndactiv > 0) { + writel(tmp | NFC_RANDOM_EN | (rnd->state << 16), + nfc->regs + NFC_REG_ECC_CTL); + if (rnd->left < cnt) + cnt = rnd->left; + } + + sunxi_nfc_write_buf(mtd, buf + offs, cnt); + + if (rndactiv > 0) + writel(tmp & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + + offs += cnt; + if (len <= offs) + break; + + sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE); + } +} + +static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; + u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + int cnt; + int offs = 0; + int rndactiv; + + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN); + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + if (rnd->page < 0) { + sunxi_nfc_read_buf(mtd, buf, len); + return; + } + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY && + nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0) + rndactiv = 1; + else + rndactiv = 0; + + if (rndactiv > 0) { + writel(tmp | NFC_RANDOM_EN | (rnd->state << 16), + nfc->regs + NFC_REG_ECC_CTL); + if (rnd->left < cnt) + cnt = rnd->left; + } + + if (buf) + sunxi_nfc_read_buf(mtd, buf + offs, cnt); + else + sunxi_nfc_read_buf(mtd, NULL, cnt); + + if (rndactiv > 0) + writel(tmp & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + + offs += cnt; + if (len <= offs) + break; + + sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ); + } +} + static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) { uint8_t ret; @@ -538,16 +743,43 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, int oob_required, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct nand_ecclayout *layout = ecc->layout; struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; + int status; int offset; int ret; u32 tmp; int i; int cnt; + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) { + status = NAND_PAGE_EMPTY; + } else { + status = NAND_PAGE_FILLED; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + } + + nand_page_set_status(mtd, page, status); + } + + if (status == NAND_PAGE_EMPTY) { + memset(buf, 0xff, mtd->writesize); + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + return 0; + } + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | @@ -556,12 +788,15 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { + bool rndactiv = false; + if (i) chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1); offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4; - chip->read_buf(mtd, NULL, ecc->size); + nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ); + nand_rnd_read_buf(mtd, NULL, ecc->size); chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); @@ -569,6 +804,25 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, if (ret) return ret; + if (i) { + cnt = ecc->bytes + 4; + if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) + rndactiv = true; + } else { + cnt = ecc->bytes + 2; + if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 && + cnt == ecc->bytes + 2) + rndactiv = true; + } + + if (rndactiv) { + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); writel(tmp, nfc->regs + NFC_REG_CMD); @@ -579,6 +833,9 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, memcpy_fromio(buf + (i * ecc->size), nfc->regs + NFC_RAM0_BASE, ecc->size); + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { mtd->ecc_stats.failed++; } else { @@ -594,9 +851,10 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, if (ret) return ret; + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); offset -= mtd->writesize; - chip->read_buf(mtd, chip->oob_poi + offset, - ecc->bytes + 4); + nand_rnd_read_buf(mtd, chip->oob_poi + offset, + ecc->bytes + 4); } } @@ -606,11 +864,14 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, offset = mtd->writesize + ecc->layout->oobfree[ecc->steps].offset; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); offset -= mtd->writesize; - chip->read_buf(mtd, chip->oob_poi + offset, cnt); + nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~NFC_ECC_EN; @@ -627,6 +888,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct nand_ecclayout *layout = ecc->layout; struct sunxi_nand_hw_ecc *data = ecc->priv; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; int offset; int ret; u32 tmp; @@ -641,22 +903,56 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { + bool rndactiv = false; + u8 oob_buf[4]; + if (i) chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); - chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); + nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; /* Fill OOB data in */ - if (oob_required) { - tmp = 0xffffffff; - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, - 4); + if (!oob_required) + memset(oob_buf, 0xff, 4); + else + memcpy(oob_buf, + chip->oob_poi + layout->oobfree[i].offset, + 4); + + + memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4); + + if (i) { + cnt = ecc->bytes + 4; + if (rnd && + nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) + rndactiv = true; } else { - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, - chip->oob_poi + offset - mtd->writesize, - 4); + cnt = ecc->bytes + 2; + if (rnd && + nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 && + cnt == ecc->bytes + 2) + rndactiv = true; + } + + if (rndactiv) { + /* pre randomize to generate FF patterns on the NAND */ + if (!i) { + u16 state = rnd->subseeds[rnd->page % rnd->nseeds]; + state = sunxi_nfc_hwrnd_single_step(state, 15); + oob_buf[0] ^= state; + state = sunxi_nfc_hwrnd_step(rnd, state, 1); + oob_buf[1] ^= state; + memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4); + } + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); } chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); @@ -671,6 +967,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); if (ret) return ret; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); } if (oob_required) { @@ -679,11 +978,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, offset = mtd->writesize + ecc->layout->oobfree[i].offset; chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); offset -= mtd->writesize; - chip->write_buf(mtd, chip->oob_poi + offset, cnt); + nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~NFC_ECC_EN; @@ -692,22 +994,76 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, return 0; } +static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state, + int column, int *left) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; + int nblks = mtd->writesize / ecc->size; + int modsize = ecc->size; + int steps; + + if (column < mtd->writesize) { + steps = column % modsize; + *left = modsize - steps; + } else if (column < mtd->writesize + + (nblks * (ecc->bytes + 4))) { + column -= mtd->writesize; + steps = column % (ecc->bytes + 4); + *left = ecc->bytes + 4 - steps; + state = rnd->subseeds[rnd->page % rnd->nseeds]; + } else { + steps = column % 4096; + *left = mtd->writesize + mtd->oobsize - column; + } + + return sunxi_nfc_hwrnd_step(rnd, state, steps); +} + static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; uint8_t *oob = chip->oob_poi; int offset = 0; int ret; + int status; int cnt; u32 tmp; int i; + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) { + status = NAND_PAGE_EMPTY; + } else { + status = NAND_PAGE_FILLED; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + } + + nand_page_set_status(mtd, page, status); + } + + if (status == NAND_PAGE_EMPTY) { + memset(buf, 0xff, mtd->writesize); + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + return 0; + } + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | @@ -716,7 +1072,17 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { - chip->read_buf(mtd, NULL, ecc->size); + nand_rnd_config(mtd, page, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, NULL, ecc->size); + + cnt = ecc->bytes + 4; + if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) { + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); writel(tmp, nfc->regs + NFC_REG_CMD); @@ -729,6 +1095,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, buf += ecc->size; offset += ecc->size; + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { mtd->ecc_stats.failed++; } else { @@ -739,7 +1108,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, if (oob_required) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad); oob += ecc->bytes + ecc->prepad; } @@ -750,10 +1120,13 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, cnt = mtd->oobsize - (oob - chip->oob_poi); if (cnt > 0) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - chip->read_buf(mtd, oob, cnt); + nand_rnd_config(mtd, page, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, nfc->regs + NFC_REG_ECC_CTL); @@ -768,6 +1141,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = chip->cur_ecc; struct sunxi_nand_hw_ecc *data = ecc->priv; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; uint8_t *oob = chip->oob_poi; int offset = 0; int ret; @@ -783,7 +1157,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { - chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); offset += ecc->size; /* Fill OOB data in */ @@ -796,6 +1171,16 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, 4); } + cnt = ecc->bytes + 4; + if (rnd && + nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) { + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); + tmp |= NFC_RANDOM_EN; + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | (1 << 30); writel(tmp, nfc->regs + NFC_REG_CMD); @@ -804,6 +1189,9 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, if (ret) return ret; + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); + offset += ecc->bytes + ecc->prepad; oob += ecc->bytes + ecc->prepad; } @@ -812,9 +1200,11 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, cnt = mtd->oobsize - (oob - chip->oob_poi); if (cnt > 0) { chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); - chip->write_buf(mtd, oob, cnt); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, cnt); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~NFC_ECC_EN; @@ -824,6 +1214,128 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, return 0; } +static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state, + int column, int *left) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; + int eccsteps = mtd->writesize / ecc->size; + int modsize = ecc->size + ecc->prepad + ecc->bytes; + int steps; + + if (column < (eccsteps * modsize)) { + steps = column % modsize; + *left = modsize - steps; + if (steps >= ecc->size) { + steps -= ecc->size; + state = rnd->subseeds[rnd->page % rnd->nseeds]; + } + } else { + steps = column % 4096; + *left = mtd->writesize + mtd->oobsize - column; + } + + return sunxi_nfc_hwrnd_step(rnd, state, steps); +} + +static u16 default_seeds[] = {0x4a80}; + +static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd) +{ + struct sunxi_nand_hw_rnd *hwrnd = rnd->priv; + + if (hwrnd->seeds != default_seeds) + kfree(hwrnd->seeds); + kfree(hwrnd->subseeds); + kfree(rnd->layout); + kfree(hwrnd); +} + +static int sunxi_nand_rnd_ctrl_init(struct mtd_info *mtd, + struct nand_rnd_ctrl *rnd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + struct sunxi_nand_hw_rnd *hwrnd; + struct nand_rnd_layout *layout = NULL; + int ret; + + hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL); + if (!hwrnd) + return -ENOMEM; + + hwrnd->seeds = default_seeds; + hwrnd->nseeds = ARRAY_SIZE(default_seeds); + + if (of_get_property(np, "nand-randomizer-seeds", &ret)) { + hwrnd->nseeds = ret / sizeof(*hwrnd->seeds); + hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds), + GFP_KERNEL); + if (!hwrnd->seeds) { + ret = -ENOMEM; + goto err; + } + + ret = of_property_read_u16_array(np, "nand-randomizer-seeds", + hwrnd->seeds, hwrnd->nseeds); + if (ret) + goto err; + } + + switch (ecc->mode) { + case NAND_ECC_HW_SYNDROME: + hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps; + break; + + case NAND_ECC_HW: + hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps; + + default: + layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree), + GFP_KERNEL); + if (!layout) { + ret = -ENOMEM; + goto err; + } + layout->nranges = 1; + layout->ranges[0].offset = mtd->writesize; + layout->ranges[0].length = 2; + rnd->layout = layout; + break; + } + + if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) { + int i; + + hwrnd->subseeds = kzalloc(hwrnd->nseeds * + sizeof(*hwrnd->subseeds), + GFP_KERNEL); + if (!hwrnd->subseeds) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < hwrnd->nseeds; i++) + hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd, + hwrnd->seeds[i], + ecc->size); + } + + rnd->config = sunxi_nfc_hwrnd_config; + rnd->read_buf = sunxi_nfc_hwrnd_read_buf; + rnd->write_buf = sunxi_nfc_hwrnd_write_buf; + rnd->priv = hwrnd; + + return 0; + +err: + kfree(hwrnd); + kfree(layout); + + return ret; +} + static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, const struct nand_sdr_timings *timings) { @@ -1084,6 +1596,40 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, return 0; } +static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd) +{ + switch (rnd->mode) { + case NAND_RND_HW: + sunxi_nand_rnd_ctrl_cleanup(rnd); + break; + default: + break; + } +} + +static int sunxi_nand_rnd_init(struct mtd_info *mtd, + struct nand_rnd_ctrl *rnd, + struct nand_ecc_ctrl *ecc, + struct device_node *np) +{ + int ret; + + rnd->mode = NAND_RND_NONE; + + ret = of_get_nand_rnd_mode(np); + if (ret >= 0) + rnd->mode = ret; + + switch (rnd->mode) { + case NAND_RND_HW: + return sunxi_nand_rnd_ctrl_init(mtd, rnd, ecc, np); + default: + break; + } + + return 0; +} + static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) { switch (ecc->mode) { @@ -1175,7 +1721,14 @@ struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master, if (ret) goto err; + ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp); + if (ret) { + sunxi_nand_ecc_cleanup(&part->ecc); + goto err; + } + part->part.ecc = &part->ecc; + part->part.rnd = &part->rnd; return &part->part; @@ -1300,18 +1853,30 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, if (ret) return ret; + chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!chip->buffer) + return -ENOMEM; + ret = sunxi_nand_chip_init_timings(chip, np); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); return ret; } + ret = nand_pst_create(mtd); + if (ret) + return ret; + ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); if (ret) { dev_err(dev, "ECC init failed: %d\n", ret); return ret; } + ret = sunxi_nand_rnd_init(mtd, &nand->rnd, &nand->ecc, np); + if (ret) + return ret; + ret = nand_scan_tail(mtd); if (ret) { dev_err(dev, "nand_scan_tail failed: %d\n", ret); @@ -1367,6 +1932,8 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) node); nand_release(&chip->mtd); sunxi_nand_ecc_cleanup(&chip->nand.ecc); + sunxi_nand_rnd_cleanup(&chip->nand.rnd); + kfree(chip->buffer); } } From 72b12241d479b73b5acae1c64b78153c394687d3 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Sun, 24 Aug 2014 10:40:44 +0200 Subject: [PATCH 14/33] mtd: nand: sunxi: Fallback to chip config when partition config is not available Fallback to chip config for partitions where ecc/rnd config are not specified in the device tree. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/sunxi_nand.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 2f6ab39f1c1d..74f2caf42015 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1711,28 +1711,37 @@ static void sunxi_nand_part_release(struct nand_part *part) struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master, struct device_node *pp) { + struct nand_chip *chip = master->priv; struct sunxi_nand_part *part; int ret; part = kzalloc(sizeof(*part), GFP_KERNEL); part->part.release = sunxi_nand_part_release; - ret = sunxi_nand_ecc_init(master, &part->ecc, pp); - if (ret) - goto err; + if (of_find_property(pp, "nand-ecc-mode", NULL)) { + ret = sunxi_nand_ecc_init(master, &part->ecc, pp); + if (ret) + goto err; - ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp); - if (ret) { - sunxi_nand_ecc_cleanup(&part->ecc); - goto err; + part->part.ecc = &part->ecc; } - part->part.ecc = &part->ecc; - part->part.rnd = &part->rnd; + if (of_find_property(pp, "nand-rnd-mode", NULL)) { + ret = sunxi_nand_rnd_init(master, &part->rnd, + part->part.ecc ? part->part.ecc : &chip->ecc, + pp); + if (ret) + goto err; + + part->part.rnd = &part->rnd; + } return &part->part; err: + if (part->part.ecc) + sunxi_nand_ecc_cleanup(part->part.ecc); + kfree(part); return ERR_PTR(ret); } From 6989a5b3dd8f7289f41ec4d9a39095b7a13a1b44 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Tue, 26 May 2015 17:01:33 +0200 Subject: [PATCH 15/33] ARM: dts: sun4i: Add NAND controller pin definitions Define the NAND controller pin configs. Signed-off-by: Michal Suchanek Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun4i-a10.dtsi | 80 ++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 61c03d1fe530..86b94d120174 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -896,6 +896,86 @@ allwinner,drive = ; allwinner,pull = ; }; + + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15", "PC16"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs4_pins_a: nand_cs@4 { + allwinner,pins = "PC19"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs5_pins_a: nand_cs@5 { + allwinner,pins = "PC20"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs6_pins_a: nand_cs@6 { + allwinner,pins = "PC21"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs7_pins_a: nand_cs@7 { + allwinner,pins = "PC22"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; timer@01c20c00 { From 18eedc8f348d88a5ea127da9cdaa2544a711af99 Mon Sep 17 00:00:00 2001 From: Petros Angelatos Date: Mon, 25 Aug 2014 23:52:47 +0200 Subject: [PATCH 16/33] ARM: dts: sun7i: Enable NAND on cubieboard2 Add a node describing the NAND controller and partitions. Signed-off-by: Petros Angelatos Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts index 39a51d5143f7..f35957d1b8c4 100644 --- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts @@ -142,6 +142,63 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a>, <&nand_cs0_pins_a>, <&nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-on-flash-bbt; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; From 027a0ba6f6375859dee7a92dc663f0d6fd7d935a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:03:18 +0200 Subject: [PATCH 17/33] ARM: dts: sun7i: Enable NAND on cubietruck board Add a node describing the NAND controller and partitions. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts index 4611e2f5a99e..457be6bb4611 100644 --- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts @@ -191,6 +191,61 @@ allwinner,pull = ; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a>, <&nand_cs0_pins_a>, <&nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0x1ffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; From d460e067b1997f847e83d358aa4ba9056bb287b0 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:01:22 +0200 Subject: [PATCH 18/33] ARM: dts: sun7i: Add NFC node to Allwinner A20 SoC Add NAND Flash controller node definition to the A20 SoC. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun7i-a20.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 6a63f30c9a69..1ecc3b7c4430 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -586,6 +586,17 @@ #dma-cells = <2>; }; + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <0 37 4>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi0: spi@01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>; From 6b54b195517716de2324692276051a259ecd3872 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 14:08:15 +0200 Subject: [PATCH 19/33] ARM: dts: sun7i: Add NAND controller pin definitions Define the NAND controller pin configs. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun7i-a20.dtsi | 80 ++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 1ecc3b7c4430..bf96c0618998 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -1100,6 +1100,86 @@ allwinner,drive = ; allwinner,pull = ; }; + + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15", "PC16"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs4_pins_a: nand_cs@4 { + allwinner,pins = "PC19"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs5_pins_a: nand_cs@5 { + allwinner,pins = "PC20"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs6_pins_a: nand_cs@6 { + allwinner,pins = "PC21"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs7_pins_a: nand_cs@7 { + allwinner,pins = "PC22"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; timer@01c20c00 { From 514114aaab2134156db916a15c3d4f4aa3c4d71a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 May 2015 21:06:22 +0200 Subject: [PATCH 20/33] ARM: dts: sun5i: Enable NAND on A13 OLinuxIno board Add a node describing the NAND controller and partitions. Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun5i-a13-olinuxino.dts | 59 +++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts index 42324005eb7c..40c245575e47 100644 --- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts +++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts @@ -155,6 +155,65 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-strength = <40>; + nand-ecc-step-size = <1024>; + nand-on-flash-bbt; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; From 89ba812006046eb90004affa4d9fc5860cb9260f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 May 2015 18:01:50 +0200 Subject: [PATCH 21/33] ARM: dts: sun5i: Add NFC node to Allwinner A13/A10s SoC Add NAND Flash controller node definition to the A13/a10s SoC. Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun5i.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/sun5i.dtsi b/arch/arm/boot/dts/sun5i.dtsi index 571cce499049..3f2971766a4e 100644 --- a/arch/arm/boot/dts/sun5i.dtsi +++ b/arch/arm/boot/dts/sun5i.dtsi @@ -337,6 +337,17 @@ #dma-cells = <2>; }; + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <37>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi0: spi@01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>; From 828042f885a419ce58f3876bcd84a05c33352e9f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 May 2015 17:18:26 +0200 Subject: [PATCH 22/33] ARM: dts: sun5i: Add NAND controller pin definitions Define the NAND controller pin configs. Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun5i-a10s.dtsi | 14 ++++++++++++ arch/arm/boot/dts/sun5i.dtsi | 38 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi index 7ce79f566eff..74f4da129c9d 100644 --- a/arch/arm/boot/dts/sun5i-a10s.dtsi +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi @@ -194,6 +194,20 @@ allwinner,drive = ; allwinner,pull = ; }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; }; &sram_a { diff --git a/arch/arm/boot/dts/sun5i.dtsi b/arch/arm/boot/dts/sun5i.dtsi index 3f2971766a4e..a16edf47cbd9 100644 --- a/arch/arm/boot/dts/sun5i.dtsi +++ b/arch/arm/boot/dts/sun5i.dtsi @@ -528,6 +528,44 @@ allwinner,pull = ; }; + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + uart3_pins_a: uart3@0 { allwinner,pins = "PG9", "PG10"; allwinner,function = "uart3"; From b7f51a87cb64e6d75da647d407a631e4b903b8f9 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Fri, 12 Dec 2014 19:19:12 +0100 Subject: [PATCH 23/33] ARM: dts: sun4i: Enable NAND on cubieboard Add a node describing the NAND controller and partitions. Signed-off-by: Michal Suchanek Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts index 9afb4e018593..97c863eb5e62 100644 --- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts @@ -147,6 +147,63 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-on-flash-bbt; + + boot0@0 { + label = "boot0"; + reg = /bits/ 64 <0x0 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + boot0-rescue@200000 { + label = "boot0-rescue"; + reg = /bits/ 64 <0x200000 0x200000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + main@200000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; From 6acea074389259b6f1c656f4fd9edf81e6acb4d8 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Tue, 26 May 2015 17:03:41 +0200 Subject: [PATCH 24/33] ARM: dts: sun4i: Add NFC node to Allwinner A10 SoC Add NAND Flash controller node definition to the A10 SoC. Signed-off-by: Michal Suchanek Signed-off-by: Hans de Goede --- arch/arm/boot/dts/sun4i-a10.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 86b94d120174..e50b082accc4 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -498,6 +498,17 @@ #dma-cells = <2>; }; + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <37>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + spi0: spi@01c05000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c05000 0x1000>; From e24662922ecab8a21c77a6eec05c7dc146cfa790 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 15 Jun 2015 10:17:00 +0200 Subject: [PATCH 25/33] nand: sunxi: fix write to USER_DATA reg The USER_DATA register cannot be updated with readb on A13 SoCs, thus triggering a bug when using memcpy_toio on this register. Use writel (plus a temporary variable to old the USER_DATA value) to address that problem. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 38 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 74f2caf42015..15d3e902e408 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -904,7 +904,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, for (i = 0; i < ecc->steps; i++) { bool rndactiv = false; - u8 oob_buf[4]; + u32 user_data; if (i) chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); @@ -915,15 +915,13 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; /* Fill OOB data in */ - if (!oob_required) - memset(oob_buf, 0xff, 4); - else - memcpy(oob_buf, - chip->oob_poi + layout->oobfree[i].offset, - 4); - - - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4); + if (!oob_required) { + user_data = 0xffffffff; + } else { + memcpy(&user_data, + chip->oob_poi + layout->oobfree[i].offset, 4); + user_data = le32_to_cpu(user_data); + } if (i) { cnt = ecc->bytes + 4; @@ -942,12 +940,16 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, if (rndactiv) { /* pre randomize to generate FF patterns on the NAND */ if (!i) { + u8 oob_tmp[2]; u16 state = rnd->subseeds[rnd->page % rnd->nseeds]; + oob_tmp[0] = user_data; + oob_tmp[1] = user_data >> 8; state = sunxi_nfc_hwrnd_single_step(state, 15); - oob_buf[0] ^= state; + oob_tmp[0] ^= state; state = sunxi_nfc_hwrnd_step(rnd, state, 1); - oob_buf[1] ^= state; - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4); + oob_tmp[1] ^= state; + user_data &= ~0xffff; + user_data |= oob_tmp[0] | (oob_tmp[1] << 8); } tmp = readl(nfc->regs + NFC_REG_ECC_CTL); tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); @@ -955,6 +957,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, writel(tmp, nfc->regs + NFC_REG_ECC_CTL); } + writel(user_data, nfc->regs + NFC_REG_USER_DATA_BASE); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); @@ -1164,13 +1168,13 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, /* Fill OOB data in */ if (oob_required) { tmp = 0xffffffff; - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, - 4); } else { - memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob, - 4); + memcpy(&tmp, oob, sizeof(tmp)); + tmp = le32_to_cpu(tmp); } + writel(tmp, nfc->regs + NFC_REG_USER_DATA_BASE); + cnt = ecc->bytes + 4; if (rnd && nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 && From db3364e87817616f185c2d7544164c65bba50c62 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 23 Jun 2015 12:01:52 +0200 Subject: [PATCH 26/33] mtd: nand: hynix: add read-retry support for H27Q chips Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_hynix.c | 173 ++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c index 0d051bf5e2f0..77678b55072f 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/nand_hynix.c @@ -20,9 +20,14 @@ static u8 h27ucg8t2a_read_retry_regs[] = { 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf }; +static u8 h27q_read_retry_regs[] = { + 0x38, 0x39, 0x3a, 0x3b, +}; + struct hynix_read_retry { + int nregs; u8 *regs; - u8 values[64]; + u8 values[0]; }; struct hynix_nand { @@ -33,12 +38,12 @@ int nand_setup_read_retry_hynix(struct mtd_info *mtd, int retry_mode) { struct nand_chip *chip = mtd->priv; struct hynix_nand *hynix = chip->manuf_priv; - int offset = retry_mode * 8; + int offset = retry_mode * hynix->read_retry.nregs; int status; int i; chip->cmdfunc(mtd, 0x36, -1, -1); - for (i = 0; i < 8; i++) { + for (i = 0; i < hynix->read_retry.nregs; i++) { int column = hynix->read_retry.regs[i]; column |= column << 8; chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); @@ -53,7 +58,7 @@ int nand_setup_read_retry_hynix(struct mtd_info *mtd, int retry_mode) return 0; } -static void h27ucg8t2a_cleanup(struct mtd_info *mtd) +static void h27_cleanup(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; kfree(chip->manuf_priv); @@ -106,18 +111,20 @@ static int h27ucg8t2a_init(struct mtd_info *mtd, const uint8_t *id) chip->select_chip(mtd, -1); if (!ret) { - hynix = kzalloc(sizeof(*hynix), GFP_KERNEL); + hynix = kzalloc(sizeof(*hynix) + (8 * 8), + GFP_KERNEL); if (!hynix) { ret = -ENOMEM; goto leave; } + hynix->read_retry.nregs = 8; hynix->read_retry.regs = h27ucg8t2a_read_retry_regs; memcpy(hynix->read_retry.values, buf, 64); chip->manuf_priv = hynix; chip->setup_read_retry = nand_setup_read_retry_hynix; chip->read_retries = 8; - chip->manuf_cleanup = h27ucg8t2a_cleanup; + chip->manuf_cleanup = h27_cleanup; } leave: @@ -126,6 +133,156 @@ static int h27ucg8t2a_init(struct mtd_info *mtd, const uint8_t *id) return ret; } +static int h27q_get_best_val(const u8 *buf, int size, int min_cnt) +{ + int *val, *cnt, best = -1, max_cnt = 0; + int i, j; + + val = kzalloc(size * 2 * sizeof(int), GFP_KERNEL); + cnt = val + size; + + for (i = 0; i < size; i++) { + if (val[i] < 0) + continue; + + val[i] = buf[i]; + cnt[i] = 1; + + for (j = i + 1; j < size; j++) { + if (buf[j] == val[i]) { + val[j] = -1; + cnt[i]++; + } + } + + if (max_cnt < cnt[i]) { + max_cnt = cnt[i]; + best = i; + } + } + + if (best >= 0) + best = val[best]; + + kfree(val); + + if (best < 0 || max_cnt < min_cnt) + return -EINVAL; + + return best; +} + +#define H27Q_RR_TABLE_SIZE 784 +#define H27Q_RR_TABLE_NSETS 8 + +static int h27q_init(struct mtd_info *mtd, const uint8_t *id) +{ + struct nand_chip *chip = mtd->priv; + struct hynix_nand *hynix = NULL; + u8 * buf = NULL, tmp_buf[H27Q_RR_TABLE_NSETS]; + u8 total_rr_count, rr_reg_count; + int i, j, k; + int ret; + + buf = kzalloc(H27Q_RR_TABLE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x36, 0x38, -1); + chip->write_byte(mtd, 0x52); + chip->cmdfunc(mtd, 0x16, -1, -1); + chip->cmdfunc(mtd, 0x17, -1, -1); + chip->cmdfunc(mtd, 0x04, -1, -1); + chip->cmdfunc(mtd, 0x19, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, 0x21f); + + chip->read_buf(mtd, buf, H27Q_RR_TABLE_SIZE); + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, 0x36, 0x38, -1); + chip->write_byte(mtd, 0x0); + chip->cmdfunc(mtd, 0x16, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, -1); + chip->select_chip(mtd, -1); + + /* TODO: support multi-die chips */ + + ret = h27q_get_best_val(buf, 8, 5); + if (ret < 0) + goto err; + total_rr_count = ret; + + ret = h27q_get_best_val(buf + 8, 8, 5); + if (ret < 0) + goto err; + rr_reg_count = ret; + + if (rr_reg_count != sizeof(h27q_read_retry_regs)) { + ret = -EINVAL; + goto err; + } + + hynix = kzalloc(sizeof(*hynix) + + (total_rr_count * rr_reg_count), + GFP_KERNEL); + if (!hynix) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < total_rr_count; i++) { + for (j = 0; j < rr_reg_count; j++) { + int offset = 16 + (i * rr_reg_count) + j; + + for (k = 0; k < H27Q_RR_TABLE_NSETS; k++) { + int set_offset = k * rr_reg_count * + total_rr_count * 2; + + tmp_buf[k] = buf[offset + set_offset]; + } + + ret = h27q_get_best_val(tmp_buf, H27Q_RR_TABLE_NSETS, + 5); + if (ret >= 0) { + hynix->read_retry.values[(i * rr_reg_count) + j] = ret; + continue; + } + + offset += rr_reg_count * total_rr_count; + for (k = 0; k < H27Q_RR_TABLE_NSETS; k++) { + int set_offset = k * rr_reg_count * + total_rr_count; + + tmp_buf[k] = buf[offset + set_offset]; + } + ret = h27q_get_best_val(tmp_buf, H27Q_RR_TABLE_NSETS, + 5); + if (ret < 0) + goto err; + hynix->read_retry.values[(i * rr_reg_count) + j] = ~ret; + } + } + + kfree(buf); + + hynix->read_retry.nregs = rr_reg_count; + hynix->read_retry.regs = h27q_read_retry_regs; + chip->manuf_priv = hynix; + chip->setup_read_retry = nand_setup_read_retry_hynix; + chip->read_retries = total_rr_count; + chip->manuf_cleanup = h27_cleanup; + + return 0; + +err: + kfree(buf); + kfree(hynix); + + return ret; +} + struct hynix_nand_initializer { u8 id[6]; int (*init)(struct mtd_info *mtd, const uint8_t *id); @@ -136,6 +293,10 @@ struct hynix_nand_initializer initializers[] = { .id = {NAND_MFR_HYNIX, 0xde, 0x94, 0xda, 0x74, 0xc4}, .init = h27ucg8t2a_init, }, + { + .id = {NAND_MFR_HYNIX, 0xde, 0x14, 0xa7, 0x42, 0x4a}, + .init = h27q_init, + }, }; int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id) From 40cae0e53cfcd622fde8a5a394fbb38a7773b20a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 24 Jun 2015 22:16:54 +0200 Subject: [PATCH 27/33] mtd: adapt nand_ecclayout eccpos table size Signed-off-by: Boris Brezillon --- include/linux/mtd/mtd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index f17fa75809aa..a83e394bfbdb 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -95,7 +95,7 @@ struct mtd_oob_ops { }; #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32 -#define MTD_MAX_ECCPOS_ENTRIES_LARGE 640 +#define MTD_MAX_ECCPOS_ENTRIES_LARGE 1664 /* * Internal ECC layout control structure. For historical reasons, there is a * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained From c70148a86e298973093cd20cd7392ce31358981b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 24 Jun 2015 22:17:27 +0200 Subject: [PATCH 28/33] mtd: nand: add full id for H27Q chips Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_ids.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index c1730ae6b938..5583ff9c3405 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -48,6 +48,10 @@ struct nand_flash_dev nand_flash_ids[] = { { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K), 4 }, + {"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit", + { .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1664, NAND_ECC_INFO(40, SZ_1K), + 2 }, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), From 9275fd555d58a624b0bdc56bb38343e598c06ccf Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 24 Jun 2015 22:19:06 +0200 Subject: [PATCH 29/33] ARM: sunxi/dt: add nand defs to sun5i-r8-chip dts Signed-off-by: Boris Brezillon --- arch/arm/boot/dts/sun5i-r8-chip.dts | 133 ++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/arch/arm/boot/dts/sun5i-r8-chip.dts b/arch/arm/boot/dts/sun5i-r8-chip.dts index 2dcbbd74a469..f6a409d15c6f 100644 --- a/arch/arm/boot/dts/sun5i-r8-chip.dts +++ b/arch/arm/boot/dts/sun5i-r8-chip.dts @@ -239,6 +239,139 @@ status = "okay"; }; +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-on-flash-bbt; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + + spl@0 { + label = "SPL"; + reg = /bits/ 64 <0x0 0x400000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <40>; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + spl-backup@400000 { + label = "SPL.backup"; + reg = /bits/ 64 <0x400000 0x400000>; + nand-ecc-mode = "hw_syndrome"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <40>; + nand-randomizer-seeds = /bits/ 16 <0x4a80>; + }; + + u-boot@800000 { + label = "U-Boot"; + reg = /bits/ 64 <0x800000 0x400000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + + env@c00000 { + label = "env"; + reg = /bits/ 64 <0xc00000 0x400000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + + rootfs@1000000 { + label = "rootfs"; + reg = /bits/ 64 <0x1000000 0x1ff000000>; + nand-ecc-mode = "hw"; + nand-rnd-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <56>; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + }; + }; +}; + &ohci0 { status = "okay"; }; From 87861635edff949c05027b641c974b02b71b2a56 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 8 Jul 2015 11:46:36 +0200 Subject: [PATCH 30/33] ubifs: Kill unneeded locking in ubifs_init_security Fixes the following lockdep splat: [ 1.244527] ============================================= [ 1.245193] [ INFO: possible recursive locking detected ] [ 1.245193] 4.2.0-rc1+ #37 Not tainted [ 1.245193] --------------------------------------------- [ 1.245193] cp/742 is trying to acquire lock: [ 1.245193] (&sb->s_type->i_mutex_key#9){+.+.+.}, at: [] ubifs_init_security+0x29/0xb0 [ 1.245193] [ 1.245193] but task is already holding lock: [ 1.245193] (&sb->s_type->i_mutex_key#9){+.+.+.}, at: [] path_openat+0x3af/0x1280 [ 1.245193] [ 1.245193] other info that might help us debug this: [ 1.245193] Possible unsafe locking scenario: [ 1.245193] [ 1.245193] CPU0 [ 1.245193] ---- [ 1.245193] lock(&sb->s_type->i_mutex_key#9); [ 1.245193] lock(&sb->s_type->i_mutex_key#9); [ 1.245193] [ 1.245193] *** DEADLOCK *** [ 1.245193] [ 1.245193] May be due to missing lock nesting notation [ 1.245193] [ 1.245193] 2 locks held by cp/742: [ 1.245193] #0: (sb_writers#5){.+.+.+}, at: [] mnt_want_write+0x1f/0x50 [ 1.245193] #1: (&sb->s_type->i_mutex_key#9){+.+.+.}, at: [] path_openat+0x3af/0x1280 [ 1.245193] [ 1.245193] stack backtrace: [ 1.245193] CPU: 2 PID: 742 Comm: cp Not tainted 4.2.0-rc1+ #37 [ 1.245193] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140816_022509-build35 04/01/2014 [ 1.245193] ffffffff8252d530 ffff88007b023a38 ffffffff814f6f49 ffffffff810b56c5 [ 1.245193] ffff88007c30cc80 ffff88007b023af8 ffffffff810a150d ffff88007b023a68 [ 1.245193] 000000008101302a ffff880000000000 00000008f447e23f ffffffff8252d500 [ 1.245193] Call Trace: [ 1.245193] [] dump_stack+0x4c/0x65 [ 1.245193] [] ? console_unlock+0x1c5/0x510 [ 1.245193] [] __lock_acquire+0x1a6d/0x1ea0 [ 1.245193] [] ? __lock_is_held+0x58/0x80 [ 1.245193] [] lock_acquire+0xd3/0x270 [ 1.245193] [] ? ubifs_init_security+0x29/0xb0 [ 1.245193] [] mutex_lock_nested+0x6b/0x3a0 [ 1.245193] [] ? ubifs_init_security+0x29/0xb0 [ 1.245193] [] ? ubifs_init_security+0x29/0xb0 [ 1.245193] [] ubifs_init_security+0x29/0xb0 [ 1.245193] [] ubifs_create+0xa6/0x1f0 [ 1.245193] [] ? path_openat+0x3af/0x1280 [ 1.245193] [] vfs_create+0x95/0xc0 [ 1.245193] [] path_openat+0x7cc/0x1280 [ 1.245193] [] ? __lock_acquire+0x543/0x1ea0 [ 1.245193] [] ? sched_clock_cpu+0x90/0xc0 [ 1.245193] [] ? calc_global_load_tick+0x60/0x90 [ 1.245193] [] ? sched_clock_cpu+0x90/0xc0 [ 1.245193] [] ? __alloc_fd+0xaf/0x180 [ 1.245193] [] do_filp_open+0x75/0xd0 [ 1.245193] [] ? _raw_spin_unlock+0x26/0x40 [ 1.245193] [] ? __alloc_fd+0xaf/0x180 [ 1.245193] [] do_sys_open+0x129/0x200 [ 1.245193] [] SyS_open+0x19/0x20 [ 1.245193] [] entry_SYSCALL_64_fastpath+0x12/0x6f While the lockdep splat is a false positive, becuase path_openat holds i_mutex of the parent directory and ubifs_init_security() tries to acquire i_mutex of a new inode, it reveals that taking i_mutex in ubifs_init_security() is in vain because it is only being called in the inode allocation path and therefore nobody else can see the inode yet. Reported-by: Boris Brezillon Signed-off-by: Richard Weinberger --- fs/ubifs/xattr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 96f3448b6eb4..fd65b3f1923c 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -652,11 +652,8 @@ int ubifs_init_security(struct inode *dentry, struct inode *inode, { int err; - mutex_lock(&inode->i_mutex); err = security_inode_init_security(inode, dentry, qstr, &init_xattrs, 0); - mutex_unlock(&inode->i_mutex); - if (err) { struct ubifs_info *c = dentry->i_sb->s_fs_info; ubifs_err(c, "cannot initialize security for inode %lu, error %d", From 26b2cf31145dd2a8fdb8d328e4ae8955b51ff94b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 28 Jul 2015 12:07:02 +0200 Subject: [PATCH 31/33] mtd: nand: add the infrastructure to support SLC mode on MLC/TLC NANDs Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 129 +++++++++++++++++++++++++++------- drivers/mtd/nand/ofnandpart.c | 5 +- include/linux/mtd/nand.h | 5 ++ 3 files changed, 111 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d8f5ec82cb6b..c1b86cf30773 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -97,6 +97,22 @@ static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); +static void nand_set_slc_mode(struct mtd_info *mtd, int chipnr, bool enable) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->slc_mode == enable) + return; + + chip->slc_mode = enable; + chip->pagebuf = -1; + if (chip->set_slc_mode) { + chip->select_chip(mtd, chipnr); + chip->set_slc_mode(mtd); + chip->select_chip(mtd, -1); + } +} + /* * For devices which display every fart in the system on a separate LED. Is * compiled away when LED support is disabled. @@ -107,16 +123,20 @@ static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct nand_chip *chip = mtd->priv; + int shift = chip->phys_erase_shift; int ret = 0; + if (chip->slc_mode) + shift--; + /* Start address must align on block boundary */ - if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { + if (ofs & ((1ULL << shift) - 1)) { pr_debug("%s: unaligned address\n", __func__); ret = -EINVAL; } /* Length must align on block boundary */ - if (len & ((1ULL << chip->phys_erase_shift) - 1)) { + if (len & ((1ULL << shift) - 1)) { pr_debug("%s: length not block aligned\n", __func__); ret = -EINVAL; } @@ -517,6 +537,9 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); + if (chip->slc_mode) + ofs *= 2; + /* Return info from the table */ return nand_isbad_bbt(mtd, ofs, allowbbt); } @@ -724,6 +747,12 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, column >> 8, ctrl); } if (page_addr != -1) { + if (chip->slc_mode && chip->fix_page && + (command == NAND_CMD_ERASE1 || + command == NAND_CMD_READ0 || + command == NAND_CMD_SEQIN)) + chip->fix_page(mtd, &page_addr); + chip->cmd_ctrl(mtd, page_addr, ctrl); chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE | NAND_ALE); @@ -1229,14 +1258,19 @@ EXPORT_SYMBOL(nand_page_is_empty); int nand_page_get_status(struct mtd_info *mtd, int page) { struct nand_chip *chip = mtd->priv; - u8 shift = (page % 4) * 2; - uint64_t offset = page / 4; - int ret = NAND_PAGE_STATUS_UNKNOWN; + u8 shift; + uint64_t offset; - if (chip->pst) - ret = (chip->pst[offset] >> shift) & 0x3; + if (!chip->pst) + return NAND_PAGE_STATUS_UNKNOWN; - return ret; + if (chip->slc_mode) + page *= 2; + + shift = (page % 4) * 2; + offset = page / 4; + + return (chip->pst[offset] >> shift) & 0x3; } EXPORT_SYMBOL(nand_page_get_status); @@ -1256,6 +1290,9 @@ void nand_page_set_status(struct mtd_info *mtd, int page, if (!chip->pst) return; + if (chip->slc_mode) + page *= 2; + shift = (page % 4) * 2; offset = page / 4; chip->pst[offset] &= ~(0x3 << shift); @@ -1279,7 +1316,7 @@ int nand_pst_create(struct mtd_info *mtd) return 0; chip->pst = kzalloc(mtd->size >> - (chip->page_shift + mtd->subpage_sft + 2), + (chip->page_shift - mtd->subpage_sft + 2), GFP_KERNEL); if (!chip->pst) return -ENOMEM; @@ -2006,14 +2043,17 @@ static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len, struct nand_chip *chip = mtd->priv; struct nand_part *part = to_nand_part(mtd); struct mtd_oob_ops ops; + int chipnr; int ret; - from += part->offset; + from += part->slc_mode ? part->offset / 2 : part->offset; nand_get_device(part->master, FL_READING); if (part->ecc) chip->cur_ecc = part->ecc; if (part->rnd) chip->cur_rnd = part->rnd; + chipnr = from >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; @@ -2333,6 +2373,7 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, struct nand_chip *chip = mtd->priv; struct nand_part *part = to_nand_part(mtd); int ret = -ENOTSUPP; + int chipnr; ops->retlen = 0; @@ -2343,12 +2384,14 @@ static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } - from += part->offset; + from += part->slc_mode ? part->offset / 2 : part->offset; nand_get_device(part->master, FL_READING); if (part->ecc) chip->cur_ecc = part->ecc; if (part->rnd) chip->cur_rnd = part->rnd; + chipnr = from >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -2868,7 +2911,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, subpage < (column + writelen) / chip->subpagesize; subpage++) nand_page_set_status(mtd, - (page << mtd->subpage_sft) + + (realpage << mtd->subpage_sft) + subpage, NAND_PAGE_FILLED); @@ -2950,9 +2993,9 @@ static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, struct nand_chip *chip = mtd->priv; struct nand_part *part = to_nand_part(mtd); struct mtd_oob_ops ops; - int ret; + int ret, chipnr; - to += part->offset; + to += part->slc_mode ? part->offset / 2 : part->offset; /* Wait for the device to get ready */ panic_nand_wait(part->master, chip, 400); @@ -2962,6 +3005,8 @@ static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, chip->cur_ecc = part->ecc; if (part->rnd) chip->cur_rnd = part->rnd; + chipnr = to >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); ops.len = len; ops.datbuf = (uint8_t *)buf; @@ -3019,14 +3064,16 @@ static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, struct nand_chip *chip = mtd->priv; struct nand_part *part = to_nand_part(mtd); struct mtd_oob_ops ops; - int ret; + int ret, chipnr; - to += part->offset; + to += part->slc_mode ? part->offset / 2 : part->offset; nand_get_device(part->master, FL_WRITING); if (part->ecc) chip->cur_ecc = part->ecc; if (part->rnd) chip->cur_rnd = part->rnd; + chipnr = to >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; @@ -3180,7 +3227,7 @@ static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, { struct nand_chip *chip = mtd->priv; struct nand_part *part = to_nand_part(mtd); - int ret = -ENOTSUPP; + int ret = -ENOTSUPP, chipnr; ops->retlen = 0; @@ -3191,12 +3238,14 @@ static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; } - to += part->offset; + to += part->slc_mode ? part->offset / 2 : part->offset; nand_get_device(part->master, FL_WRITING); if (part->ecc) chip->cur_ecc = part->ecc; if (part->rnd) chip->cur_rnd = part->rnd; + chipnr = to >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -3258,15 +3307,18 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) */ static int nand_part_erase(struct mtd_info *mtd, struct erase_info *instr) { + struct nand_chip *chip = mtd->priv; struct nand_part *part = to_nand_part(mtd); - int ret; + int ret, chipnr; - instr->addr += part->offset; + instr->addr += part->slc_mode ? part->offset / 2 : part->offset; + chipnr = instr->addr >> (chip->chip_shift - (part->slc_mode ? 1 : 0)); + nand_set_slc_mode(mtd, chipnr, part->slc_mode); ret = nand_erase_nand(part->master, instr, 0); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; + instr->fail_addr -= part->slc_mode ? part->offset / 2 : part->offset; + instr->addr -= part->slc_mode ? part->offset / 2 : part->offset; } return ret; @@ -3285,6 +3337,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, { int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; + uint32_t erasesize = mtd->erasesize; loff_t len; int i; @@ -3305,6 +3358,11 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Calculate pages in each block */ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + if (chip->slc_mode) { + pages_per_block /= 2; + erasesize /= 2; + } + /* Select the NAND device */ chip->select_chip(mtd, chipnr); @@ -3372,7 +3430,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, } /* Increment page address and decrement length */ - len -= (1ULL << chip->phys_erase_shift); + len -= erasesize; page += pages_per_block; /* Check, if we cross a chip boundary */ @@ -3434,8 +3492,12 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) static int nand_part_block_isbad(struct mtd_info *mtd, loff_t offs) { struct nand_part *part = to_nand_part(mtd); + loff_t part_ofs = part->offset; + + if (part->slc_mode) + part_ofs /= 2; - return nand_block_checkbad(part->master, part->offset + offs, 1, 0); + return nand_block_checkbad(part->master, part_ofs + offs, 1, 0); } /** @@ -3467,9 +3529,13 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_part_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_part *part = to_nand_part(mtd); + loff_t part_ofs = part->offset; int ret; - ofs += part->offset; + if (part->slc_mode) + part_ofs /= 2; + + ofs += part_ofs; ret = nand_block_isbad(part->master, ofs); if (ret) { /* If it was bad already, return success and do nothing */ @@ -4778,6 +4844,9 @@ int nand_add_partition(struct mtd_info *master, struct nand_part *part) bool inserted = false; int ret; + if (!chip->set_slc_mode) + part->slc_mode = false; + /* set up the MTD object for this partition */ mtd->type = master->type; mtd->flags = master->flags & ~mtd->flags; @@ -4787,6 +4856,12 @@ int nand_add_partition(struct mtd_info *master, struct nand_part *part) mtd->oobavail = master->oobavail; mtd->subpage_sft = master->subpage_sft; mtd->erasesize = master->erasesize; + mtd->size = part->size; + + if (part->slc_mode) { + mtd->size /= 2; + mtd->erasesize /= 2; + } mtd->priv = chip; mtd->owner = master->owner; @@ -4834,9 +4909,9 @@ int nand_add_partition(struct mtd_info *master, struct nand_part *part) mutex_lock(&chip->part_lock); list_for_each_entry(pos, &chip->partitions, node) { - if (part->offset >= pos->offset + pos->mtd.size) { + if (part->offset >= pos->offset + pos->size) { continue; - } else if (part->offset + mtd->size > pos->offset) { + } else if (part->offset + part->size > pos->offset) { ret = -EINVAL; goto out; } diff --git a/drivers/mtd/nand/ofnandpart.c b/drivers/mtd/nand/ofnandpart.c index 293daee6d4af..c7bc34d07558 100644 --- a/drivers/mtd/nand/ofnandpart.c +++ b/drivers/mtd/nand/ofnandpart.c @@ -76,11 +76,14 @@ int ofnandpart_parse(struct mtd_info *master, continue; part->offset = offset; + part->size = size; part->master = master; part->mtd.name = partname; - part->mtd.size = size; part->mtd.flags = mask_flags; + if (of_property_read_bool(pp, "slc-mode")) + part->slc_mode = true; + if (nand_add_partition(master, part)) { if (part->release) part->release(part); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f6a506d81a8d..f185da5d6ca3 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -754,9 +754,12 @@ struct nand_chip { int feature_addr, uint8_t *subfeature_para); int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); void (*manuf_cleanup)(struct mtd_info *mtd); + void (*set_slc_mode)(struct mtd_info *mtd); + void (*fix_page)(struct mtd_info *mtd, int *page); void *manuf_priv; + bool slc_mode; int chip_delay; unsigned int options; unsigned int bbt_options; @@ -834,9 +837,11 @@ struct nand_part { struct mtd_info mtd; struct mtd_info *master; uint64_t offset; + uint64_t size; struct nand_ecc_ctrl *ecc; struct nand_rnd_ctrl *rnd; void (*release)(struct nand_part *part); + bool slc_mode; }; static inline struct nand_part *to_nand_part(struct mtd_info *mtd) From e4207da1a61b03a6bf1a6575bd010b90a3676769 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 28 Jul 2015 12:10:39 +0200 Subject: [PATCH 32/33] mtd: nand: hynix: add SLC mode support Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_hynix.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c index 77678b55072f..1c8a4b4b7714 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/nand_hynix.c @@ -175,6 +175,29 @@ static int h27q_get_best_val(const u8 *buf, int size, int min_cnt) #define H27Q_RR_TABLE_SIZE 784 #define H27Q_RR_TABLE_NSETS 8 +static void h27q_set_slc_mode(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + u8 cmd = chip->slc_mode ? 0xbf : 0xbe; + + chip->cmdfunc(mtd, cmd, -1, -1); +} + +static void h27q_fix_page(struct mtd_info *mtd, int *page) +{ + struct nand_chip *chip = mtd->priv; + int blkmsk; + int tmp; + + if (!chip->slc_mode) + return; + + blkmsk = ((1 << (chip->phys_erase_shift - chip->page_shift)) - 1); + tmp = *page & (blkmsk >> 1); + tmp |= (*page << 1) & ~blkmsk; + *page = tmp; +} + static int h27q_init(struct mtd_info *mtd, const uint8_t *id) { struct nand_chip *chip = mtd->priv; @@ -273,6 +296,8 @@ static int h27q_init(struct mtd_info *mtd, const uint8_t *id) chip->setup_read_retry = nand_setup_read_retry_hynix; chip->read_retries = total_rr_count; chip->manuf_cleanup = h27_cleanup; + chip->set_slc_mode = h27q_set_slc_mode; + chip->fix_page = h27q_fix_page; return 0; From f936ac4494426dab8a22b04345fdd4f9f2089c4d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 28 Jul 2015 12:09:01 +0200 Subject: [PATCH 33/33] ARM: sunxi/dt: chip: enable SLC mode on the rootfs partition Signed-off-by: Boris Brezillon --- arch/arm/boot/dts/sun5i-r8-chip.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/sun5i-r8-chip.dts b/arch/arm/boot/dts/sun5i-r8-chip.dts index f6a409d15c6f..1bbd2361de89 100644 --- a/arch/arm/boot/dts/sun5i-r8-chip.dts +++ b/arch/arm/boot/dts/sun5i-r8-chip.dts @@ -347,6 +347,7 @@ rootfs@1000000 { label = "rootfs"; reg = /bits/ 64 <0x1000000 0x1ff000000>; + slc-mode; nand-ecc-mode = "hw"; nand-rnd-mode = "hw"; nand-ecc-step-size = <1024>;