Skip to content

Commit 2de89fd

Browse files
peter50216Enric Balletbo i Serra
authored andcommitted
platform/chrome: cros_ec: Add EC host command support using rpmsg
Add EC host command support through rpmsg. Signed-off-by: Pi-Hsun Shih <[email protected]> Signed-off-by: Enric Balletbo i Serra <[email protected]>
1 parent 9e2b0e0 commit 2de89fd

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

drivers/platform/chrome/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ config CROS_EC_I2C
5959
a checksum. Failing accesses will be retried three times to
6060
improve reliability.
6161

62+
config CROS_EC_RPMSG
63+
tristate "ChromeOS Embedded Controller (rpmsg)"
64+
depends on MFD_CROS_EC && RPMSG && OF
65+
help
66+
If you say Y here, you get support for talking to the ChromeOS EC
67+
through rpmsg. This uses a simple byte-level protocol with a
68+
checksum. Also since there's no addition EC-to-host interrupt, this
69+
use a byte in message to distinguish host event from host command.
70+
71+
To compile this driver as a module, choose M here: the
72+
module will be called cros_ec_rpmsg.
73+
6274
config CROS_EC_SPI
6375
tristate "ChromeOS Embedded Controller (SPI)"
6476
depends on MFD_CROS_EC && SPI

drivers/platform/chrome/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
44
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
55
obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o
66
obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o
7+
obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
78
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
89
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o
910
cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
//
3+
// Copyright 2018 Google LLC.
4+
5+
#include <linux/completion.h>
6+
#include <linux/delay.h>
7+
#include <linux/kernel.h>
8+
#include <linux/module.h>
9+
#include <linux/mfd/cros_ec.h>
10+
#include <linux/mfd/cros_ec_commands.h>
11+
#include <linux/of.h>
12+
#include <linux/platform_device.h>
13+
#include <linux/rpmsg.h>
14+
#include <linux/slab.h>
15+
16+
#define EC_MSG_TIMEOUT_MS 200
17+
#define HOST_COMMAND_MARK 1
18+
#define HOST_EVENT_MARK 2
19+
20+
/**
21+
* struct cros_ec_rpmsg_response - rpmsg message format from from EC.
22+
*
23+
* @type: The type of message, should be either HOST_COMMAND_MARK or
24+
* HOST_EVENT_MARK, representing that the message is a response to
25+
* host command, or a host event.
26+
* @data: ec_host_response for host command.
27+
*/
28+
struct cros_ec_rpmsg_response {
29+
u8 type;
30+
u8 data[] __aligned(4);
31+
};
32+
33+
/**
34+
* struct cros_ec_rpmsg - information about a EC over rpmsg.
35+
*
36+
* @rpdev: rpmsg device we are connected to
37+
* @xfer_ack: completion for host command transfer.
38+
* @host_event_work: Work struct for pending host event.
39+
*/
40+
struct cros_ec_rpmsg {
41+
struct rpmsg_device *rpdev;
42+
struct completion xfer_ack;
43+
struct work_struct host_event_work;
44+
};
45+
46+
/**
47+
* cros_ec_cmd_xfer_rpmsg - Transfer a message over rpmsg and receive the reply
48+
*
49+
* @ec_dev: ChromeOS EC device
50+
* @ec_msg: Message to transfer
51+
*
52+
* This is only used for old EC proto version, and is not supported for this
53+
* driver.
54+
*
55+
* Return: -EINVAL
56+
*/
57+
static int cros_ec_cmd_xfer_rpmsg(struct cros_ec_device *ec_dev,
58+
struct cros_ec_command *ec_msg)
59+
{
60+
return -EINVAL;
61+
}
62+
63+
/**
64+
* cros_ec_pkt_xfer_rpmsg - Transfer a packet over rpmsg and receive the reply
65+
*
66+
* @ec_dev: ChromeOS EC device
67+
* @ec_msg: Message to transfer
68+
*
69+
* Return: number of bytes of the reply on success or negative error code.
70+
*/
71+
static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev,
72+
struct cros_ec_command *ec_msg)
73+
{
74+
struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
75+
struct rpmsg_device *rpdev = ec_rpmsg->rpdev;
76+
struct ec_host_response *response;
77+
unsigned long timeout;
78+
int len;
79+
int ret;
80+
u8 sum;
81+
int i;
82+
83+
ec_msg->result = 0;
84+
len = cros_ec_prepare_tx(ec_dev, ec_msg);
85+
dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
86+
87+
reinit_completion(&ec_rpmsg->xfer_ack);
88+
ret = rpmsg_send(rpdev->ept, ec_dev->dout, len);
89+
if (ret) {
90+
dev_err(ec_dev->dev, "rpmsg send failed\n");
91+
return ret;
92+
}
93+
94+
timeout = msecs_to_jiffies(EC_MSG_TIMEOUT_MS);
95+
ret = wait_for_completion_timeout(&ec_rpmsg->xfer_ack, timeout);
96+
if (!ret) {
97+
dev_err(ec_dev->dev, "rpmsg send timeout\n");
98+
return -EIO;
99+
}
100+
101+
/* check response error code */
102+
response = (struct ec_host_response *)ec_dev->din;
103+
ec_msg->result = response->result;
104+
105+
ret = cros_ec_check_result(ec_dev, ec_msg);
106+
if (ret)
107+
goto exit;
108+
109+
if (response->data_len > ec_msg->insize) {
110+
dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
111+
response->data_len, ec_msg->insize);
112+
ret = -EMSGSIZE;
113+
goto exit;
114+
}
115+
116+
/* copy response packet payload and compute checksum */
117+
memcpy(ec_msg->data, ec_dev->din + sizeof(*response),
118+
response->data_len);
119+
120+
sum = 0;
121+
for (i = 0; i < sizeof(*response) + response->data_len; i++)
122+
sum += ec_dev->din[i];
123+
124+
if (sum) {
125+
dev_err(ec_dev->dev, "bad packet checksum, calculated %x\n",
126+
sum);
127+
ret = -EBADMSG;
128+
goto exit;
129+
}
130+
131+
ret = response->data_len;
132+
exit:
133+
if (ec_msg->command == EC_CMD_REBOOT_EC)
134+
msleep(EC_REBOOT_DELAY_MS);
135+
136+
return ret;
137+
}
138+
139+
static void
140+
cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)
141+
{
142+
struct cros_ec_rpmsg *ec_rpmsg = container_of(host_event_work,
143+
struct cros_ec_rpmsg,
144+
host_event_work);
145+
struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev);
146+
bool wake_event = true;
147+
int ret;
148+
149+
ret = cros_ec_get_next_event(ec_dev, &wake_event);
150+
151+
/*
152+
* Signal only if wake host events or any interrupt if
153+
* cros_ec_get_next_event() returned an error (default value for
154+
* wake_event is true)
155+
*/
156+
if (wake_event && device_may_wakeup(ec_dev->dev))
157+
pm_wakeup_event(ec_dev->dev, 0);
158+
159+
if (ret > 0)
160+
blocking_notifier_call_chain(&ec_dev->event_notifier,
161+
0, ec_dev);
162+
}
163+
164+
static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
165+
int len, void *priv, u32 src)
166+
{
167+
struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);
168+
struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
169+
struct cros_ec_rpmsg_response *resp;
170+
171+
if (!len) {
172+
dev_warn(ec_dev->dev, "rpmsg received empty response");
173+
return -EINVAL;
174+
}
175+
176+
resp = data;
177+
len -= offsetof(struct cros_ec_rpmsg_response, data);
178+
if (resp->type == HOST_COMMAND_MARK) {
179+
if (len > ec_dev->din_size) {
180+
dev_warn(ec_dev->dev,
181+
"received length %d > din_size %d, truncating",
182+
len, ec_dev->din_size);
183+
len = ec_dev->din_size;
184+
}
185+
186+
memcpy(ec_dev->din, resp->data, len);
187+
complete(&ec_rpmsg->xfer_ack);
188+
} else if (resp->type == HOST_EVENT_MARK) {
189+
schedule_work(&ec_rpmsg->host_event_work);
190+
} else {
191+
dev_warn(ec_dev->dev, "rpmsg received invalid type = %d",
192+
resp->type);
193+
return -EINVAL;
194+
}
195+
196+
return 0;
197+
}
198+
199+
static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
200+
{
201+
struct device *dev = &rpdev->dev;
202+
struct cros_ec_rpmsg *ec_rpmsg;
203+
struct cros_ec_device *ec_dev;
204+
205+
ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
206+
if (!ec_dev)
207+
return -ENOMEM;
208+
209+
ec_rpmsg = devm_kzalloc(dev, sizeof(*ec_rpmsg), GFP_KERNEL);
210+
if (!ec_rpmsg)
211+
return -ENOMEM;
212+
213+
ec_dev->dev = dev;
214+
ec_dev->priv = ec_rpmsg;
215+
ec_dev->cmd_xfer = cros_ec_cmd_xfer_rpmsg;
216+
ec_dev->pkt_xfer = cros_ec_pkt_xfer_rpmsg;
217+
ec_dev->phys_name = dev_name(&rpdev->dev);
218+
ec_dev->din_size = sizeof(struct ec_host_response) +
219+
sizeof(struct ec_response_get_protocol_info);
220+
ec_dev->dout_size = sizeof(struct ec_host_request);
221+
dev_set_drvdata(dev, ec_dev);
222+
223+
ec_rpmsg->rpdev = rpdev;
224+
init_completion(&ec_rpmsg->xfer_ack);
225+
INIT_WORK(&ec_rpmsg->host_event_work,
226+
cros_ec_rpmsg_host_event_function);
227+
228+
return cros_ec_register(ec_dev);
229+
}
230+
231+
static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev)
232+
{
233+
struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);
234+
struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
235+
236+
cancel_work_sync(&ec_rpmsg->host_event_work);
237+
}
238+
239+
static const struct of_device_id cros_ec_rpmsg_of_match[] = {
240+
{ .compatible = "google,cros-ec-rpmsg", },
241+
{ }
242+
};
243+
MODULE_DEVICE_TABLE(of, cros_ec_rpmsg_of_match);
244+
245+
static struct rpmsg_driver cros_ec_driver_rpmsg = {
246+
.drv = {
247+
.name = "cros-ec-rpmsg",
248+
.of_match_table = cros_ec_rpmsg_of_match,
249+
},
250+
.probe = cros_ec_rpmsg_probe,
251+
.remove = cros_ec_rpmsg_remove,
252+
.callback = cros_ec_rpmsg_callback,
253+
};
254+
255+
module_rpmsg_driver(cros_ec_driver_rpmsg);
256+
257+
MODULE_LICENSE("GPL v2");
258+
MODULE_DESCRIPTION("ChromeOS EC multi function device (rpmsg)");

0 commit comments

Comments
 (0)