static int cros_ec_check_version(struct cros_ec_dev *dev) { struct ec_params_hello req; struct ec_response_hello *resp; struct dm_cros_ec_ops *ops; int ret; ops = dm_cros_ec_get_ops(dev->dev); if (ops->check_version) { ret = ops->check_version(dev->dev); if (ret) return ret; } /* * TODO([email protected]). * There is a strange oddity here with the EC. We could just ignore * the response, i.e. pass the last two parameters as NULL and 0. * In this case we won't read back very many bytes from the EC. * On the I2C bus the EC gets upset about this and will try to send * the bytes anyway. This means that we will have to wait for that * to complete before continuing with a new EC command. * * This problem is probably unique to the I2C bus. * * So for now, just read all the data anyway. */ /* Try sending a version 3 packet */ dev->protocol_version = 3; req.in_data = 0; if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), (uint8_t **)&resp, sizeof(*resp)) > 0) { return 0; } /* Try sending a version 2 packet */ dev->protocol_version = 2; if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), (uint8_t **)&resp, sizeof(*resp)) > 0) { return 0; } /* * Fail if we're still here, since the EC doesn't understand any * protcol version we speak. Version 1 interface without command * version is no longer supported, and we don't know about any new * protocol versions. */ dev->protocol_version = 0; printf("%s: ERROR: old EC interface not supported\n", __func__); return -1; }
int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, uint8_t flags) { struct ec_params_reboot_ec p; p.cmd = cmd; p.flags = flags; if (ec_command_inptr(dev, EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0) < 0) return -1; if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) { /* * EC reboot will take place immediately so delay to allow it * to complete. Note that some reboot types (EC_REBOOT_COLD) * will reboot the AP as well, in which case we won't actually * get to this point. */ /* * TODO([email protected]): Would be nice if we had a * better way to determine when the reboot is complete. Could * we poll a memory-mapped LPC value? */ udelay(50000); } return 0; }
int cros_ec_read_id(struct udevice *dev, char *id, int maxlen) { struct ec_response_get_version *r; int ret; ret = ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, (uint8_t **)&r, sizeof(*r)); if (ret != sizeof(*r)) { log_err("Got rc %d, expected %u\n", ret, (uint)sizeof(*r)); return -1; } if (maxlen > (int)sizeof(r->version_string_ro)) maxlen = sizeof(r->version_string_ro); switch (r->current_image) { case EC_IMAGE_RO: memcpy(id, r->version_string_ro, maxlen); break; case EC_IMAGE_RW: memcpy(id, r->version_string_rw, maxlen); break; default: log_err("Invalid EC image %d\n", r->current_image); return -1; } id[maxlen - 1] = '\0'; return 0; }
int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen) { struct ec_response_get_version *r; if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, (uint8_t **)&r, sizeof(*r)) != sizeof(*r)) return -1; if (maxlen > (int)sizeof(r->version_string_ro)) maxlen = sizeof(r->version_string_ro); switch (r->current_image) { case EC_IMAGE_RO: memcpy(id, r->version_string_ro, maxlen); break; case EC_IMAGE_RW: memcpy(id, r->version_string_rw, maxlen); break; default: return -1; } id[maxlen - 1] = '\0'; return 0; }
int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp) { if (ec_command_inptr(dev, EC_CMD_GET_BUILD_INFO, 0, NULL, 0, (uint8_t **)strp, EC_PROTO2_MAX_PARAM_SIZE) < 0) return -1; return 0; }
int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, uint32_t size) { struct ec_params_flash_erase p; p.offset = offset; p.size = size; return ec_command_inptr(dev, EC_CMD_FLASH_ERASE, 0, &p, sizeof(p), NULL, 0); }
int cros_ec_read_version(struct cros_ec_dev *dev, struct ec_response_get_version **versionp) { if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, (uint8_t **)versionp, sizeof(**versionp)) != sizeof(**versionp)) return -1; return 0; }
int cros_ec_read_current_image(struct cros_ec_dev *dev, enum ec_current_image *image) { struct ec_response_get_version *r; if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, (uint8_t **)&r, sizeof(*r)) != sizeof(*r)) return -1; *image = r->current_image; return 0; }
int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state) { struct ec_params_ldo_set params; params.index = index; params.state = state; if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, ¶ms, sizeof(params), NULL, 0)) return -1; return 0; }
/** * Write a single block to the flash * * Write a block of data to the EC flash. The size must not exceed the flash * write block size which you can obtain from cros_ec_flash_write_burst_size(). * * The offset starts at 0. You can obtain the region information from * cros_ec_flash_offset() to find out where to write for a particular region. * * Attempting to write to the region where the EC is currently running from * will result in an error. * * @param dev CROS-EC device * @param data Pointer to data buffer to write * @param offset Offset within flash to write to. * @param size Number of bytes to write * @return 0 if ok, -1 on error */ static int cros_ec_flash_write_block(struct cros_ec_dev *dev, const uint8_t *data, uint32_t offset, uint32_t size) { struct ec_params_flash_write p; p.offset = offset; p.size = size; assert(data && p.size <= EC_FLASH_WRITE_VER0_SIZE); memcpy(&p + 1, data, p.size); return ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0, &p, sizeof(p), NULL, 0) >= 0 ? 0 : -1; }
int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block) { struct ec_params_vbnvcontext p; int len; p.op = EC_VBNV_CONTEXT_OP_WRITE; memcpy(p.block, block, sizeof(p.block)); len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, &p, sizeof(p), NULL, 0); if (len < 0) return -1; return 0; }
int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state) { struct ec_params_ldo_get params; struct ec_response_ldo_get *resp; params.index = index; if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, ¶ms, sizeof(params), (uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp)) return -1; *state = resp->state; return 0; }
int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events) { struct ec_params_host_event_mask params; params.mask = events; /* * Use the B copy of the event flags, so it affects the data returned * by cros_ec_get_host_events(). */ if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_CLEAR_B, 0, ¶ms, sizeof(params), NULL, 0) < 0) return -1; return 0; }
int cros_ec_write_nvdata(struct udevice *dev, const uint8_t *block, int size) { struct ec_params_vbnvcontext p; int len; if (size != EC_VBNV_BLOCK_SIZE && size != EC_VBNV_BLOCK_SIZE_V2) return -EINVAL; p.op = EC_VBNV_CONTEXT_OP_WRITE; memcpy(p.block, block, size); len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, &p, sizeof(uint32_t) + size, NULL, 0); if (len < 0) return -1; return 0; }
int cros_ec_test(struct cros_ec_dev *dev) { struct ec_params_hello req; struct ec_response_hello *resp; req.in_data = 0x12345678; if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) { printf("ec_command_inptr() returned error\n"); return -1; } if (resp->out_data != req.in_data + 0x01020304) { printf("Received invalid handshake %x\n", resp->out_data); return -1; } return 0; }
int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr) { struct ec_response_host_event_mask *resp; /* * Use the B copy of the event flags, because the main copy is already * used by ACPI/SMI. */ if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, (uint8_t **)&resp, sizeof(*resp)) < (int)sizeof(*resp)) return -1; if (resp->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) return -1; *events_ptr = resp->mask; return 0; }
int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, uint32_t *offset, uint32_t *size) { struct ec_params_flash_region_info p; struct ec_response_flash_region_info *r; int ret; p.region = region; ret = ec_command_inptr(dev, EC_CMD_FLASH_REGION_INFO, EC_VER_FLASH_REGION_INFO, &p, sizeof(p), (uint8_t **)&r, sizeof(*r)); if (ret != sizeof(*r)) return -1; if (offset) *offset = r->offset; if (size) *size = r->size; return 0; }
/** * Write a single block to the flash * * Write a block of data to the EC flash. The size must not exceed the flash * write block size which you can obtain from cros_ec_flash_write_burst_size(). * * The offset starts at 0. You can obtain the region information from * cros_ec_flash_offset() to find out where to write for a particular region. * * Attempting to write to the region where the EC is currently running from * will result in an error. * * @param dev CROS-EC device * @param data Pointer to data buffer to write * @param offset Offset within flash to write to. * @param size Number of bytes to write * @return 0 if ok, -1 on error */ static int cros_ec_flash_write_block(struct udevice *dev, const uint8_t *data, uint32_t offset, uint32_t size) { struct ec_params_flash_write *p; int ret; p = malloc(sizeof(*p) + size); if (!p) return -ENOMEM; p->offset = offset; p->size = size; assert(data && p->size <= EC_FLASH_WRITE_VER0_SIZE); memcpy(p + 1, data, p->size); ret = ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0, p, sizeof(*p) + size, NULL, 0) >= 0 ? 0 : -1; free(p); return ret; }
/** * Send a command to the CROS-EC device and return the reply. * * The device's internal input/output buffers are used. * * @param dev CROS-EC device * @param cmd Command to send (EC_CMD_...) * @param cmd_version Version of command to send (EC_VER_...) * @param dout Output data (may be NULL If dout_len=0) * @param dout_len Size of output data in bytes * @param din Response data (may be NULL If din_len=0). * It not NULL, it is a place for ec_command() to copy the * data to. * @param din_len Maximum size of response in bytes * @return number of bytes in response, or -ve on error */ static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const void *dout, int dout_len, void *din, int din_len) { uint8_t *in_buffer; int len; assert((din_len == 0) || din); len = ec_command_inptr(dev, cmd, cmd_version, dout, dout_len, &in_buffer, din_len); if (len > 0) { /* * If we were asked to put it somewhere, do so, otherwise just * disregard the result. */ if (din && in_buffer) { assert(len <= din_len); memmove(din, in_buffer, len); } } return len; }
static int cros_ec_invalidate_hash(struct cros_ec_dev *dev) { struct ec_params_vboot_hash p; struct ec_response_vboot_hash *hash; /* We don't have an explict command for the EC to discard its current * hash value, so we'll just tell it to calculate one that we know is * wrong (we claim that hashing zero bytes is always invalid). */ p.cmd = EC_VBOOT_HASH_RECALC; p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; p.nonce_size = 0; p.offset = 0; p.size = 0; debug("%s:\n", __func__); if (ec_command_inptr(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), (uint8_t **)&hash, sizeof(*hash)) < 0) return -1; /* No need to wait for it to finish */ return 0; }