diff options
author | Thomas Gleixner <tglx@cruncher.tec.linutronix.de> | 2006-05-23 06:00:46 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@cruncher.tec.linutronix.de> | 2006-05-23 06:00:46 -0400 |
commit | 6dfc6d250d0b7ebaa6423c44dcd09fcfe68deabd (patch) | |
tree | 2be25b1e302eca5984a8ad5ed3e5bde77bafaabb /drivers/mtd/nand/nand_base.c | |
parent | 7aa65bfd6793a56cc3bbce8436abbfea3a7bdd1f (diff) |
[MTD] NAND modularize ECC
First step of modularizing ECC support.
- Move ECC related functionality into a seperate embedded data structure
- Get rid of the hardware dependend constants to simplify new ECC models
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/mtd/nand/nand_base.c')
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 146 |
1 files changed, 48 insertions, 98 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 37db98a58c34..98792ec4c2dc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -879,9 +879,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag | |||
879 | { | 879 | { |
880 | int i, status; | 880 | int i, status; |
881 | uint8_t ecc_code[32]; | 881 | uint8_t ecc_code[32]; |
882 | int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; | 882 | int eccmode = oobsel->useecc ? this->ecc.mode : NAND_ECC_NONE; |
883 | int *oob_config = oobsel->eccpos; | 883 | int *oob_config = oobsel->eccpos; |
884 | int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; | 884 | int datidx = 0, eccidx = 0, eccsteps = this->ecc.steps; |
885 | int eccbytes = 0; | 885 | int eccbytes = 0; |
886 | 886 | ||
887 | /* FIXME: Enable cached programming */ | 887 | /* FIXME: Enable cached programming */ |
@@ -901,20 +901,20 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag | |||
901 | /* Software ecc 3/256, write all */ | 901 | /* Software ecc 3/256, write all */ |
902 | case NAND_ECC_SOFT: | 902 | case NAND_ECC_SOFT: |
903 | for (; eccsteps; eccsteps--) { | 903 | for (; eccsteps; eccsteps--) { |
904 | this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); | 904 | this->ecc.calculate(mtd, &this->data_poi[datidx], ecc_code); |
905 | for (i = 0; i < 3; i++, eccidx++) | 905 | for (i = 0; i < 3; i++, eccidx++) |
906 | oob_buf[oob_config[eccidx]] = ecc_code[i]; | 906 | oob_buf[oob_config[eccidx]] = ecc_code[i]; |
907 | datidx += this->eccsize; | 907 | datidx += this->ecc.size; |
908 | } | 908 | } |
909 | this->write_buf(mtd, this->data_poi, mtd->oobblock); | 909 | this->write_buf(mtd, this->data_poi, mtd->oobblock); |
910 | break; | 910 | break; |
911 | default: | 911 | default: |
912 | eccbytes = this->eccbytes; | 912 | eccbytes = this->ecc.bytes; |
913 | for (; eccsteps; eccsteps--) { | 913 | for (; eccsteps; eccsteps--) { |
914 | /* enable hardware ecc logic for write */ | 914 | /* enable hardware ecc logic for write */ |
915 | this->enable_hwecc(mtd, NAND_ECC_WRITE); | 915 | this->ecc.hwctl(mtd, NAND_ECC_WRITE); |
916 | this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); | 916 | this->write_buf(mtd, &this->data_poi[datidx], this->ecc.size); |
917 | this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); | 917 | this->ecc.calculate(mtd, &this->data_poi[datidx], ecc_code); |
918 | for (i = 0; i < eccbytes; i++, eccidx++) | 918 | for (i = 0; i < eccbytes; i++, eccidx++) |
919 | oob_buf[oob_config[eccidx]] = ecc_code[i]; | 919 | oob_buf[oob_config[eccidx]] = ecc_code[i]; |
920 | /* If the hardware ecc provides syndromes then | 920 | /* If the hardware ecc provides syndromes then |
@@ -922,7 +922,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this, int pag | |||
922 | * the data bytes (words) */ | 922 | * the data bytes (words) */ |
923 | if (this->options & NAND_HWECC_SYNDROME) | 923 | if (this->options & NAND_HWECC_SYNDROME) |
924 | this->write_buf(mtd, ecc_code, eccbytes); | 924 | this->write_buf(mtd, ecc_code, eccbytes); |
925 | datidx += this->eccsize; | 925 | datidx += this->ecc.size; |
926 | } | 926 | } |
927 | break; | 927 | break; |
928 | } | 928 | } |
@@ -1155,7 +1155,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1155 | if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) | 1155 | if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) |
1156 | oobsel = this->autooob; | 1156 | oobsel = this->autooob; |
1157 | 1157 | ||
1158 | eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; | 1158 | eccmode = oobsel->useecc ? this->ecc.mode : NAND_ECC_NONE; |
1159 | oob_config = oobsel->eccpos; | 1159 | oob_config = oobsel->eccpos; |
1160 | 1160 | ||
1161 | /* Select the NAND device */ | 1161 | /* Select the NAND device */ |
@@ -1170,8 +1170,8 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1170 | col = from & (mtd->oobblock - 1); | 1170 | col = from & (mtd->oobblock - 1); |
1171 | 1171 | ||
1172 | end = mtd->oobblock; | 1172 | end = mtd->oobblock; |
1173 | ecc = this->eccsize; | 1173 | ecc = this->ecc.size; |
1174 | eccbytes = this->eccbytes; | 1174 | eccbytes = this->ecc.bytes; |
1175 | 1175 | ||
1176 | if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) | 1176 | if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) |
1177 | compareecc = 0; | 1177 | compareecc = 0; |
@@ -1216,7 +1216,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1216 | oobsel->useecc == MTD_NANDECC_AUTOPL_USR) | 1216 | oobsel->useecc == MTD_NANDECC_AUTOPL_USR) |
1217 | oob_data = &this->data_buf[end]; | 1217 | oob_data = &this->data_buf[end]; |
1218 | 1218 | ||
1219 | eccsteps = this->eccsteps; | 1219 | eccsteps = this->ecc.steps; |
1220 | 1220 | ||
1221 | switch (eccmode) { | 1221 | switch (eccmode) { |
1222 | case NAND_ECC_NONE:{ | 1222 | case NAND_ECC_NONE:{ |
@@ -1234,12 +1234,12 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1234 | case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ | 1234 | case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ |
1235 | this->read_buf(mtd, data_poi, end); | 1235 | this->read_buf(mtd, data_poi, end); |
1236 | for (i = 0, datidx = 0; eccsteps; eccsteps--, i += 3, datidx += ecc) | 1236 | for (i = 0, datidx = 0; eccsteps; eccsteps--, i += 3, datidx += ecc) |
1237 | this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); | 1237 | this->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); |
1238 | break; | 1238 | break; |
1239 | 1239 | ||
1240 | default: | 1240 | default: |
1241 | for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { | 1241 | for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) { |
1242 | this->enable_hwecc(mtd, NAND_ECC_READ); | 1242 | this->ecc.hwctl(mtd, NAND_ECC_READ); |
1243 | this->read_buf(mtd, &data_poi[datidx], ecc); | 1243 | this->read_buf(mtd, &data_poi[datidx], ecc); |
1244 | 1244 | ||
1245 | /* HW ecc with syndrome calculation must read the | 1245 | /* HW ecc with syndrome calculation must read the |
@@ -1247,19 +1247,19 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1247 | if (!compareecc) { | 1247 | if (!compareecc) { |
1248 | /* Some hw ecc generators need to know when the | 1248 | /* Some hw ecc generators need to know when the |
1249 | * syndrome is read from flash */ | 1249 | * syndrome is read from flash */ |
1250 | this->enable_hwecc(mtd, NAND_ECC_READSYN); | 1250 | this->ecc.hwctl(mtd, NAND_ECC_READSYN); |
1251 | this->read_buf(mtd, &oob_data[i], eccbytes); | 1251 | this->read_buf(mtd, &oob_data[i], eccbytes); |
1252 | /* We calc error correction directly, it checks the hw | 1252 | /* We calc error correction directly, it checks the hw |
1253 | * generator for an error, reads back the syndrome and | 1253 | * generator for an error, reads back the syndrome and |
1254 | * does the error correction on the fly */ | 1254 | * does the error correction on the fly */ |
1255 | ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); | 1255 | ecc_status = this->ecc.correct(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); |
1256 | if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { | 1256 | if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { |
1257 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " | 1257 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " |
1258 | "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); | 1258 | "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); |
1259 | ecc_failed++; | 1259 | ecc_failed++; |
1260 | } | 1260 | } |
1261 | } else { | 1261 | } else { |
1262 | this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); | 1262 | this->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]); |
1263 | } | 1263 | } |
1264 | } | 1264 | } |
1265 | break; | 1265 | break; |
@@ -1277,8 +1277,8 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1277 | ecc_code[j] = oob_data[oob_config[j]]; | 1277 | ecc_code[j] = oob_data[oob_config[j]]; |
1278 | 1278 | ||
1279 | /* correct data, if necessary */ | 1279 | /* correct data, if necessary */ |
1280 | for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { | 1280 | for (i = 0, j = 0, datidx = 0; i < this->ecc.steps; i++, datidx += ecc) { |
1281 | ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); | 1281 | ecc_status = this->ecc.correct(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); |
1282 | 1282 | ||
1283 | /* Get next chunk of ecc bytes */ | 1283 | /* Get next chunk of ecc bytes */ |
1284 | j += eccbytes; | 1284 | j += eccbytes; |
@@ -1315,7 +1315,7 @@ int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
1315 | break; | 1315 | break; |
1316 | case MTD_NANDECC_PLACE: | 1316 | case MTD_NANDECC_PLACE: |
1317 | /* YAFFS1 legacy mode */ | 1317 | /* YAFFS1 legacy mode */ |
1318 | oob_data += this->eccsteps * sizeof(int); | 1318 | oob_data += this->ecc.steps * sizeof(int); |
1319 | default: | 1319 | default: |
1320 | oob_data += mtd->oobsize; | 1320 | oob_data += mtd->oobsize; |
1321 | } | 1321 | } |
@@ -2648,99 +2648,49 @@ int nand_scan(struct mtd_info *mtd, int maxchips) | |||
2648 | * check ECC mode, default to software if 3byte/512byte hardware ECC is | 2648 | * check ECC mode, default to software if 3byte/512byte hardware ECC is |
2649 | * selected and we have 256 byte pagesize fallback to software ECC | 2649 | * selected and we have 256 byte pagesize fallback to software ECC |
2650 | */ | 2650 | */ |
2651 | this->eccsize = 256; | 2651 | switch (this->ecc.mode) { |
2652 | this->eccbytes = 3; | 2652 | case NAND_ECC_HW: |
2653 | 2653 | case NAND_ECC_HW_SYNDROME: | |
2654 | switch (this->eccmode) { | 2654 | if (!this->ecc.calculate || !this->ecc.correct || |
2655 | case NAND_ECC_HW12_2048: | 2655 | !this->ecc.hwctl) { |
2656 | if (mtd->oobblock < 2048) { | 2656 | printk(KERN_WARNING "No ECC functions supplied, " |
2657 | printk(KERN_WARNING "2048 byte HW ECC not possible on " | 2657 | "Hardware ECC not possible\n"); |
2658 | "%d byte page size, fallback to SW ECC\n", | 2658 | BUG(); |
2659 | mtd->oobblock); | 2659 | } |
2660 | this->eccmode = NAND_ECC_SOFT; | 2660 | if (mtd->oobblock >= this->ecc.size) |
2661 | this->calculate_ecc = nand_calculate_ecc; | 2661 | break; |
2662 | this->correct_data = nand_correct_data; | 2662 | printk(KERN_WARNING "%d byte HW ECC not possible on " |
2663 | } else | 2663 | "%d byte page size, fallback to SW ECC\n", |
2664 | this->eccsize = 2048; | 2664 | this->ecc.size, mtd->oobblock); |
2665 | break; | 2665 | this->ecc.mode = NAND_ECC_SOFT; |
2666 | |||
2667 | case NAND_ECC_HW3_512: | ||
2668 | case NAND_ECC_HW6_512: | ||
2669 | case NAND_ECC_HW8_512: | ||
2670 | if (mtd->oobblock == 256) { | ||
2671 | printk(KERN_WARNING "512 byte HW ECC not possible on " | ||
2672 | "256 Byte pagesize, fallback to SW ECC \n"); | ||
2673 | this->eccmode = NAND_ECC_SOFT; | ||
2674 | this->calculate_ecc = nand_calculate_ecc; | ||
2675 | this->correct_data = nand_correct_data; | ||
2676 | } else | ||
2677 | this->eccsize = 512; /* set eccsize to 512 */ | ||
2678 | break; | ||
2679 | 2666 | ||
2680 | case NAND_ECC_HW3_256: | 2667 | case NAND_ECC_SOFT: |
2668 | this->ecc.calculate = nand_calculate_ecc; | ||
2669 | this->ecc.correct = nand_correct_data; | ||
2670 | this->ecc.size = 256; | ||
2671 | this->ecc.bytes = 3; | ||
2681 | break; | 2672 | break; |
2682 | 2673 | ||
2683 | case NAND_ECC_NONE: | 2674 | case NAND_ECC_NONE: |
2684 | printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " | 2675 | printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " |
2685 | "This is not recommended !!\n"); | 2676 | "This is not recommended !!\n"); |
2686 | this->eccmode = NAND_ECC_NONE; | 2677 | this->ecc.size = mtd->oobblock; |
2678 | this->ecc.bytes = 0; | ||
2687 | break; | 2679 | break; |
2688 | |||
2689 | case NAND_ECC_SOFT: | ||
2690 | this->calculate_ecc = nand_calculate_ecc; | ||
2691 | this->correct_data = nand_correct_data; | ||
2692 | break; | ||
2693 | |||
2694 | default: | 2680 | default: |
2695 | printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", | 2681 | printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", |
2696 | this->eccmode); | 2682 | this->ecc.mode); |
2697 | BUG(); | ||
2698 | } | ||
2699 | |||
2700 | /* | ||
2701 | * Check hardware ecc function availability and adjust number of ecc | ||
2702 | * bytes per calculation step | ||
2703 | */ | ||
2704 | switch (this->eccmode) { | ||
2705 | case NAND_ECC_HW12_2048: | ||
2706 | this->eccbytes += 4; | ||
2707 | case NAND_ECC_HW8_512: | ||
2708 | this->eccbytes += 2; | ||
2709 | case NAND_ECC_HW6_512: | ||
2710 | this->eccbytes += 3; | ||
2711 | case NAND_ECC_HW3_512: | ||
2712 | case NAND_ECC_HW3_256: | ||
2713 | if (this->calculate_ecc && this->correct_data && | ||
2714 | this->enable_hwecc) | ||
2715 | break; | ||
2716 | printk(KERN_WARNING "No ECC functions supplied, " | ||
2717 | "Hardware ECC not possible\n"); | ||
2718 | BUG(); | 2683 | BUG(); |
2719 | } | 2684 | } |
2720 | 2685 | ||
2721 | mtd->eccsize = this->eccsize; | ||
2722 | |||
2723 | /* | 2686 | /* |
2724 | * Set the number of read / write steps for one page depending on ECC | 2687 | * Set the number of read / write steps for one page depending on ECC |
2725 | * mode | 2688 | * mode |
2726 | */ | 2689 | */ |
2727 | switch (this->eccmode) { | 2690 | this->ecc.steps = mtd->oobblock / this->ecc.size; |
2728 | case NAND_ECC_HW12_2048: | 2691 | if(this->ecc.steps * this->ecc.size != mtd->oobblock) { |
2729 | this->eccsteps = mtd->oobblock / 2048; | 2692 | printk(KERN_WARNING "Invalid ecc parameters\n"); |
2730 | break; | 2693 | BUG(); |
2731 | case NAND_ECC_HW3_512: | ||
2732 | case NAND_ECC_HW6_512: | ||
2733 | case NAND_ECC_HW8_512: | ||
2734 | this->eccsteps = mtd->oobblock / 512; | ||
2735 | break; | ||
2736 | case NAND_ECC_HW3_256: | ||
2737 | case NAND_ECC_SOFT: | ||
2738 | this->eccsteps = mtd->oobblock / 256; | ||
2739 | break; | ||
2740 | |||
2741 | case NAND_ECC_NONE: | ||
2742 | this->eccsteps = 1; | ||
2743 | break; | ||
2744 | } | 2694 | } |
2745 | 2695 | ||
2746 | /* Initialize state, waitqueue and spinlock */ | 2696 | /* Initialize state, waitqueue and spinlock */ |