1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Chromium OS cros_ec driver
4  *
5  * Copyright (c) 2016 The Chromium OS Authors.
6  * Copyright (c) 2016 National Instruments Corp
7  */
8 
9 #include <common.h>
10 #include <command.h>
11 #include <cros_ec.h>
12 #include <dm.h>
13 #include <flash.h>
14 #include <log.h>
15 #include <dm/device-internal.h>
16 #include <dm/uclass-internal.h>
17 
18 /* Note: depends on enum ec_current_image */
19 static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"};
20 
21 /**
22  * Decode a flash region parameter
23  *
24  * @param argc Number of params remaining
25  * @param argv List of remaining parameters
26  * @return flash region (EC_FLASH_REGION_...) or -1 on error
27  */
cros_ec_decode_region(int argc,char * const argv[])28 static int cros_ec_decode_region(int argc, char *const argv[])
29 {
30 	if (argc > 0) {
31 		if (0 == strcmp(*argv, "rw"))
32 			return EC_FLASH_REGION_ACTIVE;
33 		else if (0 == strcmp(*argv, "ro"))
34 			return EC_FLASH_REGION_RO;
35 
36 		debug("%s: Invalid region '%s'\n", __func__, *argv);
37 	} else {
38 		debug("%s: Missing region parameter\n", __func__);
39 	}
40 
41 	return -1;
42 }
43 
44 /**
45  * Perform a flash read or write command
46  *
47  * @param dev		CROS-EC device to read/write
48  * @param is_write	1 do to a write, 0 to do a read
49  * @param argc		Number of arguments
50  * @param argv		Arguments (2 is region, 3 is address)
51  * @return 0 for ok, 1 for a usage error or -ve for ec command error
52  *	(negative EC_RES_...)
53  */
do_read_write(struct udevice * dev,int is_write,int argc,char * const argv[])54 static int do_read_write(struct udevice *dev, int is_write, int argc,
55 			 char *const argv[])
56 {
57 	uint32_t offset, size = -1U, region_size;
58 	unsigned long addr;
59 	char *endp;
60 	int region;
61 	int ret;
62 
63 	region = cros_ec_decode_region(argc - 2, argv + 2);
64 	if (region == -1)
65 		return 1;
66 	if (argc < 4)
67 		return 1;
68 	addr = simple_strtoul(argv[3], &endp, 16);
69 	if (*argv[3] == 0 || *endp != 0)
70 		return 1;
71 	if (argc > 4) {
72 		size = simple_strtoul(argv[4], &endp, 16);
73 		if (*argv[4] == 0 || *endp != 0)
74 			return 1;
75 	}
76 
77 	ret = cros_ec_flash_offset(dev, region, &offset, &region_size);
78 	if (ret) {
79 		debug("%s: Could not read region info\n", __func__);
80 		return ret;
81 	}
82 	if (size == -1U)
83 		size = region_size;
84 
85 	ret = is_write ?
86 		cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) :
87 		cros_ec_flash_read(dev, (uint8_t *)addr, offset, size);
88 	if (ret) {
89 		debug("%s: Could not %s region\n", __func__,
90 		      is_write ? "write" : "read");
91 		return ret;
92 	}
93 
94 	return 0;
95 }
96 
97 static const char *const feat_name[64] = {
98 	"limited",
99 	"flash",
100 	"pwm_fan",
101 	"pwm_keyb",
102 	"lightbar",
103 	"led",
104 	"motion_sense",
105 	"keyb",
106 	"pstore",
107 	"port80",
108 	"thermal",
109 	"bklight_switch",
110 	"wifi_switch",
111 	"host_events",
112 	"gpio",
113 	"i2c",
114 	"charger",
115 	"battery",
116 	"smart_battery",
117 	"hang_detect",
118 	"pmu",
119 	"sub_mcu",
120 	"usb_pd",
121 	"usb_mux",
122 	"motion_sense_fifo",
123 	"vstore",
124 	"usbc_ss_mux_virtual",
125 	"rtc",
126 	"fingerprint",
127 	"touchpad",
128 	"rwsig",
129 	"device_event",
130 	"unified_wake_masks",
131 	"host_event64",
132 	"exec_in_ram",
133 	"cec",
134 	"motion_sense_tight_timestamps",
135 	"refined_tablet_mode_hysteresis",
136 	"efs2",
137 	"scp",
138 	"ish",
139 	"typec_cmd",
140 	"typec_require_ap_mode_entry",
141 	"typec_mux_require_ap_ack",
142 };
143 
do_show_features(struct udevice * dev)144 static int do_show_features(struct udevice *dev)
145 {
146 	u64 feat;
147 	int ret;
148 	uint i;
149 
150 	ret = cros_ec_get_features(dev, &feat);
151 	if (ret)
152 		return ret;
153 	for (i = 0; i < ARRAY_SIZE(feat_name); i++) {
154 		if (feat & (1ULL << i)) {
155 			if (feat_name[i])
156 				printf("%s\n", feat_name[i]);
157 			else
158 				printf("unknown %d\n", i);
159 		}
160 	}
161 
162 	return 0;
163 }
164 
165 static const char *const switch_name[8] = {
166 	"lid open",
167 	"power button pressed",
168 	"write-protect disabled",
169 	NULL,
170 	"dedicated recovery",
171 	NULL,
172 	NULL,
173 	NULL,
174 };
175 
do_show_switches(struct udevice * dev)176 static int do_show_switches(struct udevice *dev)
177 {
178 	uint switches;
179 	int ret;
180 	uint i;
181 
182 	ret = cros_ec_get_switches(dev);
183 	if (ret < 0)
184 		return log_msg_ret("get", ret);
185 	switches = ret;
186 	for (i = 0; i < ARRAY_SIZE(switch_name); i++) {
187 		uint mask = 1 << i;
188 
189 		if (switches & mask) {
190 			if (switch_name[i])
191 				printf("%s\n", switch_name[i]);
192 			else
193 				printf("unknown %02x\n", mask);
194 		}
195 	}
196 
197 	return 0;
198 }
199 
200 static const char *const event_name[] = {
201 	"lid_closed",
202 	"lid_open",
203 	"power_button",
204 	"ac_connected",
205 	"ac_disconnected",
206 	"battery_low",
207 	"battery_critical",
208 	"battery",
209 	"thermal_threshold",
210 	"device",
211 	"thermal",
212 	"usb_charger",
213 	"key_pressed",
214 	"interface_ready",
215 	"keyboard_recovery",
216 	"thermal_shutdown",
217 	"battery_shutdown",
218 	"throttle_start",
219 	"throttle_stop",
220 	"hang_detect",
221 	"hang_reboot",
222 	"pd_mcu",
223 	"battery_status",
224 	"panic",
225 	"keyboard_fastboot",
226 	"rtc",
227 	"mkbp",
228 	"usb_mux",
229 	"mode_change",
230 	"keyboard_recovery_hw_reinit",
231 	"extended",
232 	"invalid",
233 };
234 
do_show_events(struct udevice * dev)235 static int do_show_events(struct udevice *dev)
236 {
237 	u32 events;
238 	int ret;
239 	uint i;
240 
241 	ret = cros_ec_get_host_events(dev, &events);
242 	if (ret)
243 		return ret;
244 	printf("%08x\n", events);
245 	for (i = 0; i < ARRAY_SIZE(event_name); i++) {
246 		enum host_event_code code = i + 1;
247 		u64 mask = EC_HOST_EVENT_MASK(code);
248 
249 		if (events & mask) {
250 			if (event_name[i])
251 				printf("%s\n", event_name[i]);
252 			else
253 				printf("unknown code %#x\n", code);
254 		}
255 	}
256 
257 	return 0;
258 }
259 
do_cros_ec(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])260 static int do_cros_ec(struct cmd_tbl *cmdtp, int flag, int argc,
261 		      char *const argv[])
262 {
263 	struct udevice *dev;
264 	const char *cmd;
265 	int ret = 0;
266 
267 	if (argc < 2)
268 		return CMD_RET_USAGE;
269 
270 	cmd = argv[1];
271 	if (0 == strcmp("init", cmd)) {
272 		/* Remove any existing device */
273 		ret = uclass_find_device(UCLASS_CROS_EC, 0, &dev);
274 		if (!ret)
275 			device_remove(dev, DM_REMOVE_NORMAL);
276 		ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
277 		if (ret) {
278 			printf("Could not init cros_ec device (err %d)\n", ret);
279 			return 1;
280 		}
281 		return 0;
282 	}
283 
284 	ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
285 	if (ret) {
286 		printf("Cannot get cros-ec device (err=%d)\n", ret);
287 		return 1;
288 	}
289 	if (0 == strcmp("id", cmd)) {
290 		char id[MSG_BYTES];
291 
292 		if (cros_ec_read_id(dev, id, sizeof(id))) {
293 			debug("%s: Could not read KBC ID\n", __func__);
294 			return 1;
295 		}
296 		printf("%s\n", id);
297 	} else if (0 == strcmp("info", cmd)) {
298 		struct ec_response_mkbp_info info;
299 
300 		if (cros_ec_info(dev, &info)) {
301 			debug("%s: Could not read KBC info\n", __func__);
302 			return 1;
303 		}
304 		printf("rows     = %u\n", info.rows);
305 		printf("cols     = %u\n", info.cols);
306 	} else if (!strcmp("features", cmd)) {
307 		ret = do_show_features(dev);
308 
309 		if (ret)
310 			printf("Error: %d\n", ret);
311 	} else if (!strcmp("switches", cmd)) {
312 		ret = do_show_switches(dev);
313 
314 		if (ret)
315 			printf("Error: %d\n", ret);
316 	} else if (0 == strcmp("curimage", cmd)) {
317 		enum ec_current_image image;
318 
319 		if (cros_ec_read_current_image(dev, &image)) {
320 			debug("%s: Could not read KBC image\n", __func__);
321 			return 1;
322 		}
323 		printf("%d\n", image);
324 	} else if (0 == strcmp("hash", cmd)) {
325 		struct ec_response_vboot_hash hash;
326 		int i;
327 
328 		if (cros_ec_read_hash(dev, EC_VBOOT_HASH_OFFSET_ACTIVE, &hash)) {
329 			debug("%s: Could not read KBC hash\n", __func__);
330 			return 1;
331 		}
332 
333 		if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
334 			printf("type:    SHA-256\n");
335 		else
336 			printf("type:    %d\n", hash.hash_type);
337 
338 		printf("offset:  0x%08x\n", hash.offset);
339 		printf("size:    0x%08x\n", hash.size);
340 
341 		printf("digest:  ");
342 		for (i = 0; i < hash.digest_size; i++)
343 			printf("%02x", hash.hash_digest[i]);
344 		printf("\n");
345 	} else if (0 == strcmp("reboot", cmd)) {
346 		int region;
347 		enum ec_reboot_cmd cmd;
348 
349 		if (argc >= 3 && !strcmp(argv[2], "cold")) {
350 			cmd = EC_REBOOT_COLD;
351 		} else {
352 			region = cros_ec_decode_region(argc - 2, argv + 2);
353 			if (region == EC_FLASH_REGION_RO)
354 				cmd = EC_REBOOT_JUMP_RO;
355 			else if (region == EC_FLASH_REGION_ACTIVE)
356 				cmd = EC_REBOOT_JUMP_RW;
357 			else
358 				return CMD_RET_USAGE;
359 		}
360 
361 		if (cros_ec_reboot(dev, cmd, 0)) {
362 			debug("%s: Could not reboot KBC\n", __func__);
363 			return 1;
364 		}
365 	} else if (0 == strcmp("events", cmd)) {
366 		ret = do_show_events(dev);
367 
368 		if (ret)
369 			printf("Error: %d\n", ret);
370 	} else if (0 == strcmp("clrevents", cmd)) {
371 		uint32_t events = 0x7fffffff;
372 
373 		if (argc >= 3)
374 			events = simple_strtol(argv[2], NULL, 0);
375 
376 		if (cros_ec_clear_host_events(dev, events)) {
377 			debug("%s: Could not clear host events\n", __func__);
378 			return 1;
379 		}
380 	} else if (0 == strcmp("read", cmd)) {
381 		ret = do_read_write(dev, 0, argc, argv);
382 		if (ret > 0)
383 			return CMD_RET_USAGE;
384 	} else if (0 == strcmp("write", cmd)) {
385 		ret = do_read_write(dev, 1, argc, argv);
386 		if (ret > 0)
387 			return CMD_RET_USAGE;
388 	} else if (0 == strcmp("erase", cmd)) {
389 		int region = cros_ec_decode_region(argc - 2, argv + 2);
390 		uint32_t offset, size;
391 
392 		if (region == -1)
393 			return CMD_RET_USAGE;
394 		if (cros_ec_flash_offset(dev, region, &offset, &size)) {
395 			debug("%s: Could not read region info\n", __func__);
396 			ret = -1;
397 		} else {
398 			ret = cros_ec_flash_erase(dev, offset, size);
399 			if (ret) {
400 				debug("%s: Could not erase region\n",
401 				      __func__);
402 			}
403 		}
404 	} else if (0 == strcmp("regioninfo", cmd)) {
405 		int region = cros_ec_decode_region(argc - 2, argv + 2);
406 		uint32_t offset, size;
407 
408 		if (region == -1)
409 			return CMD_RET_USAGE;
410 		ret = cros_ec_flash_offset(dev, region, &offset, &size);
411 		if (ret) {
412 			debug("%s: Could not read region info\n", __func__);
413 		} else {
414 			printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
415 					"RO" : "RW");
416 			printf("Offset: %x\n", offset);
417 			printf("Size:   %x\n", size);
418 		}
419 	} else if (0 == strcmp("flashinfo", cmd)) {
420 		struct ec_response_flash_info p;
421 
422 		ret = cros_ec_read_flashinfo(dev, &p);
423 		if (!ret) {
424 			printf("Flash size:         %u\n", p.flash_size);
425 			printf("Write block size:   %u\n", p.write_block_size);
426 			printf("Erase block size:   %u\n", p.erase_block_size);
427 		}
428 	} else if (0 == strcmp("vbnvcontext", cmd)) {
429 		uint8_t block[EC_VBNV_BLOCK_SIZE];
430 		char buf[3];
431 		int i, len;
432 		unsigned long result;
433 
434 		if (argc <= 2) {
435 			ret = cros_ec_read_nvdata(dev, block,
436 						  EC_VBNV_BLOCK_SIZE);
437 			if (!ret) {
438 				printf("vbnv_block: ");
439 				for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
440 					printf("%02x", block[i]);
441 				putc('\n');
442 			}
443 		} else {
444 			/*
445 			 * TODO(clchiou): Move this to a utility function as
446 			 * cmd_spi might want to call it.
447 			 */
448 			memset(block, 0, EC_VBNV_BLOCK_SIZE);
449 			len = strlen(argv[2]);
450 			buf[2] = '\0';
451 			for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
452 				if (i * 2 >= len)
453 					break;
454 				buf[0] = argv[2][i * 2];
455 				if (i * 2 + 1 >= len)
456 					buf[1] = '0';
457 				else
458 					buf[1] = argv[2][i * 2 + 1];
459 				strict_strtoul(buf, 16, &result);
460 				block[i] = result;
461 			}
462 			ret = cros_ec_write_nvdata(dev, block,
463 						   EC_VBNV_BLOCK_SIZE);
464 		}
465 		if (ret) {
466 			debug("%s: Could not %s VbNvContext\n", __func__,
467 			      argc <= 2 ?  "read" : "write");
468 		}
469 	} else if (0 == strcmp("test", cmd)) {
470 		int result = cros_ec_test(dev);
471 
472 		if (result)
473 			printf("Test failed with error %d\n", result);
474 		else
475 			puts("Test passed\n");
476 	} else if (0 == strcmp("version", cmd)) {
477 		struct ec_response_get_version *p;
478 		char *build_string;
479 
480 		ret = cros_ec_read_version(dev, &p);
481 		if (!ret) {
482 			/* Print versions */
483 			printf("RO version:    %1.*s\n",
484 			       (int)sizeof(p->version_string_ro),
485 			       p->version_string_ro);
486 			printf("RW version:    %1.*s\n",
487 			       (int)sizeof(p->version_string_rw),
488 			       p->version_string_rw);
489 			printf("Firmware copy: %s\n",
490 			       (p->current_image <
491 			       ARRAY_SIZE(ec_current_image_name) ?
492 			       ec_current_image_name[p->current_image] :
493 			       "?"));
494 			ret = cros_ec_read_build_info(dev, &build_string);
495 			if (!ret)
496 				printf("Build info:    %s\n", build_string);
497 		}
498 	} else if (0 == strcmp("ldo", cmd)) {
499 		uint8_t index, state;
500 		char *endp;
501 
502 		if (argc < 3)
503 			return CMD_RET_USAGE;
504 		index = simple_strtoul(argv[2], &endp, 10);
505 		if (*argv[2] == 0 || *endp != 0)
506 			return CMD_RET_USAGE;
507 		if (argc > 3) {
508 			state = simple_strtoul(argv[3], &endp, 10);
509 			if (*argv[3] == 0 || *endp != 0)
510 				return CMD_RET_USAGE;
511 			ret = cros_ec_set_ldo(dev, index, state);
512 		} else {
513 			ret = cros_ec_get_ldo(dev, index, &state);
514 			if (!ret) {
515 				printf("LDO%d: %s\n", index,
516 				       state == EC_LDO_STATE_ON ?
517 				       "on" : "off");
518 			}
519 		}
520 
521 		if (ret) {
522 			debug("%s: Could not access LDO%d\n", __func__, index);
523 			return ret;
524 		}
525 	} else if (!strcmp("sku", cmd)) {
526 		ret = cros_ec_get_sku_id(dev);
527 
528 		if (ret >= 0) {
529 			printf("%d\n", ret);
530 			ret = 0;
531 		} else {
532 			printf("Error: %d\n", ret);
533 		}
534 	} else {
535 		return CMD_RET_USAGE;
536 	}
537 
538 	if (ret < 0) {
539 		printf("Error: CROS-EC command failed (error %d)\n", ret);
540 		ret = 1;
541 	}
542 
543 	return ret;
544 }
545 
546 U_BOOT_CMD(
547 	crosec,	6,	1,	do_cros_ec,
548 	"CROS-EC utility command",
549 	"init                Re-init CROS-EC (done on startup automatically)\n"
550 	"crosec id                  Read CROS-EC ID\n"
551 	"crosec info                Read CROS-EC info\n"
552 	"crosec features            Read CROS-EC features\n"
553 	"crosec switches            Read CROS-EC switches\n"
554 	"crosec curimage            Read CROS-EC current image\n"
555 	"crosec hash                Read CROS-EC hash\n"
556 	"crosec reboot [rw | ro | cold]  Reboot CROS-EC\n"
557 	"crosec events              Read CROS-EC host events\n"
558 	"crosec eventsb             Read CROS-EC host events_b\n"
559 	"crosec clrevents [mask]    Clear CROS-EC host events\n"
560 	"crosec regioninfo <ro|rw>  Read image info\n"
561 	"crosec flashinfo           Read flash info\n"
562 	"crosec erase <ro|rw>       Erase EC image\n"
563 	"crosec read <ro|rw> <addr> [<size>]   Read EC image\n"
564 	"crosec write <ro|rw> <addr> [<size>]  Write EC image\n"
565 	"crosec vbnvcontext [hexstring]        Read [write] VbNvContext from EC\n"
566 	"crosec ldo <idx> [<state>] Switch/Read LDO state\n"
567 	"crosec sku                 Read board SKU ID\n"
568 	"crosec test                run tests on cros_ec\n"
569 	"crosec version             Read CROS-EC version"
570 );
571