diff options
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 184 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/gpmc.h | 11 |
2 files changed, 195 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 | } |
922 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); | 923 | EXPORT_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 | */ | ||
935 | int 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 | } | ||
966 | EXPORT_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 | */ | ||
976 | int 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 | } | ||
1011 | EXPORT_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 | */ | ||
1019 | int 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 | } | ||
1054 | EXPORT_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 | */ | ||
1062 | int 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 | } | ||
1104 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8); | ||
1105 | |||
1106 | #endif /* CONFIG_ARCH_OMAP3 */ | ||
diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h index 1527929b445a..f37764a36072 100644 --- a/arch/arm/plat-omap/include/plat/gpmc.h +++ b/arch/arm/plat-omap/include/plat/gpmc.h | |||
@@ -92,6 +92,8 @@ enum omap_ecc { | |||
92 | OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ | 92 | OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ |
93 | /* 1-bit ecc: stored at beginning of spare area as romcode */ | 93 | /* 1-bit ecc: stored at beginning of spare area as romcode */ |
94 | OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ | 94 | OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ |
95 | OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */ | ||
96 | OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */ | ||
95 | }; | 97 | }; |
96 | 98 | ||
97 | /* | 99 | /* |
@@ -157,4 +159,13 @@ extern int gpmc_nand_write(int cs, int cmd, int wval); | |||
157 | 159 | ||
158 | int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size); | 160 | int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size); |
159 | int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code); | 161 | int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code); |
162 | |||
163 | #ifdef CONFIG_ARCH_OMAP3 | ||
164 | int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors); | ||
165 | int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, | ||
166 | int nerrors); | ||
167 | int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc); | ||
168 | int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc); | ||
169 | #endif /* CONFIG_ARCH_OMAP3 */ | ||
170 | |||
160 | #endif | 171 | #endif |