staticint cros_ec_map_error(uint32_t result)
{ int ret = 0;
if (result != EC_RES_SUCCESS) { if (result < ARRAY_SIZE(cros_ec_error_map) && cros_ec_error_map[result])
ret = cros_ec_error_map[result]; else
ret = -EPROTO;
}
if (!xfer_fxn) { /* * This error can happen if a communication error happened and * the EC is trying to use protocol v2, on an underlying * communication mechanism that does not support v2.
*/
dev_err_once(ec_dev->dev, "missing EC transfer API, cannot send command\n"); return -EIO;
}
trace_cros_ec_request_start(msg);
ret = (*xfer_fxn)(ec_dev, msg);
trace_cros_ec_request_done(msg, ret);
/* Query the EC's status until it's no longer busy or we encounter an error. */ for (i = 0; i < EC_COMMAND_RETRIES; ++i) {
usleep_range(10000, 11000);
ret = cros_ec_xfer_command(ec_dev, msg); if (ret == -EAGAIN) continue; if (ret < 0) return ret;
*result = msg->result; if (msg->result != EC_RES_SUCCESS) return ret;
if (ret == 0) {
ret = -EPROTO; break;
}
if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) return ret;
}
if (i >= EC_COMMAND_RETRIES)
ret = -EAGAIN;
return ret;
}
staticint cros_ec_send_command(struct cros_ec_device *ec_dev, struct cros_ec_command *msg)
{ int ret = cros_ec_xfer_command(ec_dev, msg);
if (msg->result == EC_RES_IN_PROGRESS)
ret = cros_ec_wait_until_complete(ec_dev, &msg->result);
return ret;
}
/** * cros_ec_prepare_tx() - Prepare an outgoing message in the output buffer. * @ec_dev: Device to register. * @msg: Message to write. * * This is used by all ChromeOS EC drivers to prepare the outgoing message * according to different protocol versions. * * Return: number of prepared bytes on success or negative error code.
*/ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, struct cros_ec_command *msg)
{ if (ec_dev->proto_version > 2) return prepare_tx(ec_dev, msg);
/** * cros_ec_check_result() - Check ec_msg->result. * @ec_dev: EC device. * @msg: Message to check. * * This is used by ChromeOS EC drivers to check the ec_msg->result for * EC_RES_IN_PROGRESS and to warn about them. * * The function should not check for furthermore error codes. Otherwise, * it would break the ABI. * * Return: -EAGAIN if ec_msg->result == EC_RES_IN_PROGRESS. Otherwise, 0.
*/ int cros_ec_check_result(struct cros_ec_device *ec_dev, struct cros_ec_command *msg)
{ switch (msg->result) { case EC_RES_SUCCESS: return 0; case EC_RES_IN_PROGRESS:
dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
msg->command); return -EAGAIN; default:
dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
msg->command, msg->result); return 0;
}
}
EXPORT_SYMBOL(cros_ec_check_result);
/** * cros_ec_get_host_event_wake_mask * * Get the mask of host events that cause wake from suspend. * * @ec_dev: EC device to call * @mask: result when function returns 0. * * LOCKING: * the caller has ec_dev->lock mutex, or the caller knows there is * no other command in progress.
*/ staticint cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, uint32_t *mask)
{ struct cros_ec_command *msg; struct ec_response_host_event_mask *r; int ret, mapped;
msg = kzalloc(sizeof(*msg) + sizeof(*r), GFP_KERNEL); if (!msg) return -ENOMEM;
ret = cros_ec_send_command(ec_dev, msg); if (ret < 0) gotoexit;
mapped = cros_ec_map_error(msg->result); if (mapped) {
ret = mapped; gotoexit;
}
if (ret == 0) {
ret = -EPROTO; gotoexit;
}
r = (struct ec_response_host_event_mask *)msg->data;
*mask = r->mask;
ret = 0; exit:
kfree(msg); return ret;
}
int cros_ec_rwsig_continue(struct cros_ec_device *ec_dev)
{ struct cros_ec_command *msg; struct ec_params_rwsig_action *rwsig_action; int ret = 0; int error_count = 0;
ec_dev->proto_version = 3;
msg = kmalloc(sizeof(*msg) + sizeof(*rwsig_action), GFP_KERNEL); if (!msg) return -ENOMEM;
for (int i = 0; i < RWSIG_CONTINUE_RETRIES; i++) {
ret = cros_ec_send_command(ec_dev, msg);
if (ret < 0) { if (++error_count >= RWSIG_CONTINUE_MAX_ERRORS_IN_ROW) break;
} elseif (msg->result == EC_RES_INVALID_COMMAND) { /* * If EC_RES_INVALID_COMMAND is retured, it means RWSIG * is not supported or EC is already in RW, so there is * nothing left to do.
*/ break;
} elseif (msg->result != EC_RES_SUCCESS) { /* Unexpected command error. */
ret = cros_ec_map_error(msg->result); break;
} else { /* * The EC_CMD_RWSIG_ACTION succeed. Send the command * more times, to make sure EC is in RW. A following * command can timeout, because EC may need some time to * initialize after jump to RW.
*/
error_count = 0;
}
if (ret != -ETIMEDOUT)
usleep_range(90000, 100000);
}
dev_dbg(ec_dev->dev, "falling back to proto v2\n");
ret = 0; exit:
kfree(msg); return ret;
}
/** * cros_ec_get_host_command_version_mask * * Get the version mask of a given command. * * @ec_dev: EC device to call * @cmd: command to get the version of. * @mask: result when function returns 0. * * @return 0 on success, error code otherwise * * LOCKING: * the caller has ec_dev->lock mutex or the caller knows there is * no other command in progress.
*/ staticint cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, u16 cmd, u32 *mask)
{ struct ec_params_get_cmd_versions *pver; struct ec_response_get_cmd_versions *rver; struct cros_ec_command *msg; int ret, mapped;
/** * cros_ec_query_all() - Query the protocol version supported by the * ChromeOS EC. * @ec_dev: Device to register. * * Return: 0 on success or negative error code.
*/ int cros_ec_query_all(struct cros_ec_device *ec_dev)
{ struct device *dev = ec_dev->dev;
u32 ver_mask; int ret;
/* First try sending with proto v3. */ if (!cros_ec_get_proto_info(ec_dev, CROS_EC_DEV_EC_INDEX)) { /* Check for PD. */
cros_ec_get_proto_info(ec_dev, CROS_EC_DEV_PD_INDEX);
} else { /* Try querying with a v2 hello message. */
ret = cros_ec_get_proto_info_legacy(ec_dev); if (ret) { /* * It's possible for a test to occur too early when * the EC isn't listening. If this happens, we'll * test later when the first command is run.
*/
ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret); return ret;
}
}
ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); if (!ec_dev->din) {
ret = -ENOMEM; gotoexit;
}
ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); if (!ec_dev->dout) {
devm_kfree(dev, ec_dev->din);
ret = -ENOMEM; gotoexit;
}
/* Probe if MKBP event is supported */
ret = cros_ec_get_host_command_version_mask(ec_dev, EC_CMD_GET_NEXT_EVENT, &ver_mask); if (ret < 0 || ver_mask == 0) {
ec_dev->mkbp_event_supported = 0;
} else {
ec_dev->mkbp_event_supported = fls(ver_mask);
dev_dbg(ec_dev->dev, "MKBP support version %u\n", ec_dev->mkbp_event_supported - 1);
}
/* Probe if host sleep v1 is supported for S0ix failure detection. */
ret = cros_ec_get_host_command_version_mask(ec_dev, EC_CMD_HOST_SLEEP_EVENT, &ver_mask);
ec_dev->host_sleep_v1 = (ret == 0 && (ver_mask & EC_VER_MASK(1)));
/* Get host event wake mask. */
ret = cros_ec_get_host_event_wake_mask(ec_dev, &ec_dev->host_event_wake_mask); if (ret < 0) { /* * If the EC doesn't support EC_CMD_HOST_EVENT_GET_WAKE_MASK, * use a reasonable default. Note that we ignore various * battery, AC status, and power-state events, because (a) * those can be quite common (e.g., when sitting at full * charge, on AC) and (b) these are not actionable wake events; * if anything, we'd like to continue suspending (to save * power), not wake up.
*/
ec_dev->host_event_wake_mask = U32_MAX &
~(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED) |
EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED) |
EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW) |
EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL) |
EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY) |
EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS)); /* * Old ECs may not support this command. Complain about all * other errors.
*/ if (ret != -EOPNOTSUPP)
dev_err(ec_dev->dev, "failed to retrieve wake mask: %d\n", ret);
}
/** * cros_ec_cmd_xfer() - Send a command to the ChromeOS EC. * @ec_dev: EC device. * @msg: Message to write. * * Call this to send a command to the ChromeOS EC. This should be used instead * of calling the EC's cmd_xfer() callback directly. This function does not * convert EC command execution error codes to Linux error codes. Most * in-kernel users will want to use cros_ec_cmd_xfer_status() instead since * that function implements the conversion. * * Return: * >0 - EC command was executed successfully. The return value is the number * of bytes returned by the EC (excluding the header). * =0 - EC communication was successful. EC command execution results are * reported in msg->result. The result will be EC_RES_SUCCESS if the * command was executed successfully or report an EC command execution * error. * <0 - EC communication error. Return value is the Linux error code.
*/ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg)
{ int ret;
mutex_lock(&ec_dev->lock); if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
ret = cros_ec_query_all(ec_dev); if (ret) {
dev_err(ec_dev->dev, "EC version unknown and query failed; aborting command\n");
mutex_unlock(&ec_dev->lock); return ret;
}
}
if (msg->command < EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX)) { if (msg->outsize > ec_dev->max_request) {
dev_err(ec_dev->dev, "request of size %u is too big (max: %u)\n",
msg->outsize,
ec_dev->max_request);
mutex_unlock(&ec_dev->lock); return -EMSGSIZE;
}
} else { if (msg->outsize > ec_dev->max_passthru) {
dev_err(ec_dev->dev, "passthru rq of size %u is too big (max: %u)\n",
msg->outsize,
ec_dev->max_passthru);
mutex_unlock(&ec_dev->lock); return -EMSGSIZE;
}
}
ret = cros_ec_send_command(ec_dev, msg);
mutex_unlock(&ec_dev->lock);
return ret;
}
EXPORT_SYMBOL(cros_ec_cmd_xfer);
/** * cros_ec_cmd_xfer_status() - Send a command to the ChromeOS EC. * @ec_dev: EC device. * @msg: Message to write. * * Call this to send a command to the ChromeOS EC. This should be used instead of calling the EC's * cmd_xfer() callback directly. It returns success status only if both the command was transmitted * successfully and the EC replied with success status. * * Return: * >=0 - The number of bytes transferred. * <0 - Linux error code
*/ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, struct cros_ec_command *msg)
{ int ret, mapped;
ret = cros_ec_cmd_xfer(ec_dev, msg); if (ret < 0) return ret;
mapped = cros_ec_map_error(msg->result); if (mapped) {
dev_dbg(ec_dev->dev, "Command result (err: %d [%d])\n",
msg->result, mapped);
ret = mapped;
}
if (ec_dev->suspended) {
dev_dbg(ec_dev->dev, "Device suspended.\n"); return -EHOSTDOWN;
}
if (cmd_version == 0) {
size = sizeof(struct ec_response_get_next_event);
} elseif (cmd_version < 3) {
size = sizeof(struct ec_response_get_next_event_v1);
} else { /* * The max version we support is v3. So, we speak v3 even if the * EC says it supports v4+.
*/
cmd_version = 3;
size = sizeof(struct ec_response_get_next_event_v3);
}
/** * cros_ec_get_next_event() - Fetch next event from the ChromeOS EC. * @ec_dev: Device to fetch event from. * @wake_event: Pointer to a bool set to true upon return if the event might be * treated as a wake event. Ignored if null. * @has_more_events: Pointer to bool set to true if more than one event is * pending. * Some EC will set this flag to indicate cros_ec_get_next_event() * can be called multiple times in a row. * It is an optimization to prevent issuing a EC command for * nothing or wait for another interrupt from the EC to process * the next message. * Ignored if null. * * Return: negative error code on errors; 0 for no data; or else number of * bytes received (i.e., an event was retrieved successfully). Event types are * written out to @ec_dev->event_data.event_type on success.
*/ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event, bool *has_more_events)
{
u8 event_type;
u32 host_event; int ret;
u32 ver_mask;
/* * Default value for wake_event. * Wake up on keyboard event, wake up for spurious interrupt or link * error to the EC.
*/ if (wake_event)
*wake_event = true;
/* * Default value for has_more_events. * EC will raise another interrupt if AP does not process all events * anyway.
*/ if (has_more_events)
*has_more_events = false;
if (!ec_dev->mkbp_event_supported) return get_keyboard_state_event(ec_dev);
ret = get_next_event(ec_dev); /* * -ENOPROTOOPT is returned when EC returns EC_RES_INVALID_VERSION. * This can occur when EC based device (e.g. Fingerprint MCU) jumps to * the RO image which doesn't support newer version of the command. In * this case we will attempt to update maximum supported version of the * EC_CMD_GET_NEXT_EVENT.
*/ if (ret == -ENOPROTOOPT) {
dev_dbg(ec_dev->dev, "GET_NEXT_EVENT returned invalid version error.\n");
mutex_lock(&ec_dev->lock);
ret = cros_ec_get_host_command_version_mask(ec_dev,
EC_CMD_GET_NEXT_EVENT,
&ver_mask);
mutex_unlock(&ec_dev->lock); if (ret < 0 || ver_mask == 0) /* * Do not change the MKBP supported version if we can't * obtain supported version correctly. Please note that * calling EC_CMD_GET_NEXT_EVENT returned * EC_RES_INVALID_VERSION which means that the command * is present.
*/ return -ENOPROTOOPT;
ec_dev->mkbp_event_supported = fls(ver_mask);
dev_dbg(ec_dev->dev, "MKBP support version changed to %u\n",
ec_dev->mkbp_event_supported - 1);
/* Try to get next event with new MKBP support version set. */
ret = get_next_event(ec_dev);
}
if (ret <= 0) return ret;
if (has_more_events)
*has_more_events = ec_dev->event_data.event_type &
EC_MKBP_HAS_MORE_EVENTS;
ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK;
if (wake_event) {
event_type = ec_dev->event_data.event_type;
host_event = cros_ec_get_host_event(ec_dev);
/* * Sensor events need to be parsed by the sensor sub-device. * Defer them, and don't report the wakeup here.
*/ if (event_type == EC_MKBP_EVENT_SENSOR_FIFO) {
*wake_event = false;
} elseif (host_event) { /* rtc_update_irq() already handles wakeup events. */ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC))
*wake_event = false; /* Masked host-events should not count as wake events. */ if (!(host_event & ec_dev->host_event_wake_mask))
*wake_event = false;
}
}
/** * cros_ec_get_host_event() - Return a mask of event set by the ChromeOS EC. * @ec_dev: Device to fetch event from. * * When MKBP is supported, when the EC raises an interrupt, we collect the * events raised and call the functions in the ec notifier. This function * is a helper to know which events are raised. * * Return: 0 on error or non-zero bitmask of one or more EC_HOST_EVENT_*.
*/
u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
{
u32 host_event;
if (!ec_dev->mkbp_event_supported) return 0;
if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT) return 0;
/** * cros_ec_check_features() - Test for the presence of EC features * * @ec: EC device, does not have to be connected directly to the AP, * can be daisy chained through another device. * @feature: One of ec_feature_code bit. * * Call this function to test whether the ChromeOS EC supports a feature. * * Return: true if supported, false if not (or if an error was encountered).
*/ bool cros_ec_check_features(struct cros_ec_dev *ec, int feature)
{ struct ec_response_get_features *features = &ec->features; int ret;
if (features->flags[0] == -1U && features->flags[1] == -1U) { /* features bitmap not read yet */
ret = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_GET_FEATURES + ec->cmd_offset,
NULL, 0, features, sizeof(*features)); if (ret < 0) {
dev_warn(ec->dev, "cannot get EC features: %d\n", ret);
memset(features, 0, sizeof(*features));
}
dev_dbg(ec->dev, "EC features %08x %08x\n",
features->flags[0], features->flags[1]);
}
/** * cros_ec_get_sensor_count() - Return the number of MEMS sensors supported. * * @ec: EC device, does not have to be connected directly to the AP, * can be daisy chained through another device. * Return: < 0 in case of error.
*/ int cros_ec_get_sensor_count(struct cros_ec_dev *ec)
{ /* * Issue a command to get the number of sensor reported. * If not supported, check for legacy mode.
*/ int ret, sensor_count; struct ec_params_motion_sense *params; struct ec_response_motion_sense *resp; struct cros_ec_command *msg; struct cros_ec_device *ec_dev = ec->ec_dev;
u8 status;
/* * Check legacy mode: Let's find out if sensors are accessible * via LPC interface.
*/ if (sensor_count < 0 && ec->cmd_offset == 0 && ec_dev->cmd_readmem) {
ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS,
1, &status); if (ret >= 0 &&
(status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) { /* * We have 2 sensors, one in the lid, one in the base.
*/
sensor_count = 2;
} else { /* * EC uses LPC interface and no sensors are presented.
*/
sensor_count = 0;
}
} return sensor_count;
}
EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count);
/** * cros_ec_cmd - Send a command to the EC. * * @ec_dev: EC device * @version: EC command version * @command: EC command * @outdata: EC command output data * @outsize: Size of outdata * @indata: EC command input data * @insize: Size of indata * * Return: >= 0 on success, negative error number on failure.
*/ int cros_ec_cmd(struct cros_ec_device *ec_dev, unsignedint version, int command, constvoid *outdata,
size_t outsize, void *indata,
size_t insize)
{ struct cros_ec_command *msg; int ret;
/** * cros_ec_get_cmd_versions - Get supported version mask. * * @ec_dev: EC device * @cmd: Command to test * * Return: version mask on success, negative error number on failure.
*/ int cros_ec_get_cmd_versions(struct cros_ec_device *ec_dev, u16 cmd)
{ struct ec_params_get_cmd_versions req_v0; struct ec_params_get_cmd_versions_v1 req_v1; struct ec_response_get_cmd_versions resp; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.