1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2009-2013 ADVANSEE
4  * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
5  *
6  * Based on the mpc512x iim code:
7  * Copyright 2008 Silicon Turnkey Express, Inc.
8  * Martha Marx <mmarx@silicontkx.com>
9  */
10 
11 #include <command.h>
12 #include <console.h>
13 #include <fuse.h>
14 #include <mapmem.h>
15 #include <vsprintf.h>
16 #include <linux/errno.h>
17 #include <linux/string.h>
18 
confirm_prog(void)19 static int confirm_prog(void)
20 {
21 	puts("Warning: Programming fuses is an irreversible operation!\n"
22 			"         This may brick your system.\n"
23 			"         Use this command only if you are sure of "
24 					"what you are doing!\n"
25 			"\nReally perform this fuse programming? <y/N>\n");
26 
27 	if (confirm_yesno())
28 		return 1;
29 
30 	puts("Fuse programming aborted\n");
31 	return 0;
32 }
33 
do_fuse(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])34 static int do_fuse(struct cmd_tbl *cmdtp, int flag, int argc,
35 		   char *const argv[])
36 {
37 	const char *op = cmd_arg1(argc, argv);
38 	int confirmed = argc >= 3 && !strcmp(argv[2], "-y");
39 	u32 bank, word, cnt, val, cmp;
40 	ulong addr;
41 	void *buf, *start;
42 	int ret, i;
43 
44 	argc -= 2 + confirmed;
45 	argv += 2 + confirmed;
46 
47 	if (IS_ENABLED(CONFIG_CMD_FUSE_WRITEBUFF) && !strcmp(op, "writebuff")) {
48 		if (argc == 1)
49 			addr = simple_strtoul(argv[0], NULL, 16);
50 		else
51 			return CMD_RET_USAGE;
52 	} else {
53 		if (argc < 2)
54 			return CMD_RET_USAGE;
55 
56 		bank = simple_strtoul(argv[0], NULL, 0);
57 		word = simple_strtoul(argv[1], NULL, 0);
58 	}
59 
60 	if (!strcmp(op, "read")) {
61 		if (argc == 2)
62 			cnt = 1;
63 		else if (argc == 3)
64 			cnt = simple_strtoul(argv[2], NULL, 0);
65 		else
66 			return CMD_RET_USAGE;
67 
68 		printf("Reading bank %u:\n", bank);
69 		for (i = 0; i < cnt; i++, word++) {
70 			if (!(i % 4))
71 				printf("\nWord 0x%.8x:", word);
72 
73 			ret = fuse_read(bank, word, &val);
74 			if (ret)
75 				goto err;
76 
77 			printf(" %.8x", val);
78 		}
79 		putc('\n');
80 	} else if (!strcmp(op, "readm")) {
81 		if (argc == 3)
82 			cnt = 1;
83 		else if (argc == 4)
84 			cnt = simple_strtoul(argv[3], NULL, 0);
85 		else
86 			return CMD_RET_USAGE;
87 
88 		addr = simple_strtoul(argv[2], NULL, 16);
89 
90 		start = map_sysmem(addr, 4);
91 		buf = start;
92 
93 		printf("Reading bank %u len %u to 0x%lx\n", bank, cnt, addr);
94 		for (i = 0; i < cnt; i++, word++) {
95 			ret = fuse_read(bank, word, &val);
96 			if (ret)
97 				goto err;
98 
99 			*((u32 *)buf) = val;
100 			buf += 4;
101 		}
102 
103 		unmap_sysmem(start);
104 	} else if (!strcmp(op, "cmp")) {
105 		if (argc == 3)
106 			cmp = simple_strtoul(argv[2], NULL, 0);
107 		else
108 			return CMD_RET_USAGE;
109 
110 		printf("Comparing bank %u:\n", bank);
111 		printf("\nWord 0x%.8x:", word);
112 		printf("\nValue 0x%.8x:", cmp);
113 
114 		ret = fuse_read(bank, word, &val);
115 		if (ret)
116 			goto err;
117 
118 		printf("0x%.8x\n", val);
119 		if (val != cmp) {
120 			printf("failed\n");
121 			return CMD_RET_FAILURE;
122 		}
123 		printf("passed\n");
124 	} else if (!strcmp(op, "sense")) {
125 		if (argc == 2)
126 			cnt = 1;
127 		else if (argc == 3)
128 			cnt = simple_strtoul(argv[2], NULL, 0);
129 		else
130 			return CMD_RET_USAGE;
131 
132 		printf("Sensing bank %u:\n", bank);
133 		for (i = 0; i < cnt; i++, word++) {
134 			if (!(i % 4))
135 				printf("\nWord 0x%.8x:", word);
136 
137 			ret = fuse_sense(bank, word, &val);
138 			if (ret)
139 				goto err;
140 
141 			printf(" %.8x", val);
142 		}
143 		putc('\n');
144 	} else if (!strcmp(op, "prog")) {
145 		if (argc < 3)
146 			return CMD_RET_USAGE;
147 
148 		for (i = 2; i < argc; i++, word++) {
149 			val = simple_strtoul(argv[i], NULL, 16);
150 
151 			printf("Programming bank %u word 0x%.8x to 0x%.8x...\n",
152 					bank, word, val);
153 			if (!confirmed && !confirm_prog())
154 				return CMD_RET_FAILURE;
155 			ret = fuse_prog(bank, word, val);
156 			if (ret)
157 				goto err;
158 		}
159 	} else if (!strcmp(op, "override")) {
160 		if (argc < 3)
161 			return CMD_RET_USAGE;
162 
163 		for (i = 2; i < argc; i++, word++) {
164 			val = simple_strtoul(argv[i], NULL, 16);
165 
166 			printf("Overriding bank %u word 0x%.8x with "
167 					"0x%.8x...\n", bank, word, val);
168 			ret = fuse_override(bank, word, val);
169 			if (ret)
170 				goto err;
171 		}
172 	} else if (IS_ENABLED(CONFIG_CMD_FUSE_WRITEBUFF) && !strcmp(op, "writebuff")) {
173 		printf("Programming fuses using a structured buffer in memory "
174 				"starting at addr 0x%lx\n", addr);
175 		if (!confirmed && !confirm_prog())
176 			return CMD_RET_FAILURE;
177 
178 		ret = fuse_writebuff(addr);
179 		if (ret)
180 			goto err;
181 	} else {
182 		return CMD_RET_USAGE;
183 	}
184 
185 	return 0;
186 
187 err:
188 	puts("ERROR\n");
189 	return CMD_RET_FAILURE;
190 }
191 
192 U_BOOT_CMD(
193 	fuse, CONFIG_SYS_MAXARGS, 0, do_fuse,
194 	"Fuse sub-system",
195 	     "read <bank> <word> [<cnt>] - read 1 or 'cnt' fuse words,\n"
196 	"    starting at 'word'\n"
197 	"fuse cmp <bank> <word> <hexval> - compare 'hexval' to fuse\n"
198 	"    at 'word'\n"
199 	"fuse readm <bank> <word> <addr> [<cnt>] - read 1 or 'cnt' fuse words,\n"
200 	"    starting at 'word' into memory at 'addr'\n"
201 	"fuse sense <bank> <word> [<cnt>] - sense 1 or 'cnt' fuse words,\n"
202 	"    starting at 'word'\n"
203 	"fuse prog [-y] <bank> <word> <hexval> [<hexval>...] - program 1 or\n"
204 	"    several fuse words, starting at 'word' (PERMANENT)\n"
205 	"fuse override <bank> <word> <hexval> [<hexval>...] - override 1 or\n"
206 	"    several fuse words, starting at 'word'\n"
207 #ifdef CONFIG_CMD_FUSE_WRITEBUFF
208 	"fuse writebuff [-y] <addr> - program fuse data\n"
209 	"    using a structured buffer in memory starting at 'addr'\n"
210 #endif /* CONFIG_CMD_FUSE_WRITEBUFF */
211 );
212