1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  */
9 #include <rtdevice.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #define NAND_SIM  "nand.bin"
15 
16 #if 1
17 #define OOB_SIZE        64
18 #define PAGE_DATA_SIZE  2048
19 #define PAGE_PER_BLOCK  64
20 #define ECC_SIZE       ((PAGE_DATA_SIZE) * 3 / 256)
21 #define BLOCK_NUM       512
22 #else
23 #define OOB_SIZE        16
24 #define PAGE_DATA_SIZE  512
25 #define PAGE_PER_BLOCK  32
26 #define ECC_SIZE       ((PAGE_DATA_SIZE) * 3 / 256)
27 #define BLOCK_NUM       512
28 #endif
29 
30 #define BLOCK_SIZE      (PAGE_SIZE * PAGE_PER_BLOCK)
31 #define PAGE_SIZE       (PAGE_DATA_SIZE + OOB_SIZE)
32 
33 static unsigned char block_data[BLOCK_SIZE];
34 static struct rt_mtd_nand_device _nanddrv_file_device;
35 static FILE *file = NULL;
36 
CountBitsInByte(rt_uint8_t byte)37 static rt_uint8_t CountBitsInByte(rt_uint8_t byte)
38 {
39     rt_uint8_t count = 0;
40 
41     while (byte > 0)
42     {
43         if (byte & 1)
44         {
45             count++;
46         }
47         byte >>= 1;
48     }
49 
50     return count;
51 }
52 
Compute256(const rt_uint8_t * data,rt_uint8_t * code)53 static void Compute256(const rt_uint8_t *data, rt_uint8_t *code)
54 {
55     rt_uint32_t i;
56     rt_uint8_t columnSum = 0;
57     rt_uint8_t evenLineCode = 0;
58     rt_uint8_t oddLineCode = 0;
59     rt_uint8_t evenColumnCode = 0;
60     rt_uint8_t oddColumnCode = 0;
61 
62     // Xor all bytes together to get the column sum;
63     // At the same time, calculate the even and odd line codes
64     for (i = 0; i < 256; i++)
65     {
66         columnSum ^= data[i];
67 
68         // If the xor sum of the byte is 0, then this byte has no incidence on
69         // the computed code; so check if the sum is 1.
70         if ((CountBitsInByte(data[i]) & 1) == 1)
71         {
72             // Parity groups are formed by forcing a particular index bit to 0
73             // (even) or 1 (odd).
74             // Example on one byte:
75             //
76             // bits (dec)  7   6   5   4   3   2   1   0
77             //      (bin) 111 110 101 100 011 010 001 000
78             //                            '---'---'---'----------.
79             //                                                   |
80             // groups P4' ooooooooooooooo eeeeeeeeeeeeeee P4     |
81             //        P2' ooooooo eeeeeee ooooooo eeeeeee P2     |
82             //        P1' ooo eee ooo eee ooo eee ooo eee P1     |
83             //                                                   |
84             // We can see that:                                  |
85             //  - P4  -> bit 2 of index is 0 --------------------'
86             //  - P4' -> bit 2 of index is 1.
87             //  - P2  -> bit 1 of index if 0.
88             //  - etc...
89             // We deduce that a bit position has an impact on all even Px if
90             // the log2(x)nth bit of its index is 0
91             //     ex: log2(4) = 2, bit2 of the index must be 0 (-> 0 1 2 3)
92             // and on all odd Px' if the log2(x)nth bit of its index is 1
93             //     ex: log2(2) = 1, bit1 of the index must be 1 (-> 0 1 4 5)
94             //
95             // As such, we calculate all the possible Px and Px' values at the
96             // same time in two variables, evenLineCode and oddLineCode, such as
97             //     evenLineCode bits: P128  P64  P32  P16  P8  P4  P2  P1
98             //     oddLineCode  bits: P128' P64' P32' P16' P8' P4' P2' P1'
99             //
100             evenLineCode ^= (255 - i);
101             oddLineCode ^= i;
102         }
103     }
104 
105     // At this point, we have the line parities, and the column sum. First, We
106     // must caculate the parity group values on the column sum.
107     for (i = 0; i < 8; i++)
108     {
109         if (columnSum & 1)
110         {
111             evenColumnCode ^= (7 - i);
112             oddColumnCode ^= i;
113         }
114         columnSum >>= 1;
115     }
116 
117     // Now, we must interleave the parity values, to obtain the following layout:
118     // Code[0] = Line1
119     // Code[1] = Line2
120     // Code[2] = Column
121     // Line = Px' Px P(x-1)- P(x-1) ...
122     // Column = P4' P4 P2' P2 P1' P1 PadBit PadBit
123     code[0] = 0;
124     code[1] = 0;
125     code[2] = 0;
126 
127     for (i = 0; i < 4; i++)
128     {
129         code[0] <<= 2;
130         code[1] <<= 2;
131         code[2] <<= 2;
132 
133         // Line 1
134         if ((oddLineCode & 0x80) != 0)
135         {
136             code[0] |= 2;
137         }
138 
139         if ((evenLineCode & 0x80) != 0)
140         {
141             code[0] |= 1;
142         }
143 
144         // Line 2
145         if ((oddLineCode & 0x08) != 0)
146         {
147             code[1] |= 2;
148         }
149 
150         if ((evenLineCode & 0x08) != 0)
151         {
152             code[1] |= 1;
153         }
154 
155         // Column
156         if ((oddColumnCode & 0x04) != 0)
157         {
158             code[2] |= 2;
159         }
160 
161         if ((evenColumnCode & 0x04) != 0)
162         {
163             code[2] |= 1;
164         }
165 
166         oddLineCode <<= 1;
167         evenLineCode <<= 1;
168         oddColumnCode <<= 1;
169         evenColumnCode <<= 1;
170     }
171 
172     // Invert codes (linux compatibility)
173     code[0] = (~(rt_uint32_t)code[0]);
174     code[1] = (~(rt_uint32_t)code[1]);
175     code[2] = (~(rt_uint32_t)code[2]);
176 }
177 
ecc_hamming_compute256x(const rt_uint8_t * pucData,rt_uint32_t dwSize,rt_uint8_t * puCode)178 void ecc_hamming_compute256x(const rt_uint8_t *pucData, rt_uint32_t dwSize, rt_uint8_t *puCode)
179 {
180     while (dwSize > 0)
181     {
182         Compute256(pucData, puCode) ;
183 
184         pucData += 256;
185         puCode += 3;
186         dwSize -= 256;
187     }
188 }
189 
190 /* read chip id */
nanddrv_file_read_id(struct rt_mtd_nand_device * device)191 static rt_uint32_t nanddrv_file_read_id(struct rt_mtd_nand_device *device)
192 {
193     return 0x00;
194 }
195 
196 /* read/write/move page */
nanddrv_file_read_page(struct rt_mtd_nand_device * device,rt_off_t page,rt_uint8_t * data,rt_uint32_t data_len,rt_uint8_t * spare,rt_uint32_t spare_len)197 static rt_err_t nanddrv_file_read_page(struct rt_mtd_nand_device *device,
198                                        rt_off_t page,
199                                        rt_uint8_t *data, rt_uint32_t data_len,
200                                        rt_uint8_t *spare, rt_uint32_t spare_len)
201 {
202     rt_uint32_t offset;
203     rt_uint8_t oob_ecc [ECC_SIZE];
204     rt_uint8_t ecc [ECC_SIZE];
205 
206     page = page + device->block_start * device->pages_per_block;
207 
208     if (page / device->pages_per_block > device->block_end)
209     {
210         return -RT_EIO;
211     }
212 
213     /* write page */
214     offset = page * PAGE_SIZE;
215     if (data != NULL && data_len != 0)
216     {
217         fseek(file, offset, SEEK_SET);
218         fread(data, data_len, 1, file);
219         if (data_len == PAGE_DATA_SIZE)
220         {
221             /* read ecc size */
222             fread(oob_ecc, ECC_SIZE, 1, file);
223 
224             /* verify ECC */
225             ecc_hamming_compute256x(data, PAGE_DATA_SIZE, &ecc[0]);
226             if (memcmp(&oob_ecc[0], &ecc[0], ECC_SIZE) != 0)
227                 return -RT_MTD_EECC;
228         }
229     }
230 
231     if (spare != NULL && spare_len)
232     {
233         offset = page * PAGE_SIZE + PAGE_DATA_SIZE;
234         fseek(file, offset, SEEK_SET);
235         fread(spare, spare_len, 1, file);
236     }
237 
238     return RT_EOK;
239 }
240 
nanddrv_file_write_page(struct rt_mtd_nand_device * device,rt_off_t page,const rt_uint8_t * data,rt_uint32_t data_len,const rt_uint8_t * oob,rt_uint32_t spare_len)241 static rt_err_t nanddrv_file_write_page(struct rt_mtd_nand_device *device,
242                                         rt_off_t page,
243                                         const rt_uint8_t *data, rt_uint32_t data_len,
244                                         const rt_uint8_t *oob, rt_uint32_t spare_len)
245 {
246     rt_uint32_t offset;
247     rt_uint8_t ecc[ECC_SIZE];
248 
249     page = page + device->block_start * device->pages_per_block;
250     if (page / device->pages_per_block > device->block_end)
251     {
252         return -RT_EIO;
253     }
254 
255     /* write page */
256     offset = page * PAGE_SIZE;
257     if (data != RT_NULL && data_len != 0)
258     {
259         fseek(file, offset, SEEK_SET);
260         fwrite(data, data_len, 1, file);
261 
262         if (data_len == PAGE_DATA_SIZE)
263         {
264             /*write the ecc information */
265             ecc_hamming_compute256x(data, PAGE_DATA_SIZE, ecc);
266 
267             fwrite(ecc, ECC_SIZE, 1, file);
268         }
269     }
270 
271     if (oob != RT_NULL && spare_len != 0)
272     {
273         offset = page * PAGE_SIZE + PAGE_DATA_SIZE + ECC_SIZE;
274         fseek(file, offset, SEEK_SET);
275         fwrite(&oob[ECC_SIZE], spare_len-ECC_SIZE, 1, file);
276     }
277 
278     return RT_EOK;
279 }
280 
nanddrv_file_move_page(struct rt_mtd_nand_device * device,rt_off_t from,rt_off_t to)281 static rt_err_t nanddrv_file_move_page(struct rt_mtd_nand_device *device, rt_off_t from, rt_off_t to)
282 {
283     rt_uint32_t offset;
284     rt_uint8_t  page_buffer[PAGE_DATA_SIZE];
285     rt_uint8_t  oob_buffer[OOB_SIZE];
286 
287     from = from + device->block_start * device->pages_per_block;
288     to = to + device->block_start * device->pages_per_block;
289 
290     if (from / device->pages_per_block > device->block_end ||
291             to / device->pages_per_block > device->block_end)
292     {
293         return -RT_EIO;
294     }
295 
296     if (device->plane_num > 1)
297     {
298         rt_uint32_t mask;
299         rt_uint16_t from_block, to_block;
300 
301         from_block = (rt_uint16_t)(from / PAGE_PER_BLOCK);
302         to_block = (rt_uint16_t)(to / PAGE_PER_BLOCK);
303         mask = device->plane_num - 1;
304 
305         if ((from_block & mask) != (to_block & mask))
306         {
307             rt_kprintf("invalid page copy on the block. from [%d] --> to[%d]\n", from_block, to_block);
308             return -RT_EIO;
309         }
310     }
311 
312     /* read page */
313     offset = from * PAGE_SIZE;
314     fseek(file, offset, SEEK_SET);
315     fread(page_buffer, sizeof(page_buffer), 1, file);
316     fread(oob_buffer, sizeof(oob_buffer), 1, file);
317 
318     /* write page */
319     offset = to * PAGE_SIZE;
320     fseek(file, offset, SEEK_SET);
321     fwrite(page_buffer, sizeof(page_buffer), 1, file);
322     fwrite(oob_buffer, sizeof(oob_buffer), 1, file);
323 
324     return RT_EOK;
325 }
326 
327 /* erase block */
nanddrv_file_erase_block(struct rt_mtd_nand_device * device,rt_uint32_t block)328 static rt_err_t nanddrv_file_erase_block(struct rt_mtd_nand_device *device, rt_uint32_t block)
329 {
330     if (block > BLOCK_NUM) return -RT_EIO;
331 
332     /* add the start blocks */
333     block = block + device->block_start;
334 
335     fseek(file, block * BLOCK_SIZE, SEEK_SET);
336     fwrite(block_data, sizeof(block_data), 1, file);
337 
338     return RT_EOK;
339 }
340 
341 const static struct rt_mtd_nand_driver_ops _ops =
342 {
343     nanddrv_file_read_id,
344     nanddrv_file_read_page,
345     nanddrv_file_write_page,
346     nanddrv_file_move_page,
347     nanddrv_file_erase_block,
348     RT_NULL,
349     RT_NULL,
350 };
351 
352 void nand_eraseall(void);
353 
rt_hw_mtd_nand_init(void)354 void rt_hw_mtd_nand_init(void)
355 {
356     rt_uint16_t ecc_size;
357     rt_uint32_t size;
358 
359     memset(block_data, 0xff, sizeof(block_data));
360     /* open file */
361     file = fopen(NAND_SIM, "rb+");
362     if (file == NULL)
363     {
364         file = fopen(NAND_SIM, "wb+");
365     }
366     fseek(file, 0, SEEK_END);
367     size = ftell(file);
368 
369     fseek(file, 0, SEEK_SET);
370     if (size < BLOCK_NUM * BLOCK_SIZE)
371     {
372         rt_uint32_t index;
373         fseek(file, 0, SEEK_SET);
374         for (index = 0; index < BLOCK_NUM; index ++)
375         {
376             fwrite(block_data, sizeof(block_data), 1, file);
377         }
378     }
379     fseek(file, 0, SEEK_SET);
380 
381     ecc_size = (PAGE_DATA_SIZE) * 3 / 256;
382     _nanddrv_file_device.plane_num = 2;
383     _nanddrv_file_device.oob_size = OOB_SIZE;
384     _nanddrv_file_device.oob_free = OOB_SIZE - ecc_size;
385     _nanddrv_file_device.page_size = PAGE_DATA_SIZE;
386     _nanddrv_file_device.pages_per_block = PAGE_PER_BLOCK;
387     _nanddrv_file_device.block_start = 0;
388     _nanddrv_file_device.block_end = BLOCK_NUM / 2;
389     _nanddrv_file_device.block_total = _nanddrv_file_device.block_end - _nanddrv_file_device.block_start;
390     _nanddrv_file_device.ops = &_ops;
391 
392     rt_mtd_nand_register_device("nand0", &_nanddrv_file_device);
393 }
394 
395 #if defined(RT_USING_FINSH)
396 #include <finsh.h>
nand_eraseall()397 void nand_eraseall()
398 {
399     int index;
400     for (index = 0; index < _nanddrv_file_device.block_total; index ++)
401     {
402         nanddrv_file_erase_block(&_nanddrv_file_device, index);
403     }
404 }
405 FINSH_FUNCTION_EXPORT(nand_eraseall, erase all of block in the nand flash);
406 
407 #endif //RT_USING_FINSH
408