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