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