100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118
  1. From 88271cb3ae9c68dc200d627653df96fc557c2a64 Mon Sep 17 00:00:00 2001
  2. From: Weijie Gao <[email protected]>
  3. Date: Mon, 25 Jul 2022 10:55:35 +0800
  4. Subject: [PATCH 47/71] cmd: add a new command for NAND flash debugging
  5. Add a command 'nand-ext' for NAND flash debugging:
  6. - Dump a page with oob, with optional raw read support
  7. - Display all bad blocks
  8. - Mark a block as bad block
  9. - Set a bitflip on a page
  10. - Erase
  11. - Read / write data from/to any offset with any size
  12. - Read / write pages with oob
  13. - Erase, read and write support skip bad block or forced mode, support
  14. raw mode, supporot auto-oob mode
  15. - Supports operating on a specific partition
  16. - No need to specify NAND device name
  17. Signed-off-by: Weijie Gao <[email protected]>
  18. ---
  19. cmd/Kconfig | 8 +
  20. cmd/Makefile | 1 +
  21. cmd/nand-ext.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++
  22. 3 files changed, 1071 insertions(+)
  23. create mode 100644 cmd/nand-ext.c
  24. --- a/cmd/Kconfig
  25. +++ b/cmd/Kconfig
  26. @@ -1392,6 +1392,14 @@ config CMD_NAND_TORTURE
  27. endif # CMD_NAND
  28. +config CMD_NAND_EXT
  29. + bool "nand - extended nand utility for debugging"
  30. + depends on !CMD_NAND
  31. + default y if MTD_RAW_NAND || MTD_SPI_NAND || MTK_SPI_NAND
  32. + select MTD_PARTITIONS
  33. + help
  34. + NAND flash R/W and debugging support.
  35. +
  36. config CMD_NMBM
  37. depends on NMBM_MTD
  38. bool "nmbm"
  39. --- a/cmd/Makefile
  40. +++ b/cmd/Makefile
  41. @@ -127,6 +127,7 @@ obj-y += legacy-mtd-utils.o
  42. endif
  43. obj-$(CONFIG_CMD_MUX) += mux.o
  44. obj-$(CONFIG_CMD_NAND) += nand.o
  45. +obj-$(CONFIG_CMD_NAND_EXT) += nand-ext.o
  46. obj-$(CONFIG_CMD_NMBM) += nmbm.o
  47. obj-$(CONFIG_CMD_NET) += net.o
  48. obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
  49. --- /dev/null
  50. +++ b/cmd/nand-ext.c
  51. @@ -0,0 +1,1062 @@
  52. +// SPDX-License-Identifier: GPL-2.0
  53. +/*
  54. + * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
  55. + *
  56. + * Author: Weijie Gao <[email protected]>
  57. + */
  58. +
  59. +#include <command.h>
  60. +#include <stdbool.h>
  61. +#include <malloc.h>
  62. +#include <mtd.h>
  63. +#include <dm/devres.h>
  64. +#include <linux/types.h>
  65. +#include <linux/mtd/mtd.h>
  66. +
  67. +static struct mtd_info *curr_dev;
  68. +
  69. +static void mtd_show_parts(struct mtd_info *mtd, int level)
  70. +{
  71. + struct mtd_info *part;
  72. + int i;
  73. +
  74. + list_for_each_entry(part, &mtd->partitions, node) {
  75. + for (i = 0; i < level; i++)
  76. + printf("\t");
  77. + printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
  78. + part->offset, part->offset + part->size, part->name);
  79. +
  80. + mtd_show_parts(part, level + 1);
  81. + }
  82. +}
  83. +
  84. +static void mtd_show_device(struct mtd_info *mtd)
  85. +{
  86. + /* Device */
  87. + printf("* %s\n", mtd->name);
  88. +#if defined(CONFIG_DM)
  89. + if (mtd->dev) {
  90. + printf(" - device: %s\n", mtd->dev->name);
  91. + printf(" - parent: %s\n", mtd->dev->parent->name);
  92. + printf(" - driver: %s\n", mtd->dev->driver->name);
  93. + }
  94. +#endif
  95. +
  96. + /* MTD device information */
  97. + printf(" - type: ");
  98. + switch (mtd->type) {
  99. + case MTD_NANDFLASH:
  100. + printf("NAND flash\n");
  101. + break;
  102. + case MTD_MLCNANDFLASH:
  103. + printf("MLC NAND flash\n");
  104. + break;
  105. + case MTD_ABSENT:
  106. + default:
  107. + printf("Not supported\n");
  108. + break;
  109. + }
  110. +
  111. + printf(" - block size: 0x%x bytes\n", mtd->erasesize);
  112. + printf(" - page size: 0x%x bytes\n", mtd->writesize);
  113. + printf(" - OOB size: %u bytes\n", mtd->oobsize);
  114. + printf(" - OOB available: %u bytes\n", mtd->oobavail);
  115. +
  116. + if (mtd->ecc_strength) {
  117. + printf(" - ECC strength: %u bits\n", mtd->ecc_strength);
  118. + printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size);
  119. + printf(" - bitflip threshold: %u bits\n",
  120. + mtd->bitflip_threshold);
  121. + }
  122. +
  123. + printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
  124. + mtd->offset, mtd->offset + mtd->size, mtd->name);
  125. +
  126. + /* MTD partitions, if any */
  127. + mtd_show_parts(mtd, 1);
  128. +}
  129. +
  130. +static int do_nand_list(struct cmd_tbl *cmdtp, int flag, int argc,
  131. + char *const argv[])
  132. +{
  133. + struct mtd_info *mtd;
  134. + int dev_nb = 0;
  135. +
  136. + /* Ensure all devices (and their partitions) are probed */
  137. + mtd_probe_devices();
  138. +
  139. + printf("List of NAND devices:\n");
  140. + mtd_for_each_device(mtd) {
  141. + if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH)
  142. + continue;
  143. +
  144. + if (!mtd_is_partition(mtd))
  145. + mtd_show_device(mtd);
  146. +
  147. + dev_nb++;
  148. + }
  149. +
  150. + if (!dev_nb)
  151. + printf("No NAND MTD device found\n");
  152. +
  153. + return CMD_RET_SUCCESS;
  154. +}
  155. +
  156. +static struct mtd_info *nand_get_curr_dev(void)
  157. +{
  158. + struct mtd_info *mtd, *first_dev = NULL;
  159. + int err, dev_nb = 0;
  160. +
  161. + if (curr_dev) {
  162. + mtd = get_mtd_device(curr_dev, -1);
  163. + if (!IS_ERR_OR_NULL(mtd)) {
  164. + __put_mtd_device(mtd);
  165. + return mtd;
  166. + }
  167. +
  168. + curr_dev = NULL;
  169. + }
  170. +
  171. + /* Ensure all devices (and their partitions) are probed */
  172. + mtd_probe_devices();
  173. +
  174. + mtd_for_each_device(mtd) {
  175. + if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH)
  176. + continue;
  177. +
  178. + if (!mtd_is_partition(mtd)) {
  179. + if (!first_dev)
  180. + first_dev = mtd;
  181. + dev_nb++;
  182. + }
  183. + }
  184. +
  185. + if (!dev_nb) {
  186. + printf("No NAND MTD device found\n");
  187. + return NULL;
  188. + }
  189. +
  190. + if (dev_nb > 1) {
  191. + printf("No active NAND MTD device specified\n");
  192. + return NULL;
  193. + }
  194. +
  195. + err = __get_mtd_device(first_dev);
  196. + if (err) {
  197. + printf("Failed to get MTD device '%s': err %d\n",
  198. + first_dev->name, err);
  199. + return NULL;
  200. + }
  201. +
  202. + curr_dev = first_dev;
  203. +
  204. + printf("'%s' is now active device\n", first_dev->name);
  205. +
  206. + return curr_dev;
  207. +}
  208. +
  209. +static struct mtd_info *nand_get_part(struct mtd_info *master,
  210. + const char *name)
  211. +{
  212. + struct mtd_info *slave;
  213. +
  214. + list_for_each_entry(slave, &master->partitions, node) {
  215. + if (!strcmp(slave->name, name))
  216. + return slave;
  217. + }
  218. +
  219. + return NULL;
  220. +}
  221. +
  222. +static int do_nand_info(struct cmd_tbl *cmdtp, int flag, int argc,
  223. + char *const argv[])
  224. +{
  225. + struct mtd_info *mtd = nand_get_curr_dev();
  226. +
  227. + if (!mtd)
  228. + return CMD_RET_FAILURE;
  229. +
  230. + mtd_show_device(mtd);
  231. +
  232. + return 0;
  233. +}
  234. +
  235. +static int do_nand_select(struct cmd_tbl *cmdtp, int flag, int argc,
  236. + char *const argv[])
  237. +{
  238. + struct mtd_info *mtd, *old;
  239. +
  240. + if (argc < 2) {
  241. + printf("MTD device name must be specified\n");
  242. + return CMD_RET_USAGE;
  243. + }
  244. +
  245. + mtd = get_mtd_device_nm(argv[1]);
  246. + if (!mtd) {
  247. + printf("MTD device '%s' not found\n", argv[1]);
  248. + return CMD_RET_FAILURE;
  249. + }
  250. +
  251. + if (mtd_is_partition(mtd)) {
  252. + printf("Error: '%s' is a MTD partition\n", argv[1]);
  253. + __put_mtd_device(mtd);
  254. + return CMD_RET_FAILURE;
  255. + }
  256. +
  257. + if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) {
  258. + printf("Error: '%s' is not a NAND device\n", argv[1]);
  259. + __put_mtd_device(mtd);
  260. + return CMD_RET_FAILURE;
  261. + }
  262. +
  263. + if (mtd == curr_dev) {
  264. + __put_mtd_device(mtd);
  265. + return CMD_RET_SUCCESS;
  266. + }
  267. +
  268. + if (curr_dev) {
  269. + old = get_mtd_device(curr_dev, -1);
  270. + if (!IS_ERR_OR_NULL(old)) {
  271. + __put_mtd_device(old);
  272. + __put_mtd_device(curr_dev);
  273. + }
  274. +
  275. + curr_dev = NULL;
  276. + }
  277. +
  278. + curr_dev = mtd;
  279. +
  280. + printf("'%s' is now active device\n", curr_dev->name);
  281. +
  282. + return CMD_RET_SUCCESS;
  283. +}
  284. +
  285. +static void dump_buf(const u8 *data, size_t size, u64 addr)
  286. +{
  287. + const u8 *p = data;
  288. + u32 i, chklen;
  289. +
  290. + while (size) {
  291. + chklen = 16;
  292. + if (chklen > size)
  293. + chklen = (u32)size;
  294. +
  295. + printf("%08llx: ", addr);
  296. +
  297. + for (i = 0; i < chklen; i++) {
  298. + if (i && (i % 4 == 0))
  299. + printf(" ");
  300. +
  301. + printf("%02x ", p[i]);
  302. + }
  303. +
  304. + for (i = chklen; i < 16; i++) {
  305. + if (i && (i % 4 == 0))
  306. + printf(" ");
  307. +
  308. + printf(" ");
  309. + }
  310. + printf(" ");
  311. +
  312. + for (i = 0; i < chklen; i++) {
  313. + if (p[i] < 32 || p[i] >= 0x7f)
  314. + printf(".");
  315. + else
  316. + printf("%c", p[i]);
  317. + }
  318. + printf("\n");
  319. +
  320. + p += chklen;
  321. + size -= chklen;
  322. + addr += chklen;
  323. + }
  324. +}
  325. +
  326. +static int do_nand_dump(struct cmd_tbl *cmdtp, int flag, int argc,
  327. + char *const argv[])
  328. +{
  329. + struct mtd_info *mtd = nand_get_curr_dev();
  330. + struct mtd_oob_ops io_op = {};
  331. + bool raw = false;
  332. + int ret;
  333. + u64 off;
  334. + u8 *buf;
  335. +
  336. + if (!mtd)
  337. + return CMD_RET_FAILURE;
  338. +
  339. + if (strstr(argv[0], ".raw"))
  340. + raw = true;
  341. +
  342. + if (argc < 2) {
  343. + printf("Dump offset must be specified\n");
  344. + return CMD_RET_USAGE;
  345. + }
  346. +
  347. + off = simple_strtoull(argv[1], NULL, 0);
  348. + if (off >= mtd->size) {
  349. + printf("Offset 0x%llx is larger than flash size\n", off);
  350. + return CMD_RET_FAILURE;
  351. + }
  352. +
  353. + off &= ~(u64)mtd->writesize_mask;
  354. +
  355. + buf = malloc(mtd->writesize + mtd->oobsize);
  356. + if (!buf) {
  357. + printf("Failed to allocate buffer\n");
  358. + return CMD_RET_FAILURE;
  359. + }
  360. +
  361. + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
  362. + io_op.len = mtd->writesize;
  363. + io_op.datbuf = buf;
  364. + io_op.ooblen = mtd->oobsize;
  365. + io_op.oobbuf = buf + mtd->writesize;
  366. +
  367. + ret = mtd_read_oob(mtd, off, &io_op);
  368. + if (ret < 0 && ret != -EUCLEAN && ret != -EBADMSG) {
  369. + printf("Failed to read page at 0x%llx, err %d\n", off, ret);
  370. + free(buf);
  371. + return CMD_RET_FAILURE;
  372. + }
  373. +
  374. + printf("Dump of %spage at 0x%llx:\n", raw ? "raw " : "", off);
  375. + dump_buf(buf, mtd->writesize, off);
  376. +
  377. + printf("\n");
  378. + printf("OOB:\n");
  379. + dump_buf(buf + mtd->writesize, mtd->oobsize, 0);
  380. +
  381. + free(buf);
  382. +
  383. + return CMD_RET_SUCCESS;
  384. +}
  385. +
  386. +static int do_nand_bad(struct cmd_tbl *cmdtp, int flag, int argc,
  387. + char *const argv[])
  388. +{
  389. + struct mtd_info *mtd = nand_get_curr_dev();
  390. + u64 off = 0;
  391. +
  392. + if (!mtd)
  393. + return CMD_RET_FAILURE;
  394. +
  395. + while (off < mtd->size) {
  396. + if (mtd_block_isbad(mtd, off))
  397. + printf("\t%08llx\n", off);
  398. +
  399. + off += mtd->erasesize;
  400. + }
  401. +
  402. + return 0;
  403. +}
  404. +
  405. +static int do_nand_markbad(struct cmd_tbl *cmdtp, int flag, int argc,
  406. + char *const argv[])
  407. +{
  408. + struct mtd_info *mtd = nand_get_curr_dev();
  409. + u64 off;
  410. + int ret;
  411. +
  412. + if (!mtd)
  413. + return CMD_RET_FAILURE;
  414. +
  415. + if (argc < 2) {
  416. + printf("Missing address within a block to be marked bad\n");
  417. + return CMD_RET_USAGE;
  418. + }
  419. +
  420. + off = simple_strtoull(argv[1], NULL, 0);
  421. + if (off >= mtd->size) {
  422. + printf("Offset 0x%llx is larger than flash size\n", off);
  423. + return CMD_RET_FAILURE;
  424. + }
  425. +
  426. + off &= ~(u64)mtd->erasesize_mask;
  427. +
  428. + ret = mtd_block_markbad(mtd, off);
  429. +
  430. + if (!ret)
  431. + printf("Block at 0x%08llx has been marked bad\n", off);
  432. + else
  433. + printf("Failed to mark bad block at 0x%08llx\n", off);
  434. +
  435. + return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
  436. +}
  437. +
  438. +static int do_nand_bitflip(struct cmd_tbl *cmdtp, int flag, int argc,
  439. + char *const argv[])
  440. +{
  441. + struct mtd_info *mtd = nand_get_curr_dev();
  442. + struct mtd_oob_ops io_op = {};
  443. + u32 col, bit;
  444. + bool res;
  445. + u64 off;
  446. + u8 *buf;
  447. + int ret;
  448. +
  449. + if (!mtd)
  450. + return CMD_RET_FAILURE;
  451. +
  452. + if (argc < 2) {
  453. + printf("Missing address to generate bitflip\n");
  454. + return CMD_RET_USAGE;
  455. + }
  456. +
  457. + off = simple_strtoull(argv[1], NULL, 0);
  458. + if (off >= mtd->size) {
  459. + printf("Offset 0x%llx is larger than flash size\n", off);
  460. + return CMD_RET_FAILURE;
  461. + }
  462. +
  463. + if (argc < 3) {
  464. + printf("Missing column address\n");
  465. + return CMD_RET_USAGE;
  466. + }
  467. +
  468. + col = simple_strtoul(argv[2], NULL, 0);
  469. + if (col >= mtd->writesize + mtd->oobsize) {
  470. + printf("Column address must be less than %u\n",
  471. + mtd->writesize + mtd->oobsize);
  472. + return CMD_RET_FAILURE;
  473. + }
  474. +
  475. + if (argc < 4) {
  476. + printf("Missing bit position\n");
  477. + return CMD_RET_USAGE;
  478. + }
  479. +
  480. + bit = simple_strtoul(argv[3], NULL, 0);
  481. + if (bit > 7) {
  482. + printf("Bit position must be less than 8\n");
  483. + return CMD_RET_FAILURE;
  484. + }
  485. +
  486. + off &= ~(u64)mtd->writesize_mask;
  487. +
  488. + buf = malloc(mtd->writesize + mtd->oobsize);
  489. + if (!buf) {
  490. + printf("Failed to allocate buffer\n");
  491. + return CMD_RET_FAILURE;
  492. + }
  493. +
  494. + io_op.mode = MTD_OPS_RAW;
  495. + io_op.len = mtd->writesize;
  496. + io_op.datbuf = buf;
  497. + io_op.ooblen = mtd->oobsize;
  498. + io_op.oobbuf = buf + mtd->writesize;
  499. +
  500. + ret = mtd_read_oob(mtd, off, &io_op);
  501. + if (ret < 0) {
  502. + printf("Failed to read page at 0x%llx, err %d\n", off, ret);
  503. + free(buf);
  504. + return CMD_RET_FAILURE;
  505. + }
  506. +
  507. + if (!(buf[col] & (1 << bit))) {
  508. + printf("Bit %u at byte %u is already zero\n", bit, col);
  509. + free(buf);
  510. + return CMD_RET_FAILURE;
  511. + }
  512. +
  513. + buf[col] &= ~(1 << bit);
  514. +
  515. + memset(&io_op, 0, sizeof(io_op));
  516. + io_op.mode = MTD_OPS_RAW;
  517. + io_op.len = mtd->writesize;
  518. + io_op.datbuf = buf;
  519. + io_op.ooblen = mtd->oobsize;
  520. + io_op.oobbuf = buf + mtd->writesize;
  521. +
  522. + ret = mtd_write_oob(mtd, off, &io_op);
  523. +
  524. + if (ret < 0) {
  525. + printf("Failed to write page at 0x%llx, err %d\n", off, ret);
  526. + return CMD_RET_FAILURE;
  527. + }
  528. +
  529. + memset(&io_op, 0, sizeof(io_op));
  530. + io_op.mode = MTD_OPS_RAW;
  531. + io_op.len = mtd->writesize;
  532. + io_op.datbuf = buf;
  533. + io_op.ooblen = mtd->oobsize;
  534. + io_op.oobbuf = buf + mtd->writesize;
  535. +
  536. + ret = mtd_read_oob(mtd, off, &io_op);
  537. + if (ret < 0) {
  538. + printf("Failed to read page at 0x%llx, err %d\n", off, ret);
  539. + free(buf);
  540. + return CMD_RET_FAILURE;
  541. + }
  542. +
  543. + res = (buf[col] & (1 << bit)) == 0;
  544. + free(buf);
  545. +
  546. + if (res) {
  547. + printf("Bit %u at byte %u has been changed to 0\n", bit, col);
  548. + return CMD_RET_SUCCESS;
  549. + }
  550. +
  551. + printf("Failed to change bit %u at byte %u to 0\n", bit, col);
  552. + return CMD_RET_FAILURE;
  553. +}
  554. +
  555. +static int do_nand_erase(struct cmd_tbl *cmdtp, int flag, int argc,
  556. + char *const argv[])
  557. +{
  558. + struct mtd_info *mtd = nand_get_curr_dev(), *part;
  559. + bool spread = false, force = false;
  560. + u64 off, size, end, limit;
  561. + struct erase_info ei;
  562. + char *ends;
  563. + int ret;
  564. +
  565. + if (!mtd)
  566. + return CMD_RET_FAILURE;
  567. +
  568. + if (strstr(argv[0], ".spread"))
  569. + spread = true;
  570. +
  571. + if (strstr(argv[0], ".force"))
  572. + force = true;
  573. +
  574. + if (spread && force) {
  575. + printf("spread and force must not be set at the same time\n");
  576. + return CMD_RET_FAILURE;
  577. + }
  578. +
  579. + if (argc < 2) {
  580. + printf("Erase start offset/partition must be specified\n");
  581. + return CMD_RET_USAGE;
  582. + }
  583. +
  584. + part = nand_get_part(mtd, argv[1]);
  585. + if (part) {
  586. + off = part->offset;
  587. +
  588. + if (argc < 3)
  589. + size = part->size;
  590. + else
  591. + size = simple_strtoull(argv[2], NULL, 0);
  592. +
  593. + if (size > part->size) {
  594. + printf("Erase end offset is larger than partition size\n");
  595. + printf("Erase size reduced to 0x%llx\n", part->size);
  596. +
  597. + size = part->size;
  598. + }
  599. +
  600. + limit = off + part->size;
  601. + } else {
  602. + off = simple_strtoull(argv[1], &ends, 0);
  603. +
  604. + if (ends == argv[1] || *ends) {
  605. + printf("Partition '%s' not found\n", argv[1]);
  606. + return CMD_RET_FAILURE;
  607. + }
  608. +
  609. + if (off >= mtd->size) {
  610. + printf("Offset 0x%llx is larger than flash size\n", off);
  611. + return CMD_RET_FAILURE;
  612. + }
  613. +
  614. + if (argc < 3) {
  615. + printf("Erase size offset must be specified\n");
  616. + return CMD_RET_USAGE;
  617. + }
  618. +
  619. + size = simple_strtoull(argv[2], NULL, 0);
  620. +
  621. + if (off + size > mtd->size) {
  622. + printf("Erase end offset is larger than flash size\n");
  623. +
  624. + size = mtd->size - off;
  625. + printf("Erase size reduced to 0x%llx\n", size);
  626. + }
  627. +
  628. + limit = mtd->size;
  629. + }
  630. +
  631. + end = off + size;
  632. + off &= ~(u64)mtd->erasesize_mask;
  633. + end = (end + mtd->erasesize_mask) & (~(u64)mtd->erasesize_mask);
  634. + size = end - off;
  635. +
  636. + printf("Erasing from 0x%llx to 0x%llx, size 0x%llx ...\n",
  637. + off, end - 1, end - off);
  638. +
  639. + while (size && off < limit) {
  640. + if (mtd_block_isbad(mtd, off)) {
  641. + printf("Bad block at 0x%llx", off);
  642. +
  643. + if (spread) {
  644. + printf(" ... skipped\n");
  645. + off += mtd->erasesize;
  646. + continue;
  647. + }
  648. +
  649. + if (!force) {
  650. + printf(" ... aborted\n");
  651. + return CMD_RET_FAILURE;
  652. + }
  653. +
  654. + printf(" ... will be force erased\n");
  655. + }
  656. +
  657. + memset(&ei, 0, sizeof(ei));
  658. +
  659. + ei.mtd = mtd;
  660. + ei.addr = off;
  661. + ei.len = mtd->erasesize;
  662. + ei.scrub = force;
  663. +
  664. + ret = mtd_erase(mtd, &ei);
  665. + if (ret) {
  666. + printf("Erase failed at 0x%llx\n", off);
  667. + return CMD_RET_FAILURE;
  668. + }
  669. +
  670. + off += mtd->erasesize;
  671. + size -= mtd->erasesize;
  672. + }
  673. +
  674. + printf("Succeeded\n");
  675. +
  676. + return CMD_RET_SUCCESS;
  677. +}
  678. +
  679. +static bool is_empty_page(const u8 *buf, size_t size)
  680. +{
  681. + size_t i;
  682. +
  683. + for (i = 0; i < size; i++) {
  684. + if (buf[i] != 0xff)
  685. + return false;
  686. + }
  687. +
  688. + return true;
  689. +}
  690. +
  691. +static int do_nand_io_normal(int argc, char *const argv[])
  692. +{
  693. + struct mtd_info *mtd = nand_get_curr_dev(), *part;
  694. + bool spread = false, force = false, raw = false, writeff = false;
  695. + bool read = false, checkbad = true;
  696. + struct mtd_oob_ops io_op = {};
  697. + size_t size, padding, chksz;
  698. + uintptr_t addr;
  699. + u64 off, offp;
  700. + char *ends;
  701. + u8 *buf;
  702. + int ret;
  703. +
  704. + if (!mtd)
  705. + return CMD_RET_FAILURE;
  706. +
  707. + if (!strncmp(argv[0], "read", 4))
  708. + read = true;
  709. +
  710. + if (strstr(argv[0], ".spread"))
  711. + spread = true;
  712. +
  713. + if (strstr(argv[0], ".force"))
  714. + force = true;
  715. +
  716. + if (strstr(argv[0], ".raw"))
  717. + raw = true;
  718. +
  719. + if (strstr(argv[0], ".ff"))
  720. + writeff = true;
  721. +
  722. + if (spread && force) {
  723. + printf("spread and force must not be set at the same time\n");
  724. + return CMD_RET_FAILURE;
  725. + }
  726. +
  727. + if (argc < 2) {
  728. + printf("Data address must be specified\n");
  729. + return CMD_RET_USAGE;
  730. + }
  731. +
  732. + addr = simple_strtoul(argv[1], NULL, 0);
  733. +
  734. + if (argc < 3) {
  735. + printf("Flash address/partition must be specified\n");
  736. + return CMD_RET_USAGE;
  737. + }
  738. +
  739. + part = nand_get_part(mtd, argv[2]);
  740. + if (part) {
  741. + if (argc < 4) {
  742. + off = 0;
  743. + } else {
  744. + off = simple_strtoull(argv[3], NULL, 0);
  745. + if (off + part->offset >= part->size) {
  746. + printf("Offset is larger than partition size\n");
  747. + return CMD_RET_FAILURE;
  748. + }
  749. + }
  750. +
  751. + if (argc < 5) {
  752. + size = part->size - off;
  753. + } else {
  754. + size = simple_strtoul(argv[4], NULL, 0);
  755. + if (off + size > part->size) {
  756. + printf("Data size is too large\n");
  757. + return CMD_RET_FAILURE;
  758. + }
  759. + }
  760. +
  761. + off += part->offset;
  762. + } else {
  763. + off = simple_strtoull(argv[2], &ends, 0);
  764. +
  765. + if (ends == argv[1] || *ends) {
  766. + printf("Partition '%s' not found\n", argv[2]);
  767. + return CMD_RET_FAILURE;
  768. + }
  769. +
  770. + if (off >= mtd->size) {
  771. + printf("Offset 0x%llx is larger than flash size\n", off);
  772. + return CMD_RET_FAILURE;
  773. + }
  774. +
  775. + if (argc < 4) {
  776. + printf("Data size must be specified\n");
  777. + return CMD_RET_USAGE;
  778. + }
  779. +
  780. + size = simple_strtoul(argv[3], NULL, 0);
  781. + if (off + size > mtd->size) {
  782. + printf("Data size is too large\n");
  783. + return CMD_RET_FAILURE;
  784. + }
  785. + }
  786. +
  787. + buf = malloc(mtd->writesize);
  788. + if (!buf) {
  789. + printf("Failed to allocate buffer\n");
  790. + return CMD_RET_FAILURE;
  791. + }
  792. +
  793. + printf("%s from 0x%llx to 0x%llx, size 0x%zx ...\n",
  794. + read ? "Reading" : "Writing", off, off + size - 1, size);
  795. +
  796. + while (size && off < mtd->size) {
  797. + if (checkbad || !(off & mtd->erasesize_mask)) {
  798. + offp = off & ~(u64)mtd->erasesize_mask;
  799. +
  800. + if (mtd_block_isbad(mtd, offp)) {
  801. + printf("Bad block at 0x%llx", offp);
  802. +
  803. + if (spread) {
  804. + printf(" ... skipped\n");
  805. + off += mtd->erasesize;
  806. + checkbad = true;
  807. + continue;
  808. + }
  809. +
  810. + if (!force) {
  811. + printf(" ... aborted\n");
  812. + goto err_out;
  813. + }
  814. +
  815. + printf(" ... continue\n");
  816. + }
  817. +
  818. + checkbad = false;
  819. + }
  820. +
  821. + padding = off & mtd->writesize_mask;
  822. + chksz = mtd->writesize - padding;
  823. + chksz = min_t(size_t, chksz, size);
  824. +
  825. + offp = off & ~(u64)mtd->writesize_mask;
  826. +
  827. + memset(&io_op, 0, sizeof(io_op));
  828. + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
  829. + io_op.len = mtd->writesize;
  830. +
  831. + if (chksz < mtd->writesize)
  832. + io_op.datbuf = buf;
  833. + else
  834. + io_op.datbuf = (void *)addr;
  835. +
  836. + if (read) {
  837. + ret = mtd_read_oob(mtd, offp, &io_op);
  838. + if (ret && ret != -EUCLEAN && ret != -EBADMSG)
  839. + goto io_err;
  840. +
  841. + if (chksz < mtd->writesize)
  842. + memcpy((void *)addr, buf + padding, chksz);
  843. + } else {
  844. + if (chksz < mtd->writesize) {
  845. + memset(buf, 0xff, mtd->writesize);
  846. + memcpy(buf + padding, (void *)addr, chksz);
  847. + }
  848. +
  849. + if (is_empty_page(io_op.datbuf, io_op.len) && !writeff)
  850. + ret = 0;
  851. + else
  852. + ret = mtd_write_oob(mtd, offp, &io_op);
  853. +
  854. + if (ret)
  855. + goto io_err;
  856. + }
  857. +
  858. + size -= chksz;
  859. + addr += chksz;
  860. + off += chksz;
  861. + }
  862. +
  863. + if (!size) {
  864. + printf("Succeeded\n");
  865. + ret = CMD_RET_SUCCESS;
  866. + goto out;
  867. + }
  868. +
  869. + printf("0x%zx byte%s remained for %s\n", size, size > 1 ? "s" : "",
  870. + read ? "read" : "write");
  871. + goto err_out;
  872. +
  873. +io_err:
  874. + printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, offp);
  875. +
  876. +err_out:
  877. + ret = CMD_RET_FAILURE;
  878. +
  879. +out:
  880. + free(buf);
  881. + return ret;
  882. +}
  883. +
  884. +static int do_nand_io_page(int argc, char *const argv[])
  885. +{
  886. + struct mtd_info *mtd = nand_get_curr_dev(), *part;
  887. + bool spread = false, force = false, raw = false, autooob = false;
  888. + bool read = false, checkbad = true, writeff = false;
  889. + struct mtd_oob_ops io_op = {};
  890. + uintptr_t addr;
  891. + u64 off, offp;
  892. + char *ends;
  893. + u32 count;
  894. + int ret;
  895. +
  896. + if (!mtd)
  897. + return CMD_RET_FAILURE;
  898. +
  899. + if (!strncmp(argv[0], "read", 4))
  900. + read = true;
  901. +
  902. + if (strstr(argv[0], ".spread"))
  903. + spread = true;
  904. +
  905. + if (strstr(argv[0], ".force"))
  906. + force = true;
  907. +
  908. + if (strstr(argv[0], ".raw"))
  909. + raw = true;
  910. +
  911. + if (strstr(argv[0], ".auto"))
  912. + autooob = true;
  913. +
  914. + if (spread && force) {
  915. + printf("spread and force must not be set at the same time\n");
  916. + return CMD_RET_FAILURE;
  917. + }
  918. +
  919. + if (raw && autooob) {
  920. + printf("raw and auto must not be set at the same time\n");
  921. + return CMD_RET_FAILURE;
  922. + }
  923. +
  924. + if (argc < 2) {
  925. + printf("Data address must be specified\n");
  926. + return CMD_RET_USAGE;
  927. + }
  928. +
  929. + addr = simple_strtoul(argv[1], NULL, 0);
  930. +
  931. + if (argc < 3) {
  932. + printf("Flash address/partition must be specified\n");
  933. + return CMD_RET_USAGE;
  934. + }
  935. +
  936. + part = nand_get_part(mtd, argv[2]);
  937. + if (part) {
  938. + if (argc < 4) {
  939. + printf("Partition offset / page count must be specified\n");
  940. + return CMD_RET_USAGE;
  941. + }
  942. +
  943. + if (argc < 5) {
  944. + off = 0;
  945. +
  946. + count = simple_strtoul(argv[3], NULL, 0);
  947. + if (part->offset + count * mtd->writesize > part->size) {
  948. + printf("Page count exceeds partition size\n");
  949. + return CMD_RET_FAILURE;
  950. + }
  951. + } else {
  952. + off = simple_strtoull(argv[3], NULL, 0);
  953. + if (off >= part->size) {
  954. + printf("Offset 0x%llx is larger than partition size\n", off);
  955. + return CMD_RET_FAILURE;
  956. + }
  957. +
  958. + off &= ~(u64)mtd->writesize_mask;
  959. +
  960. + count = simple_strtoul(argv[4], NULL, 0);
  961. + if (part->offset + off + count * mtd->writesize > part->size) {
  962. + printf("Page count exceeds partition size\n");
  963. + return CMD_RET_FAILURE;
  964. + }
  965. + }
  966. +
  967. + off += part->offset;
  968. + } else {
  969. + off = simple_strtoull(argv[2], &ends, 0);
  970. +
  971. + if (ends == argv[1] || *ends) {
  972. + printf("Partition '%s' not found\n", argv[2]);
  973. + return CMD_RET_FAILURE;
  974. + }
  975. +
  976. + if (off >= mtd->size) {
  977. + printf("Offset 0x%llx is larger than flash size\n", off);
  978. + return CMD_RET_FAILURE;
  979. + }
  980. +
  981. + off &= ~(u64)mtd->writesize_mask;
  982. +
  983. + if (argc < 4) {
  984. + printf("Page count must be specified\n");
  985. + return CMD_RET_USAGE;
  986. + }
  987. +
  988. + count = simple_strtoul(argv[3], NULL, 0);
  989. + if (off + count * mtd->writesize > mtd->size) {
  990. + printf("Page count exceeds flash size\n");
  991. + return CMD_RET_FAILURE;
  992. + }
  993. + }
  994. +
  995. + printf("%s from 0x%llx to 0x%llx (+%u), count %u ...\n",
  996. + read ? "Reading" : "Writing", off,
  997. + off + count * mtd->writesize - 1, mtd->oobsize, count);
  998. +
  999. + while (count && off < mtd->size) {
  1000. + if (checkbad || !(off & mtd->erasesize_mask)) {
  1001. + offp = off & ~(u64)mtd->erasesize_mask;
  1002. +
  1003. + if (mtd_block_isbad(mtd, offp)) {
  1004. + printf("Bad block at 0x%llx", offp);
  1005. +
  1006. + if (spread) {
  1007. + printf(" ... skipped\n");
  1008. + off += mtd->erasesize;
  1009. + checkbad = true;
  1010. + continue;
  1011. + }
  1012. +
  1013. + if (!force) {
  1014. + printf(" ... aborted\n");
  1015. + return CMD_RET_FAILURE;
  1016. + }
  1017. +
  1018. + printf(" ... continue\n");
  1019. + }
  1020. +
  1021. + checkbad = false;
  1022. + }
  1023. +
  1024. + memset(&io_op, 0, sizeof(io_op));
  1025. +
  1026. + if (raw)
  1027. + io_op.mode = MTD_OPS_RAW;
  1028. + else if (autooob)
  1029. + io_op.mode = MTD_OPS_AUTO_OOB;
  1030. + else
  1031. + io_op.mode = MTD_OPS_PLACE_OOB;
  1032. +
  1033. + io_op.len = mtd->writesize;
  1034. + io_op.ooblen = mtd->oobsize;
  1035. + io_op.datbuf = (void *)addr;
  1036. + io_op.oobbuf = io_op.datbuf + mtd->writesize;
  1037. +
  1038. + if (read) {
  1039. + ret = mtd_read_oob(mtd, off, &io_op);
  1040. + if (ret && ret != -EUCLEAN && ret != -EBADMSG)
  1041. + goto io_err;
  1042. + } else {
  1043. + if (is_empty_page((void *)addr, mtd->writesize + mtd->oobsize) && !writeff)
  1044. + ret = 0;
  1045. + else
  1046. + ret = mtd_write_oob(mtd, off, &io_op);
  1047. +
  1048. + if (ret)
  1049. + goto io_err;
  1050. + }
  1051. +
  1052. + count--;
  1053. + addr += mtd->writesize + mtd->oobsize;
  1054. + off += mtd->writesize;
  1055. + }
  1056. +
  1057. + if (!count) {
  1058. + printf("Succeeded\n");
  1059. + return CMD_RET_SUCCESS;
  1060. + }
  1061. +
  1062. + printf("%u page%s remained for %s\n", count, count > 1 ? "s" : "",
  1063. + read ? "read" : "write");
  1064. + return CMD_RET_FAILURE;
  1065. +
  1066. +io_err:
  1067. + printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, off);
  1068. + return CMD_RET_FAILURE;
  1069. +}
  1070. +
  1071. +static int do_nand_io(struct cmd_tbl *cmdtp, int flag, int argc,
  1072. + char *const argv[])
  1073. +{
  1074. + if (strstr(argv[0], ".oob"))
  1075. + return do_nand_io_page(argc, argv);
  1076. +
  1077. + return do_nand_io_normal(argc, argv);
  1078. +}
  1079. +
  1080. +#ifdef CONFIG_SYS_LONGHELP
  1081. +static char nand_help_text[] =
  1082. + "- NAND flash R/W and debugging utility\n"
  1083. + "nand list\n"
  1084. + "nand info - Show active NAND devices\n"
  1085. + "nand select <name> - Select active NAND devices\n"
  1086. + "nand dump[.raw] <off>\n"
  1087. + "nand bad\n"
  1088. + "nand markbad <off>\n"
  1089. + "nand bitflip <off> <col> <bit>\n"
  1090. + "nand erase[.spread|.force] [<off> <size>|<part> [<size>]]\n"
  1091. + "nand read[.spread|.force][.raw] <addr> <off> <size>\n"
  1092. + " <addr> <part> [<off> [<size>]]\n"
  1093. + "nand write[.spread|.force][.raw][.ff] <addr> <off> <size>\n"
  1094. + " <addr> <part> [<off> [<size>]]\n"
  1095. + "nand read.oob[.spread|.force][.raw|.auto] <addr> <off> <count>\n"
  1096. + " <addr> <part> [<off>] <count>\n"
  1097. + "nand write.oob[.spread|.force][.raw|.auto][.ff] <addr> <off> <count>\n"
  1098. + " <addr> <part> [<off>] <count>\n";
  1099. +#endif
  1100. +
  1101. +U_BOOT_CMD_WITH_SUBCMDS(nand, "NAND utility",
  1102. + nand_help_text,
  1103. + U_BOOT_SUBCMD_MKENT(list, 1, 0, do_nand_list),
  1104. + U_BOOT_SUBCMD_MKENT(info, 1, 0, do_nand_info),
  1105. + U_BOOT_SUBCMD_MKENT(select, 2, 0, do_nand_select),
  1106. + U_BOOT_SUBCMD_MKENT(dump, 2, 0, do_nand_dump),
  1107. + U_BOOT_SUBCMD_MKENT(bad, 1, 0, do_nand_bad),
  1108. + U_BOOT_SUBCMD_MKENT(markbad, 2, 0, do_nand_markbad),
  1109. + U_BOOT_SUBCMD_MKENT(bitflip, 4, 0, do_nand_bitflip),
  1110. + U_BOOT_SUBCMD_MKENT(erase, 3, 0, do_nand_erase),
  1111. + U_BOOT_SUBCMD_MKENT(read, 5, 0, do_nand_io),
  1112. + U_BOOT_SUBCMD_MKENT(write, 5, 0, do_nand_io)
  1113. +);