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