diff options
Diffstat (limited to 'cmd/cros_ec.c')
-rw-r--r-- | cmd/cros_ec.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/cmd/cros_ec.c b/cmd/cros_ec.c new file mode 100644 index 0000000..abf11f0 --- /dev/null +++ b/cmd/cros_ec.c @@ -0,0 +1,365 @@ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2016 The Chromium OS Authors. + * Copyright (c) 2016 National Instruments Corp + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <cros_ec.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* Note: depends on enum ec_current_image */ +static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; + +DECLARE_GLOBAL_DATA_PTR; + +/** + * Perform a flash read or write command + * + * @param dev CROS-EC device to read/write + * @param is_write 1 do to a write, 0 to do a read + * @param argc Number of arguments + * @param argv Arguments (2 is region, 3 is address) + * @return 0 for ok, 1 for a usage error or -ve for ec command error + * (negative EC_RES_...) + */ +static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc, + char * const argv[]) +{ + uint32_t offset, size = -1U, region_size; + unsigned long addr; + char *endp; + int region; + int ret; + + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == -1) + return 1; + if (argc < 4) + return 1; + addr = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return 1; + if (argc > 4) { + size = simple_strtoul(argv[4], &endp, 16); + if (*argv[4] == 0 || *endp != 0) + return 1; + } + + ret = cros_ec_flash_offset(dev, region, &offset, ®ion_size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + return ret; + } + if (size == -1U) + size = region_size; + + ret = is_write ? + cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) : + cros_ec_flash_read(dev, (uint8_t *)addr, offset, size); + if (ret) { + debug("%s: Could not %s region\n", __func__, + is_write ? "write" : "read"); + return ret; + } + + return 0; +} + +static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct cros_ec_dev *dev; + struct udevice *udev; + const char *cmd; + int ret = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = argv[1]; + if (0 == strcmp("init", cmd)) { + /* Remove any existing device */ + ret = uclass_find_device(UCLASS_CROS_EC, 0, &udev); + if (!ret) + device_remove(udev); + ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev); + if (ret) { + printf("Could not init cros_ec device (err %d)\n", ret); + return 1; + } + return 0; + } + + ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev); + if (ret) { + printf("Cannot get cros-ec device (err=%d)\n", ret); + return 1; + } + dev = dev_get_uclass_priv(udev); + if (0 == strcmp("id", cmd)) { + char id[MSG_BYTES]; + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return 1; + } + printf("%s\n", id); + } else if (0 == strcmp("info", cmd)) { + struct ec_response_mkbp_info info; + + if (cros_ec_info(dev, &info)) { + debug("%s: Could not read KBC info\n", __func__); + return 1; + } + printf("rows = %u\n", info.rows); + printf("cols = %u\n", info.cols); + printf("switches = %#x\n", info.switches); + } else if (0 == strcmp("curimage", cmd)) { + enum ec_current_image image; + + if (cros_ec_read_current_image(dev, &image)) { + debug("%s: Could not read KBC image\n", __func__); + return 1; + } + printf("%d\n", image); + } else if (0 == strcmp("hash", cmd)) { + struct ec_response_vboot_hash hash; + int i; + + if (cros_ec_read_hash(dev, &hash)) { + debug("%s: Could not read KBC hash\n", __func__); + return 1; + } + + if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256) + printf("type: SHA-256\n"); + else + printf("type: %d\n", hash.hash_type); + + printf("offset: 0x%08x\n", hash.offset); + printf("size: 0x%08x\n", hash.size); + + printf("digest: "); + for (i = 0; i < hash.digest_size; i++) + printf("%02x", hash.hash_digest[i]); + printf("\n"); + } else if (0 == strcmp("reboot", cmd)) { + int region; + enum ec_reboot_cmd cmd; + + if (argc >= 3 && !strcmp(argv[2], "cold")) { + cmd = EC_REBOOT_COLD; + } else { + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == EC_FLASH_REGION_RO) + cmd = EC_REBOOT_JUMP_RO; + else if (region == EC_FLASH_REGION_RW) + cmd = EC_REBOOT_JUMP_RW; + else + return CMD_RET_USAGE; + } + + if (cros_ec_reboot(dev, cmd, 0)) { + debug("%s: Could not reboot KBC\n", __func__); + return 1; + } + } else if (0 == strcmp("events", cmd)) { + uint32_t events; + + if (cros_ec_get_host_events(dev, &events)) { + debug("%s: Could not read host events\n", __func__); + return 1; + } + printf("0x%08x\n", events); + } else if (0 == strcmp("clrevents", cmd)) { + uint32_t events = 0x7fffffff; + + if (argc >= 3) + events = simple_strtol(argv[2], NULL, 0); + + if (cros_ec_clear_host_events(dev, events)) { + debug("%s: Could not clear host events\n", __func__); + return 1; + } + } else if (0 == strcmp("read", cmd)) { + ret = do_read_write(dev, 0, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("write", cmd)) { + ret = do_read_write(dev, 1, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("erase", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + if (cros_ec_flash_offset(dev, region, &offset, &size)) { + debug("%s: Could not read region info\n", __func__); + ret = -1; + } else { + ret = cros_ec_flash_erase(dev, offset, size); + if (ret) { + debug("%s: Could not erase region\n", + __func__); + } + } + } else if (0 == strcmp("regioninfo", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + ret = cros_ec_flash_offset(dev, region, &offset, &size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + } else { + printf("Region: %s\n", region == EC_FLASH_REGION_RO ? + "RO" : "RW"); + printf("Offset: %x\n", offset); + printf("Size: %x\n", size); + } + } else if (0 == strcmp("flashinfo", cmd)) { + struct ec_response_flash_info p; + + ret = cros_ec_read_flashinfo(dev, &p); + if (!ret) { + printf("Flash size: %u\n", p.flash_size); + printf("Write block size: %u\n", p.write_block_size); + printf("Erase block size: %u\n", p.erase_block_size); + } + } else if (0 == strcmp("vbnvcontext", cmd)) { + uint8_t block[EC_VBNV_BLOCK_SIZE]; + char buf[3]; + int i, len; + unsigned long result; + + if (argc <= 2) { + ret = cros_ec_read_vbnvcontext(dev, block); + if (!ret) { + printf("vbnv_block: "); + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) + printf("%02x", block[i]); + putc('\n'); + } + } else { + /* + * TODO(clchiou): Move this to a utility function as + * cmd_spi might want to call it. + */ + memset(block, 0, EC_VBNV_BLOCK_SIZE); + len = strlen(argv[2]); + buf[2] = '\0'; + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) { + if (i * 2 >= len) + break; + buf[0] = argv[2][i * 2]; + if (i * 2 + 1 >= len) + buf[1] = '0'; + else + buf[1] = argv[2][i * 2 + 1]; + strict_strtoul(buf, 16, &result); + block[i] = result; + } + ret = cros_ec_write_vbnvcontext(dev, block); + } + if (ret) { + debug("%s: Could not %s VbNvContext\n", __func__, + argc <= 2 ? "read" : "write"); + } + } else if (0 == strcmp("test", cmd)) { + int result = cros_ec_test(dev); + + if (result) + printf("Test failed with error %d\n", result); + else + puts("Test passed\n"); + } else if (0 == strcmp("version", cmd)) { + struct ec_response_get_version *p; + char *build_string; + + ret = cros_ec_read_version(dev, &p); + if (!ret) { + /* Print versions */ + printf("RO version: %1.*s\n", + (int)sizeof(p->version_string_ro), + p->version_string_ro); + printf("RW version: %1.*s\n", + (int)sizeof(p->version_string_rw), + p->version_string_rw); + printf("Firmware copy: %s\n", + (p->current_image < + ARRAY_SIZE(ec_current_image_name) ? + ec_current_image_name[p->current_image] : + "?")); + ret = cros_ec_read_build_info(dev, &build_string); + if (!ret) + printf("Build info: %s\n", build_string); + } + } else if (0 == strcmp("ldo", cmd)) { + uint8_t index, state; + char *endp; + + if (argc < 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], &endp, 10); + if (*argv[2] == 0 || *endp != 0) + return CMD_RET_USAGE; + if (argc > 3) { + state = simple_strtoul(argv[3], &endp, 10); + if (*argv[3] == 0 || *endp != 0) + return CMD_RET_USAGE; + ret = cros_ec_set_ldo(udev, index, state); + } else { + ret = cros_ec_get_ldo(udev, index, &state); + if (!ret) { + printf("LDO%d: %s\n", index, + state == EC_LDO_STATE_ON ? + "on" : "off"); + } + } + + if (ret) { + debug("%s: Could not access LDO%d\n", __func__, index); + return ret; + } + } else { + return CMD_RET_USAGE; + } + + if (ret < 0) { + printf("Error: CROS-EC command failed (error %d)\n", ret); + ret = 1; + } + + return ret; +} + +U_BOOT_CMD( + crosec, 6, 1, do_cros_ec, + "CROS-EC utility command", + "init Re-init CROS-EC (done on startup automatically)\n" + "crosec id Read CROS-EC ID\n" + "crosec info Read CROS-EC info\n" + "crosec curimage Read CROS-EC current image\n" + "crosec hash Read CROS-EC hash\n" + "crosec reboot [rw | ro | cold] Reboot CROS-EC\n" + "crosec events Read CROS-EC host events\n" + "crosec clrevents [mask] Clear CROS-EC host events\n" + "crosec regioninfo <ro|rw> Read image info\n" + "crosec flashinfo Read flash info\n" + "crosec erase <ro|rw> Erase EC image\n" + "crosec read <ro|rw> <addr> [<size>] Read EC image\n" + "crosec write <ro|rw> <addr> [<size>] Write EC image\n" + "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" + "crosec ldo <idx> [<state>] Switch/Read LDO state\n" + "crosec test run tests on cros_ec\n" + "crosec version Read CROS-EC version" +); |