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> setup()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 * tearDown()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 */ 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 testBug12673()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 */ 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, ®ion_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 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 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 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 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