1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2011-12-05     Bernard      the first version
9  */
10 
11 /*
12  * COPYRIGHT (C) 2012, Shanghai Real Thread
13  */
14 
15 #include <rtdevice.h>
16 
17 #ifdef RT_USING_MTD_NAND
18 
19 /**
20  * RT-Thread Generic Device Interface
21  */
_mtd_init(rt_device_t dev)22 static rt_err_t _mtd_init(rt_device_t dev)
23 {
24     return RT_EOK;
25 }
26 
_mtd_open(rt_device_t dev,rt_uint16_t oflag)27 static rt_err_t _mtd_open(rt_device_t dev, rt_uint16_t oflag)
28 {
29     return RT_EOK;
30 }
31 
_mtd_close(rt_device_t dev)32 static rt_err_t _mtd_close(rt_device_t dev)
33 {
34     return RT_EOK;
35 }
36 
_mtd_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)37 static rt_ssize_t _mtd_read(rt_device_t dev,
38                            rt_off_t    pos,
39                            void       *buffer,
40                            rt_size_t   size)
41 {
42     return size;
43 }
44 
_mtd_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)45 static rt_ssize_t _mtd_write(rt_device_t dev,
46                             rt_off_t    pos,
47                             const void *buffer,
48                             rt_size_t   size)
49 {
50     return size;
51 }
52 
_mtd_control(rt_device_t dev,int cmd,void * args)53 static rt_err_t _mtd_control(rt_device_t dev, int cmd, void *args)
54 {
55     return RT_EOK;
56 }
57 
58 #ifdef RT_USING_DEVICE_OPS
59 const static struct rt_device_ops mtd_nand_ops =
60 {
61     _mtd_init,
62     _mtd_open,
63     _mtd_close,
64     _mtd_read,
65     _mtd_write,
66     _mtd_control
67 };
68 #endif
69 
rt_mtd_nand_register_device(const char * name,struct rt_mtd_nand_device * device)70 rt_err_t rt_mtd_nand_register_device(const char                *name,
71                                      struct rt_mtd_nand_device *device)
72 {
73     rt_device_t dev;
74 
75     dev = RT_DEVICE(device);
76     RT_ASSERT(dev != RT_NULL);
77 
78     /* set device class and generic device interface */
79     dev->type        = RT_Device_Class_MTD;
80 #ifdef RT_USING_DEVICE_OPS
81     dev->ops         = &mtd_nand_ops;
82 #else
83     dev->init        = _mtd_init;
84     dev->open        = _mtd_open;
85     dev->read        = _mtd_read;
86     dev->write       = _mtd_write;
87     dev->close       = _mtd_close;
88     dev->control     = _mtd_control;
89 #endif
90 
91     dev->rx_indicate = RT_NULL;
92     dev->tx_complete = RT_NULL;
93 
94     /* register to RT-Thread device system */
95     return rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
96 }
97 
rt_mtd_nand_read_id(struct rt_mtd_nand_device * device)98 rt_uint32_t rt_mtd_nand_read_id(struct rt_mtd_nand_device *device)
99 {
100     RT_ASSERT(device->ops->read_id);
101     return device->ops->read_id(device);
102 }
103 
rt_mtd_nand_read(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)104 rt_err_t rt_mtd_nand_read(
105     struct rt_mtd_nand_device *device,
106     rt_off_t page,
107     rt_uint8_t *data, rt_uint32_t data_len,
108     rt_uint8_t *spare, rt_uint32_t spare_len)
109 {
110     RT_ASSERT(device->ops->read_page);
111     return device->ops->read_page(device, page, data, data_len, spare, spare_len);
112 }
113 
rt_mtd_nand_write(struct rt_mtd_nand_device * device,rt_off_t page,const rt_uint8_t * data,rt_uint32_t data_len,const rt_uint8_t * spare,rt_uint32_t spare_len)114 rt_err_t rt_mtd_nand_write(
115     struct rt_mtd_nand_device *device,
116     rt_off_t page,
117     const rt_uint8_t *data, rt_uint32_t data_len,
118     const rt_uint8_t *spare, rt_uint32_t spare_len)
119 {
120     RT_ASSERT(device->ops->write_page);
121     return device->ops->write_page(device, page, data, data_len, spare, spare_len);
122 }
123 
rt_mtd_nand_move_page(struct rt_mtd_nand_device * device,rt_off_t src_page,rt_off_t dst_page)124 rt_err_t rt_mtd_nand_move_page(struct rt_mtd_nand_device *device,
125         rt_off_t src_page, rt_off_t dst_page)
126 {
127     RT_ASSERT(device->ops->move_page);
128     return device->ops->move_page(device, src_page, dst_page);
129 }
130 
rt_mtd_nand_erase_block(struct rt_mtd_nand_device * device,rt_uint32_t block)131 rt_err_t rt_mtd_nand_erase_block(struct rt_mtd_nand_device *device, rt_uint32_t block)
132 {
133     RT_ASSERT(device->ops->erase_block);
134     return device->ops->erase_block(device, block);
135 }
136 
rt_mtd_nand_check_block(struct rt_mtd_nand_device * device,rt_uint32_t block)137 rt_err_t rt_mtd_nand_check_block(struct rt_mtd_nand_device *device, rt_uint32_t block)
138 {
139     if (device->ops->check_block)
140     {
141         return device->ops->check_block(device, block);
142     }
143     else
144     {
145         return -RT_ENOSYS;
146     }
147 }
148 
rt_mtd_nand_mark_badblock(struct rt_mtd_nand_device * device,rt_uint32_t block)149 rt_err_t rt_mtd_nand_mark_badblock(struct rt_mtd_nand_device *device, rt_uint32_t block)
150 {
151     if (device->ops->mark_badblock)
152     {
153         return device->ops->mark_badblock(device, block);
154     }
155     else
156     {
157         return -RT_ENOSYS;
158     }
159 }
160 
161 #if defined(RT_MTD_NAND_DEBUG) && defined(RT_USING_FINSH)
162 #include <finsh.h>
163 #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
164 
mtd_dump_hex(const rt_uint8_t * ptr,int buflen)165 static void mtd_dump_hex(const rt_uint8_t *ptr, int buflen)
166 {
167     unsigned char *buf = (unsigned char *)ptr;
168     int i, j;
169     for (i = 0; i < buflen; i += 16)
170     {
171         rt_kprintf("%06x: ", i);
172         for (j = 0; j < 16; j++)
173             if (i + j < buflen)
174                 rt_kprintf("%02x ", buf[i + j]);
175             else
176                 rt_kprintf("   ");
177         rt_kprintf(" ");
178         for (j = 0; j < 16; j++)
179             if (i + j < buflen)
180                 rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
181         rt_kprintf("\n");
182     }
183 }
184 
mtd_nandid(const char * name)185 int mtd_nandid(const char *name)
186 {
187     struct rt_mtd_nand_device *nand;
188     nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
189     if (nand == RT_NULL)
190     {
191         rt_kprintf("no nand device found!\n");
192         return -RT_ERROR;
193     }
194 
195     return rt_mtd_nand_read_id(nand);
196 }
197 
mtd_nand_read(const char * name,int block,int page)198 int mtd_nand_read(const char *name, int block, int page)
199 {
200     rt_err_t result;
201     rt_uint8_t *page_ptr;
202     rt_uint8_t *oob_ptr;
203     struct rt_mtd_nand_device *nand;
204 
205     nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
206     if (nand == RT_NULL)
207     {
208         rt_kprintf("no nand device found!\n");
209         return -RT_ERROR;
210     }
211 
212     page_ptr = rt_malloc(nand->page_size + nand->oob_size);
213     if (page_ptr == RT_NULL)
214     {
215         rt_kprintf("out of memory!\n");
216         return -RT_ENOMEM;
217     }
218 
219     oob_ptr = page_ptr + nand->page_size;
220     rt_memset(page_ptr, 0xff, nand->page_size + nand->oob_size);
221 
222     /* calculate the page number */
223     page = block * nand->pages_per_block + page;
224     result = rt_mtd_nand_read(nand, page, page_ptr, nand->page_size,
225                               oob_ptr, nand->oob_size);
226 
227     rt_kprintf("read page, rc=%d\n", result);
228     mtd_dump_hex(page_ptr, nand->page_size);
229     mtd_dump_hex(oob_ptr, nand->oob_size);
230 
231     rt_free(page_ptr);
232     return 0;
233 }
234 
mtd_nand_readoob(const char * name,int block,int page)235 int mtd_nand_readoob(const char *name, int block, int page)
236 {
237     struct rt_mtd_nand_device *nand;
238     rt_uint8_t *oob_ptr;
239 
240     nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
241     if (nand == RT_NULL)
242     {
243         rt_kprintf("no nand device found!\n");
244         return -RT_ERROR;
245     }
246 
247     oob_ptr = rt_malloc(nand->oob_size);
248     if (oob_ptr == RT_NULL)
249     {
250         rt_kprintf("out of memory!\n");
251         return -RT_ENOMEM;
252     }
253 
254     /* calculate the page number */
255     page = block * nand->pages_per_block + page;
256     rt_mtd_nand_read(nand, page, RT_NULL, nand->page_size,
257                      oob_ptr, nand->oob_size);
258     mtd_dump_hex(oob_ptr, nand->oob_size);
259 
260     rt_free(oob_ptr);
261     return 0;
262 }
263 
mtd_nand_write(const char * name,int block,int page)264 int mtd_nand_write(const char *name, int block, int page)
265 {
266     rt_err_t result;
267     rt_uint8_t *page_ptr;
268     rt_uint8_t *oob_ptr;
269     rt_uint32_t index;
270     struct rt_mtd_nand_device *nand;
271 
272     nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
273     if (nand == RT_NULL)
274     {
275         rt_kprintf("no nand device found!\n");
276         return -RT_ERROR;
277     }
278 
279     page_ptr = rt_malloc(nand->page_size + nand->oob_size);
280     if (page_ptr == RT_NULL)
281     {
282         rt_kprintf("out of memory!\n");
283         return -RT_ENOMEM;
284     }
285 
286     oob_ptr = page_ptr + nand->page_size;
287     /* prepare page data */
288     for (index = 0; index < nand->page_size; index ++)
289     {
290         page_ptr[index] = index & 0xff;
291     }
292     /* prepare oob data */
293     for (index = 0; index < nand->oob_size; index ++)
294     {
295         oob_ptr[index] = index & 0xff;
296     }
297 
298     /* calculate the page number */
299     page = block * nand->pages_per_block + page;
300     result = rt_mtd_nand_write(nand, page, page_ptr, nand->page_size,
301                                oob_ptr, nand->oob_size);
302     if (result != RT_MTD_EOK)
303     {
304         rt_kprintf("write page failed!, rc=%d\n", result);
305     }
306 
307     rt_free(page_ptr);
308     return 0;
309 }
310 
mtd_nand_erase(const char * name,int block)311 int mtd_nand_erase(const char *name, int block)
312 {
313     struct rt_mtd_nand_device *nand;
314     nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
315     if (nand == RT_NULL)
316     {
317         rt_kprintf("no nand device found!\n");
318         return -RT_ERROR;
319     }
320 
321     return rt_mtd_nand_erase_block(nand, block);
322 }
323 
mtd_nand_erase_all(const char * name)324 int mtd_nand_erase_all(const char *name)
325 {
326     rt_uint32_t index = 0;
327     struct rt_mtd_nand_device *nand;
328 
329     nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
330     if (nand == RT_NULL)
331     {
332         rt_kprintf("no nand device found!\n");
333         return -RT_ERROR;
334     }
335 
336     for (index = 0; index < (nand->block_end - nand->block_start); index ++)
337     {
338         rt_mtd_nand_erase_block(nand, index);
339     }
340 
341     return 0;
342 }
343 
344 #ifdef RT_USING_FINSH
mtd_nand(int argc,char ** argv)345 static void mtd_nand(int argc, char **argv)
346 {
347     /* If the number of arguments less than 2 */
348     if (argc < 3)
349     {
350 help:
351         rt_kprintf("\n");
352         rt_kprintf("mtd_nand [OPTION] [PARAM ...]\n");
353         rt_kprintf("         id       <name>            Get nandid by given name\n");
354         rt_kprintf("         read     <name> <bn> <pn>  Read data on page <pn> of block <bn> of device <name>\n");
355         rt_kprintf("         readoob  <name> <bn> <pn>  Read oob  on page <pn> of block <bn> of device <name>\n");
356         rt_kprintf("         write    <name> <bn> <pn>  Run write test on page <pn> of block <bn> of device <name>\n");
357         rt_kprintf("         erase    <name> <bn>       Erase on block <bn> of device <name>\n");
358         rt_kprintf("         eraseall <name>            Erase all block on device <name>\n");
359         return ;
360     }
361     else if (!rt_strcmp(argv[1], "id"))
362     {
363         mtd_nandid(argv[2]);
364     }
365     else if (!rt_strcmp(argv[1], "read"))
366     {
367         if (argc < 5)
368         {
369             rt_kprintf("The input parameters are too few!\n");
370             goto help;
371         }
372         mtd_nand_read(argv[2], atoi(argv[3]), atoi(argv[4]));
373     }
374     else if (!rt_strcmp(argv[1], "readoob"))
375     {
376         if (argc < 5)
377         {
378             rt_kprintf("The input parameters are too few!\n");
379             goto help;
380         }
381         mtd_nand_readoob(argv[2], atoi(argv[3]), atoi(argv[4]));
382     }
383     else if (!rt_strcmp(argv[1], "write"))
384     {
385         if (argc < 5)
386         {
387             rt_kprintf("The input parameters are too few!\n");
388             goto help;
389         }
390         mtd_nand_write(argv[2], atoi(argv[3]), atoi(argv[4]));
391     }
392     else if (!rt_strcmp(argv[1], "erase"))
393     {
394         if (argc < 4)
395         {
396             rt_kprintf("The input parameters are too few!\n");
397             goto help;
398         }
399         mtd_nand_erase(argv[2], atoi(argv[3]));
400     }
401     else if (!rt_strcmp(argv[1], "eraseall"))
402     {
403         mtd_nand_erase_all(argv[2]);
404     }
405     else
406     {
407         rt_kprintf("Input parameters are not supported!\n");
408         goto help;
409     }
410 }
411 MSH_CMD_EXPORT(mtd_nand, MTD nand device test function);
412 #endif /* RT_USING_FINSH */
413 
414 #ifndef RT_USING_FINSH_ONLY
415 FINSH_FUNCTION_EXPORT_ALIAS(mtd_nandid, nand_id, read ID - nandid(name));
416 FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_read, nand_read, read page in nand - nand_read(name, block, page));
417 FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_readoob, nand_readoob, read spare data in nand - nand_readoob(name, block, page));
418 FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_write, nand_write, write dump data to nand - nand_write(name, block, page));
419 FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_erase, nand_erase, nand_erase(name, block));
420 FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_erase_all, nand_erase_all, erase all of nand device - nand_erase_all(name, block));
421 #endif /* RT_USING_FINSH_ONLY */
422 
423 #endif /* defined(RT_MTD_NAND_DEBUG) && defined(RT_USING_FINSH) */
424 
425 #endif /* RT_USING_MTD_NAND */
426