mtd.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * mtd.c
  4. *
  5. * Generic command to handle basic operations on any memory device.
  6. *
  7. * Copyright: Bootlin, 2018
  8. * Author: Miquèl Raynal <miquel.raynal@bootlin.com>
  9. */
  10. #include <command.h>
  11. #include <common.h>
  12. #include <console.h>
  13. #include <malloc.h>
  14. #include <mapmem.h>
  15. #include <mtd.h>
  16. #include <linux/ctype.h>
  17. static struct mtd_info *get_mtd_by_name(const char *name)
  18. {
  19. struct mtd_info *mtd;
  20. mtd_probe_devices();
  21. mtd = get_mtd_device_nm(name);
  22. if (IS_ERR_OR_NULL(mtd))
  23. printf("MTD device %s not found, ret %ld\n", name,
  24. PTR_ERR(mtd));
  25. return mtd;
  26. }
  27. static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
  28. {
  29. do_div(len, mtd->writesize);
  30. return len;
  31. }
  32. static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
  33. {
  34. return !do_div(size, mtd->writesize);
  35. }
  36. static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
  37. {
  38. return !do_div(size, mtd->erasesize);
  39. }
  40. static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
  41. {
  42. int i, j;
  43. for (i = 0; i < len; ) {
  44. printf("0x%08x:\t", offset + i);
  45. for (j = 0; j < 8; j++)
  46. printf("%02x ", buf[i + j]);
  47. printf(" ");
  48. i += 8;
  49. for (j = 0; j < 8; j++)
  50. printf("%02x ", buf[i + j]);
  51. printf("\n");
  52. i += 8;
  53. }
  54. }
  55. static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
  56. const u8 *buf, u64 len, bool woob)
  57. {
  58. bool has_pages = mtd->type == MTD_NANDFLASH ||
  59. mtd->type == MTD_MLCNANDFLASH;
  60. int npages = mtd_len_to_pages(mtd, len);
  61. uint page;
  62. if (has_pages) {
  63. for (page = 0; page < npages; page++) {
  64. u64 data_off = page * mtd->writesize;
  65. printf("\nDump %d data bytes from 0x%08llx:\n",
  66. mtd->writesize, start_off + data_off);
  67. mtd_dump_buf(&buf[data_off],
  68. mtd->writesize, start_off + data_off);
  69. if (woob) {
  70. u64 oob_off = page * mtd->oobsize;
  71. printf("Dump %d OOB bytes from page at 0x%08llx:\n",
  72. mtd->oobsize, start_off + data_off);
  73. mtd_dump_buf(&buf[len + oob_off],
  74. mtd->oobsize, 0);
  75. }
  76. }
  77. } else {
  78. printf("\nDump %lld data bytes from 0x%llx:\n",
  79. len, start_off);
  80. mtd_dump_buf(buf, len, start_off);
  81. }
  82. }
  83. static void mtd_show_parts(struct mtd_info *mtd, int level)
  84. {
  85. struct mtd_info *part;
  86. int i;
  87. list_for_each_entry(part, &mtd->partitions, node) {
  88. for (i = 0; i < level; i++)
  89. printf("\t");
  90. printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
  91. part->offset, part->offset + part->size, part->name);
  92. mtd_show_parts(part, level + 1);
  93. }
  94. }
  95. static void mtd_show_device(struct mtd_info *mtd)
  96. {
  97. /* Device */
  98. printf("* %s\n", mtd->name);
  99. #if defined(CONFIG_DM)
  100. if (mtd->dev) {
  101. printf(" - device: %s\n", mtd->dev->name);
  102. printf(" - parent: %s\n", mtd->dev->parent->name);
  103. printf(" - driver: %s\n", mtd->dev->driver->name);
  104. }
  105. #endif
  106. /* MTD device information */
  107. printf(" - type: ");
  108. switch (mtd->type) {
  109. case MTD_RAM:
  110. printf("RAM\n");
  111. break;
  112. case MTD_ROM:
  113. printf("ROM\n");
  114. break;
  115. case MTD_NORFLASH:
  116. printf("NOR flash\n");
  117. break;
  118. case MTD_NANDFLASH:
  119. printf("NAND flash\n");
  120. break;
  121. case MTD_DATAFLASH:
  122. printf("Data flash\n");
  123. break;
  124. case MTD_UBIVOLUME:
  125. printf("UBI volume\n");
  126. break;
  127. case MTD_MLCNANDFLASH:
  128. printf("MLC NAND flash\n");
  129. break;
  130. case MTD_ABSENT:
  131. default:
  132. printf("Unknown\n");
  133. break;
  134. }
  135. printf(" - block size: 0x%x bytes\n", mtd->erasesize);
  136. printf(" - min I/O: 0x%x bytes\n", mtd->writesize);
  137. if (mtd->oobsize) {
  138. printf(" - OOB size: %u bytes\n", mtd->oobsize);
  139. printf(" - OOB available: %u bytes\n", mtd->oobavail);
  140. }
  141. if (mtd->ecc_strength) {
  142. printf(" - ECC strength: %u bits\n", mtd->ecc_strength);
  143. printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size);
  144. printf(" - bitflip threshold: %u bits\n",
  145. mtd->bitflip_threshold);
  146. }
  147. printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
  148. mtd->offset, mtd->offset + mtd->size, mtd->name);
  149. /* MTD partitions, if any */
  150. mtd_show_parts(mtd, 1);
  151. }
  152. /* Logic taken from fs/ubifs/recovery.c:is_empty() */
  153. static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
  154. {
  155. int i;
  156. for (i = 0; i < op->len; i++)
  157. if (op->datbuf[i] != 0xff)
  158. return false;
  159. for (i = 0; i < op->ooblen; i++)
  160. if (op->oobbuf[i] != 0xff)
  161. return false;
  162. return true;
  163. }
  164. static int do_mtd_list(cmd_tbl_t *cmdtp, int flag, int argc,
  165. char * const argv[])
  166. {
  167. struct mtd_info *mtd;
  168. int dev_nb = 0;
  169. /* Ensure all devices (and their partitions) are probed */
  170. mtd_probe_devices();
  171. printf("List of MTD devices:\n");
  172. mtd_for_each_device(mtd) {
  173. if (!mtd_is_partition(mtd))
  174. mtd_show_device(mtd);
  175. dev_nb++;
  176. }
  177. if (!dev_nb) {
  178. printf("No MTD device found\n");
  179. return CMD_RET_FAILURE;
  180. }
  181. return CMD_RET_SUCCESS;
  182. }
  183. static int mtd_special_write_oob(struct mtd_info *mtd, u64 off,
  184. struct mtd_oob_ops *io_op,
  185. bool write_empty_pages, bool woob)
  186. {
  187. int ret = 0;
  188. /*
  189. * By default, do not write an empty page.
  190. * Skip it by simulating a successful write.
  191. */
  192. if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) {
  193. io_op->retlen = mtd->writesize;
  194. io_op->oobretlen = woob ? mtd->oobsize : 0;
  195. } else {
  196. ret = mtd_write_oob(mtd, off, io_op);
  197. }
  198. return ret;
  199. }
  200. static int do_mtd_perf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  201. {
  202. #define TEST_DATA_PATTERN (0xaa)
  203. bool scrub,write_empty_pages = false, has_pages = false;
  204. u64 start_off, off, len, remaining;
  205. struct mtd_oob_ops io_op = {};
  206. struct erase_info erase_op = {};
  207. uint user_wr_addr = 0,user_rd_addr, npages;
  208. struct mtd_info *mtd;
  209. u8 *buf,*read_buf,*write_buf;
  210. int ret;
  211. uint32_t start_us,end_us;
  212. uint erase_speed,write_speed,read_speed;
  213. if (argc < 6)
  214. return CMD_RET_USAGE;
  215. /* get mtd_info handle */
  216. mtd = get_mtd_by_name(argv[1]);
  217. if (IS_ERR_OR_NULL(mtd))
  218. return CMD_RET_FAILURE;
  219. if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
  220. has_pages = true;
  221. /* get user_wr_addr */
  222. user_wr_addr = simple_strtoul(argv[2], NULL, 16);
  223. if (!user_wr_addr) {
  224. printf("Could not map/allocate the user buffer\n");
  225. ret = CMD_RET_FAILURE;
  226. goto out_put_mtd;
  227. }
  228. /* get user_rd_addr */
  229. user_rd_addr = simple_strtoul(argv[3], NULL, 16);
  230. if (!user_rd_addr) {
  231. printf("Could not map/allocate the user buffer\n");
  232. ret = CMD_RET_FAILURE;
  233. goto out_put_mtd;
  234. }
  235. /* get offset from mtd-device */
  236. start_off = simple_strtoul(argv[4], NULL, 16);
  237. if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
  238. printf("Offset not aligned with a page (0x%x)\n",
  239. mtd->writesize);
  240. ret = CMD_RET_FAILURE;
  241. goto out_put_mtd;
  242. }
  243. /* get size for read or write */
  244. len = simple_strtoul(argv[5], NULL, 16);
  245. len = (len == 0)?mtd->writesize : len;
  246. if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
  247. len = round_up(len, mtd->writesize);
  248. printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
  249. mtd->writesize, len);
  250. }
  251. /* init test-data pattern */
  252. buf = map_sysmem(user_wr_addr,0);
  253. memset((void*)buf,TEST_DATA_PATTERN,len);
  254. buf = map_sysmem(user_rd_addr,0);
  255. memset((void*)buf,0,len);
  256. remaining = len;
  257. npages = mtd_len_to_pages(mtd, len);
  258. if (has_pages)
  259. printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx\n",
  260. "read/write", len, npages, start_off);
  261. else
  262. printf("%s %lld byte(s) at offset 0x%08llx\n",
  263. "Read/Write", len, start_off);
  264. /* erase test */
  265. off = start_off;
  266. if (!mtd_is_aligned_with_block_size(mtd, off)) {
  267. printf("Offset not aligned with a block (0x%x)\n",
  268. mtd->erasesize);
  269. ret = CMD_RET_FAILURE;
  270. goto out_put_mtd;
  271. }
  272. if (!mtd_is_aligned_with_block_size(mtd, len)) {
  273. printf("Size not a multiple of a block (0x%x)\n",
  274. mtd->erasesize);
  275. ret = CMD_RET_FAILURE;
  276. goto out_put_mtd;
  277. }
  278. printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
  279. off, off + len - 1, mtd_div_by_eb(len, mtd));
  280. scrub = true;
  281. erase_op.mtd = mtd;
  282. erase_op.addr = off;
  283. erase_op.len = len;
  284. erase_op.scrub = scrub;
  285. start_us = timer_get_us();
  286. while (erase_op.len) {
  287. ret = mtd_erase(mtd, &erase_op);
  288. /* Abort if its not a bad block error */
  289. if (ret != -EIO)
  290. break;
  291. printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);
  292. /* Skip bad block and continue behind it */
  293. erase_op.len -= erase_op.fail_addr - erase_op.addr;
  294. erase_op.len -= mtd->erasesize;
  295. erase_op.addr = erase_op.fail_addr + mtd->erasesize;
  296. }
  297. end_us = timer_get_us();
  298. erase_speed = len * 1000 /(end_us - start_us);
  299. /* write test */
  300. buf = map_sysmem(user_wr_addr,0);
  301. io_op.mode = MTD_OPS_RAW;
  302. io_op.len = has_pages ? mtd->writesize : len;
  303. io_op.ooblen = 0;
  304. io_op.datbuf = buf;
  305. io_op.oobbuf = NULL;
  306. /* Search for the first good block after the given offset */
  307. off = start_off;
  308. while (mtd_block_isbad(mtd, off))
  309. off += mtd->erasesize;
  310. /* Loop over the pages to do the actual read/write */
  311. start_us = timer_get_us();
  312. while (remaining) {
  313. /* Skip the block if it is bad */
  314. if (mtd_is_aligned_with_block_size(mtd, off) &&
  315. mtd_block_isbad(mtd, off)) {
  316. off += mtd->erasesize;
  317. continue;
  318. }
  319. ret = mtd_special_write_oob(mtd, off, &io_op,
  320. write_empty_pages, 0);
  321. if (ret) {
  322. printf("Failure while %s at offset 0x%llx\n",
  323. "reading", off);
  324. break;
  325. }
  326. off += io_op.retlen;
  327. remaining -= io_op.retlen;
  328. io_op.datbuf += io_op.retlen;
  329. io_op.oobbuf += io_op.oobretlen;
  330. }
  331. end_us = timer_get_us();
  332. write_speed = len * 1000/(end_us - start_us);
  333. /* read test */
  334. remaining = len;
  335. npages = mtd_len_to_pages(mtd, len);
  336. buf = map_sysmem(user_rd_addr,0);
  337. io_op.mode = MTD_OPS_RAW;
  338. io_op.len = has_pages ? mtd->writesize : len;
  339. io_op.ooblen = 0;
  340. io_op.datbuf = buf;
  341. io_op.oobbuf = NULL;
  342. /* Search for the first good block after the given offset */
  343. off = start_off;
  344. while (mtd_block_isbad(mtd, off))
  345. off += mtd->erasesize;
  346. /* Loop over the pages to do the actual read/write */
  347. start_us = timer_get_us();
  348. while (remaining) {
  349. /* Skip the block if it is bad */
  350. if (mtd_is_aligned_with_block_size(mtd, off) &&
  351. mtd_block_isbad(mtd, off)) {
  352. off += mtd->erasesize;
  353. continue;
  354. }
  355. ret = mtd_read_oob(mtd, off, &io_op);
  356. if (ret) {
  357. printf("Failure while %s at offset 0x%llx\n",
  358. "reading", off);
  359. break;
  360. }
  361. off += io_op.retlen;
  362. remaining -= io_op.retlen;
  363. io_op.datbuf += io_op.retlen;
  364. io_op.oobbuf += io_op.oobretlen;
  365. }
  366. end_us = timer_get_us();
  367. read_speed = len * 1000/(end_us - start_us);
  368. /*check result and output perf result*/
  369. read_buf = map_sysmem(user_rd_addr,0);
  370. write_buf = map_sysmem(user_wr_addr,0);
  371. if (memcmp((const void*)read_buf,(void*)write_buf,len) ){
  372. printf("write != read ,perf test failed\n");
  373. ret = CMD_RET_FAILURE;
  374. }else {
  375. printf("%s perf test success\n",mtd->name);
  376. printf("erase_speed: %u KB \nwrite_speed: %u KB \nread_speed: %u KB\n",
  377. erase_speed,write_speed,read_speed);
  378. ret = CMD_RET_SUCCESS;
  379. }
  380. out_put_mtd:
  381. put_mtd_device(mtd);
  382. #undef TEST_DATA_PATTERN
  383. return ret;
  384. }
  385. static int do_mtd_io(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  386. {
  387. bool dump, read, raw, woob, write_empty_pages, has_pages = false;
  388. u64 start_off, off, len, remaining, default_len;
  389. struct mtd_oob_ops io_op = {};
  390. uint user_addr = 0, npages;
  391. const char *cmd = argv[0];
  392. struct mtd_info *mtd;
  393. u32 oob_len;
  394. u8 *buf;
  395. int ret;
  396. if (argc < 2)
  397. return CMD_RET_USAGE;
  398. mtd = get_mtd_by_name(argv[1]);
  399. if (IS_ERR_OR_NULL(mtd))
  400. return CMD_RET_FAILURE;
  401. if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
  402. has_pages = true;
  403. dump = !strncmp(cmd, "dump", 4);
  404. read = dump || !strncmp(cmd, "read", 4);
  405. raw = strstr(cmd, ".raw");
  406. woob = strstr(cmd, ".oob");
  407. write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
  408. argc -= 2;
  409. argv += 2;
  410. if (!dump) {
  411. if (!argc) {
  412. ret = CMD_RET_USAGE;
  413. goto out_put_mtd;
  414. }
  415. user_addr = simple_strtoul(argv[0], NULL, 16);
  416. argc--;
  417. argv++;
  418. }
  419. start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
  420. if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
  421. printf("Offset not aligned with a page (0x%x)\n",
  422. mtd->writesize);
  423. ret = CMD_RET_FAILURE;
  424. goto out_put_mtd;
  425. }
  426. default_len = dump ? mtd->writesize : mtd->size;
  427. len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : default_len;
  428. if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
  429. len = round_up(len, mtd->writesize);
  430. printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
  431. mtd->writesize, len);
  432. }
  433. remaining = len;
  434. npages = mtd_len_to_pages(mtd, len);
  435. oob_len = woob ? npages * mtd->oobsize : 0;
  436. if (dump)
  437. buf = kmalloc(len + oob_len, GFP_KERNEL);
  438. else
  439. buf = map_sysmem(user_addr, 0);
  440. if (!buf) {
  441. printf("Could not map/allocate the user buffer\n");
  442. ret = CMD_RET_FAILURE;
  443. goto out_put_mtd;
  444. }
  445. if (has_pages)
  446. printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
  447. read ? "Reading" : "Writing", len, npages, start_off,
  448. raw ? " [raw]" : "", woob ? " [oob]" : "",
  449. !read && write_empty_pages ? " [dontskipff]" : "");
  450. else
  451. printf("%s %lld byte(s) at offset 0x%08llx\n",
  452. read ? "Reading" : "Writing", len, start_off);
  453. io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
  454. io_op.len = has_pages ? mtd->writesize : len;
  455. io_op.ooblen = woob ? mtd->oobsize : 0;
  456. io_op.datbuf = buf;
  457. io_op.oobbuf = woob ? &buf[len] : NULL;
  458. /* Search for the first good block after the given offset */
  459. off = start_off;
  460. while (mtd_block_isbad(mtd, off))
  461. off += mtd->erasesize;
  462. /* Loop over the pages to do the actual read/write */
  463. while (remaining) {
  464. /* Skip the block if it is bad */
  465. if (mtd_is_aligned_with_block_size(mtd, off) &&
  466. mtd_block_isbad(mtd, off)) {
  467. off += mtd->erasesize;
  468. continue;
  469. }
  470. if (read)
  471. ret = mtd_read_oob(mtd, off, &io_op);
  472. else
  473. ret = mtd_special_write_oob(mtd, off, &io_op,
  474. write_empty_pages, woob);
  475. if (ret) {
  476. printf("Failure while %s at offset 0x%llx\n",
  477. read ? "reading" : "writing", off);
  478. break;
  479. }
  480. off += io_op.retlen;
  481. remaining -= io_op.retlen;
  482. io_op.datbuf += io_op.retlen;
  483. io_op.oobbuf += io_op.oobretlen;
  484. }
  485. if (!ret && dump)
  486. mtd_dump_device_buf(mtd, start_off, buf, len, woob);
  487. if (dump)
  488. kfree(buf);
  489. else
  490. unmap_sysmem(buf);
  491. if (ret) {
  492. printf("%s on %s failed with error %d\n",
  493. read ? "Read" : "Write", mtd->name, ret);
  494. ret = CMD_RET_FAILURE;
  495. } else {
  496. ret = CMD_RET_SUCCESS;
  497. }
  498. out_put_mtd:
  499. put_mtd_device(mtd);
  500. return ret;
  501. }
  502. static int do_mtd_erase(cmd_tbl_t *cmdtp, int flag, int argc,
  503. char * const argv[])
  504. {
  505. struct erase_info erase_op = {};
  506. struct mtd_info *mtd;
  507. u64 off, len;
  508. bool scrub;
  509. int ret;
  510. if (argc < 2)
  511. return CMD_RET_USAGE;
  512. mtd = get_mtd_by_name(argv[1]);
  513. if (IS_ERR_OR_NULL(mtd))
  514. return CMD_RET_FAILURE;
  515. scrub = strstr(argv[0], ".dontskipbad");
  516. argc -= 2;
  517. argv += 2;
  518. off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
  519. len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size;
  520. if (!mtd_is_aligned_with_block_size(mtd, off)) {
  521. printf("Offset not aligned with a block (0x%x)\n",
  522. mtd->erasesize);
  523. ret = CMD_RET_FAILURE;
  524. goto out_put_mtd;
  525. }
  526. if (!mtd_is_aligned_with_block_size(mtd, len)) {
  527. printf("Size not a multiple of a block (0x%x)\n",
  528. mtd->erasesize);
  529. ret = CMD_RET_FAILURE;
  530. goto out_put_mtd;
  531. }
  532. printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
  533. off, off + len - 1, mtd_div_by_eb(len, mtd));
  534. erase_op.mtd = mtd;
  535. erase_op.addr = off;
  536. erase_op.len = len;
  537. erase_op.scrub = scrub;
  538. while (erase_op.len) {
  539. ret = mtd_erase(mtd, &erase_op);
  540. /* Abort if its not a bad block error */
  541. if (ret != -EIO)
  542. break;
  543. printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);
  544. /* Skip bad block and continue behind it */
  545. erase_op.len -= erase_op.fail_addr - erase_op.addr;
  546. erase_op.len -= mtd->erasesize;
  547. erase_op.addr = erase_op.fail_addr + mtd->erasesize;
  548. }
  549. if (ret && ret != -EIO)
  550. ret = CMD_RET_FAILURE;
  551. else
  552. ret = CMD_RET_SUCCESS;
  553. out_put_mtd:
  554. put_mtd_device(mtd);
  555. return ret;
  556. }
  557. static int do_mtd_bad(cmd_tbl_t *cmdtp, int flag, int argc,
  558. char * const argv[])
  559. {
  560. struct mtd_info *mtd;
  561. loff_t off;
  562. if (argc < 2)
  563. return CMD_RET_USAGE;
  564. mtd = get_mtd_by_name(argv[1]);
  565. if (IS_ERR_OR_NULL(mtd))
  566. return CMD_RET_FAILURE;
  567. if (!mtd_can_have_bb(mtd)) {
  568. printf("Only NAND-based devices can have bad blocks\n");
  569. goto out_put_mtd;
  570. }
  571. printf("MTD device %s bad blocks list:\n", mtd->name);
  572. for (off = 0; off < mtd->size; off += mtd->erasesize) {
  573. if (mtd_block_isbad(mtd, off))
  574. printf("\t0x%08llx\n", off);
  575. }
  576. out_put_mtd:
  577. put_mtd_device(mtd);
  578. return CMD_RET_SUCCESS;
  579. }
  580. #ifdef CONFIG_AUTO_COMPLETE
  581. static int mtd_name_complete(int argc, char * const argv[], char last_char,
  582. int maxv, char *cmdv[])
  583. {
  584. int len = 0, n_found = 0;
  585. struct mtd_info *mtd;
  586. argc--;
  587. argv++;
  588. if (argc > 1 ||
  589. (argc == 1 && (last_char == '\0' || isblank(last_char))))
  590. return 0;
  591. if (argc)
  592. len = strlen(argv[0]);
  593. mtd_for_each_device(mtd) {
  594. if (argc &&
  595. (len > strlen(mtd->name) ||
  596. strncmp(argv[0], mtd->name, len)))
  597. continue;
  598. if (n_found >= maxv - 2) {
  599. cmdv[n_found++] = "...";
  600. break;
  601. }
  602. cmdv[n_found++] = mtd->name;
  603. }
  604. cmdv[n_found] = NULL;
  605. return n_found;
  606. }
  607. #endif /* CONFIG_AUTO_COMPLETE */
  608. #ifdef CONFIG_SYS_LONGHELP
  609. static char mtd_help_text[] =
  610. "- generic operations on memory technology devices\n\n"
  611. "mtd list\n"
  612. "mtd read[.raw][.oob] <name> <addr> [<off> [<size>]]\n"
  613. "mtd dump[.raw][.oob] <name> [<off> [<size>]]\n"
  614. "mtd write[.raw][.oob][.dontskipff] <name> <addr> [<off> [<size>]]\n"
  615. "mtd erase[.dontskipbad] <name> [<off> [<size>]]\n"
  616. "mtd perf <name> <addr> <addr> [<off> [<size>]]\n"
  617. "\n"
  618. "Specific functions:\n"
  619. "mtd bad <name>\n"
  620. "\n"
  621. "With:\n"
  622. "\t<name>: NAND partition/chip name\n"
  623. "\t<addr>: user address from/to which data will be retrieved/stored\n"
  624. "\t<off>: offset in <name> in bytes (default: start of the part)\n"
  625. "\t\t* must be block-aligned for erase\n"
  626. "\t\t* must be page-aligned otherwise\n"
  627. "\t<size>: length of the operation in bytes (default: the entire device)\n"
  628. "\t\t* must be a multiple of a block for erase\n"
  629. "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
  630. "\n"
  631. "The .dontskipff option forces writing empty pages, don't use it if unsure.\n";
  632. #endif
  633. U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
  634. U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
  635. U_BOOT_SUBCMD_MKENT_COMPLETE(perf, 6, 0, do_mtd_perf,
  636. mtd_name_complete),
  637. U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
  638. mtd_name_complete),
  639. U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io,
  640. mtd_name_complete),
  641. U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io,
  642. mtd_name_complete),
  643. U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
  644. mtd_name_complete),
  645. U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
  646. mtd_name_complete));