Root/tools/firmware-utils/src/nand_ecc.c

1/*
2 * calculate ecc code for nand flash
3 *
4 * Copyright (C) 2008 yajin <yajin@vm-kernel.org>
5 * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 or
10 * (at your option) version 3 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <stdint.h>
29#include <fcntl.h>
30#include <stdio.h>
31
32#define DEF_NAND_PAGE_SIZE 2048
33#define DEF_NAND_OOB_SIZE 64
34#define DEF_NAND_ECC_OFFSET 0x28
35
36static int page_size = DEF_NAND_PAGE_SIZE;
37static int oob_size = DEF_NAND_OOB_SIZE;
38static int ecc_offset = DEF_NAND_ECC_OFFSET;
39
40/*
41 * Pre-calculated 256-way 1 byte column parity
42 */
43static const uint8_t nand_ecc_precalc_table[] = {
44    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
45    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
46    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
47    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
48    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
49    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
50    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
51    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
52    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
53    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
54    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
55    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
56    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
57    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
58    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
59    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
60};
61
62/**
63 * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
64 * @dat: raw data
65 * @ecc_code: buffer for ECC
66 */
67int nand_calculate_ecc(const uint8_t *dat,
68               uint8_t *ecc_code)
69{
70    uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
71    int i;
72
73    /* Initialize variables */
74    reg1 = reg2 = reg3 = 0;
75
76    /* Build up column parity */
77    for(i = 0; i < 256; i++) {
78        /* Get CP0 - CP5 from table */
79        idx = nand_ecc_precalc_table[*dat++];
80        reg1 ^= (idx & 0x3f);
81
82        /* All bit XOR = 1 ? */
83        if (idx & 0x40) {
84            reg3 ^= (uint8_t) i;
85            reg2 ^= ~((uint8_t) i);
86        }
87    }
88
89    /* Create non-inverted ECC code from line parity */
90    tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
91    tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
92    tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
93    tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
94    tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
95    tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
96    tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
97    tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
98
99    tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
100    tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
101    tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
102    tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
103    tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
104    tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
105    tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
106    tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
107
108    /* Calculate final ECC code */
109#ifdef CONFIG_MTD_NAND_ECC_SMC
110    ecc_code[0] = ~tmp2;
111    ecc_code[1] = ~tmp1;
112#else
113    ecc_code[0] = ~tmp1;
114    ecc_code[1] = ~tmp2;
115#endif
116    ecc_code[2] = ((~reg1) << 2) | 0x03;
117
118    return 0;
119}
120
121/*
122 * usage: bb-nandflash-ecc start_address size
123 */
124void usage(const char *prog)
125{
126    fprintf(stderr, "Usage: %s [options] <input> <output>\n"
127        "Options:\n"
128        " -p <pagesize> NAND page size (default: %d)\n"
129        " -o <oobsize> NAND OOB size (default: %d)\n"
130        " -e <offset> NAND ECC offset (default: %d)\n"
131        "\n", prog, DEF_NAND_PAGE_SIZE, DEF_NAND_OOB_SIZE,
132        DEF_NAND_ECC_OFFSET);
133    exit(1);
134}
135
136/*start_address/size does not include oob
137  */
138int main(int argc, char **argv)
139{
140    uint8_t *page_data = NULL;
141    uint8_t *ecc_data;
142    int infd = -1, outfd = -1;
143    int ret = 1;
144    ssize_t bytes;
145    int ch;
146
147    while ((ch = getopt(argc, argv, "e:o:p:")) != -1) {
148        switch(ch) {
149        case 'p':
150            page_size = strtoul(optarg, NULL, 0);
151            break;
152        case 'o':
153            oob_size = strtoul(optarg, NULL, 0);
154            break;
155        case 'e':
156            ecc_offset = strtoul(optarg, NULL, 0);
157            break;
158        default:
159            usage(argv[0]);
160        }
161    }
162    argc -= optind;
163    if (argc < 2)
164        usage(argv[0]);
165
166    argv += optind;
167
168    infd = open(argv[0], O_RDONLY, 0);
169    if (infd < 0) {
170        perror("open input file");
171        goto out;
172    }
173
174    outfd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0644);
175    if (outfd < 0) {
176        perror("open output file");
177        goto out;
178    }
179
180    page_data = malloc(page_size + oob_size);
181
182    while ((bytes = read(infd, page_data, page_size)) == page_size) {
183        int j;
184
185        ecc_data = page_data + page_size + ecc_offset;
186        for (j = 0; j < page_size / 256; j++)
187        {
188            nand_calculate_ecc(page_data + j * 256, ecc_data);
189            ecc_data += 3;
190        }
191        write(outfd, page_data, page_size + oob_size);
192    }
193
194    ret = 0;
195out:
196    if (infd >= 0)
197        close(infd);
198    if (outfd >= 0)
199        close(outfd);
200    if (page_data)
201        free(page_data);
202    return ret;
203}
204
205

Archive Download this file



interactive