aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Korolev <akorolev@infradead.org>2008-05-15 12:23:18 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2008-07-25 10:49:50 -0400
commit3d45955962496879dead8d4dd70bb9a23b07154b (patch)
treeaf34201ba3bfc195a22ceac43a1c22f2f7c2fbe8
parentff877ea80efa2015b6263766f78ee42c2a1b32f9 (diff)
[MTD] [NAND] subpage read feature as a way to increase performance.
This patch enables NAND subpage read functionality. If upper layer drivers are requesting to read non page aligned data NAND subpage-read functionality reads the only whose ECC regions which include requested data when original code reads whole page. This significantly improves performance in many cases. Here are some digits : UBI volume mount time No subpage reads: 5.75 seconds Subpage read patch: 2.42 seconds Open/stat time for files on JFFS2 volume: No subpage read 0m 5.36s Subpage read 0m 2.88s Signed-off-by Alexey Korolev <akorolev@infradead.org> Acked-by: Artem Bityutskiy <dedekind@infradead.org> Acked-by: Jörn Engel <joern@logfs.org> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/nand/nand_base.c87
-rw-r--r--include/linux/mtd/nand.h5
2 files changed, 91 insertions, 1 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ba1bdf787323..d1129bae6c27 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -798,6 +798,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
798} 798}
799 799
800/** 800/**
801 * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
802 * @mtd: mtd info structure
803 * @chip: nand chip info structure
804 * @dataofs offset of requested data within the page
805 * @readlen data length
806 * @buf: buffer to store read data
807 */
808static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
809{
810 int start_step, end_step, num_steps;
811 uint32_t *eccpos = chip->ecc.layout->eccpos;
812 uint8_t *p;
813 int data_col_addr, i, gaps = 0;
814 int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
815 int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
816
817 /* Column address wihin the page aligned to ECC size (256bytes). */
818 start_step = data_offs / chip->ecc.size;
819 end_step = (data_offs + readlen - 1) / chip->ecc.size;
820 num_steps = end_step - start_step + 1;
821
822 /* Data size aligned to ECC ecc.size*/
823 datafrag_len = num_steps * chip->ecc.size;
824 eccfrag_len = num_steps * chip->ecc.bytes;
825
826 data_col_addr = start_step * chip->ecc.size;
827 /* If we read not a page aligned data */
828 if (data_col_addr != 0)
829 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
830
831 p = bufpoi + data_col_addr;
832 chip->read_buf(mtd, p, datafrag_len);
833
834 /* Calculate ECC */
835 for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
836 chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
837
838 /* The performance is faster if to position offsets
839 according to ecc.pos. Let make sure here that
840 there are no gaps in ecc positions */
841 for (i = 0; i < eccfrag_len - 1; i++) {
842 if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
843 eccpos[i + start_step * chip->ecc.bytes + 1]) {
844 gaps = 1;
845 break;
846 }
847 }
848 if (gaps) {
849 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
850 chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
851 } else {
852 /* send the command to read the particular ecc bytes */
853 /* take care about buswidth alignment in read_buf */
854 aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
855 aligned_len = eccfrag_len;
856 if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
857 aligned_len++;
858 if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
859 aligned_len++;
860
861 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
862 chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
863 }
864
865 for (i = 0; i < eccfrag_len; i++)
866 chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
867
868 p = bufpoi + data_col_addr;
869 for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
870 int stat;
871
872 stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
873 if (stat == -1)
874 mtd->ecc_stats.failed++;
875 else
876 mtd->ecc_stats.corrected += stat;
877 }
878 return 0;
879}
880
881/**
801 * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function 882 * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
802 * @mtd: mtd info structure 883 * @mtd: mtd info structure
803 * @chip: nand chip info structure 884 * @chip: nand chip info structure
@@ -994,6 +1075,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
994 /* Now read the page into the buffer */ 1075 /* Now read the page into the buffer */
995 if (unlikely(ops->mode == MTD_OOB_RAW)) 1076 if (unlikely(ops->mode == MTD_OOB_RAW))
996 ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); 1077 ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
1078 else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
1079 ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
997 else 1080 else
998 ret = chip->ecc.read_page(mtd, chip, bufpoi); 1081 ret = chip->ecc.read_page(mtd, chip, bufpoi);
999 if (ret < 0) 1082 if (ret < 0)
@@ -1001,7 +1084,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
1001 1084
1002 /* Transfer not aligned data */ 1085 /* Transfer not aligned data */
1003 if (!aligned) { 1086 if (!aligned) {
1004 chip->pagebuf = realpage; 1087 if (!NAND_SUBPAGE_READ(chip) && !oob)
1088 chip->pagebuf = realpage;
1005 memcpy(buf, chip->buffers->databuf + col, bytes); 1089 memcpy(buf, chip->buffers->databuf + col, bytes);
1006 } 1090 }
1007 1091
@@ -2521,6 +2605,7 @@ int nand_scan_tail(struct mtd_info *mtd)
2521 chip->ecc.calculate = nand_calculate_ecc; 2605 chip->ecc.calculate = nand_calculate_ecc;
2522 chip->ecc.correct = nand_correct_data; 2606 chip->ecc.correct = nand_correct_data;
2523 chip->ecc.read_page = nand_read_page_swecc; 2607 chip->ecc.read_page = nand_read_page_swecc;
2608 chip->ecc.read_subpage = nand_read_subpage;
2524 chip->ecc.write_page = nand_write_page_swecc; 2609 chip->ecc.write_page = nand_write_page_swecc;
2525 chip->ecc.read_oob = nand_read_oob_std; 2610 chip->ecc.read_oob = nand_read_oob_std;
2526 chip->ecc.write_oob = nand_write_oob_std; 2611 chip->ecc.write_oob = nand_write_oob_std;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 863e22a0ddb5..83f678702dff 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -177,6 +177,7 @@ typedef enum {
177#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) 177#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
178#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) 178#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
179#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) 179#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
180#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT))
180 181
181/* Mask to zero out the chip options, which come from the id table */ 182/* Mask to zero out the chip options, which come from the id table */
182#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) 183#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
@@ -274,6 +275,10 @@ struct nand_ecc_ctrl {
274 int (*read_page)(struct mtd_info *mtd, 275 int (*read_page)(struct mtd_info *mtd,
275 struct nand_chip *chip, 276 struct nand_chip *chip,
276 uint8_t *buf); 277 uint8_t *buf);
278 int (*read_subpage)(struct mtd_info *mtd,
279 struct nand_chip *chip,
280 uint32_t offs, uint32_t len,
281 uint8_t *buf);
277 void (*write_page)(struct mtd_info *mtd, 282 void (*write_page)(struct mtd_info *mtd,
278 struct nand_chip *chip, 283 struct nand_chip *chip,
279 const uint8_t *buf); 284 const uint8_t *buf);