summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/cros_ec_i2c.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/misc/cros_ec_i2c.c b/drivers/misc/cros_ec_i2c.c
index f2f6961499..6e0934017a 100644
--- a/drivers/misc/cros_ec_i2c.c
+++ b/drivers/misc/cros_ec_i2c.c
@@ -24,6 +24,89 @@
#define debug_trace(fmt, b...)
#endif
+/**
+ * Request format for protocol v3
+ * byte 0 0xda (EC_COMMAND_PROTOCOL_3)
+ * byte 1-8 struct ec_host_request
+ * byte 10- response data
+ */
+struct ec_host_request_i2c {
+ /* Always 0xda to backward compatible with v2 struct */
+ uint8_t command_protocol;
+ struct ec_host_request ec_request;
+} __packed;
+
+/*
+ * Response format for protocol v3
+ * byte 0 result code
+ * byte 1 packet_length
+ * byte 2-9 struct ec_host_response
+ * byte 10- response data
+ */
+struct ec_host_response_i2c {
+ uint8_t result;
+ uint8_t packet_length;
+ struct ec_host_response ec_response;
+} __packed;
+
+static int cros_ec_i2c_packet(struct udevice *udev, int out_bytes, int in_bytes)
+{
+ struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
+ struct dm_i2c_chip *chip = dev_get_parent_platdata(udev);
+ struct ec_host_request_i2c *ec_request_i2c =
+ (struct ec_host_request_i2c *)dev->dout;
+ struct ec_host_response_i2c *ec_response_i2c =
+ (struct ec_host_response_i2c *)dev->din;
+ struct i2c_msg i2c_msg[2];
+ int ret;
+
+ i2c_msg[0].addr = chip->chip_addr;
+ i2c_msg[0].flags = 0;
+ i2c_msg[1].addr = chip->chip_addr;
+ i2c_msg[1].flags = I2C_M_RD;
+
+ /* one extra byte, to indicate v3 */
+ i2c_msg[0].len = out_bytes + 1;
+ i2c_msg[0].buf = dev->dout;
+
+ /* stitch on EC_COMMAND_PROTOCOL_3 */
+ memmove(&ec_request_i2c->ec_request, dev->dout, out_bytes);
+ ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
+
+ /* two extra bytes for v3 */
+ i2c_msg[1].len = in_bytes + 2;
+ i2c_msg[1].buf = dev->din;
+
+ ret = dm_i2c_xfer(udev, &i2c_msg[0], 2);
+ if (ret) {
+ printf("%s: Could not execute transfer: %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* When we send a v3 request to v2 ec, ec won't recognize the 0xda
+ * (EC_COMMAND_PROTOCOL_3) and will return with status
+ * EC_RES_INVALID_COMMAND with zero data length
+ *
+ * In case of invalid command for v3 protocol the data length
+ * will be at least sizeof(struct ec_host_response)
+ */
+ if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
+ ec_response_i2c->packet_length == 0)
+ return -EPROTONOSUPPORT;
+
+ if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
+ printf("%s: response of %u bytes too short; not a full hdr\n",
+ __func__, ec_response_i2c->packet_length);
+ return -EBADMSG;
+ }
+
+
+ /* drop result and packet_len */
+ memmove(dev->din, &ec_response_i2c->ec_response, in_bytes);
+
+ return in_bytes;
+}
+
static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd,
int cmd_version, const uint8_t *dout,
int dout_len, uint8_t **dinp, int din_len)
@@ -140,6 +223,7 @@ static int cros_ec_probe(struct udevice *dev)
static struct dm_cros_ec_ops cros_ec_ops = {
.command = cros_ec_i2c_command,
+ .packet = cros_ec_i2c_packet,
};
static const struct udevice_id cros_ec_ids[] = {