diff options
author | Gupta, Pekon <pekon@ti.com> | 2013-03-15 08:25:53 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2013-04-05 08:22:59 -0400 |
commit | 837a6ba4f3b6d23026674e6af6b6849a4634fff9 (patch) | |
tree | c13590363e4e146627a0074b2c7efa60386c5a18 /drivers/mtd/nand | |
parent | 4ff6772b5bb17ef40e64abf2c0d5f6aefd104b06 (diff) |
mtd: nand: subpage write support for hardware based ECC schemes
This patch adds support for subpage (partial-page) writes when using
hardware based ECC schemes.
Advantages:
(1) reduces storage overhead when using file-systems like UBIFS, which
store LEB header at page-size granularity.
(2) allows independent subpage writes, thereby increasing NAND storage
efficiency for non-page aligned data.
+ updated cafe_nand and lpc32xx_mlc NAND drivers for change in
chip->write_page interface.
Signed-off-by: Gupta, Pekon <pekon@ti.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/cafe_nand.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/lpc32xx_mlc.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 99 |
3 files changed, 90 insertions, 17 deletions
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index a01ccb970102..c34985a55101 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c | |||
@@ -530,8 +530,8 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, | |||
530 | } | 530 | } |
531 | 531 | ||
532 | static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, | 532 | static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
533 | const uint8_t *buf, int oob_required, int page, | 533 | uint32_t offset, int data_len, const uint8_t *buf, |
534 | int cached, int raw) | 534 | int oob_required, int page, int cached, int raw) |
535 | { | 535 | { |
536 | int status; | 536 | int status; |
537 | 537 | ||
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 0ca22ae9135c..a94facb46e5c 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c | |||
@@ -540,8 +540,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, | |||
540 | } | 540 | } |
541 | 541 | ||
542 | static int lpc32xx_write_page(struct mtd_info *mtd, struct nand_chip *chip, | 542 | static int lpc32xx_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
543 | const uint8_t *buf, int oob_required, int page, | 543 | uint32_t offset, int data_len, const uint8_t *buf, |
544 | int cached, int raw) | 544 | int oob_required, int page, int cached, int raw) |
545 | { | 545 | { |
546 | int res; | 546 | int res; |
547 | 547 | ||
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ae9790b659b1..dfcd0a565c5b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -1110,7 +1110,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
1110 | } | 1110 | } |
1111 | 1111 | ||
1112 | /** | 1112 | /** |
1113 | * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function | 1113 | * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function |
1114 | * @mtd: mtd info structure | 1114 | * @mtd: mtd info structure |
1115 | * @chip: nand chip info structure | 1115 | * @chip: nand chip info structure |
1116 | * @data_offs: offset of requested data within the page | 1116 | * @data_offs: offset of requested data within the page |
@@ -1978,6 +1978,67 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
1978 | return 0; | 1978 | return 0; |
1979 | } | 1979 | } |
1980 | 1980 | ||
1981 | |||
1982 | /** | ||
1983 | * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write | ||
1984 | * @mtd: mtd info structure | ||
1985 | * @chip: nand chip info structure | ||
1986 | * @column: column address of subpage within the page | ||
1987 | * @data_len: data length | ||
1988 | * @oob_required: must write chip->oob_poi to OOB | ||
1989 | */ | ||
1990 | static int nand_write_subpage_hwecc(struct mtd_info *mtd, | ||
1991 | struct nand_chip *chip, uint32_t offset, | ||
1992 | uint32_t data_len, const uint8_t *data_buf, | ||
1993 | int oob_required) | ||
1994 | { | ||
1995 | uint8_t *oob_buf = chip->oob_poi; | ||
1996 | uint8_t *ecc_calc = chip->buffers->ecccalc; | ||
1997 | int ecc_size = chip->ecc.size; | ||
1998 | int ecc_bytes = chip->ecc.bytes; | ||
1999 | int ecc_steps = chip->ecc.steps; | ||
2000 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
2001 | uint32_t start_step = offset / ecc_size; | ||
2002 | uint32_t end_step = (offset + data_len - 1) / ecc_size; | ||
2003 | int oob_bytes = mtd->oobsize / ecc_steps; | ||
2004 | int step, i; | ||
2005 | |||
2006 | for (step = 0; step < ecc_steps; step++) { | ||
2007 | /* configure controller for WRITE access */ | ||
2008 | chip->ecc.hwctl(mtd, NAND_ECC_WRITE); | ||
2009 | |||
2010 | /* write data (untouched subpages already masked by 0xFF) */ | ||
2011 | chip->write_buf(mtd, data_buf, ecc_size); | ||
2012 | |||
2013 | /* mask ECC of un-touched subpages by padding 0xFF */ | ||
2014 | if ((step < start_step) || (step > end_step)) | ||
2015 | memset(ecc_calc, 0xff, ecc_bytes); | ||
2016 | else | ||
2017 | chip->ecc.calculate(mtd, data_buf, ecc_calc); | ||
2018 | |||
2019 | /* mask OOB of un-touched subpages by padding 0xFF */ | ||
2020 | /* if oob_required, preserve OOB metadata of written subpage */ | ||
2021 | if (!oob_required || (step < start_step) || (step > end_step)) | ||
2022 | memset(oob_buf, 0xff, oob_bytes); | ||
2023 | |||
2024 | data_buf += ecc_size; | ||
2025 | ecc_calc += ecc_bytes; | ||
2026 | oob_buf += oob_bytes; | ||
2027 | } | ||
2028 | |||
2029 | /* copy calculated ECC for whole page to chip->buffer->oob */ | ||
2030 | /* this include masked-value(0xFF) for unwritten subpages */ | ||
2031 | ecc_calc = chip->buffers->ecccalc; | ||
2032 | for (i = 0; i < chip->ecc.total; i++) | ||
2033 | chip->oob_poi[eccpos[i]] = ecc_calc[i]; | ||
2034 | |||
2035 | /* write OOB buffer to NAND device */ | ||
2036 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
2037 | |||
2038 | return 0; | ||
2039 | } | ||
2040 | |||
2041 | |||
1981 | /** | 2042 | /** |
1982 | * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write | 2043 | * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write |
1983 | * @mtd: mtd info structure | 2044 | * @mtd: mtd info structure |
@@ -2030,6 +2091,8 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, | |||
2030 | * nand_write_page - [REPLACEABLE] write one page | 2091 | * nand_write_page - [REPLACEABLE] write one page |
2031 | * @mtd: MTD device structure | 2092 | * @mtd: MTD device structure |
2032 | * @chip: NAND chip descriptor | 2093 | * @chip: NAND chip descriptor |
2094 | * @offset: address offset within the page | ||
2095 | * @data_len: length of actual data to be written | ||
2033 | * @buf: the data to write | 2096 | * @buf: the data to write |
2034 | * @oob_required: must write chip->oob_poi to OOB | 2097 | * @oob_required: must write chip->oob_poi to OOB |
2035 | * @page: page number to write | 2098 | * @page: page number to write |
@@ -2037,15 +2100,25 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, | |||
2037 | * @raw: use _raw version of write_page | 2100 | * @raw: use _raw version of write_page |
2038 | */ | 2101 | */ |
2039 | static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, | 2102 | static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
2040 | const uint8_t *buf, int oob_required, int page, | 2103 | uint32_t offset, int data_len, const uint8_t *buf, |
2041 | int cached, int raw) | 2104 | int oob_required, int page, int cached, int raw) |
2042 | { | 2105 | { |
2043 | int status; | 2106 | int status, subpage; |
2107 | |||
2108 | if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && | ||
2109 | chip->ecc.write_subpage) | ||
2110 | subpage = offset || (data_len < mtd->writesize); | ||
2111 | else | ||
2112 | subpage = 0; | ||
2044 | 2113 | ||
2045 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); | 2114 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); |
2046 | 2115 | ||
2047 | if (unlikely(raw)) | 2116 | if (unlikely(raw)) |
2048 | status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); | 2117 | status = chip->ecc.write_page_raw(mtd, chip, buf, |
2118 | oob_required); | ||
2119 | else if (subpage) | ||
2120 | status = chip->ecc.write_subpage(mtd, chip, offset, data_len, | ||
2121 | buf, oob_required); | ||
2049 | else | 2122 | else |
2050 | status = chip->ecc.write_page(mtd, chip, buf, oob_required); | 2123 | status = chip->ecc.write_page(mtd, chip, buf, oob_required); |
2051 | 2124 | ||
@@ -2159,7 +2232,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
2159 | 2232 | ||
2160 | uint8_t *oob = ops->oobbuf; | 2233 | uint8_t *oob = ops->oobbuf; |
2161 | uint8_t *buf = ops->datbuf; | 2234 | uint8_t *buf = ops->datbuf; |
2162 | int ret, subpage; | 2235 | int ret; |
2163 | int oob_required = oob ? 1 : 0; | 2236 | int oob_required = oob ? 1 : 0; |
2164 | 2237 | ||
2165 | ops->retlen = 0; | 2238 | ops->retlen = 0; |
@@ -2174,10 +2247,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
2174 | } | 2247 | } |
2175 | 2248 | ||
2176 | column = to & (mtd->writesize - 1); | 2249 | column = to & (mtd->writesize - 1); |
2177 | subpage = column || (writelen & (mtd->writesize - 1)); | ||
2178 | |||
2179 | if (subpage && oob) | ||
2180 | return -EINVAL; | ||
2181 | 2250 | ||
2182 | chipnr = (int)(to >> chip->chip_shift); | 2251 | chipnr = (int)(to >> chip->chip_shift); |
2183 | chip->select_chip(mtd, chipnr); | 2252 | chip->select_chip(mtd, chipnr); |
@@ -2226,9 +2295,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
2226 | /* We still need to erase leftover OOB data */ | 2295 | /* We still need to erase leftover OOB data */ |
2227 | memset(chip->oob_poi, 0xff, mtd->oobsize); | 2296 | memset(chip->oob_poi, 0xff, mtd->oobsize); |
2228 | } | 2297 | } |
2229 | 2298 | ret = chip->write_page(mtd, chip, column, bytes, wbuf, | |
2230 | ret = chip->write_page(mtd, chip, wbuf, oob_required, page, | 2299 | oob_required, page, cached, |
2231 | cached, (ops->mode == MTD_OPS_RAW)); | 2300 | (ops->mode == MTD_OPS_RAW)); |
2232 | if (ret) | 2301 | if (ret) |
2233 | break; | 2302 | break; |
2234 | 2303 | ||
@@ -3414,6 +3483,10 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
3414 | chip->ecc.read_oob = nand_read_oob_std; | 3483 | chip->ecc.read_oob = nand_read_oob_std; |
3415 | if (!chip->ecc.write_oob) | 3484 | if (!chip->ecc.write_oob) |
3416 | chip->ecc.write_oob = nand_write_oob_std; | 3485 | chip->ecc.write_oob = nand_write_oob_std; |
3486 | if (!chip->ecc.read_subpage) | ||
3487 | chip->ecc.read_subpage = nand_read_subpage; | ||
3488 | if (!chip->ecc.write_subpage) | ||
3489 | chip->ecc.write_subpage = nand_write_subpage_hwecc; | ||
3417 | 3490 | ||
3418 | case NAND_ECC_HW_SYNDROME: | 3491 | case NAND_ECC_HW_SYNDROME: |
3419 | if ((!chip->ecc.calculate || !chip->ecc.correct || | 3492 | if ((!chip->ecc.calculate || !chip->ecc.correct || |