diff options
author | Todd Doucet <todd.doucet@timesys.com> | 2010-02-03 17:06:33 -0500 |
---|---|---|
committer | Todd Doucet <todd.doucet@timesys.com> | 2010-02-03 17:06:33 -0500 |
commit | ff238a4df84428befc55d49f58864dfc47ff853d (patch) | |
tree | 8796b828b396f5b7ed017904ff77b614dbf38677 /drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c | |
parent | 4a6908a3a050aacc9c3a2f36b276b46c0629ad91 (diff) |
Kernel as received from Digi for their Wi-Mx51 SoC running on their
CCWMX51JS carrier board.
Diffstat (limited to 'drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c')
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c | 196 |
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; +} + |