linux-old/drivers/mtd/nand/nand_ecc.c
<<
>>
Prefs
   1/*
   2 *  drivers/mtd/nand_ecc.c
   3 *
   4 *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
   5 *                     Toshiba America Electronics Components, Inc.
   6 *
   7 * $Id: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public License
  11 * version 2.1 as published by the Free Software Foundation.
  12 *
  13 * This file contains an ECC algorithm from Toshiba that detects and
  14 * corrects 1 bit errors in a 256 byte block of data.
  15 */
  16
  17#include <linux/types.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20
  21/*
  22 * Pre-calculated 256-way 1 byte column parity
  23 */
  24static const u_char nand_ecc_precalc_table[] = {
  25        0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
  26        0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
  27        0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
  28        0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
  29        0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
  30        0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
  31        0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
  32        0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
  33        0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
  34        0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
  35        0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
  36        0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
  37        0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
  38        0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
  39        0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
  40        0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
  41};
  42
  43
  44/*
  45 * Creates non-inverted ECC code from line parity
  46 */
  47static void nand_trans_result(u_char reg2, u_char reg3,
  48        u_char *ecc_code)
  49{
  50        u_char a, b, i, tmp1, tmp2;
  51        
  52        /* Initialize variables */
  53        a = b = 0x80;
  54        tmp1 = tmp2 = 0;
  55        
  56        /* Calculate first ECC byte */
  57        for (i = 0; i < 4; i++) {
  58                if (reg3 & a)           /* LP15,13,11,9 --> ecc_code[0] */
  59                        tmp1 |= b;
  60                b >>= 1;
  61                if (reg2 & a)           /* LP14,12,10,8 --> ecc_code[0] */
  62                        tmp1 |= b;
  63                b >>= 1;
  64                a >>= 1;
  65        }
  66        
  67        /* Calculate second ECC byte */
  68        b = 0x80;
  69        for (i = 0; i < 4; i++) {
  70                if (reg3 & a)           /* LP7,5,3,1 --> ecc_code[1] */
  71                        tmp2 |= b;
  72                b >>= 1;
  73                if (reg2 & a)           /* LP6,4,2,0 --> ecc_code[1] */
  74                        tmp2 |= b;
  75                b >>= 1;
  76                a >>= 1;
  77        }
  78        
  79        /* Store two of the ECC bytes */
  80        ecc_code[0] = tmp1;
  81        ecc_code[1] = tmp2;
  82}
  83
  84/*
  85 * Calculate 3 byte ECC code for 256 byte block
  86 */
  87void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
  88{
  89        u_char idx, reg1, reg2, reg3;
  90        int j;
  91        
  92        /* Initialize variables */
  93        reg1 = reg2 = reg3 = 0;
  94        ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
  95        
  96        /* Build up column parity */ 
  97        for(j = 0; j < 256; j++) {
  98                
  99                /* Get CP0 - CP5 from table */
 100                idx = nand_ecc_precalc_table[dat[j]];
 101                reg1 ^= (idx & 0x3f);
 102                
 103                /* All bit XOR = 1 ? */
 104                if (idx & 0x40) {
 105                        reg3 ^= (u_char) j;
 106                        reg2 ^= ~((u_char) j);
 107                }
 108        }
 109        
 110        /* Create non-inverted ECC code from line parity */
 111        nand_trans_result(reg2, reg3, ecc_code);
 112        
 113        /* Calculate final ECC code */
 114        ecc_code[0] = ~ecc_code[0];
 115        ecc_code[1] = ~ecc_code[1];
 116        ecc_code[2] = ((~reg1) << 2) | 0x03;
 117}
 118
 119/*
 120 * Detect and correct a 1 bit error for 256 byte block
 121 */
 122int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
 123{
 124        u_char a, b, c, d1, d2, d3, add, bit, i;
 125        
 126        /* Do error detection */ 
 127        d1 = calc_ecc[0] ^ read_ecc[0];
 128        d2 = calc_ecc[1] ^ read_ecc[1];
 129        d3 = calc_ecc[2] ^ read_ecc[2];
 130        
 131        if ((d1 | d2 | d3) == 0) {
 132                /* No errors */
 133                return 0;
 134        }
 135        else {
 136                a = (d1 ^ (d1 >> 1)) & 0x55;
 137                b = (d2 ^ (d2 >> 1)) & 0x55;
 138                c = (d3 ^ (d3 >> 1)) & 0x54;
 139                
 140                /* Found and will correct single bit error in the data */
 141                if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
 142                        c = 0x80;
 143                        add = 0;
 144                        a = 0x80;
 145                        for (i=0; i<4; i++) {
 146                                if (d1 & c)
 147                                        add |= a;
 148                                c >>= 2;
 149                                a >>= 1;
 150                        }
 151                        c = 0x80;
 152                        for (i=0; i<4; i++) {
 153                                if (d2 & c)
 154                                        add |= a;
 155                                c >>= 2;
 156                                a >>= 1;
 157                        }
 158                        bit = 0;
 159                        b = 0x04;
 160                        c = 0x80;
 161                        for (i=0; i<3; i++) {
 162                                if (d3 & c)
 163                                        bit |= b;
 164                                c >>= 2;
 165                                b >>= 1;
 166                        }
 167                        b = 0x01;
 168                        a = dat[add];
 169                        a ^= (b << bit);
 170                        dat[add] = a;
 171                        return 1;
 172                }
 173                else {
 174                        i = 0;
 175                        while (d1) {
 176                                if (d1 & 0x01)
 177                                        ++i;
 178                                d1 >>= 1;
 179                        }
 180                        while (d2) {
 181                                if (d2 & 0x01)
 182                                        ++i;
 183                                d2 >>= 1;
 184                        }
 185                        while (d3) {
 186                                if (d3 & 0x01)
 187                                        ++i;
 188                                d3 >>= 1;
 189                        }
 190                        if (i == 1) {
 191                                /* ECC Code Error Correction */
 192                                read_ecc[0] = calc_ecc[0];
 193                                read_ecc[1] = calc_ecc[1];
 194                                read_ecc[2] = calc_ecc[2];
 195                                return 2;
 196                        }
 197                        else {
 198                                /* Uncorrectable Error */
 199                                return -1;
 200                        }
 201                }
 202        }
 203        
 204        /* Should never happen */
 205        return -1;
 206}
 207
 208EXPORT_SYMBOL(nand_calculate_ecc);
 209EXPORT_SYMBOL(nand_correct_data);
 210
 211MODULE_LICENSE("GPL");
 212MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com>");
 213MODULE_DESCRIPTION("Generic NAND ECC support");
 214