1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
4  */
5 
6 #include <nand.h>
7 #include <part.h>
8 #include <rand.h>
9 #include <dm/test.h>
10 #include <test/test.h>
11 #include <test/ut.h>
12 #include <linux/mtd/mtd.h>
13 #include <linux/mtd/rawnand.h>
14 
run_test_nand(struct unit_test_state * uts,int dev,bool end)15 static int run_test_nand(struct unit_test_state *uts, int dev, bool end)
16 {
17 	nand_erase_options_t opts = { };
18 	struct mtd_info *mtd;
19 	size_t length;
20 	loff_t size;
21 	char *buf;
22 	int *gold;
23 	u8 oob[NAND_MAX_OOBSIZE];
24 	int i;
25 	loff_t off = 0;
26 	mtd_oob_ops_t ops = { };
27 
28 	/* Seed RNG for bit errors */
29 	srand((off >> 32) ^ off ^ ~dev);
30 
31 	mtd = get_nand_dev_by_index(dev);
32 	ut_assertnonnull(mtd);
33 	size = mtd->erasesize * 4;
34 	length = size;
35 
36 	buf = malloc(size);
37 	ut_assertnonnull(buf);
38 	gold = malloc(size);
39 	ut_assertnonnull(gold);
40 
41 	/* Mark a block as bad */
42 	ut_assertok(mtd_block_markbad(mtd, off + mtd->erasesize));
43 
44 	/* Erase some stuff */
45 	if (end)
46 		off = mtd->size - size - mtd->erasesize;
47 	opts.offset = off;
48 	opts.length = size;
49 	opts.spread = 1;
50 	opts.lim = U32_MAX;
51 	ut_assertok(nand_erase_opts(mtd, &opts));
52 
53 	/* Make sure everything is erased */
54 	memset(gold, 0xff, size);
55 	ut_assertok(nand_read_skip_bad(mtd, off, &length, NULL, U64_MAX, buf));
56 	ut_asserteq(size, length);
57 	ut_asserteq_mem(gold, buf, size);
58 
59 	/* ...but our bad block marker is still there */
60 	ops.oobbuf = oob;
61 	ops.ooblen = mtd->oobsize;
62 	ut_assertok(mtd_read_oob(mtd, mtd->erasesize, &ops));
63 	ut_asserteq(0, oob[mtd_to_nand(mtd)->badblockpos]);
64 
65 	/* Generate some data and write it */
66 	for (i = 0; i < size / sizeof(int); i++)
67 		gold[i] = rand();
68 	ut_assertok(nand_write_skip_bad(mtd, off, &length, NULL, U64_MAX,
69 					(void *)gold, 0));
70 	ut_asserteq(size, length);
71 
72 	/* Verify */
73 	ut_assertok(nand_read_skip_bad(mtd, off, &length, NULL, U64_MAX, buf));
74 	ut_asserteq(size, length);
75 	ut_asserteq_mem(gold, buf, size);
76 
77 	/* Erase some blocks */
78 	memset(((char *)gold) + mtd->erasesize, 0xff, mtd->erasesize * 2);
79 	opts.offset = off + mtd->erasesize;
80 	opts.length = mtd->erasesize * 2;
81 	ut_assertok(nand_erase_opts(mtd, &opts));
82 
83 	/* Verify */
84 	ut_assertok(nand_read_skip_bad(mtd, off, &length, NULL, U64_MAX, buf));
85 	ut_asserteq(size, length);
86 	ut_asserteq_mem(gold, buf, size);
87 
88 	return 0;
89 }
90 
dm_test_nand0_start(struct unit_test_state * uts)91 static int dm_test_nand0_start(struct unit_test_state *uts)
92 {
93 	ut_assertok(run_test_nand(uts, 0, false));
94 
95 	return 0;
96 }
97 DM_TEST(dm_test_nand0_start, UTF_SCAN_FDT);
98 
dm_test_nand1_start(struct unit_test_state * uts)99 static int dm_test_nand1_start(struct unit_test_state *uts)
100 {
101 	ut_assertok(run_test_nand(uts, 1, false));
102 
103 	return 0;
104 }
105 DM_TEST(dm_test_nand1_start, UTF_SCAN_FDT);
106 
dm_test_nand0_end(struct unit_test_state * uts)107 static int dm_test_nand0_end(struct unit_test_state *uts)
108 {
109 	ut_assertok(run_test_nand(uts, 0, true));
110 
111 	return 0;
112 }
113 DM_TEST(dm_test_nand0_end, UTF_SCAN_FDT);
114 
dm_test_nand1_end(struct unit_test_state * uts)115 static int dm_test_nand1_end(struct unit_test_state *uts)
116 {
117 	ut_assertok(run_test_nand(uts, 1, true));
118 
119 	return 0;
120 }
121 DM_TEST(dm_test_nand1_end, UTF_SCAN_FDT);
122