Prepare v2024.10
[platform/kernel/u-boot.git] / cmd / cros_ec.c
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 */
17 static 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  */
26 static 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  */
52 static 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
95 static 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
142 static 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
163 static 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
174 static 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
198 static 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
233 static 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
258 static 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
544 U_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 );