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 <flash.h>
  14#include <log.h>
  15#include <dm/device-internal.h>
  16#include <dm/uclass-internal.h>
  17
  18/* Note: depends on enum ec_current_image */
  19static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"};
  20
  21/**
  22 * Decode a flash region parameter
  23 *
  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 */
  28static 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
  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 */
  54static 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 = hextoul(argv[3], &endp);
  69        if (*argv[3] == 0 || *endp != 0)
  70                return 1;
  71        if (argc > 4) {
  72                size = hextoul(argv[4], &endp);
  73                if (*argv[4] == 0 || *endp != 0)
  74                        return 1;
  75        }
  76
  77        ret = cros_ec_flash_offset(dev, region, &offset, &region_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
  97static 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
 144static 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
 165static 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
 176static 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
 200static 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
 235static 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
 260static 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 = dectoul(argv[2], &endp);
 505                if (*argv[2] == 0 || *endp != 0)
 506                        return CMD_RET_USAGE;
 507                if (argc > 3) {
 508                        state = dectoul(argv[3], &endp);
 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
 546U_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