Skip to content
Snippets Groups Projects
cros_ec.c 33.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • #ifdef CONFIG_CMD_CROS_EC
    
    
    Hung-ying Tyan's avatar
    Hung-ying Tyan committed
    /**
     * Perform a flash read or write command
     *
     * @param dev		CROS-EC device to read/write
     * @param is_write	1 do to a write, 0 to do a read
     * @param argc		Number of arguments
     * @param argv		Arguments (2 is region, 3 is address)
     * @return 0 for ok, 1 for a usage error or -ve for ec command error
     *	(negative EC_RES_...)
     */
    static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc,
    			 char * const argv[])
    {
    	uint32_t offset, size = -1U, region_size;
    	unsigned long addr;
    	char *endp;
    	int region;
    	int ret;
    
    	region = cros_ec_decode_region(argc - 2, argv + 2);
    	if (region == -1)
    		return 1;
    	if (argc < 4)
    		return 1;
    	addr = simple_strtoul(argv[3], &endp, 16);
    	if (*argv[3] == 0 || *endp != 0)
    		return 1;
    	if (argc > 4) {
    		size = simple_strtoul(argv[4], &endp, 16);
    		if (*argv[4] == 0 || *endp != 0)
    			return 1;
    	}
    
    	ret = cros_ec_flash_offset(dev, region, &offset, &region_size);
    	if (ret) {
    		debug("%s: Could not read region info\n", __func__);
    		return ret;
    	}
    	if (size == -1U)
    		size = region_size;
    
    	ret = is_write ?
    		cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) :
    		cros_ec_flash_read(dev, (uint8_t *)addr, offset, size);
    	if (ret) {
    		debug("%s: Could not %s region\n", __func__,
    		      is_write ? "write" : "read");
    		return ret;
    	}
    
    	return 0;
    }
    
    static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
    	struct cros_ec_dev *dev = last_dev;
    	const char *cmd;
    	int ret = 0;
    
    	if (argc < 2)
    		return CMD_RET_USAGE;
    
    	cmd = argv[1];
    	if (0 == strcmp("init", cmd)) {
    		ret = cros_ec_init(gd->fdt_blob, &dev);
    		if (ret) {
    			printf("Could not init cros_ec device (err %d)\n", ret);
    			return 1;
    		}
    		return 0;
    	}
    
    	/* Just use the last allocated device; there should be only one */
    	if (!last_dev) {
    		printf("No CROS-EC device available\n");
    		return 1;
    	}
    	if (0 == strcmp("id", cmd)) {
    		char id[MSG_BYTES];
    
    		if (cros_ec_read_id(dev, id, sizeof(id))) {
    			debug("%s: Could not read KBC ID\n", __func__);
    			return 1;
    		}
    		printf("%s\n", id);
    	} else if (0 == strcmp("info", cmd)) {
    
    		struct ec_response_mkbp_info info;
    
    Hung-ying Tyan's avatar
    Hung-ying Tyan committed
    
    		if (cros_ec_info(dev, &info)) {
    			debug("%s: Could not read KBC info\n", __func__);
    			return 1;
    		}
    		printf("rows     = %u\n", info.rows);
    		printf("cols     = %u\n", info.cols);
    		printf("switches = %#x\n", info.switches);
    	} else if (0 == strcmp("curimage", cmd)) {
    		enum ec_current_image image;
    
    		if (cros_ec_read_current_image(dev, &image)) {
    			debug("%s: Could not read KBC image\n", __func__);
    			return 1;
    		}
    		printf("%d\n", image);
    	} else if (0 == strcmp("hash", cmd)) {
    		struct ec_response_vboot_hash hash;
    		int i;
    
    		if (cros_ec_read_hash(dev, &hash)) {
    			debug("%s: Could not read KBC hash\n", __func__);
    			return 1;
    		}
    
    		if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256)
    			printf("type:    SHA-256\n");
    		else
    			printf("type:    %d\n", hash.hash_type);
    
    		printf("offset:  0x%08x\n", hash.offset);
    		printf("size:    0x%08x\n", hash.size);
    
    		printf("digest:  ");
    		for (i = 0; i < hash.digest_size; i++)
    			printf("%02x", hash.hash_digest[i]);
    		printf("\n");
    	} else if (0 == strcmp("reboot", cmd)) {
    		int region;
    		enum ec_reboot_cmd cmd;
    
    		if (argc >= 3 && !strcmp(argv[2], "cold"))
    			cmd = EC_REBOOT_COLD;
    		else {
    			region = cros_ec_decode_region(argc - 2, argv + 2);
    			if (region == EC_FLASH_REGION_RO)
    				cmd = EC_REBOOT_JUMP_RO;
    			else if (region == EC_FLASH_REGION_RW)
    				cmd = EC_REBOOT_JUMP_RW;
    			else
    				return CMD_RET_USAGE;
    		}
    
    		if (cros_ec_reboot(dev, cmd, 0)) {
    			debug("%s: Could not reboot KBC\n", __func__);
    			return 1;
    		}
    	} else if (0 == strcmp("events", cmd)) {
    		uint32_t events;
    
    		if (cros_ec_get_host_events(dev, &events)) {
    			debug("%s: Could not read host events\n", __func__);
    			return 1;
    		}
    		printf("0x%08x\n", events);
    	} else if (0 == strcmp("clrevents", cmd)) {
    		uint32_t events = 0x7fffffff;
    
    		if (argc >= 3)
    			events = simple_strtol(argv[2], NULL, 0);
    
    		if (cros_ec_clear_host_events(dev, events)) {
    			debug("%s: Could not clear host events\n", __func__);
    			return 1;
    		}
    	} else if (0 == strcmp("read", cmd)) {
    		ret = do_read_write(dev, 0, argc, argv);
    		if (ret > 0)
    			return CMD_RET_USAGE;
    	} else if (0 == strcmp("write", cmd)) {
    		ret = do_read_write(dev, 1, argc, argv);
    		if (ret > 0)
    			return CMD_RET_USAGE;
    	} else if (0 == strcmp("erase", cmd)) {
    		int region = cros_ec_decode_region(argc - 2, argv + 2);
    		uint32_t offset, size;
    
    		if (region == -1)
    			return CMD_RET_USAGE;
    		if (cros_ec_flash_offset(dev, region, &offset, &size)) {
    			debug("%s: Could not read region info\n", __func__);
    			ret = -1;
    		} else {
    			ret = cros_ec_flash_erase(dev, offset, size);
    			if (ret) {
    				debug("%s: Could not erase region\n",
    				      __func__);
    			}
    		}
    	} else if (0 == strcmp("regioninfo", cmd)) {
    		int region = cros_ec_decode_region(argc - 2, argv + 2);
    		uint32_t offset, size;
    
    		if (region == -1)
    			return CMD_RET_USAGE;
    		ret = cros_ec_flash_offset(dev, region, &offset, &size);
    		if (ret) {
    			debug("%s: Could not read region info\n", __func__);
    		} else {
    			printf("Region: %s\n", region == EC_FLASH_REGION_RO ?
    					"RO" : "RW");
    			printf("Offset: %x\n", offset);
    			printf("Size:   %x\n", size);
    		}
    	} else if (0 == strcmp("vbnvcontext", cmd)) {
    		uint8_t block[EC_VBNV_BLOCK_SIZE];
    		char buf[3];
    		int i, len;
    		unsigned long result;
    
    		if (argc <= 2) {
    			ret = cros_ec_read_vbnvcontext(dev, block);
    			if (!ret) {
    				printf("vbnv_block: ");
    				for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
    					printf("%02x", block[i]);
    				putc('\n');
    			}
    		} else {
    			/*
    			 * TODO(clchiou): Move this to a utility function as
    			 * cmd_spi might want to call it.
    			 */
    			memset(block, 0, EC_VBNV_BLOCK_SIZE);
    			len = strlen(argv[2]);
    			buf[2] = '\0';
    			for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) {
    				if (i * 2 >= len)
    					break;
    				buf[0] = argv[2][i * 2];
    				if (i * 2 + 1 >= len)
    					buf[1] = '0';
    				else
    					buf[1] = argv[2][i * 2 + 1];
    				strict_strtoul(buf, 16, &result);
    				block[i] = result;
    			}
    			ret = cros_ec_write_vbnvcontext(dev, block);
    		}
    		if (ret) {
    			debug("%s: Could not %s VbNvContext\n", __func__,
    					argc <= 2 ?  "read" : "write");
    		}
    	} else if (0 == strcmp("test", cmd)) {
    		int result = cros_ec_test(dev);
    
    		if (result)
    			printf("Test failed with error %d\n", result);
    		else
    			puts("Test passed\n");
    	} else if (0 == strcmp("version", cmd)) {
    		struct ec_response_get_version *p;
    		char *build_string;
    
    		ret = cros_ec_read_version(dev, &p);
    		if (!ret) {
    			/* Print versions */
    			printf("RO version:    %1.*s\n",
    			       sizeof(p->version_string_ro),
    			       p->version_string_ro);
    			printf("RW version:    %1.*s\n",
    			       sizeof(p->version_string_rw),
    			       p->version_string_rw);
    			printf("Firmware copy: %s\n",
    				(p->current_image <
    					ARRAY_SIZE(ec_current_image_name) ?
    				ec_current_image_name[p->current_image] :
    				"?"));
    			ret = cros_ec_read_build_info(dev, &build_string);
    			if (!ret)
    				printf("Build info:    %s\n", build_string);
    		}
    	} else if (0 == strcmp("ldo", cmd)) {
    		uint8_t index, state;
    		char *endp;
    
    		if (argc < 3)
    			return CMD_RET_USAGE;
    		index = simple_strtoul(argv[2], &endp, 10);
    		if (*argv[2] == 0 || *endp != 0)
    			return CMD_RET_USAGE;
    		if (argc > 3) {
    			state = simple_strtoul(argv[3], &endp, 10);
    			if (*argv[3] == 0 || *endp != 0)
    				return CMD_RET_USAGE;
    			ret = cros_ec_set_ldo(dev, index, state);
    		} else {
    			ret = cros_ec_get_ldo(dev, index, &state);
    			if (!ret) {
    				printf("LDO%d: %s\n", index,
    					state == EC_LDO_STATE_ON ?
    					"on" : "off");
    			}
    		}
    
    		if (ret) {
    			debug("%s: Could not access LDO%d\n", __func__, index);
    			return ret;
    		}
    	} else {
    		return CMD_RET_USAGE;
    	}
    
    	if (ret < 0) {
    		printf("Error: CROS-EC command failed (error %d)\n", ret);
    		ret = 1;
    	}
    
    	return ret;
    }
    
    U_BOOT_CMD(
    	crosec,	5,	1,	do_cros_ec,
    	"CROS-EC utility command",
    	"init                Re-init CROS-EC (done on startup automatically)\n"
    	"crosec id                  Read CROS-EC ID\n"
    	"crosec info                Read CROS-EC info\n"
    	"crosec curimage            Read CROS-EC current image\n"
    	"crosec hash                Read CROS-EC hash\n"
    	"crosec reboot [rw | ro | cold]  Reboot CROS-EC\n"
    	"crosec events              Read CROS-EC host events\n"
    	"crosec clrevents [mask]    Clear CROS-EC host events\n"
    	"crosec regioninfo <ro|rw>  Read image info\n"
    	"crosec erase <ro|rw>       Erase EC image\n"
    	"crosec read <ro|rw> <addr> [<size>]   Read EC image\n"
    	"crosec write <ro|rw> <addr> [<size>]  Write EC image\n"
    	"crosec vbnvcontext [hexstring]        Read [write] VbNvContext from EC\n"
    	"crosec ldo <idx> [<state>] Switch/Read LDO state\n"
    	"crosec test                run tests on cros_ec\n"
    	"crosec version             Read CROS-EC version"
    );
    #endif