Commit 3d0f511dfa873c8d4dc09dd6fe33a1bad5192ed1
Committed by
Hebbar, Gururaja
1 parent
23409519e1
Exists in
master
omap3:nand: bch ecc support added
bch error correction (t=4 and t=8) for 512 bytes support added. Tested in omap-3630 es-1.1 silicon. Need to select the bch-ecc from board file. E.g. arch/arm/mach-omap2/board-flash.c: board_nand_init() board_nand_data.ecc_opt = OMAP_ECC_BCH4_CODE_HW This patch has dependency on - http://www.mail-archive.com/linux-omap@vger.kernel.org/msg42658.html Signed-off-by: Parth Mauria Saxena <parth.saxena@ti.com> Signed-off-by: Sukumar Ghorai <s-ghorai@ti.com> Signed-off-by: Sriramakrishnan A G <srk@ti.com> Signed-off-by: Abhilash K V <abhilash.kv@ti.com> Signed-off-by: Philip, Avinash <avinashphilip@ti.com> Signed-off-by: Hebbar, Gururaja <gururaja.hebbar@ti.com>
Showing 5 changed files with 561 additions and 56 deletions Side-by-side Diff
arch/arm/mach-omap2/gpmc.c
... | ... | @@ -49,6 +49,7 @@ |
49 | 49 | #define GPMC_ECC_CONTROL 0x1f8 |
50 | 50 | #define GPMC_ECC_SIZE_CONFIG 0x1fc |
51 | 51 | #define GPMC_ECC1_RESULT 0x200 |
52 | +#define GPMC_ECC_BCH_RESULT_0 0x240 | |
52 | 53 | |
53 | 54 | #define GPMC_CS0_OFFSET 0x60 |
54 | 55 | #define GPMC_CS_SIZE 0x30 |
... | ... | @@ -95,7 +96,6 @@ |
95 | 96 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; |
96 | 97 | static DEFINE_SPINLOCK(gpmc_mem_lock); |
97 | 98 | static unsigned int gpmc_cs_map; /* flag for cs which are initialized */ |
98 | -static int gpmc_ecc_used = -EINVAL; /* cs using ecc engine */ | |
99 | 99 | |
100 | 100 | static void __iomem *gpmc_base; |
101 | 101 | |
102 | 102 | |
103 | 103 | |
104 | 104 | |
105 | 105 | |
106 | 106 | |
107 | 107 | |
108 | 108 | |
109 | 109 | |
110 | 110 | |
111 | 111 | |
112 | 112 | |
... | ... | @@ -844,53 +844,78 @@ |
844 | 844 | |
845 | 845 | /** |
846 | 846 | * gpmc_enable_hwecc - enable hardware ecc functionality |
847 | + * @ecc_type: ecc type e.g. Hamming, BCH | |
847 | 848 | * @cs: chip select number |
848 | 849 | * @mode: read/write mode |
849 | 850 | * @dev_width: device bus width(1 for x16, 0 for x8) |
850 | 851 | * @ecc_size: bytes for which ECC will be generated |
851 | 852 | */ |
852 | -int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size) | |
853 | +int gpmc_enable_hwecc(int ecc_type, int cs, int mode, | |
854 | + int dev_width, int ecc_size) | |
853 | 855 | { |
854 | - unsigned int val; | |
856 | + unsigned int bch_mod = 0, bch_wrapmode = 0, eccsize1 = 0, eccsize0 = 0; | |
857 | + unsigned int ecc_conf_val = 0, ecc_size_conf_val = 0; | |
855 | 858 | |
856 | - /* check if ecc module is in used */ | |
857 | - if (gpmc_ecc_used != -EINVAL) | |
858 | - return -EINVAL; | |
859 | - | |
860 | - gpmc_ecc_used = cs; | |
861 | - | |
862 | - /* clear ecc and enable bits */ | |
863 | - val = ((0x00000001<<8) | 0x00000001); | |
864 | - gpmc_write_reg(GPMC_ECC_CONTROL, val); | |
865 | - | |
866 | - /* program ecc and result sizes */ | |
867 | - val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F)); | |
868 | - gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, val); | |
869 | - | |
870 | 859 | switch (mode) { |
871 | 860 | case GPMC_ECC_READ: |
872 | - gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | |
861 | + if (ecc_type == OMAP_ECC_BCH4_CODE_HW) { | |
862 | + eccsize1 = 0xD; eccsize0 = 0x48; | |
863 | + bch_mod = 0; | |
864 | + bch_wrapmode = 0x09; | |
865 | + } else if (ecc_type == OMAP_ECC_BCH8_CODE_HW) { | |
866 | + eccsize1 = 0x1A; eccsize0 = 0x18; | |
867 | + bch_mod = 1; | |
868 | + bch_wrapmode = 0x04; | |
869 | + } else | |
870 | + eccsize1 = ((ecc_size >> 1) - 1); | |
873 | 871 | break; |
872 | + | |
874 | 873 | case GPMC_ECC_READSYN: |
875 | - gpmc_write_reg(GPMC_ECC_CONTROL, 0x100); | |
876 | 874 | break; |
875 | + | |
877 | 876 | case GPMC_ECC_WRITE: |
878 | - gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | |
877 | + if (ecc_type == OMAP_ECC_BCH4_CODE_HW) { | |
878 | + eccsize1 = 0x20; eccsize0 = 0x00; | |
879 | + bch_mod = 0; | |
880 | + bch_wrapmode = 0x06; | |
881 | + } else if (ecc_type == OMAP_ECC_BCH8_CODE_HW) { | |
882 | + eccsize1 = 0x20; eccsize0 = 0x00; | |
883 | + bch_mod = 1; | |
884 | + bch_wrapmode = 0x06; | |
885 | + } else | |
886 | + eccsize1 = ((ecc_size >> 1) - 1); | |
879 | 887 | break; |
888 | + | |
880 | 889 | default: |
881 | 890 | printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode); |
882 | 891 | break; |
883 | 892 | } |
884 | 893 | |
885 | - /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ | |
886 | - val = (dev_width << 7) | (cs << 1) | (0x1); | |
887 | - gpmc_write_reg(GPMC_ECC_CONFIG, val); | |
894 | + /* clear ecc and enable bits */ | |
895 | + if ((ecc_type == OMAP_ECC_BCH4_CODE_HW) || | |
896 | + (ecc_type == OMAP_ECC_BCH8_CODE_HW)) { | |
897 | + gpmc_write_reg(GPMC_ECC_CONTROL, 0x00000001); | |
898 | + ecc_size_conf_val = (eccsize1 << 22) | (eccsize0 << 12); | |
899 | + ecc_conf_val = ((0x01 << 16) | (bch_mod << 12) | |
900 | + | (bch_wrapmode << 8) | (dev_width << 7) | |
901 | + | (0x03 << 4) | (cs << 1) | (0x1)); | |
902 | + } else { | |
903 | + gpmc_write_reg(GPMC_ECC_CONTROL, 0x00000101); | |
904 | + ecc_size_conf_val = (eccsize1 << 22) | 0x0000000F; | |
905 | + ecc_conf_val = (dev_width << 7) | (cs << 1) | (0x1); | |
906 | + } | |
907 | + | |
908 | + gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, ecc_size_conf_val); | |
909 | + gpmc_write_reg(GPMC_ECC_CONFIG, ecc_conf_val); | |
910 | + gpmc_write_reg(GPMC_ECC_CONTROL, 0x00000101); | |
911 | + | |
888 | 912 | return 0; |
889 | 913 | } |
890 | 914 | EXPORT_SYMBOL(gpmc_enable_hwecc); |
891 | 915 | |
892 | 916 | /** |
893 | 917 | * gpmc_calculate_ecc - generate non-inverted ecc bytes |
918 | + * @ecc_type: ecc type e.g. Hamming, BCH | |
894 | 919 | * @cs: chip select number |
895 | 920 | * @dat: data pointer over which ecc is computed |
896 | 921 | * @ecc_code: ecc code buffer |
897 | 922 | |
898 | 923 | |
899 | 924 | |
900 | 925 | |
... | ... | @@ -901,21 +926,52 @@ |
901 | 926 | * an erased page will produce an ECC mismatch between generated and read |
902 | 927 | * ECC bytes that has to be dealt with separately. |
903 | 928 | */ |
904 | -int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code) | |
929 | +int gpmc_calculate_ecc(int ecc_type, int cs, | |
930 | + const u_char *dat, u_char *ecc_code) | |
905 | 931 | { |
906 | - unsigned int val = 0x0; | |
932 | + unsigned int reg; | |
933 | + unsigned int val1 = 0x0, val2 = 0x0; | |
934 | + unsigned int val3 = 0x0, val4 = 0x0; | |
935 | + int i; | |
907 | 936 | |
908 | - if (gpmc_ecc_used != cs) | |
909 | - return -EINVAL; | |
937 | + if ((ecc_type == OMAP_ECC_BCH4_CODE_HW) || | |
938 | + (ecc_type == OMAP_ECC_BCH8_CODE_HW)) { | |
939 | + for (i = 0; i < 4; i++) { | |
940 | + /* | |
941 | + * Reading HW ECC_BCH_Results | |
942 | + * 0x240-0x24C, 0x250-0x25C, 0x260-0x26C, 0x270-0x27C | |
943 | + */ | |
944 | + reg = GPMC_ECC_BCH_RESULT_0 + (0x10 * i); | |
945 | + val1 = gpmc_read_reg(reg); | |
946 | + val2 = gpmc_read_reg(reg + 4); | |
947 | + if (ecc_type == OMAP_ECC_BCH8_CODE_HW) { | |
948 | + val3 = gpmc_read_reg(reg + 8); | |
949 | + val4 = gpmc_read_reg(reg + 12); | |
910 | 950 | |
911 | - /* read ecc result */ | |
912 | - val = gpmc_read_reg(GPMC_ECC1_RESULT); | |
913 | - *ecc_code++ = val; /* P128e, ..., P1e */ | |
914 | - *ecc_code++ = val >> 16; /* P128o, ..., P1o */ | |
915 | - /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ | |
916 | - *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); | |
951 | + *ecc_code++ = (val4 & 0xFF); | |
952 | + *ecc_code++ = ((val3 >> 24) & 0xFF); | |
953 | + *ecc_code++ = ((val3 >> 16) & 0xFF); | |
954 | + *ecc_code++ = ((val3 >> 8) & 0xFF); | |
955 | + *ecc_code++ = (val3 & 0xFF); | |
956 | + *ecc_code++ = ((val2 >> 24) & 0xFF); | |
957 | + } | |
958 | + *ecc_code++ = ((val2 >> 16) & 0xFF); | |
959 | + *ecc_code++ = ((val2 >> 8) & 0xFF); | |
960 | + *ecc_code++ = (val2 & 0xFF); | |
961 | + *ecc_code++ = ((val1 >> 24) & 0xFF); | |
962 | + *ecc_code++ = ((val1 >> 16) & 0xFF); | |
963 | + *ecc_code++ = ((val1 >> 8) & 0xFF); | |
964 | + *ecc_code++ = (val1 & 0xFF); | |
965 | + } | |
966 | + } else { | |
967 | + /* read ecc result */ | |
968 | + val1 = gpmc_read_reg(GPMC_ECC1_RESULT); | |
969 | + *ecc_code++ = val1; /* P128e, ..., P1e */ | |
970 | + *ecc_code++ = val1 >> 16; /* P128o, ..., P1o */ | |
971 | + /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ | |
972 | + *ecc_code++ = ((val1 >> 8) & 0x0f) | ((val1 >> 20) & 0xf0); | |
973 | + } | |
917 | 974 | |
918 | - gpmc_ecc_used = -EINVAL; | |
919 | 975 | return 0; |
920 | 976 | } |
921 | 977 | EXPORT_SYMBOL(gpmc_calculate_ecc); |
arch/arm/plat-omap/include/plat/gpmc.h
... | ... | @@ -92,6 +92,8 @@ |
92 | 92 | OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ |
93 | 93 | /* 1-bit ecc: stored at beginning of spare area as romcode */ |
94 | 94 | OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ |
95 | + OMAP_ECC_BCH4_CODE_HW, /* gpmc bch detection & s/w method correction */ | |
96 | + OMAP_ECC_BCH8_CODE_HW, /* gpmc bch detection & s/w method correction */ | |
95 | 97 | }; |
96 | 98 | |
97 | 99 | /* |
... | ... | @@ -155,7 +157,7 @@ |
155 | 157 | extern int gpmc_nand_read(int cs, int cmd); |
156 | 158 | 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); | |
159 | -int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code); | |
160 | +int gpmc_enable_hwecc(int ecc, int cs, int mode, int dev_width, int ecc_size); | |
161 | +int gpmc_calculate_ecc(int ecc, int cs, const u_char *dat, u_char *ecc_code); | |
160 | 162 | #endif |
drivers/mtd/nand/Makefile
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o |
31 | 31 | obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o |
32 | 32 | obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o |
33 | +obj-$(CONFIG_MTD_NAND_OMAP2) += omap_bch_decoder.o | |
33 | 34 | obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o |
34 | 35 | obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o |
35 | 36 | obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o |
drivers/mtd/nand/omap2.c
... | ... | @@ -99,6 +99,8 @@ |
99 | 99 | |
100 | 100 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
101 | 101 | |
102 | +int decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc); | |
103 | + | |
102 | 104 | /* oob info generated runtime depending on ecc algorithm and layout selected */ |
103 | 105 | static struct nand_ecclayout omap_oobinfo; |
104 | 106 | /* Define some generic bad / good block scan pattern which are used |
... | ... | @@ -131,7 +133,8 @@ |
131 | 133 | OMAP_NAND_IO_WRITE, /* write */ |
132 | 134 | } iomode; |
133 | 135 | u_char *buf; |
134 | - int buf_len; | |
136 | + int buf_len; | |
137 | + int ecc_opt; | |
135 | 138 | }; |
136 | 139 | |
137 | 140 | /** |
... | ... | @@ -528,7 +531,6 @@ |
528 | 531 | struct omap_nand_info *info = container_of(mtd, |
529 | 532 | struct omap_nand_info, mtd); |
530 | 533 | int ret = 0; |
531 | - | |
532 | 534 | if (len <= mtd->oobsize) { |
533 | 535 | omap_read_buf_pref(mtd, buf, len); |
534 | 536 | return; |
... | ... | @@ -808,6 +810,8 @@ |
808 | 810 | mtd); |
809 | 811 | int blockCnt = 0, i = 0, ret = 0; |
810 | 812 | int stat = 0; |
813 | + int j, eccsize, eccflag, count; | |
814 | + unsigned int err_loc[8]; | |
811 | 815 | |
812 | 816 | /* Ex NAND_ECC_HW12_2048 */ |
813 | 817 | if ((info->nand.ecc.mode == NAND_ECC_HW) && |
814 | 818 | |
... | ... | @@ -816,17 +820,59 @@ |
816 | 820 | else |
817 | 821 | blockCnt = 1; |
818 | 822 | |
819 | - for (i = 0; i < blockCnt; i++) { | |
820 | - if (memcmp(read_ecc, calc_ecc, 3) != 0) { | |
821 | - ret = omap_compare_ecc(read_ecc, calc_ecc, dat); | |
822 | - if (ret < 0) | |
823 | - return ret; | |
824 | - /* keep track of the number of corrected errors */ | |
825 | - stat += ret; | |
823 | + switch (info->ecc_opt) { | |
824 | + case OMAP_ECC_HAMMING_CODE_HW: | |
825 | + case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: | |
826 | + for (i = 0; i < blockCnt; i++) { | |
827 | + if (memcmp(read_ecc, calc_ecc, 3) != 0) { | |
828 | + ret = omap_compare_ecc(read_ecc, calc_ecc, dat); | |
829 | + if (ret < 0) | |
830 | + return ret; | |
831 | + | |
832 | + /* keep track of number of corrected errors */ | |
833 | + stat += ret; | |
834 | + } | |
835 | + read_ecc += 3; | |
836 | + calc_ecc += 3; | |
837 | + dat += 512; | |
826 | 838 | } |
827 | - read_ecc += 3; | |
828 | - calc_ecc += 3; | |
829 | - dat += 512; | |
839 | + break; | |
840 | + | |
841 | + case OMAP_ECC_BCH4_CODE_HW: | |
842 | + eccsize = 7; | |
843 | + gpmc_calculate_ecc(info->ecc_opt, info->gpmc_cs, dat, calc_ecc); | |
844 | + for (i = 0; i < blockCnt; i++) { | |
845 | + /* check if any ecc error */ | |
846 | + eccflag = 0; | |
847 | + for (j = 0; (j < eccsize) && (eccflag == 0); j++) | |
848 | + if (calc_ecc[j] != 0) | |
849 | + eccflag = 1; | |
850 | + | |
851 | + if (eccflag == 1) { | |
852 | + eccflag = 0; | |
853 | + for (j = 0; (j < eccsize) && | |
854 | + (eccflag == 0); j++) | |
855 | + if (read_ecc[j] != 0xFF) | |
856 | + eccflag = 1; | |
857 | + } | |
858 | + | |
859 | + count = 0; | |
860 | + if (eccflag == 1) | |
861 | + count = decode_bch(0, calc_ecc, err_loc); | |
862 | + | |
863 | + for (j = 0; j < count; j++) { | |
864 | + if (err_loc[j] < 4096) | |
865 | + dat[err_loc[j] >> 3] ^= | |
866 | + 1 << (err_loc[j] & 7); | |
867 | + /* else, not interested to correct ecc */ | |
868 | + } | |
869 | + | |
870 | + stat += count; | |
871 | + calc_ecc = calc_ecc + eccsize; | |
872 | + read_ecc = read_ecc + eccsize; | |
873 | + dat += 512; | |
874 | + } | |
875 | + break; | |
830 | 876 | } |
831 | 877 | return stat; |
832 | 878 | } |
... | ... | @@ -848,7 +894,7 @@ |
848 | 894 | { |
849 | 895 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
850 | 896 | mtd); |
851 | - return gpmc_calculate_ecc(info->gpmc_cs, dat, ecc_code); | |
897 | + return gpmc_calculate_ecc(info->ecc_opt, info->gpmc_cs, dat, ecc_code); | |
852 | 898 | } |
853 | 899 | |
854 | 900 | /** |
... | ... | @@ -863,7 +909,8 @@ |
863 | 909 | struct nand_chip *chip = mtd->priv; |
864 | 910 | unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; |
865 | 911 | |
866 | - gpmc_enable_hwecc(info->gpmc_cs, mode, dev_width, info->nand.ecc.size); | |
912 | + gpmc_enable_hwecc(info->ecc_opt, info->gpmc_cs, mode, | |
913 | + dev_width, info->nand.ecc.size); | |
867 | 914 | } |
868 | 915 | |
869 | 916 | /** |
... | ... | @@ -960,6 +1007,7 @@ |
960 | 1007 | info->mtd.priv = &info->nand; |
961 | 1008 | info->mtd.name = dev_name(&pdev->dev); |
962 | 1009 | info->mtd.owner = THIS_MODULE; |
1010 | + info->ecc_opt = pdata->ecc_opt; | |
963 | 1011 | |
964 | 1012 | info->nand.options = pdata->devsize; |
965 | 1013 | info->nand.options |= NAND_SKIP_BBTSCAN; |
... | ... | @@ -998,7 +1046,6 @@ |
998 | 1046 | info->nand.waitfunc = omap_wait; |
999 | 1047 | info->nand.chip_delay = 50; |
1000 | 1048 | } |
1001 | - | |
1002 | 1049 | switch (pdata->xfer_type) { |
1003 | 1050 | case NAND_OMAP_PREFETCH_POLLED: |
1004 | 1051 | info->nand.read_buf = omap_read_buf_pref; |
... | ... | @@ -1059,10 +1106,17 @@ |
1059 | 1106 | /* selsect the ecc type */ |
1060 | 1107 | if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) |
1061 | 1108 | info->nand.ecc.mode = NAND_ECC_SOFT; |
1062 | - else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) || | |
1063 | - (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { | |
1064 | - info->nand.ecc.bytes = 3; | |
1065 | - info->nand.ecc.size = 512; | |
1109 | + else { | |
1110 | + if (pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) { | |
1111 | + info->nand.ecc.bytes = 4*7; | |
1112 | + info->nand.ecc.size = 4*512; | |
1113 | + } else if (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW) { | |
1114 | + info->nand.ecc.bytes = 13; | |
1115 | + info->nand.ecc.size = 4*512; | |
1116 | + } else { | |
1117 | + info->nand.ecc.bytes = 3; | |
1118 | + info->nand.ecc.size = 512; | |
1119 | + } | |
1066 | 1120 | info->nand.ecc.calculate = omap_calculate_ecc; |
1067 | 1121 | info->nand.ecc.hwctl = omap_enable_hwecc; |
1068 | 1122 | info->nand.ecc.correct = omap_correct_data; |
drivers/mtd/nand/omap_bch_decoder.c
1 | +/* | |
2 | + * drivers/mtd/nand/omap_omap_bch_decoder.c | |
3 | + * | |
4 | + * Whole BCH ECC Decoder (Post hardware generated syndrome decoding) | |
5 | + * | |
6 | + * Copyright (c) 2007 Texas Instruments | |
7 | + * | |
8 | + * Author: Sukumar Ghorai <s-ghorai@ti.com | |
9 | + * Michael Fillinger <m-fillinger@ti.com> | |
10 | + * | |
11 | + * This program is free software; you can redistribute it and/or modify | |
12 | + * it under the terms of the GNU General Public License version 2 as | |
13 | + * published by the Free Software Foundation. | |
14 | + */ | |
15 | +#undef DEBUG | |
16 | + | |
17 | +#include <linux/kernel.h> | |
18 | +#include <linux/module.h> | |
19 | + | |
20 | +#define mm 13 | |
21 | +#define kk_shorten 4096 | |
22 | +#define nn 8191 /* Length of codeword, n = 2**mm - 1 */ | |
23 | + | |
24 | +#define PPP 0x201B /* Primary Polynomial : x^13 + x^4 + x^3 + x + 1 */ | |
25 | +#define P 0x001B /* With omitted x^13 */ | |
26 | +#define POLY 12 /* degree of the primary Polynomial less one */ | |
27 | + | |
28 | +/** | |
29 | + * mpy_mod_gf - GALOIS field multiplier | |
30 | + * Input : A(x), B(x) | |
31 | + * Output : A(x)*B(x) mod P(x) | |
32 | + */ | |
33 | +static unsigned int mpy_mod_gf(unsigned int a, unsigned int b) | |
34 | +{ | |
35 | + unsigned int R = 0; | |
36 | + unsigned int R1 = 0; | |
37 | + unsigned int k = 0; | |
38 | + | |
39 | + for (k = 0; k < mm; k++) { | |
40 | + | |
41 | + R = (R << 1) & 0x1FFE; | |
42 | + if (R1 == 1) | |
43 | + R ^= P; | |
44 | + | |
45 | + if (((a >> (POLY - k)) & 1) == 1) | |
46 | + R ^= b; | |
47 | + | |
48 | + if (k < POLY) | |
49 | + R1 = (R >> POLY) & 1; | |
50 | + } | |
51 | + return R; | |
52 | +} | |
53 | + | |
54 | +/** | |
55 | + * chien - CHIEN search | |
56 | + * | |
57 | + * @location - Error location vector pointer | |
58 | + * | |
59 | + * Inputs : ELP(z) | |
60 | + * No. of found errors | |
61 | + * Size of input codeword | |
62 | + * Outputs : Up to 8 locations | |
63 | + * No. of errors | |
64 | + */ | |
65 | +static int chien(unsigned int select_4_8, int err_nums, | |
66 | + unsigned int err[], unsigned int *location) | |
67 | +{ | |
68 | + int i, count; /* Number of dectected errors */ | |
69 | + /* Contains accumulation of evaluation at x^i (i:1->8) */ | |
70 | + unsigned int gammas[8] = {0}; | |
71 | + unsigned int alpha; | |
72 | + unsigned int bit, ecc_bits; | |
73 | + unsigned int elp_sum; | |
74 | + | |
75 | + ecc_bits = (select_4_8 == 0) ? 52 : 104; | |
76 | + | |
77 | + /* Start evaluation at Alpha**8192 and decreasing */ | |
78 | + for (i = 0; i < 8; i++) | |
79 | + gammas[i] = err[i]; | |
80 | + | |
81 | + count = 0; | |
82 | + for (i = 1; (i <= nn) && (count < err_nums); i++) { | |
83 | + | |
84 | + /* Result of evaluation at root */ | |
85 | + elp_sum = 1 ^ gammas[0] ^ gammas[1] ^ | |
86 | + gammas[2] ^ gammas[3] ^ | |
87 | + gammas[4] ^ gammas[5] ^ | |
88 | + gammas[6] ^ gammas[7]; | |
89 | + | |
90 | + alpha = PPP >> 1; | |
91 | + gammas[0] = mpy_mod_gf(gammas[0], alpha); | |
92 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-2 */ | |
93 | + gammas[1] = mpy_mod_gf(gammas[1], alpha); | |
94 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-2 */ | |
95 | + gammas[2] = mpy_mod_gf(gammas[2], alpha); | |
96 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-3 */ | |
97 | + gammas[3] = mpy_mod_gf(gammas[3], alpha); | |
98 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-4 */ | |
99 | + gammas[4] = mpy_mod_gf(gammas[4], alpha); | |
100 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-5 */ | |
101 | + gammas[5] = mpy_mod_gf(gammas[5], alpha); | |
102 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-6 */ | |
103 | + gammas[6] = mpy_mod_gf(gammas[6], alpha); | |
104 | + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-7 */ | |
105 | + gammas[7] = mpy_mod_gf(gammas[7], alpha); | |
106 | + | |
107 | + if (elp_sum == 0) { | |
108 | + /* calculate bit position in main data area */ | |
109 | + bit = ((i-1) & ~7)|(7-((i-1) & 7)); | |
110 | + if (i >= 2 * ecc_bits) | |
111 | + location[count++] = | |
112 | + kk_shorten - (bit - 2 * ecc_bits) - 1; | |
113 | + } | |
114 | + } | |
115 | + | |
116 | + /* Failure: No. of detected errors != No. or corrected errors */ | |
117 | + if (count != err_nums) { | |
118 | + count = -1; | |
119 | + printk(KERN_ERR "BCH decoding failed\n"); | |
120 | + } | |
121 | + for (i = 0; i < count; i++) | |
122 | + pr_debug("%d ", location[i]); | |
123 | + | |
124 | + return count; | |
125 | +} | |
126 | + | |
127 | +/* synd : 16 Syndromes | |
128 | + * return: gamaas - Coefficients to the error polynomial | |
129 | + * return: : Number of detected errors | |
130 | +*/ | |
131 | +static unsigned int berlekamp(unsigned int select_4_8, | |
132 | + unsigned int synd[], unsigned int err[]) | |
133 | +{ | |
134 | + int loop, iteration; | |
135 | + unsigned int LL = 0; /* Detected errors */ | |
136 | + unsigned int d = 0; /* Distance between Syndromes and ELP[n](z) */ | |
137 | + unsigned int invd = 0; /* Inverse of d */ | |
138 | + /* Intermediate ELP[n](z). | |
139 | + * Final ELP[n](z) is Error Location Polynomial | |
140 | + */ | |
141 | + unsigned int gammas[16] = {0}; | |
142 | + /* Intermediate normalized ELP[n](z) : D[n](z) */ | |
143 | + unsigned int D[16] = {0}; | |
144 | + /* Temporary value that holds an ELP[n](z) coefficient */ | |
145 | + unsigned int next_gamma = 0; | |
146 | + | |
147 | + int e = 0; | |
148 | + unsigned int sign = 0; | |
149 | + unsigned int u = 0; | |
150 | + unsigned int v = 0; | |
151 | + unsigned int C1 = 0, C2 = 0; | |
152 | + unsigned int ss = 0; | |
153 | + unsigned int tmp_v = 0, tmp_s = 0; | |
154 | + unsigned int tmp_poly; | |
155 | + | |
156 | + /*-------------- Step 0 ------------------*/ | |
157 | + for (loop = 0; loop < 16; loop++) | |
158 | + gammas[loop] = 0; | |
159 | + gammas[0] = 1; | |
160 | + D[1] = 1; | |
161 | + | |
162 | + iteration = 0; | |
163 | + LL = 0; | |
164 | + while ((iteration < ((select_4_8+1)*2*4)) && | |
165 | + (LL <= ((select_4_8+1)*4))) { | |
166 | + | |
167 | + pr_debug("\nIteration.............%d\n", iteration); | |
168 | + d = 0; | |
169 | + /* Step: 0 */ | |
170 | + for (loop = 0; loop <= LL; loop++) { | |
171 | + tmp_poly = mpy_mod_gf( | |
172 | + gammas[loop], synd[iteration - loop]); | |
173 | + d ^= tmp_poly; | |
174 | + pr_debug("%02d. s=0 LL=%x poly %x\n", | |
175 | + loop, LL, tmp_poly); | |
176 | + } | |
177 | + | |
178 | + /* Step 1: 1 cycle only to perform inversion */ | |
179 | + v = d << 1; | |
180 | + e = -1; | |
181 | + sign = 1; | |
182 | + ss = 0x2000; | |
183 | + invd = 0; | |
184 | + u = PPP; | |
185 | + for (loop = 0; (d != 0) && (loop <= (2 * POLY)); loop++) { | |
186 | + pr_debug("%02d. s=1 LL=%x poly NULL\n", | |
187 | + loop, LL); | |
188 | + C1 = (v >> 13) & 1; | |
189 | + C2 = C1 & sign; | |
190 | + | |
191 | + sign ^= C2 ^ (e == 0); | |
192 | + | |
193 | + tmp_v = v; | |
194 | + tmp_s = ss; | |
195 | + | |
196 | + if (C1 == 1) { | |
197 | + v ^= u; | |
198 | + ss ^= invd; | |
199 | + } | |
200 | + v = (v << 1) & 0x3FFF; | |
201 | + if (C2 == 1) { | |
202 | + u = tmp_v; | |
203 | + invd = tmp_s; | |
204 | + e = -e; | |
205 | + } | |
206 | + invd >>= 1; | |
207 | + e--; | |
208 | + } | |
209 | + | |
210 | + for (loop = 0; (d != 0) && (loop <= (iteration + 1)); loop++) { | |
211 | + /* Step 2 | |
212 | + * Interleaved with Step 3, if L<(n-k) | |
213 | + * invd: Update of ELP[n](z) = ELP[n-1](z) - d.D[n-1](z) | |
214 | + */ | |
215 | + | |
216 | + /* Holds value of ELP coefficient until precedent | |
217 | + * value does not have to be used anymore | |
218 | + */ | |
219 | + tmp_poly = mpy_mod_gf(d, D[loop]); | |
220 | + pr_debug("%02d. s=2 LL=%x poly %x\n", | |
221 | + loop, LL, tmp_poly); | |
222 | + | |
223 | + next_gamma = gammas[loop] ^ tmp_poly; | |
224 | + if ((2 * LL) < (iteration + 1)) { | |
225 | + /* Interleaving with Step 3 | |
226 | + * for parallelized update of ELP(z) and D(z) | |
227 | + */ | |
228 | + } else { | |
229 | + /* Update of ELP(z) only -> stay in Step 2 */ | |
230 | + gammas[loop] = next_gamma; | |
231 | + if (loop == (iteration + 1)) { | |
232 | + /* to step 4 */ | |
233 | + break; | |
234 | + } | |
235 | + } | |
236 | + | |
237 | + /* Step 3 | |
238 | + * Always interleaved with Step 2 (case when L<(n-k)) | |
239 | + * Update of D[n-1](z) = ELP[n-1](z)/d | |
240 | + */ | |
241 | + D[loop] = mpy_mod_gf(gammas[loop], invd); | |
242 | + pr_debug("%02d. s=3 LL=%x poly %x\n", | |
243 | + loop, LL, D[loop]); | |
244 | + | |
245 | + /* Can safely update ELP[n](z) */ | |
246 | + gammas[loop] = next_gamma; | |
247 | + | |
248 | + if (loop == (iteration + 1)) { | |
249 | + /* If update finished */ | |
250 | + LL = iteration - LL + 1; | |
251 | + /* to step 4 */ | |
252 | + break; | |
253 | + } | |
254 | + /* Else, interleaving to step 2*/ | |
255 | + } | |
256 | + | |
257 | + /* Step 4: Update D(z): i:0->L */ | |
258 | + /* Final update of D[n](z) = D[n](z).z*/ | |
259 | + for (loop = 0; loop < 15; loop++) /* Left Shift */ | |
260 | + D[15 - loop] = D[14 - loop]; | |
261 | + | |
262 | + D[0] = 0; | |
263 | + | |
264 | + iteration++; | |
265 | + } /* while */ | |
266 | + | |
267 | + /* Processing finished, copy ELP to final registers : 0->2t-1*/ | |
268 | + for (loop = 0; loop < 8; loop++) | |
269 | + err[loop] = gammas[loop+1]; | |
270 | + | |
271 | + pr_debug("\n Err poly:"); | |
272 | + for (loop = 0; loop < 8; loop++) | |
273 | + pr_debug("0x%x ", err[loop]); | |
274 | + | |
275 | + return LL; | |
276 | +} | |
277 | + | |
278 | +/* | |
279 | + * syndrome - Generate syndrome components from hw generate syndrome | |
280 | + * r(x) = c(x) + e(x) | |
281 | + * s(x) = c(x) mod g(x) + e(x) mod g(x) = e(x) mod g(x) | |
282 | + * so receiver checks if the syndrome s(x) = r(x) mod g(x) is equal to zero. | |
283 | + * unsigned int s[16]; - Syndromes | |
284 | + */ | |
285 | +static void syndrome(unsigned int select_4_8, | |
286 | + unsigned char *ecc, unsigned int syn[]) | |
287 | +{ | |
288 | + unsigned int k, l, t; | |
289 | + unsigned int alpha_bit, R_bit; | |
290 | + int ecc_pos, ecc_min; | |
291 | + | |
292 | + /* 2t-1 = 15 (for t=8) minimal polynomials of the first 15 powers of a | |
293 | + * primitive elemmants of GF(m); Even powers minimal polynomials are | |
294 | + * duplicate of odd powers' minimal polynomials. | |
295 | + * Odd powers of alpha (1 to 15) | |
296 | + */ | |
297 | + unsigned int pow_alpha[8] = {0x0002, 0x0008, 0x0020, 0x0080, | |
298 | + 0x0200, 0x0800, 0x001B, 0x006C}; | |
299 | + | |
300 | + pr_debug("\n ECC[0..n]: "); | |
301 | + for (k = 0; k < 13; k++) | |
302 | + pr_debug("0x%x ", ecc[k]); | |
303 | + | |
304 | + if (select_4_8 == 0) { | |
305 | + t = 4; | |
306 | + ecc_pos = 55; /* bits(52-bits): 55->4 */ | |
307 | + ecc_min = 4; | |
308 | + } else { | |
309 | + t = 8; | |
310 | + ecc_pos = 103; /* bits: 103->0 */ | |
311 | + ecc_min = 0; | |
312 | + } | |
313 | + | |
314 | + /* total numbber of syndrom to be used is 2t */ | |
315 | + /* Step1: calculate the odd syndrome(s) */ | |
316 | + R_bit = ((ecc[ecc_pos/8] >> (7 - ecc_pos%8)) & 1); | |
317 | + ecc_pos--; | |
318 | + for (k = 0; k < t; k++) | |
319 | + syn[2 * k] = R_bit; | |
320 | + | |
321 | + while (ecc_pos >= ecc_min) { | |
322 | + R_bit = ((ecc[ecc_pos/8] >> (7 - ecc_pos%8)) & 1); | |
323 | + ecc_pos--; | |
324 | + | |
325 | + for (k = 0; k < t; k++) { | |
326 | + /* Accumulate value of x^i at alpha^(2k+1) */ | |
327 | + if (R_bit == 1) | |
328 | + syn[2*k] ^= pow_alpha[k]; | |
329 | + | |
330 | + /* Compute a**(2k+1), using LSFR */ | |
331 | + for (l = 0; l < (2 * k + 1); l++) { | |
332 | + alpha_bit = (pow_alpha[k] >> POLY) & 1; | |
333 | + pow_alpha[k] = (pow_alpha[k] << 1) & 0x1FFF; | |
334 | + if (alpha_bit == 1) | |
335 | + pow_alpha[k] ^= P; | |
336 | + } | |
337 | + } | |
338 | + } | |
339 | + | |
340 | + /* Step2: calculate the even syndrome(s) | |
341 | + * Compute S(a), where a is an even power of alpha | |
342 | + * Evenry even power of primitive element has the same minimal | |
343 | + * polynomial as some odd power of elemets. | |
344 | + * And based on S(a^2) = S^2(a) | |
345 | + */ | |
346 | + for (k = 0; k < t; k++) | |
347 | + syn[2*k+1] = mpy_mod_gf(syn[k], syn[k]); | |
348 | + | |
349 | + pr_debug("\n Syndromes: "); | |
350 | + for (k = 0; k < 16; k++) | |
351 | + pr_debug("0x%x ", syn[k]); | |
352 | +} | |
353 | + | |
354 | +/** | |
355 | + * decode_bch - BCH decoder for 4- and 8-bit error correction | |
356 | + * | |
357 | + * @ecc - ECC syndrome generated by hw BCH engine | |
358 | + * @err_loc - pointer to error location array | |
359 | + * | |
360 | + * This function does post sydrome generation (hw generated) decoding | |
361 | + * for:- | |
362 | + * Dimension of Galoise Field: m = 13 | |
363 | + * Length of codeword: n = 2**m - 1 | |
364 | + * Number of errors that can be corrected: 4- or 8-bits | |
365 | + * Length of information bit: kk = nn - rr | |
366 | + */ | |
367 | +int decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc) | |
368 | +{ | |
369 | + int no_of_err; | |
370 | + unsigned int syn[16] = {0,}; /* 16 Syndromes */ | |
371 | + unsigned int err_poly[8] = {0,}; | |
372 | + /* Coefficients to the error polynomial | |
373 | + * ELP(x) = 1 + err0.x + err1.x^2 + ... + err7.x^8 | |
374 | + */ | |
375 | + | |
376 | + /* Decoting involes three steps | |
377 | + * 1. Compute the syndrom from teh received codeword, | |
378 | + * 2. Find the error location polynomial from a set of equations | |
379 | + * derived from the syndrome, | |
380 | + * 3. Use the error location polynomial to identify errants bits, | |
381 | + * | |
382 | + * And correcttion done by bit flips using error locaiton and expected | |
383 | + * to be outseide of this implementation. | |
384 | + */ | |
385 | + syndrome(select_4_8, ecc, syn); | |
386 | + no_of_err = berlekamp(select_4_8, syn, err_poly); | |
387 | + if (no_of_err <= (4 << select_4_8)) | |
388 | + no_of_err = chien(select_4_8, no_of_err, err_poly, err_loc); | |
389 | + | |
390 | + return no_of_err; | |
391 | +} | |
392 | +EXPORT_SYMBOL(decode_bch); |