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