310 lines · c
1// SPDX-License-Identifier: GPL-2.02//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/of.h>10#include <linux/platform_data/cros_ec_commands.h>11#include <linux/platform_data/cros_ec_proto.h>12#include <linux/platform_device.h>13#include <linux/rpmsg.h>14#include <linux/slab.h>15 16#include "cros_ec.h"17 18#define EC_MSG_TIMEOUT_MS 20019#define HOST_COMMAND_MARK 120#define HOST_EVENT_MARK 221 22/**23 * struct cros_ec_rpmsg_response - rpmsg message format from from EC.24 *25 * @type: The type of message, should be either HOST_COMMAND_MARK or26 * HOST_EVENT_MARK, representing that the message is a response to27 * host command, or a host event.28 * @data: ec_host_response for host command.29 */30struct cros_ec_rpmsg_response {31 u8 type;32 u8 data[] __aligned(4);33};34 35/**36 * struct cros_ec_rpmsg - information about a EC over rpmsg.37 *38 * @rpdev: rpmsg device we are connected to39 * @xfer_ack: completion for host command transfer.40 * @host_event_work: Work struct for pending host event.41 * @ept: The rpmsg endpoint of this channel.42 * @has_pending_host_event: Boolean used to check if there is a pending event.43 * @probe_done: Flag to indicate that probe is done.44 */45struct cros_ec_rpmsg {46 struct rpmsg_device *rpdev;47 struct completion xfer_ack;48 struct work_struct host_event_work;49 struct rpmsg_endpoint *ept;50 bool has_pending_host_event;51 bool probe_done;52};53 54/**55 * cros_ec_cmd_xfer_rpmsg - Transfer a message over rpmsg and receive the reply56 *57 * @ec_dev: ChromeOS EC device58 * @ec_msg: Message to transfer59 *60 * This is only used for old EC proto version, and is not supported for this61 * driver.62 *63 * Return: -EINVAL64 */65static int cros_ec_cmd_xfer_rpmsg(struct cros_ec_device *ec_dev,66 struct cros_ec_command *ec_msg)67{68 return -EINVAL;69}70 71/**72 * cros_ec_pkt_xfer_rpmsg - Transfer a packet over rpmsg and receive the reply73 *74 * @ec_dev: ChromeOS EC device75 * @ec_msg: Message to transfer76 *77 * Return: number of bytes of the reply on success or negative error code.78 */79static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev,80 struct cros_ec_command *ec_msg)81{82 struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;83 struct ec_host_response *response;84 unsigned long timeout;85 int len;86 int ret;87 u8 sum;88 int i;89 90 ec_msg->result = 0;91 len = cros_ec_prepare_tx(ec_dev, ec_msg);92 if (len < 0)93 return len;94 dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);95 96 reinit_completion(&ec_rpmsg->xfer_ack);97 ret = rpmsg_send(ec_rpmsg->ept, ec_dev->dout, len);98 if (ret) {99 dev_err(ec_dev->dev, "rpmsg send failed\n");100 return ret;101 }102 103 timeout = msecs_to_jiffies(EC_MSG_TIMEOUT_MS);104 ret = wait_for_completion_timeout(&ec_rpmsg->xfer_ack, timeout);105 if (!ret) {106 dev_err(ec_dev->dev, "rpmsg send timeout\n");107 return -EIO;108 }109 110 /* check response error code */111 response = (struct ec_host_response *)ec_dev->din;112 ec_msg->result = response->result;113 114 ret = cros_ec_check_result(ec_dev, ec_msg);115 if (ret)116 goto exit;117 118 if (response->data_len > ec_msg->insize) {119 dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",120 response->data_len, ec_msg->insize);121 ret = -EMSGSIZE;122 goto exit;123 }124 125 /* copy response packet payload and compute checksum */126 memcpy(ec_msg->data, ec_dev->din + sizeof(*response),127 response->data_len);128 129 sum = 0;130 for (i = 0; i < sizeof(*response) + response->data_len; i++)131 sum += ec_dev->din[i];132 133 if (sum) {134 dev_err(ec_dev->dev, "bad packet checksum, calculated %x\n",135 sum);136 ret = -EBADMSG;137 goto exit;138 }139 140 ret = response->data_len;141exit:142 if (ec_msg->command == EC_CMD_REBOOT_EC)143 msleep(EC_REBOOT_DELAY_MS);144 145 return ret;146}147 148static void149cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)150{151 struct cros_ec_rpmsg *ec_rpmsg = container_of(host_event_work,152 struct cros_ec_rpmsg,153 host_event_work);154 155 cros_ec_irq_thread(0, dev_get_drvdata(&ec_rpmsg->rpdev->dev));156}157 158static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,159 int len, void *priv, u32 src)160{161 struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);162 struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;163 struct cros_ec_rpmsg_response *resp;164 165 if (!len) {166 dev_warn(ec_dev->dev, "rpmsg received empty response");167 return -EINVAL;168 }169 170 resp = data;171 len -= offsetof(struct cros_ec_rpmsg_response, data);172 if (resp->type == HOST_COMMAND_MARK) {173 if (len > ec_dev->din_size) {174 dev_warn(ec_dev->dev,175 "received length %d > din_size %d, truncating",176 len, ec_dev->din_size);177 len = ec_dev->din_size;178 }179 180 memcpy(ec_dev->din, resp->data, len);181 complete(&ec_rpmsg->xfer_ack);182 } else if (resp->type == HOST_EVENT_MARK) {183 /*184 * If the host event is sent before cros_ec_register is185 * finished, queue the host event.186 */187 if (ec_rpmsg->probe_done)188 schedule_work(&ec_rpmsg->host_event_work);189 else190 ec_rpmsg->has_pending_host_event = true;191 } else {192 dev_warn(ec_dev->dev, "rpmsg received invalid type = %d",193 resp->type);194 return -EINVAL;195 }196 197 return 0;198}199 200static struct rpmsg_endpoint *201cros_ec_rpmsg_create_ept(struct rpmsg_device *rpdev)202{203 struct rpmsg_channel_info chinfo = {};204 205 strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);206 chinfo.src = rpdev->src;207 chinfo.dst = RPMSG_ADDR_ANY;208 209 return rpmsg_create_ept(rpdev, cros_ec_rpmsg_callback, NULL, chinfo);210}211 212static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)213{214 struct device *dev = &rpdev->dev;215 struct cros_ec_rpmsg *ec_rpmsg;216 struct cros_ec_device *ec_dev;217 int ret;218 219 ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);220 if (!ec_dev)221 return -ENOMEM;222 223 ec_rpmsg = devm_kzalloc(dev, sizeof(*ec_rpmsg), GFP_KERNEL);224 if (!ec_rpmsg)225 return -ENOMEM;226 227 ec_dev->dev = dev;228 ec_dev->priv = ec_rpmsg;229 ec_dev->cmd_xfer = cros_ec_cmd_xfer_rpmsg;230 ec_dev->pkt_xfer = cros_ec_pkt_xfer_rpmsg;231 ec_dev->phys_name = dev_name(&rpdev->dev);232 ec_dev->din_size = sizeof(struct ec_host_response) +233 sizeof(struct ec_response_get_protocol_info);234 ec_dev->dout_size = sizeof(struct ec_host_request);235 dev_set_drvdata(dev, ec_dev);236 237 ec_rpmsg->rpdev = rpdev;238 init_completion(&ec_rpmsg->xfer_ack);239 INIT_WORK(&ec_rpmsg->host_event_work,240 cros_ec_rpmsg_host_event_function);241 242 ec_rpmsg->ept = cros_ec_rpmsg_create_ept(rpdev);243 if (!ec_rpmsg->ept)244 return -ENOMEM;245 246 ret = cros_ec_register(ec_dev);247 if (ret < 0) {248 rpmsg_destroy_ept(ec_rpmsg->ept);249 cancel_work_sync(&ec_rpmsg->host_event_work);250 return ret;251 }252 253 ec_rpmsg->probe_done = true;254 255 if (ec_rpmsg->has_pending_host_event)256 schedule_work(&ec_rpmsg->host_event_work);257 258 return 0;259}260 261static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev)262{263 struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);264 struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;265 266 cros_ec_unregister(ec_dev);267 rpmsg_destroy_ept(ec_rpmsg->ept);268 cancel_work_sync(&ec_rpmsg->host_event_work);269}270 271#ifdef CONFIG_PM_SLEEP272static int cros_ec_rpmsg_suspend(struct device *dev)273{274 struct cros_ec_device *ec_dev = dev_get_drvdata(dev);275 276 return cros_ec_suspend(ec_dev);277}278 279static int cros_ec_rpmsg_resume(struct device *dev)280{281 struct cros_ec_device *ec_dev = dev_get_drvdata(dev);282 283 return cros_ec_resume(ec_dev);284}285#endif286 287static SIMPLE_DEV_PM_OPS(cros_ec_rpmsg_pm_ops, cros_ec_rpmsg_suspend,288 cros_ec_rpmsg_resume);289 290static const struct of_device_id cros_ec_rpmsg_of_match[] = {291 { .compatible = "google,cros-ec-rpmsg", },292 { }293};294MODULE_DEVICE_TABLE(of, cros_ec_rpmsg_of_match);295 296static struct rpmsg_driver cros_ec_driver_rpmsg = {297 .drv = {298 .name = "cros-ec-rpmsg",299 .of_match_table = cros_ec_rpmsg_of_match,300 .pm = &cros_ec_rpmsg_pm_ops,301 },302 .probe = cros_ec_rpmsg_probe,303 .remove = cros_ec_rpmsg_remove,304};305 306module_rpmsg_driver(cros_ec_driver_rpmsg);307 308MODULE_LICENSE("GPL v2");309MODULE_DESCRIPTION("ChromeOS EC multi function device (rpmsg)");310