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 */ 18 static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; 19 20 DECLARE_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 */ 29 static 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 */ 55 static 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, ®ion_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 98 static 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, DM_REMOVE_NORMAL); 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 368 U_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