xref: /openbmc/u-boot/cmd/cros_ec.c (revision 6645fd2c)
1 /*
2  * Chromium OS cros_ec driver
3  *
4  * Copyright (c) 2016 The Chromium OS Authors.
5  * Copyright (c) 2016 National Instruments Corp
6  *
7  * SPDX-License-Identifier:	GPL-2.0+
8  */
9 
10 #include <common.h>
11 #include <command.h>
12 #include <cros_ec.h>
13 #include <dm.h>
14 #include <dm/device-internal.h>
15 #include <dm/uclass-internal.h>
16 
17 /* Note: depends on enum ec_current_image */
18 static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"};
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 /**
23  * Perform a flash read or write command
24  *
25  * @param dev		CROS-EC device to read/write
26  * @param is_write	1 do to a write, 0 to do a read
27  * @param argc		Number of arguments
28  * @param argv		Arguments (2 is region, 3 is address)
29  * @return 0 for ok, 1 for a usage error or -ve for ec command error
30  *	(negative EC_RES_...)
31  */
32 static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc,
33 			 char * const argv[])
34 {
35 	uint32_t offset, size = -1U, region_size;
36 	unsigned long addr;
37 	char *endp;
38 	int region;
39 	int ret;
40 
41 	region = cros_ec_decode_region(argc - 2, argv + 2);
42 	if (region == -1)
43 		return 1;
44 	if (argc < 4)
45 		return 1;
46 	addr = simple_strtoul(argv[3], &endp, 16);
47 	if (*argv[3] == 0 || *endp != 0)
48 		return 1;
49 	if (argc > 4) {
50 		size = simple_strtoul(argv[4], &endp, 16);
51 		if (*argv[4] == 0 || *endp != 0)
52 			return 1;
53 	}
54 
55 	ret = cros_ec_flash_offset(dev, region, &offset, &region_size);
56 	if (ret) {
57 		debug("%s: Could not read region info\n", __func__);
58 		return ret;
59 	}
60 	if (size == -1U)
61 		size = region_size;
62 
63 	ret = is_write ?
64 		cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) :
65 		cros_ec_flash_read(dev, (uint8_t *)addr, offset, size);
66 	if (ret) {
67 		debug("%s: Could not %s region\n", __func__,
68 		      is_write ? "write" : "read");
69 		return ret;
70 	}
71 
72 	return 0;
73 }
74 
75 static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
76 {
77 	struct cros_ec_dev *dev;
78 	struct udevice *udev;
79 	const char *cmd;
80 	int ret = 0;
81 
82 	if (argc < 2)
83 		return CMD_RET_USAGE;
84 
85 	cmd = argv[1];
86 	if (0 == strcmp("init", cmd)) {
87 		/* Remove any existing device */
88 		ret = uclass_find_device(UCLASS_CROS_EC, 0, &udev);
89 		if (!ret)
90 			device_remove(udev);
91 		ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev);
92 		if (ret) {
93 			printf("Could not init cros_ec device (err %d)\n", ret);
94 			return 1;
95 		}
96 		return 0;
97 	}
98 
99 	ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev);
100 	if (ret) {
101 		printf("Cannot get cros-ec device (err=%d)\n", ret);
102 		return 1;
103 	}
104 	dev = dev_get_uclass_priv(udev);
105 	if (0 == strcmp("id", cmd)) {
106 		char id[MSG_BYTES];
107 
108 		if (cros_ec_read_id(dev, id, sizeof(id))) {
109 			debug("%s: Could not read KBC ID\n", __func__);
110 			return 1;
111 		}
112 		printf("%s\n", id);
113 	} else if (0 == strcmp("info", cmd)) {
114 		struct ec_response_mkbp_info info;
115 
116 		if (cros_ec_info(dev, &info)) {
117 			debug("%s: Could not read KBC info\n", __func__);
118 			return 1;
119 		}
120 		printf("rows     = %u\n", info.rows);
121 		printf("cols     = %u\n", info.cols);
122 		printf("switches = %#x\n", info.switches);
123 	} else if (0 == strcmp("curimage", cmd)) {
124 		enum ec_current_image image;
125 
126 		if (cros_ec_read_current_image(dev, &image)) {
127 			debug("%s: Could not read KBC image\n", __func__);
128 			return 1;
129 		}
130 		printf("%d\n", image);
131 	} else if (0 == strcmp("hash", cmd)) {
132 		struct ec_response_vboot_hash hash;
133 		int i;
134 
135 		if (cros_ec_read_hash(dev, &hash)) {
136 			debug("%s: Could not read KBC hash\n", __func__);
137 			return 1;
138 		}
139 
140 		if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
141 			printf("type:    SHA-256\n");
142 		else
143 			printf("type:    %d\n", hash.hash_type);
144 
145 		printf("offset:  0x%08x\n", hash.offset);
146 		printf("size:    0x%08x\n", hash.size);
147 
148 		printf("digest:  ");
149 		for (i = 0; i < hash.digest_size; i++)
150 			printf("%02x", hash.hash_digest[i]);
151 		printf("\n");
152 	} else if (0 == strcmp("reboot", cmd)) {
153 		int region;
154 		enum ec_reboot_cmd cmd;
155 
156 		if (argc >= 3 && !strcmp(argv[2], "cold")) {
157 			cmd = EC_REBOOT_COLD;
158 		} else {
159 			region = cros_ec_decode_region(argc - 2, argv + 2);
160 			if (region == EC_FLASH_REGION_RO)
161 				cmd = EC_REBOOT_JUMP_RO;
162 			else if (region == EC_FLASH_REGION_RW)
163 				cmd = EC_REBOOT_JUMP_RW;
164 			else
165 				return CMD_RET_USAGE;
166 		}
167 
168 		if (cros_ec_reboot(dev, cmd, 0)) {
169 			debug("%s: Could not reboot KBC\n", __func__);
170 			return 1;
171 		}
172 	} else if (0 == strcmp("events", cmd)) {
173 		uint32_t events;
174 
175 		if (cros_ec_get_host_events(dev, &events)) {
176 			debug("%s: Could not read host events\n", __func__);
177 			return 1;
178 		}
179 		printf("0x%08x\n", events);
180 	} else if (0 == strcmp("clrevents", cmd)) {
181 		uint32_t events = 0x7fffffff;
182 
183 		if (argc >= 3)
184 			events = simple_strtol(argv[2], NULL, 0);
185 
186 		if (cros_ec_clear_host_events(dev, events)) {
187 			debug("%s: Could not clear host events\n", __func__);
188 			return 1;
189 		}
190 	} else if (0 == strcmp("read", cmd)) {
191 		ret = do_read_write(dev, 0, argc, argv);
192 		if (ret > 0)
193 			return CMD_RET_USAGE;
194 	} else if (0 == strcmp("write", cmd)) {
195 		ret = do_read_write(dev, 1, argc, argv);
196 		if (ret > 0)
197 			return CMD_RET_USAGE;
198 	} else if (0 == strcmp("erase", cmd)) {
199 		int region = cros_ec_decode_region(argc - 2, argv + 2);
200 		uint32_t offset, size;
201 
202 		if (region == -1)
203 			return CMD_RET_USAGE;
204 		if (cros_ec_flash_offset(dev, region, &offset, &size)) {
205 			debug("%s: Could not read region info\n", __func__);
206 			ret = -1;
207 		} else {
208 			ret = cros_ec_flash_erase(dev, offset, size);
209 			if (ret) {
210 				debug("%s: Could not erase region\n",
211 				      __func__);
212 			}
213 		}
214 	} else if (0 == strcmp("regioninfo", cmd)) {
215 		int region = cros_ec_decode_region(argc - 2, argv + 2);
216 		uint32_t offset, size;
217 
218 		if (region == -1)
219 			return CMD_RET_USAGE;
220 		ret = cros_ec_flash_offset(dev, region, &offset, &size);
221 		if (ret) {
222 			debug("%s: Could not read region info\n", __func__);
223 		} else {
224 			printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
225 					"RO" : "RW");
226 			printf("Offset: %x\n", offset);
227 			printf("Size:   %x\n", size);
228 		}
229 	} else if (0 == strcmp("flashinfo", cmd)) {
230 		struct ec_response_flash_info p;
231 
232 		ret = cros_ec_read_flashinfo(dev, &p);
233 		if (!ret) {
234 			printf("Flash size:         %u\n", p.flash_size);
235 			printf("Write block size:   %u\n", p.write_block_size);
236 			printf("Erase block size:   %u\n", p.erase_block_size);
237 		}
238 	} else if (0 == strcmp("vbnvcontext", cmd)) {
239 		uint8_t block[EC_VBNV_BLOCK_SIZE];
240 		char buf[3];
241 		int i, len;
242 		unsigned long result;
243 
244 		if (argc <= 2) {
245 			ret = cros_ec_read_vbnvcontext(dev, block);
246 			if (!ret) {
247 				printf("vbnv_block: ");
248 				for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
249 					printf("%02x", block[i]);
250 				putc('\n');
251 			}
252 		} else {
253 			/*
254 			 * TODO(clchiou): Move this to a utility function as
255 			 * cmd_spi might want to call it.
256 			 */
257 			memset(block, 0, EC_VBNV_BLOCK_SIZE);
258 			len = strlen(argv[2]);
259 			buf[2] = '\0';
260 			for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
261 				if (i * 2 >= len)
262 					break;
263 				buf[0] = argv[2][i * 2];
264 				if (i * 2 + 1 >= len)
265 					buf[1] = '0';
266 				else
267 					buf[1] = argv[2][i * 2 + 1];
268 				strict_strtoul(buf, 16, &result);
269 				block[i] = result;
270 			}
271 			ret = cros_ec_write_vbnvcontext(dev, block);
272 		}
273 		if (ret) {
274 			debug("%s: Could not %s VbNvContext\n", __func__,
275 			      argc <= 2 ?  "read" : "write");
276 		}
277 	} else if (0 == strcmp("test", cmd)) {
278 		int result = cros_ec_test(dev);
279 
280 		if (result)
281 			printf("Test failed with error %d\n", result);
282 		else
283 			puts("Test passed\n");
284 	} else if (0 == strcmp("version", cmd)) {
285 		struct ec_response_get_version *p;
286 		char *build_string;
287 
288 		ret = cros_ec_read_version(dev, &p);
289 		if (!ret) {
290 			/* Print versions */
291 			printf("RO version:    %1.*s\n",
292 			       (int)sizeof(p->version_string_ro),
293 			       p->version_string_ro);
294 			printf("RW version:    %1.*s\n",
295 			       (int)sizeof(p->version_string_rw),
296 			       p->version_string_rw);
297 			printf("Firmware copy: %s\n",
298 			       (p->current_image <
299 			       ARRAY_SIZE(ec_current_image_name) ?
300 			       ec_current_image_name[p->current_image] :
301 			       "?"));
302 			ret = cros_ec_read_build_info(dev, &build_string);
303 			if (!ret)
304 				printf("Build info:    %s\n", build_string);
305 		}
306 	} else if (0 == strcmp("ldo", cmd)) {
307 		uint8_t index, state;
308 		char *endp;
309 
310 		if (argc < 3)
311 			return CMD_RET_USAGE;
312 		index = simple_strtoul(argv[2], &endp, 10);
313 		if (*argv[2] == 0 || *endp != 0)
314 			return CMD_RET_USAGE;
315 		if (argc > 3) {
316 			state = simple_strtoul(argv[3], &endp, 10);
317 			if (*argv[3] == 0 || *endp != 0)
318 				return CMD_RET_USAGE;
319 			ret = cros_ec_set_ldo(udev, index, state);
320 		} else {
321 			ret = cros_ec_get_ldo(udev, index, &state);
322 			if (!ret) {
323 				printf("LDO%d: %s\n", index,
324 				       state == EC_LDO_STATE_ON ?
325 				       "on" : "off");
326 			}
327 		}
328 
329 		if (ret) {
330 			debug("%s: Could not access LDO%d\n", __func__, index);
331 			return ret;
332 		}
333 	} else {
334 		return CMD_RET_USAGE;
335 	}
336 
337 	if (ret < 0) {
338 		printf("Error: CROS-EC command failed (error %d)\n", ret);
339 		ret = 1;
340 	}
341 
342 	return ret;
343 }
344 
345 U_BOOT_CMD(
346 	crosec,	6,	1,	do_cros_ec,
347 	"CROS-EC utility command",
348 	"init                Re-init CROS-EC (done on startup automatically)\n"
349 	"crosec id                  Read CROS-EC ID\n"
350 	"crosec info                Read CROS-EC info\n"
351 	"crosec curimage            Read CROS-EC current image\n"
352 	"crosec hash                Read CROS-EC hash\n"
353 	"crosec reboot [rw | ro | cold]  Reboot CROS-EC\n"
354 	"crosec events              Read CROS-EC host events\n"
355 	"crosec clrevents [mask]    Clear CROS-EC host events\n"
356 	"crosec regioninfo <ro|rw>  Read image info\n"
357 	"crosec flashinfo           Read flash info\n"
358 	"crosec erase <ro|rw>       Erase EC image\n"
359 	"crosec read <ro|rw> <addr> [<size>]   Read EC image\n"
360 	"crosec write <ro|rw> <addr> [<size>]  Write EC image\n"
361 	"crosec vbnvcontext [hexstring]        Read [write] VbNvContext from EC\n"
362 	"crosec ldo <idx> [<state>] Switch/Read LDO state\n"
363 	"crosec test                run tests on cros_ec\n"
364 	"crosec version             Read CROS-EC version"
365 );
366