aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/omap2.c
diff options
context:
space:
mode:
authorAfzal Mohammed <afzal@ti.com>2012-10-04 09:33:06 -0400
committerAfzal Mohammed <afzal@ti.com>2012-10-15 05:12:12 -0400
commit2ef9f3ddec55f1b77615613d4aab418f073220bb (patch)
tree1e9b383e32fc062d35630022a3f3997e126c430b /drivers/mtd/nand/omap2.c
parent2fdf0c98969fdac8f7b191d4988e2e436717c857 (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.c100
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)
1034static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) 1034static 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 */
1131static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) 1214static 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)