summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c')
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
new file mode 100644
index 000000000000..8d3975efbc6e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
@@ -0,0 +1,196 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "gpmi-hamming-22-16.h"
+
+#define BIT_VAL(v, n) (((v) >> (n)) & 0x1)
+#define B(n) (BIT_VAL(d, n))
+#define BSEQ(a1, a2, a3, a4, a5, a6, a7, a8) \
+ (B(a1) ^ B(a2) ^ B(a3) ^ B(a4) ^ B(a5) ^ B(a6) ^ B(a7) ^ B(a8))
+static u8 calculate_parity(u16 d)
+{
+ u8 p = 0;
+
+ if (d == 0 || d == 0xFFFF)
+ return 0; /* optimization :) */
+
+ p |= BSEQ(15, 12, 11, 8, 5, 4, 3, 2) << 0;
+ p |= BSEQ(13, 12, 11, 10, 9, 7, 3, 1) << 1;
+ p |= BSEQ(15, 14, 13, 11, 10, 9, 6, 5) << 2;
+ p |= BSEQ(15, 14, 13, 8, 7, 6, 4, 0) << 3;
+ p |= BSEQ(12, 9, 8, 7, 6, 2, 1, 0) << 4;
+ p |= BSEQ(14, 10, 5, 4, 3, 2, 1, 0) << 5;
+ return p;
+}
+
+static inline int even_number_of_1s(u8 byte)
+{
+ int even = 1;
+
+ while (byte > 0) {
+ even ^= (byte & 0x1);
+ byte >>= 1;
+ }
+ return even;
+}
+
+static int lookup_single_error(u8 syndrome)
+{
+ int i;
+ u8 syndrome_table[] = {
+ 0x38, 0x32, 0x31, 0x23, 0x29, 0x25, 0x1C, 0x1A,
+ 0x19, 0x16, 0x26, 0x07, 0x13, 0x0E, 0x2C, 0x0D,
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(syndrome_table); i++)
+ if (syndrome_table[i] == syndrome)
+ return i;
+ return -ENOENT;
+}
+
+int gpmi_verify_hamming_22_16(void *data, u8 *parity, size_t size)
+{
+ int i, j;
+ int bit_index, bit_to_flip;
+ u16 *pdata = data;
+ u8 p = 0, np, syndrome;
+ int errors = 0;
+
+ for (bit_index = i = j = 0;
+ i < size / sizeof(u16);
+ i ++, pdata++) {
+
+ switch (bit_index) {
+
+ case 0:
+ p = parity[j] & 0x3F;
+ break;
+ case 2:
+ p = (parity[j++] & 0xC0) >> 6;
+ p |= (parity[j] & 0x0F) << 2;
+ break;
+ case 4:
+ p = (parity[j++] & 0xF0) >> 4;
+ p |= (parity[j] & 0x03) << 4;
+ break;
+ case 6:
+ p = (parity[j++] & 0xFC) >> 2;
+ break;
+ default:
+ BUG(); /* how did you get this ?! */
+ break;
+ }
+ bit_index = (bit_index + 2) % 8;
+
+ np = calculate_parity(*pdata);
+ syndrome = np ^ p;
+ if (syndrome == 0) /* cool */ {
+ continue;
+ }
+
+ if (even_number_of_1s(syndrome))
+ return -i; /* can't recover */
+
+ bit_to_flip = lookup_single_error(syndrome);
+ if (bit_to_flip < 0)
+ return -i; /* can't fix the error */
+
+ if (bit_to_flip < 16) {
+ *pdata ^= (1 << bit_to_flip);
+ errors++;
+ }
+ }
+ return errors;
+}
+
+void gpmi_encode_hamming_22_16(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size)
+{
+ int i, j, bit_index;
+ u16 *src = source_block;
+ u8 *ecc = source_ecc;
+ u8 np;
+
+ for (bit_index = j = i = 0;
+ j < src_size/sizeof(u16) && i < ecc_size;
+ j++) {
+
+ np = calculate_parity(src[j]);
+
+ switch (bit_index) {
+
+ case 0:
+ ecc[i] = np & 0x3F;
+ break;
+ case 2:
+ ecc[i++] |= (np & 0x03) << 6;
+ ecc[i] = (np & 0x3C) >> 2;
+ break;
+ case 4:
+ ecc[i++] |= (np & 0x0F) << 4;
+ ecc[i] = (np & 0x30) >> 4;
+ break;
+ case 6:
+ ecc[i++] |= (np & 0x3F) << 2;
+ break;
+ }
+ bit_index = (bit_index + 2) % 8;
+ }
+
+}
+
+void gpmi_encode_hamming_ncb_22_16(void *source_block, size_t source_size,
+ void *target_block, size_t target_size)
+{
+ u8 *dst = target_block;
+ u8 ecc[NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES];
+
+ gpmi_encode_hamming_22_16(source_block,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ecc,
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ /* create THREE copies of source block */
+ memcpy(dst + NAND_HC_ECC_OFFSET_FIRST_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_THIRD_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ /* ..and three copies of ECC block */
+ memcpy(dst + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+}
+
+unsigned gpmi_hamming_ecc_size_22_16(int block_size)
+{
+ return (((block_size * 8) / 16) * 6) / 8;
+}
+