diff options
-rw-r--r-- | drivers/mtd/nand/Kconfig | 40 | ||||
-rw-r--r-- | drivers/mtd/nand/omap2.c | 245 |
2 files changed, 285 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index bf0a28d513d4..31bb7e5b504a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -115,6 +115,46 @@ config MTD_NAND_OMAP2 | |||
115 | Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 | 115 | Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 |
116 | platforms. | 116 | platforms. |
117 | 117 | ||
118 | config MTD_NAND_OMAP_BCH | ||
119 | depends on MTD_NAND && MTD_NAND_OMAP2 && ARCH_OMAP3 | ||
120 | bool "Enable support for hardware BCH error correction" | ||
121 | default n | ||
122 | select BCH | ||
123 | select BCH_CONST_PARAMS | ||
124 | help | ||
125 | Support for hardware BCH error correction. | ||
126 | |||
127 | choice | ||
128 | prompt "BCH error correction capability" | ||
129 | depends on MTD_NAND_OMAP_BCH | ||
130 | |||
131 | config MTD_NAND_OMAP_BCH8 | ||
132 | bool "8 bits / 512 bytes (recommended)" | ||
133 | help | ||
134 | Support correcting up to 8 bitflips per 512-byte block. | ||
135 | This will use 13 bytes of spare area per 512 bytes of page data. | ||
136 | This is the recommended mode, as 4-bit mode does not work | ||
137 | on some OMAP3 revisions, due to a hardware bug. | ||
138 | |||
139 | config MTD_NAND_OMAP_BCH4 | ||
140 | bool "4 bits / 512 bytes" | ||
141 | help | ||
142 | Support correcting up to 4 bitflips per 512-byte block. | ||
143 | This will use 7 bytes of spare area per 512 bytes of page data. | ||
144 | Note that this mode does not work on some OMAP3 revisions, due to a | ||
145 | hardware bug. Please check your OMAP datasheet before selecting this | ||
146 | mode. | ||
147 | |||
148 | endchoice | ||
149 | |||
150 | if MTD_NAND_OMAP_BCH | ||
151 | config BCH_CONST_M | ||
152 | default 13 | ||
153 | config BCH_CONST_T | ||
154 | default 4 if MTD_NAND_OMAP_BCH4 | ||
155 | default 8 if MTD_NAND_OMAP_BCH8 | ||
156 | endif | ||
157 | |||
118 | config MTD_NAND_IDS | 158 | config MTD_NAND_IDS |
119 | tristate | 159 | tristate |
120 | 160 | ||
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 05d3562ec748..d7f681d0c9b9 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
@@ -21,6 +21,10 @@ | |||
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | 23 | ||
24 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
25 | #include <linux/bch.h> | ||
26 | #endif | ||
27 | |||
24 | #include <plat/dma.h> | 28 | #include <plat/dma.h> |
25 | #include <plat/gpmc.h> | 29 | #include <plat/gpmc.h> |
26 | #include <plat/nand.h> | 30 | #include <plat/nand.h> |
@@ -127,6 +131,11 @@ struct omap_nand_info { | |||
127 | } iomode; | 131 | } iomode; |
128 | u_char *buf; | 132 | u_char *buf; |
129 | int buf_len; | 133 | int buf_len; |
134 | |||
135 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
136 | struct bch_control *bch; | ||
137 | struct nand_ecclayout ecclayout; | ||
138 | #endif | ||
130 | }; | 139 | }; |
131 | 140 | ||
132 | /** | 141 | /** |
@@ -929,6 +938,226 @@ static int omap_dev_ready(struct mtd_info *mtd) | |||
929 | return 1; | 938 | return 1; |
930 | } | 939 | } |
931 | 940 | ||
941 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
942 | |||
943 | /** | ||
944 | * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction | ||
945 | * @mtd: MTD device structure | ||
946 | * @mode: Read/Write mode | ||
947 | */ | ||
948 | static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) | ||
949 | { | ||
950 | int nerrors; | ||
951 | unsigned int dev_width; | ||
952 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
953 | mtd); | ||
954 | struct nand_chip *chip = mtd->priv; | ||
955 | |||
956 | nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; | ||
957 | dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; | ||
958 | /* | ||
959 | * Program GPMC to perform correction on one 512-byte sector at a time. | ||
960 | * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and | ||
961 | * gives a slight (5%) performance gain (but requires additional code). | ||
962 | */ | ||
963 | (void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors); | ||
964 | } | ||
965 | |||
966 | /** | ||
967 | * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes | ||
968 | * @mtd: MTD device structure | ||
969 | * @dat: The pointer to data on which ecc is computed | ||
970 | * @ecc_code: The ecc_code buffer | ||
971 | */ | ||
972 | static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat, | ||
973 | u_char *ecc_code) | ||
974 | { | ||
975 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
976 | mtd); | ||
977 | return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code); | ||
978 | } | ||
979 | |||
980 | /** | ||
981 | * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes | ||
982 | * @mtd: MTD device structure | ||
983 | * @dat: The pointer to data on which ecc is computed | ||
984 | * @ecc_code: The ecc_code buffer | ||
985 | */ | ||
986 | static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, | ||
987 | u_char *ecc_code) | ||
988 | { | ||
989 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
990 | mtd); | ||
991 | return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code); | ||
992 | } | ||
993 | |||
994 | /** | ||
995 | * omap3_correct_data_bch - Decode received data and correct errors | ||
996 | * @mtd: MTD device structure | ||
997 | * @data: page data | ||
998 | * @read_ecc: ecc read from nand flash | ||
999 | * @calc_ecc: ecc read from HW ECC registers | ||
1000 | */ | ||
1001 | static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, | ||
1002 | u_char *read_ecc, u_char *calc_ecc) | ||
1003 | { | ||
1004 | int i, count; | ||
1005 | /* cannot correct more than 8 errors */ | ||
1006 | unsigned int errloc[8]; | ||
1007 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
1008 | mtd); | ||
1009 | |||
1010 | count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, | ||
1011 | errloc); | ||
1012 | if (count > 0) { | ||
1013 | /* correct errors */ | ||
1014 | for (i = 0; i < count; i++) { | ||
1015 | /* correct data only, not ecc bytes */ | ||
1016 | if (errloc[i] < 8*512) | ||
1017 | data[errloc[i]/8] ^= 1 << (errloc[i] & 7); | ||
1018 | pr_debug("corrected bitflip %u\n", errloc[i]); | ||
1019 | } | ||
1020 | } else if (count < 0) { | ||
1021 | pr_err("ecc unrecoverable error\n"); | ||
1022 | } | ||
1023 | return count; | ||
1024 | } | ||
1025 | |||
1026 | /** | ||
1027 | * omap3_free_bch - Release BCH ecc resources | ||
1028 | * @mtd: MTD device structure | ||
1029 | */ | ||
1030 | static void omap3_free_bch(struct mtd_info *mtd) | ||
1031 | { | ||
1032 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
1033 | mtd); | ||
1034 | if (info->bch) { | ||
1035 | free_bch(info->bch); | ||
1036 | info->bch = NULL; | ||
1037 | } | ||
1038 | } | ||
1039 | |||
1040 | /** | ||
1041 | * omap3_init_bch - Initialize BCH ECC | ||
1042 | * @mtd: MTD device structure | ||
1043 | * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) | ||
1044 | */ | ||
1045 | static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) | ||
1046 | { | ||
1047 | int ret, max_errors; | ||
1048 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
1049 | mtd); | ||
1050 | #ifdef CONFIG_MTD_NAND_OMAP_BCH8 | ||
1051 | const int hw_errors = 8; | ||
1052 | #else | ||
1053 | const int hw_errors = 4; | ||
1054 | #endif | ||
1055 | info->bch = NULL; | ||
1056 | |||
1057 | max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? 8 : 4; | ||
1058 | if (max_errors != hw_errors) { | ||
1059 | pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", | ||
1060 | max_errors, hw_errors); | ||
1061 | goto fail; | ||
1062 | } | ||
1063 | |||
1064 | /* initialize GPMC BCH engine */ | ||
1065 | ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors); | ||
1066 | if (ret) | ||
1067 | goto fail; | ||
1068 | |||
1069 | /* software bch library is only used to detect and locate errors */ | ||
1070 | info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); | ||
1071 | if (!info->bch) | ||
1072 | goto fail; | ||
1073 | |||
1074 | info->nand.ecc.size = 512; | ||
1075 | info->nand.ecc.hwctl = omap3_enable_hwecc_bch; | ||
1076 | info->nand.ecc.correct = omap3_correct_data_bch; | ||
1077 | info->nand.ecc.mode = NAND_ECC_HW; | ||
1078 | |||
1079 | /* | ||
1080 | * The number of corrected errors in an ecc block that will trigger | ||
1081 | * block scrubbing defaults to the ecc strength (4 or 8). | ||
1082 | * Set mtd->bitflip_threshold here to define a custom threshold. | ||
1083 | */ | ||
1084 | |||
1085 | if (max_errors == 8) { | ||
1086 | info->nand.ecc.strength = 8; | ||
1087 | info->nand.ecc.bytes = 13; | ||
1088 | info->nand.ecc.calculate = omap3_calculate_ecc_bch8; | ||
1089 | } else { | ||
1090 | info->nand.ecc.strength = 4; | ||
1091 | info->nand.ecc.bytes = 7; | ||
1092 | info->nand.ecc.calculate = omap3_calculate_ecc_bch4; | ||
1093 | } | ||
1094 | |||
1095 | pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); | ||
1096 | return 0; | ||
1097 | fail: | ||
1098 | omap3_free_bch(mtd); | ||
1099 | return -1; | ||
1100 | } | ||
1101 | |||
1102 | /** | ||
1103 | * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. | ||
1104 | * @mtd: MTD device structure | ||
1105 | */ | ||
1106 | static int omap3_init_bch_tail(struct mtd_info *mtd) | ||
1107 | { | ||
1108 | int i, steps; | ||
1109 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
1110 | mtd); | ||
1111 | struct nand_ecclayout *layout = &info->ecclayout; | ||
1112 | |||
1113 | /* build oob layout */ | ||
1114 | steps = mtd->writesize/info->nand.ecc.size; | ||
1115 | layout->eccbytes = steps*info->nand.ecc.bytes; | ||
1116 | |||
1117 | /* do not bother creating special oob layouts for small page devices */ | ||
1118 | if (mtd->oobsize < 64) { | ||
1119 | pr_err("BCH ecc is not supported on small page devices\n"); | ||
1120 | goto fail; | ||
1121 | } | ||
1122 | |||
1123 | /* reserve 2 bytes for bad block marker */ | ||
1124 | if (layout->eccbytes+2 > mtd->oobsize) { | ||
1125 | pr_err("no oob layout available for oobsize %d eccbytes %u\n", | ||
1126 | mtd->oobsize, layout->eccbytes); | ||
1127 | goto fail; | ||
1128 | } | ||
1129 | |||
1130 | /* put ecc bytes at oob tail */ | ||
1131 | for (i = 0; i < layout->eccbytes; i++) | ||
1132 | layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; | ||
1133 | |||
1134 | layout->oobfree[0].offset = 2; | ||
1135 | layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; | ||
1136 | info->nand.ecc.layout = layout; | ||
1137 | |||
1138 | if (!(info->nand.options & NAND_BUSWIDTH_16)) | ||
1139 | info->nand.badblock_pattern = &bb_descrip_flashbased; | ||
1140 | return 0; | ||
1141 | fail: | ||
1142 | omap3_free_bch(mtd); | ||
1143 | return -1; | ||
1144 | } | ||
1145 | |||
1146 | #else | ||
1147 | static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) | ||
1148 | { | ||
1149 | pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); | ||
1150 | return -1; | ||
1151 | } | ||
1152 | static int omap3_init_bch_tail(struct mtd_info *mtd) | ||
1153 | { | ||
1154 | return -1; | ||
1155 | } | ||
1156 | static void omap3_free_bch(struct mtd_info *mtd) | ||
1157 | { | ||
1158 | } | ||
1159 | #endif /* CONFIG_MTD_NAND_OMAP_BCH */ | ||
1160 | |||
932 | static int __devinit omap_nand_probe(struct platform_device *pdev) | 1161 | static int __devinit omap_nand_probe(struct platform_device *pdev) |
933 | { | 1162 | { |
934 | struct omap_nand_info *info; | 1163 | struct omap_nand_info *info; |
@@ -1067,6 +1296,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
1067 | info->nand.ecc.hwctl = omap_enable_hwecc; | 1296 | info->nand.ecc.hwctl = omap_enable_hwecc; |
1068 | info->nand.ecc.correct = omap_correct_data; | 1297 | info->nand.ecc.correct = omap_correct_data; |
1069 | info->nand.ecc.mode = NAND_ECC_HW; | 1298 | info->nand.ecc.mode = NAND_ECC_HW; |
1299 | } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || | ||
1300 | (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { | ||
1301 | err = omap3_init_bch(&info->mtd, pdata->ecc_opt); | ||
1302 | if (err) { | ||
1303 | err = -EINVAL; | ||
1304 | goto out_release_mem_region; | ||
1305 | } | ||
1070 | } | 1306 | } |
1071 | 1307 | ||
1072 | /* DIP switches on some boards change between 8 and 16 bit | 1308 | /* DIP switches on some boards change between 8 and 16 bit |
@@ -1098,6 +1334,14 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
1098 | (offset + omap_oobinfo.eccbytes); | 1334 | (offset + omap_oobinfo.eccbytes); |
1099 | 1335 | ||
1100 | info->nand.ecc.layout = &omap_oobinfo; | 1336 | info->nand.ecc.layout = &omap_oobinfo; |
1337 | } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || | ||
1338 | (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { | ||
1339 | /* build OOB layout for BCH ECC correction */ | ||
1340 | err = omap3_init_bch_tail(&info->mtd); | ||
1341 | if (err) { | ||
1342 | err = -EINVAL; | ||
1343 | goto out_release_mem_region; | ||
1344 | } | ||
1101 | } | 1345 | } |
1102 | 1346 | ||
1103 | /* second phase scan */ | 1347 | /* second phase scan */ |
@@ -1126,6 +1370,7 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
1126 | struct mtd_info *mtd = platform_get_drvdata(pdev); | 1370 | struct mtd_info *mtd = platform_get_drvdata(pdev); |
1127 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1371 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
1128 | mtd); | 1372 | mtd); |
1373 | omap3_free_bch(&info->mtd); | ||
1129 | 1374 | ||
1130 | platform_set_drvdata(pdev, NULL); | 1375 | platform_set_drvdata(pdev, NULL); |
1131 | if (info->dma_ch != -1) | 1376 | if (info->dma_ch != -1) |