diff options
author | Afzal Mohammed <afzal@ti.com> | 2012-10-04 09:33:06 -0400 |
---|---|---|
committer | Afzal Mohammed <afzal@ti.com> | 2012-10-15 05:12:12 -0400 |
commit | 2ef9f3ddec55f1b77615613d4aab418f073220bb (patch) | |
tree | 1e9b383e32fc062d35630022a3f3997e126c430b /drivers/mtd/nand/omap2.c | |
parent | 2fdf0c98969fdac8f7b191d4988e2e436717c857 (diff) |
mtd: nand: omap: handle gpmc bch[48]
gpmc-nand bch registers are now available in driver,
make use of it to handle bch[48] instead of relying
on gpmc exported functions.
And so nand driver no longer needs gpmc header, remove it.
Signed-off-by: Afzal Mohammed <afzal@ti.com>
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r-- | drivers/mtd/nand/omap2.c | 100 |
1 files changed, 89 insertions, 11 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index f0a1b1d69d32..3282b151aa03 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
@@ -28,7 +28,6 @@ | |||
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #include <plat/dma.h> | 30 | #include <plat/dma.h> |
31 | #include <plat/gpmc.h> | ||
32 | #include <linux/platform_data/mtd-nand-omap2.h> | 31 | #include <linux/platform_data/mtd-nand-omap2.h> |
33 | 32 | ||
34 | #define DRIVER_NAME "omap2-nand" | 33 | #define DRIVER_NAME "omap2-nand" |
@@ -106,6 +105,7 @@ | |||
106 | #define CS_MASK 0x7 | 105 | #define CS_MASK 0x7 |
107 | #define ENABLE_PREFETCH (0x1 << 7) | 106 | #define ENABLE_PREFETCH (0x1 << 7) |
108 | #define DMA_MPU_MODE_SHIFT 2 | 107 | #define DMA_MPU_MODE_SHIFT 2 |
108 | #define ECCSIZE0_SHIFT 12 | ||
109 | #define ECCSIZE1_SHIFT 22 | 109 | #define ECCSIZE1_SHIFT 22 |
110 | #define ECC1RESULTSIZE 0x1 | 110 | #define ECC1RESULTSIZE 0x1 |
111 | #define ECCCLEAR 0x100 | 111 | #define ECCCLEAR 0x100 |
@@ -1034,19 +1034,45 @@ static int omap_dev_ready(struct mtd_info *mtd) | |||
1034 | static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) | 1034 | static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) |
1035 | { | 1035 | { |
1036 | int nerrors; | 1036 | int nerrors; |
1037 | unsigned int dev_width; | 1037 | unsigned int dev_width, nsectors; |
1038 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1038 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
1039 | mtd); | 1039 | mtd); |
1040 | struct nand_chip *chip = mtd->priv; | 1040 | struct nand_chip *chip = mtd->priv; |
1041 | u32 val; | ||
1041 | 1042 | ||
1042 | nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; | 1043 | nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; |
1043 | dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; | 1044 | dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; |
1045 | nsectors = 1; | ||
1044 | /* | 1046 | /* |
1045 | * Program GPMC to perform correction on one 512-byte sector at a time. | 1047 | * Program GPMC to perform correction on one 512-byte sector at a time. |
1046 | * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and | 1048 | * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and |
1047 | * gives a slight (5%) performance gain (but requires additional code). | 1049 | * gives a slight (5%) performance gain (but requires additional code). |
1048 | */ | 1050 | */ |
1049 | (void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors); | 1051 | |
1052 | writel(ECC1, info->reg.gpmc_ecc_control); | ||
1053 | |||
1054 | /* | ||
1055 | * When using BCH, sector size is hardcoded to 512 bytes. | ||
1056 | * Here we are using wrapping mode 6 both for reading and writing, with: | ||
1057 | * size0 = 0 (no additional protected byte in spare area) | ||
1058 | * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) | ||
1059 | */ | ||
1060 | val = (32 << ECCSIZE1_SHIFT) | (0 << ECCSIZE0_SHIFT); | ||
1061 | writel(val, info->reg.gpmc_ecc_size_config); | ||
1062 | |||
1063 | /* BCH configuration */ | ||
1064 | val = ((1 << 16) | /* enable BCH */ | ||
1065 | (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ | ||
1066 | (0x06 << 8) | /* wrap mode = 6 */ | ||
1067 | (dev_width << 7) | /* bus width */ | ||
1068 | (((nsectors-1) & 0x7) << 4) | /* number of sectors */ | ||
1069 | (info->gpmc_cs << 1) | /* ECC CS */ | ||
1070 | (0x1)); /* enable ECC */ | ||
1071 | |||
1072 | writel(val, info->reg.gpmc_ecc_config); | ||
1073 | |||
1074 | /* clear ecc and enable bits */ | ||
1075 | writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); | ||
1050 | } | 1076 | } |
1051 | 1077 | ||
1052 | /** | 1078 | /** |
@@ -1060,7 +1086,32 @@ static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat, | |||
1060 | { | 1086 | { |
1061 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1087 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
1062 | mtd); | 1088 | mtd); |
1063 | return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code); | 1089 | unsigned long nsectors, val1, val2; |
1090 | int i; | ||
1091 | |||
1092 | nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; | ||
1093 | |||
1094 | for (i = 0; i < nsectors; i++) { | ||
1095 | |||
1096 | /* Read hw-computed remainder */ | ||
1097 | val1 = readl(info->reg.gpmc_bch_result0[i]); | ||
1098 | val2 = readl(info->reg.gpmc_bch_result1[i]); | ||
1099 | |||
1100 | /* | ||
1101 | * Add constant polynomial to remainder, in order to get an ecc | ||
1102 | * sequence of 0xFFs for a buffer filled with 0xFFs; and | ||
1103 | * left-justify the resulting polynomial. | ||
1104 | */ | ||
1105 | *ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF); | ||
1106 | *ecc_code++ = 0x13 ^ ((val2 >> 4) & 0xFF); | ||
1107 | *ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF)); | ||
1108 | *ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF); | ||
1109 | *ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF); | ||
1110 | *ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF); | ||
1111 | *ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4); | ||
1112 | } | ||
1113 | |||
1114 | return 0; | ||
1064 | } | 1115 | } |
1065 | 1116 | ||
1066 | /** | 1117 | /** |
@@ -1074,7 +1125,39 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, | |||
1074 | { | 1125 | { |
1075 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1126 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
1076 | mtd); | 1127 | mtd); |
1077 | return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code); | 1128 | unsigned long nsectors, val1, val2, val3, val4; |
1129 | int i; | ||
1130 | |||
1131 | nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; | ||
1132 | |||
1133 | for (i = 0; i < nsectors; i++) { | ||
1134 | |||
1135 | /* Read hw-computed remainder */ | ||
1136 | val1 = readl(info->reg.gpmc_bch_result0[i]); | ||
1137 | val2 = readl(info->reg.gpmc_bch_result1[i]); | ||
1138 | val3 = readl(info->reg.gpmc_bch_result2[i]); | ||
1139 | val4 = readl(info->reg.gpmc_bch_result3[i]); | ||
1140 | |||
1141 | /* | ||
1142 | * Add constant polynomial to remainder, in order to get an ecc | ||
1143 | * sequence of 0xFFs for a buffer filled with 0xFFs. | ||
1144 | */ | ||
1145 | *ecc_code++ = 0xef ^ (val4 & 0xFF); | ||
1146 | *ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF); | ||
1147 | *ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF); | ||
1148 | *ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF); | ||
1149 | *ecc_code++ = 0xed ^ (val3 & 0xFF); | ||
1150 | *ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF); | ||
1151 | *ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF); | ||
1152 | *ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF); | ||
1153 | *ecc_code++ = 0x97 ^ (val2 & 0xFF); | ||
1154 | *ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF); | ||
1155 | *ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF); | ||
1156 | *ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF); | ||
1157 | *ecc_code++ = 0xb5 ^ (val1 & 0xFF); | ||
1158 | } | ||
1159 | |||
1160 | return 0; | ||
1078 | } | 1161 | } |
1079 | 1162 | ||
1080 | /** | 1163 | /** |
@@ -1130,7 +1213,7 @@ static void omap3_free_bch(struct mtd_info *mtd) | |||
1130 | */ | 1213 | */ |
1131 | static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) | 1214 | static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) |
1132 | { | 1215 | { |
1133 | int ret, max_errors; | 1216 | int max_errors; |
1134 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1217 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
1135 | mtd); | 1218 | mtd); |
1136 | #ifdef CONFIG_MTD_NAND_OMAP_BCH8 | 1219 | #ifdef CONFIG_MTD_NAND_OMAP_BCH8 |
@@ -1147,11 +1230,6 @@ static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) | |||
1147 | goto fail; | 1230 | goto fail; |
1148 | } | 1231 | } |
1149 | 1232 | ||
1150 | /* initialize GPMC BCH engine */ | ||
1151 | ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors); | ||
1152 | if (ret) | ||
1153 | goto fail; | ||
1154 | |||
1155 | /* software bch library is only used to detect and locate errors */ | 1233 | /* software bch library is only used to detect and locate errors */ |
1156 | info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); | 1234 | info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); |
1157 | if (!info->bch) | 1235 | if (!info->bch) |