Merge tag 'u-boot-imx-next-20230404' of https://gitlab.denx.de/u-boot/custodians...
[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 <common.h>
10 #include <command.h>
11 #include <cros_ec.h>
12 #include <dm.h>
13 #include <log.h>
14 #include <dm/device-internal.h>
15 #include <dm/uclass-internal.h>
16
17 /* Note: depends on enum ec_current_image */
18 static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"};
19
20 /**
21  * Decode a flash region parameter
22  *
23  * @param argc Number of params remaining
24  * @param argv List of remaining parameters
25  * Return: flash region (EC_FLASH_REGION_...) or -1 on error
26  */
27 static int cros_ec_decode_region(int argc, char *const argv[])
28 {
29         if (argc > 0) {
30                 if (0 == strcmp(*argv, "rw"))
31                         return EC_FLASH_REGION_ACTIVE;
32                 else if (0 == strcmp(*argv, "ro"))
33                         return EC_FLASH_REGION_RO;
34
35                 debug("%s: Invalid region '%s'\n", __func__, *argv);
36         } else {
37                 debug("%s: Missing region parameter\n", __func__);
38         }
39
40         return -1;
41 }
42
43 /**
44  * Perform a flash read or write command
45  *
46  * @param dev           CROS-EC device to read/write
47  * @param is_write      1 do to a write, 0 to do a read
48  * @param argc          Number of arguments
49  * @param argv          Arguments (2 is region, 3 is address)
50  * Return: 0 for ok, 1 for a usage error or -ve for ec command error
51  *      (negative EC_RES_...)
52  */
53 static int do_read_write(struct udevice *dev, int is_write, int argc,
54                          char *const argv[])
55 {
56         uint32_t offset, size = -1U, region_size;
57         unsigned long addr;
58         char *endp;
59         int region;
60         int ret;
61
62         region = cros_ec_decode_region(argc - 2, argv + 2);
63         if (region == -1)
64                 return 1;
65         if (argc < 4)
66                 return 1;
67         addr = hextoul(argv[3], &endp);
68         if (*argv[3] == 0 || *endp != 0)
69                 return 1;
70         if (argc > 4) {
71                 size = hextoul(argv[4], &endp);
72                 if (*argv[4] == 0 || *endp != 0)
73                         return 1;
74         }
75
76         ret = cros_ec_flash_offset(dev, region, &offset, &region_size);
77         if (ret) {
78                 debug("%s: Could not read region info\n", __func__);
79                 return ret;
80         }
81         if (size == -1U)
82                 size = region_size;
83
84         ret = is_write ?
85                 cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) :
86                 cros_ec_flash_read(dev, (uint8_t *)addr, offset, size);
87         if (ret) {
88                 debug("%s: Could not %s region\n", __func__,
89                       is_write ? "write" : "read");
90                 return ret;
91         }
92
93         return 0;
94 }
95
96 static const char *const feat_name[64] = {
97         "limited",
98         "flash",
99         "pwm_fan",
100         "pwm_keyb",
101         "lightbar",
102         "led",
103         "motion_sense",
104         "keyb",
105         "pstore",
106         "port80",
107         "thermal",
108         "bklight_switch",
109         "wifi_switch",
110         "host_events",
111         "gpio",
112         "i2c",
113         "charger",
114         "battery",
115         "smart_battery",
116         "hang_detect",
117         "pmu",
118         "sub_mcu",
119         "usb_pd",
120         "usb_mux",
121         "motion_sense_fifo",
122         "vstore",
123         "usbc_ss_mux_virtual",
124         "rtc",
125         "fingerprint",
126         "touchpad",
127         "rwsig",
128         "device_event",
129         "unified_wake_masks",
130         "host_event64",
131         "exec_in_ram",
132         "cec",
133         "motion_sense_tight_timestamps",
134         "refined_tablet_mode_hysteresis",
135         "efs2",
136         "scp",
137         "ish",
138         "typec_cmd",
139         "typec_require_ap_mode_entry",
140         "typec_mux_require_ap_ack",
141 };
142
143 static int do_show_features(struct udevice *dev)
144 {
145         u64 feat;
146         int ret;
147         uint i;
148
149         ret = cros_ec_get_features(dev, &feat);
150         if (ret)
151                 return ret;
152         for (i = 0; i < ARRAY_SIZE(feat_name); i++) {
153                 if (feat & (1ULL << i)) {
154                         if (feat_name[i])
155                                 printf("%s\n", feat_name[i]);
156                         else
157                                 printf("unknown %d\n", i);
158                 }
159         }
160
161         return 0;
162 }
163
164 static const char *const switch_name[8] = {
165         "lid open",
166         "power button pressed",
167         "write-protect disabled",
168         NULL,
169         "dedicated recovery",
170         NULL,
171         NULL,
172         NULL,
173 };
174
175 static int do_show_switches(struct udevice *dev)
176 {
177         uint switches;
178         int ret;
179         uint i;
180
181         ret = cros_ec_get_switches(dev);
182         if (ret < 0)
183                 return log_msg_ret("get", ret);
184         switches = ret;
185         for (i = 0; i < ARRAY_SIZE(switch_name); i++) {
186                 uint mask = 1 << i;
187
188                 if (switches & mask) {
189                         if (switch_name[i])
190                                 printf("%s\n", switch_name[i]);
191                         else
192                                 printf("unknown %02x\n", mask);
193                 }
194         }
195
196         return 0;
197 }
198
199 static const char *const event_name[] = {
200         "lid_closed",
201         "lid_open",
202         "power_button",
203         "ac_connected",
204         "ac_disconnected",
205         "battery_low",
206         "battery_critical",
207         "battery",
208         "thermal_threshold",
209         "device",
210         "thermal",
211         "usb_charger",
212         "key_pressed",
213         "interface_ready",
214         "keyboard_recovery",
215         "thermal_shutdown",
216         "battery_shutdown",
217         "throttle_start",
218         "throttle_stop",
219         "hang_detect",
220         "hang_reboot",
221         "pd_mcu",
222         "battery_status",
223         "panic",
224         "keyboard_fastboot",
225         "rtc",
226         "mkbp",
227         "usb_mux",
228         "mode_change",
229         "keyboard_recovery_hw_reinit",
230         "extended",
231         "invalid",
232 };
233
234 static int do_show_events(struct udevice *dev)
235 {
236         u32 events;
237         int ret;
238         uint i;
239
240         ret = cros_ec_get_host_events(dev, &events);
241         if (ret)
242                 return ret;
243         printf("%08x\n", events);
244         for (i = 0; i < ARRAY_SIZE(event_name); i++) {
245                 enum host_event_code code = i + 1;
246                 u64 mask = EC_HOST_EVENT_MASK(code);
247
248                 if (events & mask) {
249                         if (event_name[i])
250                                 printf("%s\n", event_name[i]);
251                         else
252                                 printf("unknown code %#x\n", code);
253                 }
254         }
255
256         return 0;
257 }
258
259 static int do_cros_ec(struct cmd_tbl *cmdtp, int flag, int argc,
260                       char *const argv[])
261 {
262         struct udevice *dev;
263         const char *cmd;
264         int ret = 0;
265
266         if (argc < 2)
267                 return CMD_RET_USAGE;
268
269         cmd = argv[1];
270         if (0 == strcmp("init", cmd)) {
271                 /* Remove any existing device */
272                 ret = uclass_find_device(UCLASS_CROS_EC, 0, &dev);
273                 if (!ret)
274                         device_remove(dev, DM_REMOVE_NORMAL);
275                 ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
276                 if (ret) {
277                         printf("Could not init cros_ec device (err %d)\n", ret);
278                         return 1;
279                 }
280                 return 0;
281         }
282
283         ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
284         if (ret) {
285                 printf("Cannot get cros-ec device (err=%d)\n", ret);
286                 return 1;
287         }
288         if (0 == strcmp("id", cmd)) {
289                 char id[MSG_BYTES];
290
291                 if (cros_ec_read_id(dev, id, sizeof(id))) {
292                         debug("%s: Could not read KBC ID\n", __func__);
293                         return 1;
294                 }
295                 printf("%s\n", id);
296         } else if (0 == strcmp("info", cmd)) {
297                 struct ec_response_mkbp_info info;
298
299                 if (cros_ec_info(dev, &info)) {
300                         debug("%s: Could not read KBC info\n", __func__);
301                         return 1;
302                 }
303                 printf("rows     = %u\n", info.rows);
304                 printf("cols     = %u\n", info.cols);
305         } else if (!strcmp("features", cmd)) {
306                 ret = do_show_features(dev);
307
308                 if (ret)
309                         printf("Error: %d\n", ret);
310         } else if (!strcmp("switches", cmd)) {
311                 ret = do_show_switches(dev);
312
313                 if (ret)
314                         printf("Error: %d\n", ret);
315         } else if (0 == strcmp("curimage", cmd)) {
316                 enum ec_current_image image;
317
318                 if (cros_ec_read_current_image(dev, &image)) {
319                         debug("%s: Could not read KBC image\n", __func__);
320                         return 1;
321                 }
322                 printf("%d\n", image);
323         } else if (0 == strcmp("hash", cmd)) {
324                 struct ec_response_vboot_hash hash;
325                 int i;
326
327                 if (cros_ec_read_hash(dev, EC_VBOOT_HASH_OFFSET_ACTIVE, &hash)) {
328                         debug("%s: Could not read KBC hash\n", __func__);
329                         return 1;
330                 }
331
332                 if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
333                         printf("type:    SHA-256\n");
334                 else
335                         printf("type:    %d\n", hash.hash_type);
336
337                 printf("offset:  0x%08x\n", hash.offset);
338                 printf("size:    0x%08x\n", hash.size);
339
340                 printf("digest:  ");
341                 for (i = 0; i < hash.digest_size; i++)
342                         printf("%02x", hash.hash_digest[i]);
343                 printf("\n");
344         } else if (0 == strcmp("reboot", cmd)) {
345                 int region;
346                 enum ec_reboot_cmd cmd;
347
348                 if (argc >= 3 && !strcmp(argv[2], "cold")) {
349                         cmd = EC_REBOOT_COLD;
350                 } else {
351                         region = cros_ec_decode_region(argc - 2, argv + 2);
352                         if (region == EC_FLASH_REGION_RO)
353                                 cmd = EC_REBOOT_JUMP_RO;
354                         else if (region == EC_FLASH_REGION_ACTIVE)
355                                 cmd = EC_REBOOT_JUMP_RW;
356                         else
357                                 return CMD_RET_USAGE;
358                 }
359
360                 if (cros_ec_reboot(dev, cmd, 0)) {
361                         debug("%s: Could not reboot KBC\n", __func__);
362                         return 1;
363                 }
364         } else if (0 == strcmp("events", cmd)) {
365                 ret = do_show_events(dev);
366
367                 if (ret)
368                         printf("Error: %d\n", ret);
369         } else if (0 == strcmp("clrevents", cmd)) {
370                 uint32_t events = 0x7fffffff;
371
372                 if (argc >= 3)
373                         events = simple_strtol(argv[2], NULL, 0);
374
375                 if (cros_ec_clear_host_events(dev, events)) {
376                         debug("%s: Could not clear host events\n", __func__);
377                         return 1;
378                 }
379         } else if (0 == strcmp("read", cmd)) {
380                 ret = do_read_write(dev, 0, argc, argv);
381                 if (ret > 0)
382                         return CMD_RET_USAGE;
383         } else if (0 == strcmp("write", cmd)) {
384                 ret = do_read_write(dev, 1, argc, argv);
385                 if (ret > 0)
386                         return CMD_RET_USAGE;
387         } else if (0 == strcmp("erase", cmd)) {
388                 int region = cros_ec_decode_region(argc - 2, argv + 2);
389                 uint32_t offset, size;
390
391                 if (region == -1)
392                         return CMD_RET_USAGE;
393                 if (cros_ec_flash_offset(dev, region, &offset, &size)) {
394                         debug("%s: Could not read region info\n", __func__);
395                         ret = -1;
396                 } else {
397                         ret = cros_ec_flash_erase(dev, offset, size);
398                         if (ret) {
399                                 debug("%s: Could not erase region\n",
400                                       __func__);
401                         }
402                 }
403         } else if (0 == strcmp("regioninfo", cmd)) {
404                 int region = cros_ec_decode_region(argc - 2, argv + 2);
405                 uint32_t offset, size;
406
407                 if (region == -1)
408                         return CMD_RET_USAGE;
409                 ret = cros_ec_flash_offset(dev, region, &offset, &size);
410                 if (ret) {
411                         debug("%s: Could not read region info\n", __func__);
412                 } else {
413                         printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
414                                         "RO" : "RW");
415                         printf("Offset: %x\n", offset);
416                         printf("Size:   %x\n", size);
417                 }
418         } else if (0 == strcmp("flashinfo", cmd)) {
419                 struct ec_response_flash_info p;
420
421                 ret = cros_ec_read_flashinfo(dev, &p);
422                 if (!ret) {
423                         printf("Flash size:         %u\n", p.flash_size);
424                         printf("Write block size:   %u\n", p.write_block_size);
425                         printf("Erase block size:   %u\n", p.erase_block_size);
426                 }
427         } else if (0 == strcmp("vbnvcontext", cmd)) {
428                 uint8_t block[EC_VBNV_BLOCK_SIZE];
429                 char buf[3];
430                 int i, len;
431                 unsigned long result;
432
433                 if (argc <= 2) {
434                         ret = cros_ec_read_nvdata(dev, block,
435                                                   EC_VBNV_BLOCK_SIZE);
436                         if (!ret) {
437                                 printf("vbnv_block: ");
438                                 for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
439                                         printf("%02x", block[i]);
440                                 putc('\n');
441                         }
442                 } else {
443                         /*
444                          * TODO(clchiou): Move this to a utility function as
445                          * cmd_spi might want to call it.
446                          */
447                         memset(block, 0, EC_VBNV_BLOCK_SIZE);
448                         len = strlen(argv[2]);
449                         buf[2] = '\0';
450                         for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
451                                 if (i * 2 >= len)
452                                         break;
453                                 buf[0] = argv[2][i * 2];
454                                 if (i * 2 + 1 >= len)
455                                         buf[1] = '0';
456                                 else
457                                         buf[1] = argv[2][i * 2 + 1];
458                                 strict_strtoul(buf, 16, &result);
459                                 block[i] = result;
460                         }
461                         ret = cros_ec_write_nvdata(dev, block,
462                                                    EC_VBNV_BLOCK_SIZE);
463                 }
464                 if (ret) {
465                         debug("%s: Could not %s VbNvContext\n", __func__,
466                               argc <= 2 ?  "read" : "write");
467                 }
468         } else if (0 == strcmp("test", cmd)) {
469                 int result = cros_ec_test(dev);
470
471                 if (result)
472                         printf("Test failed with error %d\n", result);
473                 else
474                         puts("Test passed\n");
475         } else if (0 == strcmp("version", cmd)) {
476                 struct ec_response_get_version *p;
477                 char *build_string;
478
479                 ret = cros_ec_read_version(dev, &p);
480                 if (!ret) {
481                         /* Print versions */
482                         printf("RO version:    %1.*s\n",
483                                (int)sizeof(p->version_string_ro),
484                                p->version_string_ro);
485                         printf("RW version:    %1.*s\n",
486                                (int)sizeof(p->version_string_rw),
487                                p->version_string_rw);
488                         printf("Firmware copy: %s\n",
489                                (p->current_image <
490                                ARRAY_SIZE(ec_current_image_name) ?
491                                ec_current_image_name[p->current_image] :
492                                "?"));
493                         ret = cros_ec_read_build_info(dev, &build_string);
494                         if (!ret)
495                                 printf("Build info:    %s\n", build_string);
496                 }
497         } else if (0 == strcmp("ldo", cmd)) {
498                 uint8_t index, state;
499                 char *endp;
500
501                 if (argc < 3)
502                         return CMD_RET_USAGE;
503                 index = dectoul(argv[2], &endp);
504                 if (*argv[2] == 0 || *endp != 0)
505                         return CMD_RET_USAGE;
506                 if (argc > 3) {
507                         state = dectoul(argv[3], &endp);
508                         if (*argv[3] == 0 || *endp != 0)
509                                 return CMD_RET_USAGE;
510                         ret = cros_ec_set_ldo(dev, index, state);
511                 } else {
512                         ret = cros_ec_get_ldo(dev, index, &state);
513                         if (!ret) {
514                                 printf("LDO%d: %s\n", index,
515                                        state == EC_LDO_STATE_ON ?
516                                        "on" : "off");
517                         }
518                 }
519
520                 if (ret) {
521                         debug("%s: Could not access LDO%d\n", __func__, index);
522                         return ret;
523                 }
524         } else if (!strcmp("sku", cmd)) {
525                 ret = cros_ec_get_sku_id(dev);
526
527                 if (ret >= 0) {
528                         printf("%d\n", ret);
529                         ret = 0;
530                 } else {
531                         printf("Error: %d\n", ret);
532                 }
533         } else {
534                 return CMD_RET_USAGE;
535         }
536
537         if (ret < 0) {
538                 printf("Error: CROS-EC command failed (error %d)\n", ret);
539                 ret = 1;
540         }
541
542         return ret;
543 }
544
545 U_BOOT_CMD(
546         crosec, 6,      1,      do_cros_ec,
547         "CROS-EC utility command",
548         "init                Re-init CROS-EC (done on startup automatically)\n"
549         "crosec id                  Read CROS-EC ID\n"
550         "crosec info                Read CROS-EC info\n"
551         "crosec features            Read CROS-EC features\n"
552         "crosec switches            Read CROS-EC switches\n"
553         "crosec curimage            Read CROS-EC current image\n"
554         "crosec hash                Read CROS-EC hash\n"
555         "crosec reboot [rw | ro | cold]  Reboot CROS-EC\n"
556         "crosec events              Read CROS-EC host events\n"
557         "crosec eventsb             Read CROS-EC host events_b\n"
558         "crosec clrevents [mask]    Clear CROS-EC host events\n"
559         "crosec regioninfo <ro|rw>  Read image info\n"
560         "crosec flashinfo           Read flash info\n"
561         "crosec erase <ro|rw>       Erase EC image\n"
562         "crosec read <ro|rw> <addr> [<size>]   Read EC image\n"
563         "crosec write <ro|rw> <addr> [<size>]  Write EC image\n"
564         "crosec vbnvcontext [hexstring]        Read [write] VbNvContext from EC\n"
565         "crosec ldo <idx> [<state>] Switch/Read LDO state\n"
566         "crosec sku                 Read board SKU ID\n"
567         "crosec test                run tests on cros_ec\n"
568         "crosec version             Read CROS-EC version"
569 );