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