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, ®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 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