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 | |
| 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>
| -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 | ||||
| -rw-r--r-- | include/linux/mtd/nand.h | 8 |
4 files changed, 96 insertions, 19 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 | |||
