atmel-asf-projects/avr32/services/storage/ecc_hamming/ecc-sw.c

217 lines
7.7 KiB
C

/*****************************************************************************
*
* \file
*
* \brief Hamming ECC software implementation.
*
* This file contains a software Hamming ECC implementation.
*
* Copyright (c) 2014-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*****************************************************************************/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include "nand.h"
#include "ecc-sw.h"
/*! \brief Pre-calculated parity table
*
* This table is a pre-calculation of the 256 possible parities used when
* calculating ECC. Each value in this table represents a 6-bit value of
* the column parity and a 1-bit value of the row parity.
*
* Storage form: null [7], row parity [6], column parity [5:0]
*/
static const unsigned char ecc_parity_table[] = {
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f,
0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, 0x65, 0x30, 0x33, 0x66,
0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33,
0x30, 0x65, 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, 0x03, 0x56,
0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a,
0x00, 0x55, 0x56, 0x03, 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65,
0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03,
0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, 0x0f, 0x5a, 0x59, 0x0c,
0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59,
0x5a, 0x0f, 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, 0x6a, 0x3f,
0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33,
0x69, 0x3c, 0x3f, 0x6a, 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03,
0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03,
0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, 0x69, 0x3c, 0x3f, 0x6a,
0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f,
0x3c, 0x69, 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, 0x66, 0x33,
0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f,
0x65, 0x30, 0x33, 0x66, 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69,
0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f,
0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
};
/*! \brief Generate ECC for a 256 byte block.
*
* This function will generate a 3 byte ECC for a 256 byte data block.
*
* \param ecc Pointer to a variable to store the calculated ECC.
* \param data Pointer to a 256 byte large buffer to calculate the ECC
* value of.
*/
void ecc_make_block_256b(unsigned long *ecc, unsigned char *data)
{
unsigned int i;
unsigned char *data_p = data;
unsigned char row_parity0;
unsigned char row_parity1;
unsigned char col_parity = 0;
unsigned char row_parity_odd = 0;
unsigned char row_parity_even = 0;
for (i = 0; i < NAND_ECC_CHUNK_SIZE; i++) {
/* Fetch pre-calculated ECC value from table. */
unsigned char byte_parity = ecc_parity_table[*data_p++];
/* Only 6 LSB are related to column parity. */
col_parity ^= byte_parity & 0x3f;
/* If the row parity is 1 we toggle the value for related
* rows. Even rows have the inverted value of odd rows.
*/
if (byte_parity & 0x40) {
row_parity_odd ^= i;
row_parity_even ^= ~i;
}
}
/* Move the even and odd bits into proper ECC form. */
row_parity0 = ((row_parity_even & 0x01) << 0)
| ((row_parity_odd & 0x01) << 1)
| ((row_parity_even & 0x02) << 1)
| ((row_parity_odd & 0x02) << 2)
| ((row_parity_even & 0x04) << 2)
| ((row_parity_odd & 0x04) << 3)
| ((row_parity_even & 0x08) << 3)
| ((row_parity_odd & 0x08) << 4);
row_parity1 = ((row_parity_even & 0x10) >> 4)
| ((row_parity_odd & 0x10) >> 3)
| ((row_parity_even & 0x20) >> 3)
| ((row_parity_odd & 0x20) >> 2)
| ((row_parity_even & 0x40) >> 2)
| ((row_parity_odd & 0x40) >> 1)
| ((row_parity_even & 0x80) >> 1)
| ((row_parity_odd & 0x80) >> 0);
*ecc = row_parity0 | (row_parity1 << 8) | (0x03 << 16)
| (col_parity << 18);
}
/*! \brief Compare two ECCs for a 256 byte block.
*
* This function will compare two 3 byte ECC for a 256 byte data block.
* If an error is found the compare function will provide a fix if a single
* one bit error is found. Multiple errors are not possible to fix.
*
* \param ecc1 Pointer to a variable for ECC value to compare against ecc2.
* \param ecc2 Pointer to a variable for ECC value to compare against ecc1.
* \param data Pointer to a 256 byte large buffer used when calculating the
* ecc1 and ecc2 ECC values.
* \param error_offset Pointer to a variable to store the byte offset of a
* detected single bit error.
* \param corrected_value Pointer to a variable to store the corrected byte
* if a single bit error was detected.
*
* \return 0 on success, an error number otherwise.
*/
int ecc_compare_block_256b(unsigned long *ecc1, unsigned long *ecc2,
unsigned char *data,
unsigned long *error_offset,
unsigned char *corrected_value)
{
unsigned int i;
unsigned int bit_index;
unsigned int ecc_xored;
unsigned int byte_index;
unsigned int bit_count = 0;
if (*ecc1 == *ecc2) {
return SUCCESS;
}
ecc_xored = *ecc1 ^ *ecc2;
for (i = 0; i < 24; i++) {
bit_count += ((ecc_xored >> i) & 0x1);
}
switch (bit_count) {
case 0:
return SUCCESS;
case 1:
return -ENANDECC_BADECC;
case 11:
/* Check if ECC is damaged. */
if (((ecc_xored & 0x02) >> 1) + (ecc_xored & 0x01) != 1) {
return -ENANDECC_BADECC;
}
bit_index = ((ecc_xored >> 19) & 0x01)
| ((ecc_xored >> 20) & 0x02)
| ((ecc_xored >> 21) & 0x04);
byte_index = ((ecc_xored >> 1) & 0x01)
| ((ecc_xored >> 2) & 0x02)
| ((ecc_xored >> 3) & 0x04)
| ((ecc_xored >> 4) & 0x08)
| ((ecc_xored >> 5) & 0x10)
| ((ecc_xored >> 6) & 0x20)
| ((ecc_xored >> 7) & 0x40)
| ((ecc_xored >> 8) & 0x80);
*error_offset = byte_index;
*corrected_value = (data[byte_index] ^ (1 << bit_index));
return -ENANDECC_CORRECTABLE;
default:
return -ENANDECC_UNCORRECTABLE;
}
}