blob: 58e98a24fd0879d0e9b0688837c99a808c53583d [file] [log] [blame]
Javier Martinez Canillas062476f2015-06-09 13:04:44 +02001/*
2 * ChromeOS EC communication protocol helper functions
3 *
4 * Copyright (C) 2015 Google, Inc
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/mfd/cros_ec.h>
18#include <linux/delay.h>
19#include <linux/device.h>
20#include <linux/module.h>
21#include <linux/slab.h>
22
23#define EC_COMMAND_RETRIES 50
24
25int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
26 struct cros_ec_command *msg)
27{
28 uint8_t *out;
29 int csum, i;
30
31 BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
32 out = ec_dev->dout;
33 out[0] = EC_CMD_VERSION0 + msg->version;
34 out[1] = msg->command;
35 out[2] = msg->outsize;
36 csum = out[0] + out[1] + out[2];
37 for (i = 0; i < msg->outsize; i++)
38 csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
39 out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff);
40
41 return EC_MSG_TX_PROTO_BYTES + msg->outsize;
42}
43EXPORT_SYMBOL(cros_ec_prepare_tx);
44
45int cros_ec_check_result(struct cros_ec_device *ec_dev,
46 struct cros_ec_command *msg)
47{
48 switch (msg->result) {
49 case EC_RES_SUCCESS:
50 return 0;
51 case EC_RES_IN_PROGRESS:
52 dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
53 msg->command);
54 return -EAGAIN;
55 default:
56 dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
57 msg->command, msg->result);
58 return 0;
59 }
60}
61EXPORT_SYMBOL(cros_ec_check_result);
62
63int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
64 struct cros_ec_command *msg)
65{
66 int ret;
67
68 mutex_lock(&ec_dev->lock);
69 ret = ec_dev->cmd_xfer(ec_dev, msg);
70 if (msg->result == EC_RES_IN_PROGRESS) {
71 int i;
72 struct cros_ec_command *status_msg;
73 struct ec_response_get_comms_status *status;
74
75 status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
76 GFP_KERNEL);
77 if (!status_msg) {
78 ret = -ENOMEM;
79 goto exit;
80 }
81
82 status_msg->version = 0;
83 status_msg->command = EC_CMD_GET_COMMS_STATUS;
84 status_msg->insize = sizeof(*status);
85 status_msg->outsize = 0;
86
87 /*
88 * Query the EC's status until it's no longer busy or
89 * we encounter an error.
90 */
91 for (i = 0; i < EC_COMMAND_RETRIES; i++) {
92 usleep_range(10000, 11000);
93
94 ret = ec_dev->cmd_xfer(ec_dev, status_msg);
95 if (ret < 0)
96 break;
97
98 msg->result = status_msg->result;
99 if (status_msg->result != EC_RES_SUCCESS)
100 break;
101
102 status = (struct ec_response_get_comms_status *)
103 status_msg->data;
104 if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
105 break;
106 }
107
108 kfree(status_msg);
109 }
110exit:
111 mutex_unlock(&ec_dev->lock);
112
113 return ret;
114}
115EXPORT_SYMBOL(cros_ec_cmd_xfer);