Пример #1
0
/* Can't do a "real" reset before the PMIC is initialized in QcLib (romstage),
   but this works well enough for our purposes. */
void do_board_reset(void)
{
	google_chromeec_reboot(0, EC_REBOOT_COLD, 0);
}
Пример #2
0
/* Get charger power info in Watts.  Also returns type of charger */
int google_chromeec_get_usb_pd_power_info(enum usb_chg_type *type,
					  u32 *max_watts)
{
	struct ec_params_usb_pd_power_info req = {
		.port = PD_POWER_CHARGING_PORT,
	};
	struct ec_response_usb_pd_power_info rsp;
	struct chromeec_command cmd = {
		.cmd_code = EC_CMD_USB_PD_POWER_INFO,
		.cmd_version = 0,
		.cmd_data_in = &req,
		.cmd_size_in = sizeof(req),
		.cmd_data_out = &rsp,
		.cmd_size_out = sizeof(rsp),
		.cmd_dev_index = 0,
	};
	struct usb_chg_measures m;
	int rv = google_chromeec_command(&cmd);
	if (rv != 0)
		return rv;
	/* values are given in milliAmps and milliVolts */
	*type = rsp.type;
	m = rsp.meas;
	*max_watts = (m.current_max * m.voltage_max) / 1000000;

	return 0;
}

int google_chromeec_override_dedicated_charger_limit(u16 current_lim,
						     u16 voltage_lim)
{
	struct ec_params_dedicated_charger_limit p = {
		.current_lim = current_lim,
		.voltage_lim = voltage_lim,
	};
	struct chromeec_command cmd = {
		.cmd_code = EC_CMD_OVERRIDE_DEDICATED_CHARGER_LIMIT,
		.cmd_version = 0,
		.cmd_data_in = &p,
		.cmd_size_in = sizeof(p),
		.cmd_data_out = NULL,
		.cmd_size_out = 0,
		.cmd_dev_index = 0,
	};

	return google_chromeec_command(&cmd);
}

int google_chromeec_set_usb_pd_role(u8 port, enum usb_pd_control_role role)
{
	struct ec_params_usb_pd_control req = {
		.port = port,
		.role = role,
		.mux = USB_PD_CTRL_MUX_NO_CHANGE,
		.swap = USB_PD_CTRL_SWAP_NONE,
	};
	struct ec_response_usb_pd_control rsp;
	struct chromeec_command cmd = {
		.cmd_code = EC_CMD_USB_PD_CONTROL,
		.cmd_version = 0,
		.cmd_data_in = &req,
		.cmd_size_in = sizeof(req),
		.cmd_data_out = &rsp,
		.cmd_size_out = sizeof(rsp),
		.cmd_dev_index = 0,
	};

	return google_chromeec_command(&cmd);
}

static int google_chromeec_hello(void)
{
	struct chromeec_command cec_cmd;
	struct ec_params_hello cmd_hello;
	struct ec_response_hello rsp_hello;
	cmd_hello.in_data = 0x10203040;
	cec_cmd.cmd_code = EC_CMD_HELLO;
	cec_cmd.cmd_version = 0;
	cec_cmd.cmd_data_in = &cmd_hello.in_data;
	cec_cmd.cmd_data_out = &rsp_hello.out_data;
	cec_cmd.cmd_size_in = sizeof(cmd_hello.in_data);
	cec_cmd.cmd_size_out = sizeof(rsp_hello.out_data);
	cec_cmd.cmd_dev_index = 0;
	google_chromeec_command(&cec_cmd);
	printk(BIOS_DEBUG, "Google Chrome EC: Hello got back %x status (%x)\n",
	       rsp_hello.out_data, cec_cmd.cmd_code);
	return cec_cmd.cmd_code;
}

/* Cached EC image type (ro or rw). */
static int ec_image_type = EC_IMAGE_UNKNOWN;

static void google_chromeec_get_version(void)
{
	struct chromeec_command cec_cmd;
	struct ec_response_get_version cec_resp;
	cec_cmd.cmd_code = EC_CMD_GET_VERSION;
	cec_cmd.cmd_version = 0;
	cec_cmd.cmd_data_in = 0;
	cec_cmd.cmd_data_out = &cec_resp;
	cec_cmd.cmd_size_in = 0;
	cec_cmd.cmd_size_out = sizeof(cec_resp);
	cec_cmd.cmd_dev_index = 0;
	google_chromeec_command(&cec_cmd);

	if (cec_cmd.cmd_code) {
		printk(BIOS_DEBUG,
		       "Google Chrome EC: version command failed!\n");
	} else {
		printk(BIOS_DEBUG, "Google Chrome EC: version:\n");
		printk(BIOS_DEBUG, "    ro: %s\n", cec_resp.version_string_ro);
		printk(BIOS_DEBUG, "    rw: %s\n", cec_resp.version_string_rw);
		printk(BIOS_DEBUG, "  running image: %d\n",
		       cec_resp.current_image);
		ec_image_type = cec_resp.current_image;
	}
}

void google_chromeec_init(void)
{
	printk(BIOS_DEBUG, "Google Chrome EC: Initializing\n");

	google_chromeec_hello();

	/* Get version to check which EC image is active */
	google_chromeec_get_version();

	/* Check/update EC RW image if needed */
	if (google_chromeec_swsync()) {
		printk(BIOS_ERR, "ChromeEC: EC SW SYNC FAILED\n");
	} else if (ec_image_type != EC_IMAGE_RW) {
		/* EC RW image is up to date, switch to it if not already*/
		google_chromeec_reboot(0, EC_REBOOT_JUMP_RW, 0);
		mdelay(100);
		/* Use Hello cmd to "reset" EC now in RW mode */
		google_chromeec_hello();
		/* re-run version command & print */
		google_chromeec_get_version();
	}
}

int google_ec_running_ro(void)
{
	return (ec_image_type == EC_IMAGE_RO);
}

void google_chromeec_reboot_ro(void)
{
	/* Reboot the EC and make it come back in RO mode */
	printk(BIOS_DEBUG, "Rebooting with EC in RO mode:\n");
	post_code(0); /* clear current post code */
	google_chromeec_reboot(0, EC_REBOOT_COLD, 0);
	udelay(1000);
	board_reset();
	halt();
}

/* Timeout waiting for EC hash calculation completion */
static const int CROS_EC_HASH_TIMEOUT_MS = 2000;

/* Time to delay between polling status of EC hash calculation */
static const int CROS_EC_HASH_CHECK_DELAY_MS = 10;

int google_chromeec_swsync(void)
{
	static struct ec_response_vboot_hash resp;
	uint8_t *ec_hash;
	int ec_hash_size;
	uint8_t *ecrw_hash, *ecrw;
	int need_update = 0, i;
	size_t ecrw_size;

	/* skip if on S3 resume path */
	if (acpi_is_wakeup_s3())
		return 0;

	/* Get EC_RW hash from CBFS */
	ecrw_hash = cbfs_boot_map_with_leak("ecrw.hash", CBFS_TYPE_RAW, NULL);

	if (!ecrw_hash) {
		/* Assume no EC update file for this board */
		printk(BIOS_DEBUG, "ChromeEC SW Sync: no EC_RW update available\n");
		return 0;
	}

	/* Got an expected hash */
	printk(BIOS_DEBUG, "ChromeEC SW Sync: Expected hash: ");
	for (i = 0; i < SHA256_DIGEST_SIZE; i++)
		printk(BIOS_DEBUG, "%02x", ecrw_hash[i]);
	printk(BIOS_DEBUG, "\n");

	/* Get hash of current EC-RW */
	if (google_chromeec_read_hash(&resp)) {
		printk(BIOS_ERR, "Failed to read current EC_RW hash.\n");
		return -1;
	}
	ec_hash = resp.hash_digest;
	ec_hash_size = resp.digest_size;
	/* Check hash size */
	if (ec_hash_size != SHA256_DIGEST_SIZE) {
		printk(BIOS_ERR, "ChromeEC SW Sync: - "
			 "read_hash says size %d, not %d\n",
			 ec_hash_size, SHA256_DIGEST_SIZE);
		return -1;
	}

	/* We got a proper hash */
	printk(BIOS_DEBUG, "ChromeEC SW Sync: current EC_RW hash: ");
	for (i = 0; i < SHA256_DIGEST_SIZE; i++)
		printk(BIOS_DEBUG, "%02x", ec_hash[i]);
	printk(BIOS_DEBUG, "\n");

	/* compare hashes */
	need_update = SafeMemcmp(ec_hash, ecrw_hash, SHA256_DIGEST_SIZE);

	/* If in RW and need to update, return/reboot to RO */
	if (need_update && ec_image_type == EC_IMAGE_RW) {
		printk(BIOS_DEBUG, "ChromeEC SW Sync: EC_RW needs update but in RW; rebooting to RO\n");
		google_chromeec_reboot_ro();
		return -1;
	}

	/* Update EC if necessary */
	if (need_update) {
		printk(BIOS_DEBUG, "ChromeEC SW Sync: updating EC_RW...\n");

		/* Get ecrw image from CBFS */
		ecrw = cbfs_boot_map_with_leak("ecrw", CBFS_TYPE_RAW, &ecrw_size);
		if (!ecrw) {
			printk(BIOS_ERR, "ChromeEC SW Sync: no ecrw image found in CBFS; cannot update\n");
			return -1;
		}

		if (google_chromeec_flash_update_rw(ecrw, ecrw_size)) {
			printk(BIOS_ERR, "ChromeEC SW Sync: Failed to update EC_RW.\n");
			return -1;
		}

		/* Have EC recompute hash for new EC_RW block */
		if (google_chromeec_read_hash(&resp) ) {
			printk(BIOS_ERR, "ChromeEC SW Sync: Failed to read new EC_RW hash.\n");
			return -1;
		}

		/* Compare new EC_RW hash to value from CBFS */
		ec_hash = resp.hash_digest;
		if(SafeMemcmp(ec_hash, ecrw_hash, SHA256_DIGEST_SIZE)) {
			/* hash mismatch! */
			printk(BIOS_DEBUG, "ChromeEC SW Sync: Expected hash: ");
			for (i = 0; i < SHA256_DIGEST_SIZE; i++)
				printk(BIOS_DEBUG, "%02x", ecrw_hash[i]);
			printk(BIOS_DEBUG, "\n");
			printk(BIOS_DEBUG, "ChromeEC SW Sync: EC hash: ");
			for (i = 0; i < SHA256_DIGEST_SIZE; i++)
				printk(BIOS_DEBUG, "%02x", ec_hash[i]);
			printk(BIOS_DEBUG, "\n");
			return -1;
		}
		printk(BIOS_DEBUG, "ChromeEC SW Sync: EC_RW hashes match\n");
		printk(BIOS_DEBUG, "ChromeEC SW Sync: done\n");
	} else {
		printk(BIOS_DEBUG, "ChromeEC SW Sync: EC_RW is up to date\n");
	}

	return 0;
}

int google_chromeec_read_hash(struct ec_response_vboot_hash *hash)
{
	struct chromeec_command cec_cmd;
	struct ec_params_vboot_hash p;
	int recalc_requested = 0;
	uint64_t start = timer_us(0);

	do {
		/* Get hash if available. */
		p.cmd = EC_VBOOT_HASH_GET;
		cec_cmd.cmd_code = EC_CMD_VBOOT_HASH;
		cec_cmd.cmd_version = 0;
		cec_cmd.cmd_data_in = &p;
		cec_cmd.cmd_data_out = hash;
		cec_cmd.cmd_size_in = sizeof(p);
		cec_cmd.cmd_size_out = sizeof(*hash);
		cec_cmd.cmd_dev_index = 0;
		printk(BIOS_DEBUG, "ChromeEC: Getting hash:\n");
		if (google_chromeec_command(&cec_cmd))
			return -1;

		switch (hash->status) {
		case EC_VBOOT_HASH_STATUS_NONE:
			/* We have no valid hash - let's request a recalc
			 * if we haven't done so yet. */
			if (recalc_requested != 0) {
				mdelay(CROS_EC_HASH_CHECK_DELAY_MS);
				break;
			}
			printk(BIOS_DEBUG, "ChromeEC: No valid hash (status=%d size=%d). "
			      "Compute one...\n", hash->status, hash->size);
			p.cmd = EC_VBOOT_HASH_RECALC;
			p.hash_type = EC_VBOOT_HASH_TYPE_SHA256;
			p.nonce_size = 0;
			p.offset = EC_VBOOT_HASH_OFFSET_RW;
			p.size = 0;
			cec_cmd.cmd_code = EC_CMD_VBOOT_HASH;
			cec_cmd.cmd_version = 0;
			cec_cmd.cmd_data_in = &p;
			cec_cmd.cmd_data_out = hash;
			cec_cmd.cmd_size_in = sizeof(p);
			cec_cmd.cmd_size_out = sizeof(*hash);
			cec_cmd.cmd_dev_index = 0;
			printk(BIOS_DEBUG, "ChromeEC: Starting EC hash:\n");
			if (google_chromeec_command(&cec_cmd))
				return -1;
			recalc_requested = 1;
			/* Command will wait to return until hash is done/ready */
			break;
		case EC_VBOOT_HASH_STATUS_BUSY:
			/* Hash is still calculating. */
			mdelay(CROS_EC_HASH_CHECK_DELAY_MS);
			break;
		case EC_VBOOT_HASH_STATUS_DONE:
		default:
			/* We have a valid hash. */
			break;
		}
	} while (hash->status != EC_VBOOT_HASH_STATUS_DONE &&
		 timer_us(start) < CROS_EC_HASH_TIMEOUT_MS * 1000);
	if (hash->status != EC_VBOOT_HASH_STATUS_DONE) {
		printk(BIOS_DEBUG, "ChromeEC: Hash status not done: %d\n", hash->status);
		return -1;
	}
	return 0;
}

int google_chromeec_flash_update_rw(const uint8_t *image, int image_size)
{
	uint32_t rw_offset, rw_size;
	int ret;

	/* get max size that can be written, offset to write */
	if (google_chromeec_flash_offset(EC_FLASH_REGION_RW, &rw_offset, &rw_size))
		return -1;
	if (image_size > rw_size)
		return -1;
	/*
	 * Erase the entire RW section, so that the EC doesn't see any garbage
	 * past the new image if it's smaller than the current image.
	 *
	 */
	ret = google_chromeec_flash_erase(rw_offset, rw_size);
	if (ret)
		return ret;
	/* Write the image */
	return(google_chromeec_flash_write(image, rw_offset, image_size));
}