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 */
18 static 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 */
cros_ec_decode_region(int argc,char * const argv[])27 static 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 */
do_read_write(struct udevice * dev,int is_write,int argc,char * const argv[])53 static 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, ®ion_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
96 static 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
do_show_features(struct udevice * dev)143 static 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
164 static 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
do_show_switches(struct udevice * dev)175 static 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
199 static 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
do_show_events(struct udevice * dev)234 static 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
do_cros_ec(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])259 static 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
545 U_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