uboot/cmd/cros_ec.c
<<
>>
Prefs
   1/*
   2 * Chromium OS cros_ec driver
   3 *
   4 * Copyright (c) 2016 The Chromium OS Authors.
   5 * Copyright (c) 2016 National Instruments Corp
   6 *
   7 * SPDX-License-Identifier:     GPL-2.0+
   8 */
   9
  10#include <common.h>
  11#include <command.h>
  12#include <cros_ec.h>
  13#include <dm.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
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22/**
  23 * Decode a flash region parameter
  24 *
  25 * @param argc Number of params remaining
  26 * @param argv List of remaining parameters
  27 * @return flash region (EC_FLASH_REGION_...) or -1 on error
  28 */
  29static int cros_ec_decode_region(int argc, char * const argv[])
  30{
  31        if (argc > 0) {
  32                if (0 == strcmp(*argv, "rw"))
  33                        return EC_FLASH_REGION_RW;
  34                else if (0 == strcmp(*argv, "ro"))
  35                        return EC_FLASH_REGION_RO;
  36
  37                debug("%s: Invalid region '%s'\n", __func__, *argv);
  38        } else {
  39                debug("%s: Missing region parameter\n", __func__);
  40        }
  41
  42        return -1;
  43}
  44
  45/**
  46 * Perform a flash read or write command
  47 *
  48 * @param dev           CROS-EC device to read/write
  49 * @param is_write      1 do to a write, 0 to do a read
  50 * @param argc          Number of arguments
  51 * @param argv          Arguments (2 is region, 3 is address)
  52 * @return 0 for ok, 1 for a usage error or -ve for ec command error
  53 *      (negative EC_RES_...)
  54 */
  55static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc,
  56                         char * const argv[])
  57{
  58        uint32_t offset, size = -1U, region_size;
  59        unsigned long addr;
  60        char *endp;
  61        int region;
  62        int ret;
  63
  64        region = cros_ec_decode_region(argc - 2, argv + 2);
  65        if (region == -1)
  66                return 1;
  67        if (argc < 4)
  68                return 1;
  69        addr = simple_strtoul(argv[3], &endp, 16);
  70        if (*argv[3] == 0 || *endp != 0)
  71                return 1;
  72        if (argc > 4) {
  73                size = simple_strtoul(argv[4], &endp, 16);
  74                if (*argv[4] == 0 || *endp != 0)
  75                        return 1;
  76        }
  77
  78        ret = cros_ec_flash_offset(dev, region, &offset, &region_size);
  79        if (ret) {
  80                debug("%s: Could not read region info\n", __func__);
  81                return ret;
  82        }
  83        if (size == -1U)
  84                size = region_size;
  85
  86        ret = is_write ?
  87                cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) :
  88                cros_ec_flash_read(dev, (uint8_t *)addr, offset, size);
  89        if (ret) {
  90                debug("%s: Could not %s region\n", __func__,
  91                      is_write ? "write" : "read");
  92                return ret;
  93        }
  94
  95        return 0;
  96}
  97
  98static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  99{
 100        struct cros_ec_dev *dev;
 101        struct udevice *udev;
 102        const char *cmd;
 103        int ret = 0;
 104
 105        if (argc < 2)
 106                return CMD_RET_USAGE;
 107
 108        cmd = argv[1];
 109        if (0 == strcmp("init", cmd)) {
 110                /* Remove any existing device */
 111                ret = uclass_find_device(UCLASS_CROS_EC, 0, &udev);
 112                if (!ret)
 113                        device_remove(udev);
 114                ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev);
 115                if (ret) {
 116                        printf("Could not init cros_ec device (err %d)\n", ret);
 117                        return 1;
 118                }
 119                return 0;
 120        }
 121
 122        ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev);
 123        if (ret) {
 124                printf("Cannot get cros-ec device (err=%d)\n", ret);
 125                return 1;
 126        }
 127        dev = dev_get_uclass_priv(udev);
 128        if (0 == strcmp("id", cmd)) {
 129                char id[MSG_BYTES];
 130
 131                if (cros_ec_read_id(dev, id, sizeof(id))) {
 132                        debug("%s: Could not read KBC ID\n", __func__);
 133                        return 1;
 134                }
 135                printf("%s\n", id);
 136        } else if (0 == strcmp("info", cmd)) {
 137                struct ec_response_mkbp_info info;
 138
 139                if (cros_ec_info(dev, &info)) {
 140                        debug("%s: Could not read KBC info\n", __func__);
 141                        return 1;
 142                }
 143                printf("rows     = %u\n", info.rows);
 144                printf("cols     = %u\n", info.cols);
 145                printf("switches = %#x\n", info.switches);
 146        } else if (0 == strcmp("curimage", cmd)) {
 147                enum ec_current_image image;
 148
 149                if (cros_ec_read_current_image(dev, &image)) {
 150                        debug("%s: Could not read KBC image\n", __func__);
 151                        return 1;
 152                }
 153                printf("%d\n", image);
 154        } else if (0 == strcmp("hash", cmd)) {
 155                struct ec_response_vboot_hash hash;
 156                int i;
 157
 158                if (cros_ec_read_hash(dev, &hash)) {
 159                        debug("%s: Could not read KBC hash\n", __func__);
 160                        return 1;
 161                }
 162
 163                if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
 164                        printf("type:    SHA-256\n");
 165                else
 166                        printf("type:    %d\n", hash.hash_type);
 167
 168                printf("offset:  0x%08x\n", hash.offset);
 169                printf("size:    0x%08x\n", hash.size);
 170
 171                printf("digest:  ");
 172                for (i = 0; i < hash.digest_size; i++)
 173                        printf("%02x", hash.hash_digest[i]);
 174                printf("\n");
 175        } else if (0 == strcmp("reboot", cmd)) {
 176                int region;
 177                enum ec_reboot_cmd cmd;
 178
 179                if (argc >= 3 && !strcmp(argv[2], "cold")) {
 180                        cmd = EC_REBOOT_COLD;
 181                } else {
 182                        region = cros_ec_decode_region(argc - 2, argv + 2);
 183                        if (region == EC_FLASH_REGION_RO)
 184                                cmd = EC_REBOOT_JUMP_RO;
 185                        else if (region == EC_FLASH_REGION_RW)
 186                                cmd = EC_REBOOT_JUMP_RW;
 187                        else
 188                                return CMD_RET_USAGE;
 189                }
 190
 191                if (cros_ec_reboot(dev, cmd, 0)) {
 192                        debug("%s: Could not reboot KBC\n", __func__);
 193                        return 1;
 194                }
 195        } else if (0 == strcmp("events", cmd)) {
 196                uint32_t events;
 197
 198                if (cros_ec_get_host_events(dev, &events)) {
 199                        debug("%s: Could not read host events\n", __func__);
 200                        return 1;
 201                }
 202                printf("0x%08x\n", events);
 203        } else if (0 == strcmp("clrevents", cmd)) {
 204                uint32_t events = 0x7fffffff;
 205
 206                if (argc >= 3)
 207                        events = simple_strtol(argv[2], NULL, 0);
 208
 209                if (cros_ec_clear_host_events(dev, events)) {
 210                        debug("%s: Could not clear host events\n", __func__);
 211                        return 1;
 212                }
 213        } else if (0 == strcmp("read", cmd)) {
 214                ret = do_read_write(dev, 0, argc, argv);
 215                if (ret > 0)
 216                        return CMD_RET_USAGE;
 217        } else if (0 == strcmp("write", cmd)) {
 218                ret = do_read_write(dev, 1, argc, argv);
 219                if (ret > 0)
 220                        return CMD_RET_USAGE;
 221        } else if (0 == strcmp("erase", cmd)) {
 222                int region = cros_ec_decode_region(argc - 2, argv + 2);
 223                uint32_t offset, size;
 224
 225                if (region == -1)
 226                        return CMD_RET_USAGE;
 227                if (cros_ec_flash_offset(dev, region, &offset, &size)) {
 228                        debug("%s: Could not read region info\n", __func__);
 229                        ret = -1;
 230                } else {
 231                        ret = cros_ec_flash_erase(dev, offset, size);
 232                        if (ret) {
 233                                debug("%s: Could not erase region\n",
 234                                      __func__);
 235                        }
 236                }
 237        } else if (0 == strcmp("regioninfo", cmd)) {
 238                int region = cros_ec_decode_region(argc - 2, argv + 2);
 239                uint32_t offset, size;
 240
 241                if (region == -1)
 242                        return CMD_RET_USAGE;
 243                ret = cros_ec_flash_offset(dev, region, &offset, &size);
 244                if (ret) {
 245                        debug("%s: Could not read region info\n", __func__);
 246                } else {
 247                        printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
 248                                        "RO" : "RW");
 249                        printf("Offset: %x\n", offset);
 250                        printf("Size:   %x\n", size);
 251                }
 252        } else if (0 == strcmp("flashinfo", cmd)) {
 253                struct ec_response_flash_info p;
 254
 255                ret = cros_ec_read_flashinfo(dev, &p);
 256                if (!ret) {
 257                        printf("Flash size:         %u\n", p.flash_size);
 258                        printf("Write block size:   %u\n", p.write_block_size);
 259                        printf("Erase block size:   %u\n", p.erase_block_size);
 260                }
 261        } else if (0 == strcmp("vbnvcontext", cmd)) {
 262                uint8_t block[EC_VBNV_BLOCK_SIZE];
 263                char buf[3];
 264                int i, len;
 265                unsigned long result;
 266
 267                if (argc <= 2) {
 268                        ret = cros_ec_read_vbnvcontext(dev, block);
 269                        if (!ret) {
 270                                printf("vbnv_block: ");
 271                                for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
 272                                        printf("%02x", block[i]);
 273                                putc('\n');
 274                        }
 275                } else {
 276                        /*
 277                         * TODO(clchiou): Move this to a utility function as
 278                         * cmd_spi might want to call it.
 279                         */
 280                        memset(block, 0, EC_VBNV_BLOCK_SIZE);
 281                        len = strlen(argv[2]);
 282                        buf[2] = '\0';
 283                        for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
 284                                if (i * 2 >= len)
 285                                        break;
 286                                buf[0] = argv[2][i * 2];
 287                                if (i * 2 + 1 >= len)
 288                                        buf[1] = '0';
 289                                else
 290                                        buf[1] = argv[2][i * 2 + 1];
 291                                strict_strtoul(buf, 16, &result);
 292                                block[i] = result;
 293                        }
 294                        ret = cros_ec_write_vbnvcontext(dev, block);
 295                }
 296                if (ret) {
 297                        debug("%s: Could not %s VbNvContext\n", __func__,
 298                              argc <= 2 ?  "read" : "write");
 299                }
 300        } else if (0 == strcmp("test", cmd)) {
 301                int result = cros_ec_test(dev);
 302
 303                if (result)
 304                        printf("Test failed with error %d\n", result);
 305                else
 306                        puts("Test passed\n");
 307        } else if (0 == strcmp("version", cmd)) {
 308                struct ec_response_get_version *p;
 309                char *build_string;
 310
 311                ret = cros_ec_read_version(dev, &p);
 312                if (!ret) {
 313                        /* Print versions */
 314                        printf("RO version:    %1.*s\n",
 315                               (int)sizeof(p->version_string_ro),
 316                               p->version_string_ro);
 317                        printf("RW version:    %1.*s\n",
 318                               (int)sizeof(p->version_string_rw),
 319                               p->version_string_rw);
 320                        printf("Firmware copy: %s\n",
 321                               (p->current_image <
 322                               ARRAY_SIZE(ec_current_image_name) ?
 323                               ec_current_image_name[p->current_image] :
 324                               "?"));
 325                        ret = cros_ec_read_build_info(dev, &build_string);
 326                        if (!ret)
 327                                printf("Build info:    %s\n", build_string);
 328                }
 329        } else if (0 == strcmp("ldo", cmd)) {
 330                uint8_t index, state;
 331                char *endp;
 332
 333                if (argc < 3)
 334                        return CMD_RET_USAGE;
 335                index = simple_strtoul(argv[2], &endp, 10);
 336                if (*argv[2] == 0 || *endp != 0)
 337                        return CMD_RET_USAGE;
 338                if (argc > 3) {
 339                        state = simple_strtoul(argv[3], &endp, 10);
 340                        if (*argv[3] == 0 || *endp != 0)
 341                                return CMD_RET_USAGE;
 342                        ret = cros_ec_set_ldo(udev, index, state);
 343                } else {
 344                        ret = cros_ec_get_ldo(udev, index, &state);
 345                        if (!ret) {
 346                                printf("LDO%d: %s\n", index,
 347                                       state == EC_LDO_STATE_ON ?
 348                                       "on" : "off");
 349                        }
 350                }
 351
 352                if (ret) {
 353                        debug("%s: Could not access LDO%d\n", __func__, index);
 354                        return ret;
 355                }
 356        } else {
 357                return CMD_RET_USAGE;
 358        }
 359
 360        if (ret < 0) {
 361                printf("Error: CROS-EC command failed (error %d)\n", ret);
 362                ret = 1;
 363        }
 364
 365        return ret;
 366}
 367
 368U_BOOT_CMD(
 369        crosec, 6,      1,      do_cros_ec,
 370        "CROS-EC utility command",
 371        "init                Re-init CROS-EC (done on startup automatically)\n"
 372        "crosec id                  Read CROS-EC ID\n"
 373        "crosec info                Read CROS-EC info\n"
 374        "crosec curimage            Read CROS-EC current image\n"
 375        "crosec hash                Read CROS-EC hash\n"
 376        "crosec reboot [rw | ro | cold]  Reboot CROS-EC\n"
 377        "crosec events              Read CROS-EC host events\n"
 378        "crosec clrevents [mask]    Clear CROS-EC host events\n"
 379        "crosec regioninfo <ro|rw>  Read image info\n"
 380        "crosec flashinfo           Read flash info\n"
 381        "crosec erase <ro|rw>       Erase EC image\n"
 382        "crosec read <ro|rw> <addr> [<size>]   Read EC image\n"
 383        "crosec write <ro|rw> <addr> [<size>]  Write EC image\n"
 384        "crosec vbnvcontext [hexstring]        Read [write] VbNvContext from EC\n"
 385        "crosec ldo <idx> [<state>] Switch/Read LDO state\n"
 386        "crosec test                run tests on cros_ec\n"
 387        "crosec version             Read CROS-EC version"
 388);
 389