blob: 90921cecf602f93637e02ba2eb75c0c0b3f9d573 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Moritz Fischer62b30272016-10-04 17:08:08 -07002/*
3 * Chromium OS cros_ec driver
4 *
5 * Copyright (c) 2016 The Chromium OS Authors.
6 * Copyright (c) 2016 National Instruments Corp
Moritz Fischer62b30272016-10-04 17:08:08 -07007 */
8
9#include <common.h>
10#include <command.h>
11#include <cros_ec.h>
12#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Moritz Fischer62b30272016-10-04 17:08:08 -070014#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
Moritz Fischer62b30272016-10-04 17:08:08 -070020/**
Moritz Fischer50c14022016-11-03 08:53:52 -060021 * Decode a flash region parameter
22 *
23 * @param argc Number of params remaining
24 * @param argv List of remaining parameters
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +010025 * Return: flash region (EC_FLASH_REGION_...) or -1 on error
Moritz Fischer50c14022016-11-03 08:53:52 -060026 */
Simon Glassed38aef2020-05-10 11:40:03 -060027static int cros_ec_decode_region(int argc, char *const argv[])
Moritz Fischer50c14022016-11-03 08:53:52 -060028{
29 if (argc > 0) {
30 if (0 == strcmp(*argv, "rw"))
Simon Glass2c1e5502018-10-01 12:22:36 -060031 return EC_FLASH_REGION_ACTIVE;
Moritz Fischer50c14022016-11-03 08:53:52 -060032 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/**
Moritz Fischer62b30272016-10-04 17:08:08 -070044 * 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)
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +010050 * Return: 0 for ok, 1 for a usage error or -ve for ec command error
Moritz Fischer62b30272016-10-04 17:08:08 -070051 * (negative EC_RES_...)
52 */
Simon Glassbed43522018-10-01 12:22:22 -060053static int do_read_write(struct udevice *dev, int is_write, int argc,
Simon Glassed38aef2020-05-10 11:40:03 -060054 char *const argv[])
Moritz Fischer62b30272016-10-04 17:08:08 -070055{
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;
Simon Glass3ff49ec2021-07-24 09:03:29 -060067 addr = hextoul(argv[3], &endp);
Moritz Fischer62b30272016-10-04 17:08:08 -070068 if (*argv[3] == 0 || *endp != 0)
69 return 1;
70 if (argc > 4) {
Simon Glass3ff49ec2021-07-24 09:03:29 -060071 size = hextoul(argv[4], &endp);
Moritz Fischer62b30272016-10-04 17:08:08 -070072 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
Simon Glass959e1ce2021-01-16 14:52:26 -070096static 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
Simon Glass9d702522021-01-16 14:52:28 -0700164static 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
Simon Glass03161302021-01-16 14:52:29 -0700199static 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
Simon Glassed38aef2020-05-10 11:40:03 -0600259static int do_cros_ec(struct cmd_tbl *cmdtp, int flag, int argc,
260 char *const argv[])
Moritz Fischer62b30272016-10-04 17:08:08 -0700261{
Simon Glassbed43522018-10-01 12:22:22 -0600262 struct udevice *dev;
Moritz Fischer62b30272016-10-04 17:08:08 -0700263 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 */
Simon Glassbed43522018-10-01 12:22:22 -0600272 ret = uclass_find_device(UCLASS_CROS_EC, 0, &dev);
Moritz Fischer62b30272016-10-04 17:08:08 -0700273 if (!ret)
Simon Glassbed43522018-10-01 12:22:22 -0600274 device_remove(dev, DM_REMOVE_NORMAL);
275 ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
Moritz Fischer62b30272016-10-04 17:08:08 -0700276 if (ret) {
277 printf("Could not init cros_ec device (err %d)\n", ret);
278 return 1;
279 }
280 return 0;
281 }
282
Simon Glassbed43522018-10-01 12:22:22 -0600283 ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
Moritz Fischer62b30272016-10-04 17:08:08 -0700284 if (ret) {
285 printf("Cannot get cros-ec device (err=%d)\n", ret);
286 return 1;
287 }
Moritz Fischer62b30272016-10-04 17:08:08 -0700288 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);
Simon Glass959e1ce2021-01-16 14:52:26 -0700305 } else if (!strcmp("features", cmd)) {
306 ret = do_show_features(dev);
307
308 if (ret)
309 printf("Error: %d\n", ret);
Simon Glass9d702522021-01-16 14:52:28 -0700310 } else if (!strcmp("switches", cmd)) {
311 ret = do_show_switches(dev);
312
313 if (ret)
314 printf("Error: %d\n", ret);
Moritz Fischer62b30272016-10-04 17:08:08 -0700315 } 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
Simon Glass94564902018-10-01 12:22:38 -0600327 if (cros_ec_read_hash(dev, EC_VBOOT_HASH_OFFSET_ACTIVE, &hash)) {
Moritz Fischer62b30272016-10-04 17:08:08 -0700328 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;
Simon Glass2c1e5502018-10-01 12:22:36 -0600354 else if (region == EC_FLASH_REGION_ACTIVE)
Moritz Fischer62b30272016-10-04 17:08:08 -0700355 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)) {
Simon Glass03161302021-01-16 14:52:29 -0700365 ret = do_show_events(dev);
Moritz Fischer62b30272016-10-04 17:08:08 -0700366
Simon Glass03161302021-01-16 14:52:29 -0700367 if (ret)
368 printf("Error: %d\n", ret);
Moritz Fischer62b30272016-10-04 17:08:08 -0700369 } 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) {
Simon Glassbed43522018-10-01 12:22:22 -0600434 ret = cros_ec_read_nvdata(dev, block,
435 EC_VBNV_BLOCK_SIZE);
Moritz Fischer62b30272016-10-04 17:08:08 -0700436 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 }
Simon Glassbed43522018-10-01 12:22:22 -0600461 ret = cros_ec_write_nvdata(dev, block,
462 EC_VBNV_BLOCK_SIZE);
Moritz Fischer62b30272016-10-04 17:08:08 -0700463 }
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;
Simon Glassff9b9032021-07-24 09:03:30 -0600503 index = dectoul(argv[2], &endp);
Moritz Fischer62b30272016-10-04 17:08:08 -0700504 if (*argv[2] == 0 || *endp != 0)
505 return CMD_RET_USAGE;
506 if (argc > 3) {
Simon Glassff9b9032021-07-24 09:03:30 -0600507 state = dectoul(argv[3], &endp);
Moritz Fischer62b30272016-10-04 17:08:08 -0700508 if (*argv[3] == 0 || *endp != 0)
509 return CMD_RET_USAGE;
Simon Glassbed43522018-10-01 12:22:22 -0600510 ret = cros_ec_set_ldo(dev, index, state);
Moritz Fischer62b30272016-10-04 17:08:08 -0700511 } else {
Simon Glassbed43522018-10-01 12:22:22 -0600512 ret = cros_ec_get_ldo(dev, index, &state);
Moritz Fischer62b30272016-10-04 17:08:08 -0700513 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 }
Simon Glass33909b52021-01-16 14:52:25 -0700524 } 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 }
Moritz Fischer62b30272016-10-04 17:08:08 -0700533 } 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"
Simon Glass959e1ce2021-01-16 14:52:26 -0700551 "crosec features Read CROS-EC features\n"
Simon Glass9d702522021-01-16 14:52:28 -0700552 "crosec switches Read CROS-EC switches\n"
Moritz Fischer62b30272016-10-04 17:08:08 -0700553 "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"
Simon Glass03161302021-01-16 14:52:29 -0700557 "crosec eventsb Read CROS-EC host events_b\n"
Moritz Fischer62b30272016-10-04 17:08:08 -0700558 "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"
Simon Glass33909b52021-01-16 14:52:25 -0700566 "crosec sku Read board SKU ID\n"
Moritz Fischer62b30272016-10-04 17:08:08 -0700567 "crosec test run tests on cros_ec\n"
568 "crosec version Read CROS-EC version"
569);