platform/chrome: cros_ec: jump to RW before probing

There are EC devices, like FPMCU, that use RWSIG as a method of
authenticating RW section. After the authentication succeeds, EC device
waits some time before jumping to RW. EC can be probed before the jump,
which means there is a time window after jump to RW in which EC won't
respond, because it is not initialized. It can cause a communication
errors after probing.

To avoid such problems, send the RWSIG continue command first, which
skips waiting for the jump to RW. Send the command more times, to make
sure EC is ready in RW before the start of the actual probing process. If
a EC device doesn't support the RWSIG, it will respond with invalid
command error code and probing will continue as usual.

Signed-off-by: Dawid Niedzwiecki <dawidn@google.com>
Link: https://lore.kernel.org/r/20241206091514.2538350-2-dawidn@google.com
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
This commit is contained in:
Dawid Niedzwiecki
2024-12-06 09:15:13 +00:00
committed by Tzung-Bi Shih
parent cfa842c80c
commit 5ffa0dbfdc
9 changed files with 74 additions and 6 deletions
+5
View File
@@ -204,6 +204,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
mutex_init(&ec_dev->lock);
lockdep_set_class(&ec_dev->lock, &ec_dev->lockdep_key);
/* Send RWSIG continue to jump to RW for devices using RWSIG. */
err = cros_ec_rwsig_continue(ec_dev);
if (err)
dev_info(dev, "Failed to continue RWSIG: %d\n", err);
err = cros_ec_query_all(ec_dev);
if (err) {
dev_err(dev, "Cannot identify the EC: error %d\n", err);
+2 -1
View File
@@ -305,7 +305,8 @@ static int cros_ec_i2c_probe(struct i2c_client *client)
ec_dev->phys_name = client->adapter->name;
ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct ec_host_request_i2c);
ec_dev->dout_size = sizeof(struct ec_host_request_i2c) +
sizeof(struct ec_params_rwsig_action);
err = cros_ec_register(ec_dev);
if (err) {
+1 -1
View File
@@ -557,7 +557,7 @@ static int cros_ec_dev_init(struct ishtp_cl_data *client_data)
ec_dev->phys_name = dev_name(dev);
ec_dev->din_size = sizeof(struct cros_ish_in_msg) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct cros_ish_out_msg);
ec_dev->dout_size = sizeof(struct cros_ish_out_msg) + sizeof(struct ec_params_rwsig_action);
return cros_ec_register(ec_dev);
}
+1 -1
View File
@@ -573,7 +573,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
ec_dev->cmd_readmem = cros_ec_lpc_readmem;
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct ec_host_request);
ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
ec_dev->priv = ec_lpc;
/*
+60
View File
@@ -15,6 +15,8 @@
#include "cros_ec_trace.h"
#define EC_COMMAND_RETRIES 50
#define RWSIG_CONTINUE_RETRIES 8
#define RWSIG_CONTINUE_MAX_ERRORS_IN_ROW 3
static const int cros_ec_error_map[] = {
[EC_RES_INVALID_COMMAND] = -EOPNOTSUPP,
@@ -288,6 +290,64 @@ exit:
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;
msg->version = 0;
msg->command = EC_CMD_RWSIG_ACTION;
msg->insize = 0;
msg->outsize = sizeof(*rwsig_action);
rwsig_action = (struct ec_params_rwsig_action *)msg->data;
rwsig_action->action = RWSIG_ACTION_CONTINUE;
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;
} else if (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;
} else if (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);
}
kfree(msg);
return ret;
}
EXPORT_SYMBOL(cros_ec_rwsig_continue);
static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx)
{
struct cros_ec_command *msg;
+1 -1
View File
@@ -231,7 +231,7 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
ec_dev->phys_name = dev_name(&rpdev->dev);
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct ec_host_request);
ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
dev_set_drvdata(dev, ec_dev);
ec_rpmsg->rpdev = rpdev;
+1 -1
View File
@@ -766,7 +766,7 @@ static int cros_ec_spi_probe(struct spi_device *spi)
ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct ec_host_request);
ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
ec_spi->last_transfer_ns = ktime_get_ns();
+1 -1
View File
@@ -283,7 +283,7 @@ static int cros_ec_uart_probe(struct serdev_device *serdev)
ec_dev->pkt_xfer = cros_ec_uart_pkt_xfer;
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
ec_dev->dout_size = sizeof(struct ec_host_request);
ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
serdev_device_set_client_ops(serdev, &cros_ec_uart_client_ops);
@@ -246,6 +246,8 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg);
int cros_ec_rwsig_continue(struct cros_ec_device *ec_dev);
int cros_ec_query_all(struct cros_ec_device *ec_dev);
int cros_ec_get_next_event(struct cros_ec_device *ec_dev,