diff options
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 87 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 5 |
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 | */ | ||
808 | static 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); |