/* 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); }
/* 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)); }