aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
authorIvan Djelic <ivan.djelic@parrot.com>2012-04-26 08:17:49 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2012-05-14 00:25:42 -0400
commit8d602cf50d3bba864bc1438f486b626df69c87b3 (patch)
tree18636ef99650881a6f0473780ab22fa4f58f4f1d /arch/arm/mach-omap2
parent1951f2f710a621ae0bc4268617046a6c02c634d0 (diff)
ARM: OMAP3: gpmc: add BCH ecc api and modes
This patch adds a simple BCH ecc computation api, similar to the existing Hamming ecc api. It is intended to be used by the MTD layer. It implements the following features: - support 4-bit and 8-bit ecc computation - do not protect user bytes in spare area, only data area is protected - ecc for an erased NAND page (0xFFs) is also a sequence of 0xFFs This last feature is obtained by adding a constant polynomial to the hardware computed ecc. It allows to correct bitflips in blank pages and is extremely useful to support filesystems such as UBIFS, which expect erased pages to contain only 0xFFs. This api has been tested on an OMAP3630 board. Artem: The OMAP maintainer Tony Lindgren gave us his blessing for merging this patch via the MTD tree. Signed-off-by: Ivan Djelic <ivan.djelic@parrot.com> Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r--arch/arm/mach-omap2/gpmc.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 00d510858e28..1ca8d7fd625a 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -49,6 +49,7 @@
49#define GPMC_ECC_CONTROL 0x1f8 49#define GPMC_ECC_CONTROL 0x1f8
50#define GPMC_ECC_SIZE_CONFIG 0x1fc 50#define GPMC_ECC_SIZE_CONFIG 0x1fc
51#define GPMC_ECC1_RESULT 0x200 51#define GPMC_ECC1_RESULT 0x200
52#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */
52 53
53#define GPMC_CS0_OFFSET 0x60 54#define GPMC_CS0_OFFSET 0x60
54#define GPMC_CS_SIZE 0x30 55#define GPMC_CS_SIZE 0x30
@@ -920,3 +921,186 @@ int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code)
920 return 0; 921 return 0;
921} 922}
922EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); 923EXPORT_SYMBOL_GPL(gpmc_calculate_ecc);
924
925#ifdef CONFIG_ARCH_OMAP3
926
927/**
928 * gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality
929 * @cs: chip select number
930 * @nsectors: how many 512-byte sectors to process
931 * @nerrors: how many errors to correct per sector (4 or 8)
932 *
933 * This function must be executed before any call to gpmc_enable_hwecc_bch.
934 */
935int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors)
936{
937 /* check if ecc module is in use */
938 if (gpmc_ecc_used != -EINVAL)
939 return -EINVAL;
940
941 /* support only OMAP3 class */
942 if (!cpu_is_omap34xx()) {
943 printk(KERN_ERR "BCH ecc is not supported on this CPU\n");
944 return -EINVAL;
945 }
946
947 /*
948 * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1.
949 * Other chips may be added if confirmed to work.
950 */
951 if ((nerrors == 4) &&
952 (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) {
953 printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n");
954 return -EINVAL;
955 }
956
957 /* sanity check */
958 if (nsectors > 8) {
959 printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n",
960 nsectors);
961 return -EINVAL;
962 }
963
964 return 0;
965}
966EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch);
967
968/**
969 * gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality
970 * @cs: chip select number
971 * @mode: read/write mode
972 * @dev_width: device bus width(1 for x16, 0 for x8)
973 * @nsectors: how many 512-byte sectors to process
974 * @nerrors: how many errors to correct per sector (4 or 8)
975 */
976int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
977 int nerrors)
978{
979 unsigned int val;
980
981 /* check if ecc module is in use */
982 if (gpmc_ecc_used != -EINVAL)
983 return -EINVAL;
984
985 gpmc_ecc_used = cs;
986
987 /* clear ecc and enable bits */
988 gpmc_write_reg(GPMC_ECC_CONTROL, 0x1);
989
990 /*
991 * When using BCH, sector size is hardcoded to 512 bytes.
992 * Here we are using wrapping mode 6 both for reading and writing, with:
993 * size0 = 0 (no additional protected byte in spare area)
994 * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
995 */
996 gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12));
997
998 /* BCH configuration */
999 val = ((1 << 16) | /* enable BCH */
1000 (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
1001 (0x06 << 8) | /* wrap mode = 6 */
1002 (dev_width << 7) | /* bus width */
1003 (((nsectors-1) & 0x7) << 4) | /* number of sectors */
1004 (cs << 1) | /* ECC CS */
1005 (0x1)); /* enable ECC */
1006
1007 gpmc_write_reg(GPMC_ECC_CONFIG, val);
1008 gpmc_write_reg(GPMC_ECC_CONTROL, 0x101);
1009 return 0;
1010}
1011EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch);
1012
1013/**
1014 * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes
1015 * @cs: chip select number
1016 * @dat: The pointer to data on which ecc is computed
1017 * @ecc: The ecc output buffer
1018 */
1019int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc)
1020{
1021 int i;
1022 unsigned long nsectors, reg, val1, val2;
1023
1024 if (gpmc_ecc_used != cs)
1025 return -EINVAL;
1026
1027 nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
1028
1029 for (i = 0; i < nsectors; i++) {
1030
1031 reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
1032
1033 /* Read hw-computed remainder */
1034 val1 = gpmc_read_reg(reg + 0);
1035 val2 = gpmc_read_reg(reg + 4);
1036
1037 /*
1038 * Add constant polynomial to remainder, in order to get an ecc
1039 * sequence of 0xFFs for a buffer filled with 0xFFs; and
1040 * left-justify the resulting polynomial.
1041 */
1042 *ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF);
1043 *ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF);
1044 *ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
1045 *ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF);
1046 *ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF);
1047 *ecc++ = 0xac ^ ((val1 >> 4) & 0xFF);
1048 *ecc++ = 0x7f ^ ((val1 & 0xF) << 4);
1049 }
1050
1051 gpmc_ecc_used = -EINVAL;
1052 return 0;
1053}
1054EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4);
1055
1056/**
1057 * gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes
1058 * @cs: chip select number
1059 * @dat: The pointer to data on which ecc is computed
1060 * @ecc: The ecc output buffer
1061 */
1062int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc)
1063{
1064 int i;
1065 unsigned long nsectors, reg, val1, val2, val3, val4;
1066
1067 if (gpmc_ecc_used != cs)
1068 return -EINVAL;
1069
1070 nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
1071
1072 for (i = 0; i < nsectors; i++) {
1073
1074 reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
1075
1076 /* Read hw-computed remainder */
1077 val1 = gpmc_read_reg(reg + 0);
1078 val2 = gpmc_read_reg(reg + 4);
1079 val3 = gpmc_read_reg(reg + 8);
1080 val4 = gpmc_read_reg(reg + 12);
1081
1082 /*
1083 * Add constant polynomial to remainder, in order to get an ecc
1084 * sequence of 0xFFs for a buffer filled with 0xFFs.
1085 */
1086 *ecc++ = 0xef ^ (val4 & 0xFF);
1087 *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
1088 *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
1089 *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
1090 *ecc++ = 0xed ^ (val3 & 0xFF);
1091 *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
1092 *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
1093 *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
1094 *ecc++ = 0x97 ^ (val2 & 0xFF);
1095 *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
1096 *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
1097 *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
1098 *ecc++ = 0xb5 ^ (val1 & 0xFF);
1099 }
1100
1101 gpmc_ecc_used = -EINVAL;
1102 return 0;
1103}
1104EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8);
1105
1106#endif /* CONFIG_ARCH_OMAP3 */