uboot/cmd/cros_ec.c
<<
>>
Prefs
   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 <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 = simple_strtoul(argv[3], &endp, 16);
  67        if (*argv[3] == 0 || *endp != 0)
  68                return 1;
  69        if (argc > 4) {
  70                size = simple_strtoul(argv[4], &endp, 16);
  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 int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  96{
  97        struct udevice *dev;
  98        const char *cmd;
  99        int ret = 0;
 100
 101        if (argc < 2)
 102                return CMD_RET_USAGE;
 103
 104        cmd = argv[1];
 105        if (0 == strcmp("init", cmd)) {
 106                /* Remove any existing device */
 107                ret = uclass_find_device(UCLASS_CROS_EC, 0, &dev);
 108                if (!ret)
 109                        device_remove(dev, DM_REMOVE_NORMAL);
 110                ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
 111                if (ret) {
 112                        printf("Could not init cros_ec device (err %d)\n", ret);
 113                        return 1;
 114                }
 115                return 0;
 116        }
 117
 118        ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
 119        if (ret) {
 120                printf("Cannot get cros-ec device (err=%d)\n", ret);
 121                return 1;
 122        }
 123        if (0 == strcmp("id", cmd)) {
 124                char id[MSG_BYTES];
 125
 126                if (cros_ec_read_id(dev, id, sizeof(id))) {
 127                        debug("%s: Could not read KBC ID\n", __func__);
 128                        return 1;
 129                }
 130                printf("%s\n", id);
 131        } else if (0 == strcmp("info", cmd)) {
 132                struct ec_response_mkbp_info info;
 133
 134                if (cros_ec_info(dev, &info)) {
 135                        debug("%s: Could not read KBC info\n", __func__);
 136                        return 1;
 137                }
 138                printf("rows     = %u\n", info.rows);
 139                printf("cols     = %u\n", info.cols);
 140        } else if (0 == strcmp("curimage", cmd)) {
 141                enum ec_current_image image;
 142
 143                if (cros_ec_read_current_image(dev, &image)) {
 144                        debug("%s: Could not read KBC image\n", __func__);
 145                        return 1;
 146                }
 147                printf("%d\n", image);
 148        } else if (0 == strcmp("hash", cmd)) {
 149                struct ec_response_vboot_hash hash;
 150                int i;
 151
 152                if (cros_ec_read_hash(dev, EC_VBOOT_HASH_OFFSET_ACTIVE, &hash)) {
 153                        debug("%s: Could not read KBC hash\n", __func__);
 154                        return 1;
 155                }
 156
 157                if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
 158                        printf("type:    SHA-256\n");
 159                else
 160                        printf("type:    %d\n", hash.hash_type);
 161
 162                printf("offset:  0x%08x\n", hash.offset);
 163                printf("size:    0x%08x\n", hash.size);
 164
 165                printf("digest:  ");
 166                for (i = 0; i < hash.digest_size; i++)
 167                        printf("%02x", hash.hash_digest[i]);
 168                printf("\n");
 169        } else if (0 == strcmp("reboot", cmd)) {
 170                int region;
 171                enum ec_reboot_cmd cmd;
 172
 173                if (argc >= 3 && !strcmp(argv[2], "cold")) {
 174                        cmd = EC_REBOOT_COLD;
 175                } else {
 176                        region = cros_ec_decode_region(argc - 2, argv + 2);
 177                        if (region == EC_FLASH_REGION_RO)
 178                                cmd = EC_REBOOT_JUMP_RO;
 179                        else if (region == EC_FLASH_REGION_ACTIVE)
 180                                cmd = EC_REBOOT_JUMP_RW;
 181                        else
 182                                return CMD_RET_USAGE;
 183                }
 184
 185                if (cros_ec_reboot(dev, cmd, 0)) {
 186                        debug("%s: Could not reboot KBC\n", __func__);
 187                        return 1;
 188                }
 189        } else if (0 == strcmp("events", cmd)) {
 190                uint32_t events;
 191
 192                if (cros_ec_get_host_events(dev, &events)) {
 193                        debug("%s: Could not read host events\n", __func__);
 194                        return 1;
 195                }
 196                printf("0x%08x\n", events);
 197        } else if (0 == strcmp("clrevents", cmd)) {
 198                uint32_t events = 0x7fffffff;
 199
 200                if (argc >= 3)
 201                        events = simple_strtol(argv[2], NULL, 0);
 202
 203                if (cros_ec_clear_host_events(dev, events)) {
 204                        debug("%s: Could not clear host events\n", __func__);
 205                        return 1;
 206                }
 207        } else if (0 == strcmp("read", cmd)) {
 208                ret = do_read_write(dev, 0, argc, argv);
 209                if (ret > 0)
 210                        return CMD_RET_USAGE;
 211        } else if (0 == strcmp("write", cmd)) {
 212                ret = do_read_write(dev, 1, argc, argv);
 213                if (ret > 0)
 214                        return CMD_RET_USAGE;
 215        } else if (0 == strcmp("erase", cmd)) {
 216                int region = cros_ec_decode_region(argc - 2, argv + 2);
 217                uint32_t offset, size;
 218
 219                if (region == -1)
 220                        return CMD_RET_USAGE;
 221                if (cros_ec_flash_offset(dev, region, &offset, &size)) {
 222                        debug("%s: Could not read region info\n", __func__);
 223                        ret = -1;
 224                } else {
 225                        ret = cros_ec_flash_erase(dev, offset, size);
 226                        if (ret) {
 227                                debug("%s: Could not erase region\n",
 228                                      __func__);
 229                        }
 230                }
 231        } else if (0 == strcmp("regioninfo", cmd)) {
 232                int region = cros_ec_decode_region(argc - 2, argv + 2);
 233                uint32_t offset, size;
 234
 235                if (region == -1)
 236                        return CMD_RET_USAGE;
 237                ret = cros_ec_flash_offset(dev, region, &offset, &size);
 238                if (ret) {
 239                        debug("%s: Could not read region info\n", __func__);
 240                } else {
 241                        printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
 242                                        "RO" : "RW");
 243                        printf("Offset: %x\n", offset);
 244                        printf("Size:   %x\n", size);
 245                }
 246        } else if (0 == strcmp("flashinfo", cmd)) {
 247                struct ec_response_flash_info p;
 248
 249                ret = cros_ec_read_flashinfo(dev, &p);
 250                if (!ret) {
 251                        printf("Flash size:         %u\n", p.flash_size);
 252                        printf("Write block size:   %u\n", p.write_block_size);
 253                        printf("Erase block size:   %u\n", p.erase_block_size);
 254                }
 255        } else if (0 == strcmp("vbnvcontext", cmd)) {
 256                uint8_t block[EC_VBNV_BLOCK_SIZE];
 257                char buf[3];
 258                int i, len;
 259                unsigned long result;
 260
 261                if (argc <= 2) {
 262                        ret = cros_ec_read_nvdata(dev, block,
 263                                                  EC_VBNV_BLOCK_SIZE);
 264                        if (!ret) {
 265                                printf("vbnv_block: ");
 266                                for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
 267                                        printf("%02x", block[i]);
 268                                putc('\n');
 269                        }
 270                } else {
 271                        /*
 272                         * TODO(clchiou): Move this to a utility function as
 273                         * cmd_spi might want to call it.
 274                         */
 275                        memset(block, 0, EC_VBNV_BLOCK_SIZE);
 276                        len = strlen(argv[2]);
 277                        buf[2] = '\0';
 278                        for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
 279                                if (i * 2 >= len)
 280                                        break;
 281                                buf[0] = argv[2][i * 2];
 282                                if (i * 2 + 1 >= len)
 283                                        buf[1] = '0';
 284                                else
 285                                        buf[1] = argv[2][i * 2 + 1];
 286                                strict_strtoul(buf, 16, &result);
 287                                block[i] = result;
 288                        }
 289                        ret = cros_ec_write_nvdata(dev, block,
 290                                                   EC_VBNV_BLOCK_SIZE);
 291                }
 292                if (ret) {
 293                        debug("%s: Could not %s VbNvContext\n", __func__,
 294                              argc <= 2 ?  "read" : "write");
 295                }
 296        } else if (0 == strcmp("test", cmd)) {
 297                int result = cros_ec_test(dev);
 298
 299                if (result)
 300                        printf("Test failed with error %d\n", result);
 301                else
 302                        puts("Test passed\n");
 303        } else if (0 == strcmp("version", cmd)) {
 304                struct ec_response_get_version *p;
 305                char *build_string;
 306
 307                ret = cros_ec_read_version(dev, &p);
 308                if (!ret) {
 309                        /* Print versions */
 310                        printf("RO version:    %1.*s\n",
 311                               (int)sizeof(p->version_string_ro),
 312                               p->version_string_ro);
 313                        printf("RW version:    %1.*s\n",
 314                               (int)sizeof(p->version_string_rw),
 315                               p->version_string_rw);
 316                        printf("Firmware copy: %s\n",
 317                               (p->current_image <
 318                               ARRAY_SIZE(ec_current_image_name) ?
 319                               ec_current_image_name[p->current_image] :
 320                               "?"));
 321                        ret = cros_ec_read_build_info(dev, &build_string);
 322                        if (!ret)
 323                                printf("Build info:    %s\n", build_string);
 324                }
 325        } else if (0 == strcmp("ldo", cmd)) {
 326                uint8_t index, state;
 327                char *endp;
 328
 329                if (argc < 3)
 330                        return CMD_RET_USAGE;
 331                index = simple_strtoul(argv[2], &endp, 10);
 332                if (*argv[2] == 0 || *endp != 0)
 333                        return CMD_RET_USAGE;
 334                if (argc > 3) {
 335                        state = simple_strtoul(argv[3], &endp, 10);
 336                        if (*argv[3] == 0 || *endp != 0)
 337                                return CMD_RET_USAGE;
 338                        ret = cros_ec_set_ldo(dev, index, state);
 339                } else {
 340                        ret = cros_ec_get_ldo(dev, index, &state);
 341                        if (!ret) {
 342                                printf("LDO%d: %s\n", index,
 343                                       state == EC_LDO_STATE_ON ?
 344                                       "on" : "off");
 345                        }
 346                }
 347
 348                if (ret) {
 349                        debug("%s: Could not access LDO%d\n", __func__, index);
 350                        return ret;
 351                }
 352        } else {
 353                return CMD_RET_USAGE;
 354        }
 355
 356        if (ret < 0) {
 357                printf("Error: CROS-EC command failed (error %d)\n", ret);
 358                ret = 1;
 359        }
 360
 361        return ret;
 362}
 363
 364U_BOOT_CMD(
 365        crosec, 6,      1,      do_cros_ec,
 366        "CROS-EC utility command",
 367        "init                Re-init CROS-EC (done on startup automatically)\n"
 368        "crosec id                  Read CROS-EC ID\n"
 369        "crosec info                Read CROS-EC info\n"
 370        "crosec curimage            Read CROS-EC current image\n"
 371        "crosec hash                Read CROS-EC hash\n"
 372        "crosec reboot [rw | ro | cold]  Reboot CROS-EC\n"
 373        "crosec events              Read CROS-EC host events\n"
 374        "crosec clrevents [mask]    Clear CROS-EC host events\n"
 375        "crosec regioninfo <ro|rw>  Read image info\n"
 376        "crosec flashinfo           Read flash info\n"
 377        "crosec erase <ro|rw>       Erase EC image\n"
 378        "crosec read <ro|rw> <addr> [<size>]   Read EC image\n"
 379        "crosec write <ro|rw> <addr> [<size>]  Write EC image\n"
 380        "crosec vbnvcontext [hexstring]        Read [write] VbNvContext from EC\n"
 381        "crosec ldo <idx> [<state>] Switch/Read LDO state\n"
 382        "crosec test                run tests on cros_ec\n"
 383        "crosec version             Read CROS-EC version"
 384);
 385