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
15static int dm_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
91#define DM_NAND_TEST(dev) \
92static int dm_test_nand##dev##_start(struct unit_test_state *uts) \
93{ \
94	return dm_test_nand(uts, dev, false); \
95} \
96DM_TEST(dm_test_nand##dev##_start, UT_TESTF_SCAN_FDT); \
97static int dm_test_nand##dev##_end(struct unit_test_state *uts) \
98{ \
99	return dm_test_nand(uts, dev, true); \
100} \
101DM_TEST(dm_test_nand##dev##_end, UT_TESTF_SCAN_FDT)
102
103DM_NAND_TEST(0);
104DM_NAND_TEST(1);
105