18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright 2018 Google LLC. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/completion.h> 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_commands.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_proto.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/rpmsg.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "cros_ec.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define EC_MSG_TIMEOUT_MS 200 198c2ecf20Sopenharmony_ci#define HOST_COMMAND_MARK 1 208c2ecf20Sopenharmony_ci#define HOST_EVENT_MARK 2 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/** 238c2ecf20Sopenharmony_ci * struct cros_ec_rpmsg_response - rpmsg message format from from EC. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * @type: The type of message, should be either HOST_COMMAND_MARK or 268c2ecf20Sopenharmony_ci * HOST_EVENT_MARK, representing that the message is a response to 278c2ecf20Sopenharmony_ci * host command, or a host event. 288c2ecf20Sopenharmony_ci * @data: ec_host_response for host command. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistruct cros_ec_rpmsg_response { 318c2ecf20Sopenharmony_ci u8 type; 328c2ecf20Sopenharmony_ci u8 data[] __aligned(4); 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/** 368c2ecf20Sopenharmony_ci * struct cros_ec_rpmsg - information about a EC over rpmsg. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * @rpdev: rpmsg device we are connected to 398c2ecf20Sopenharmony_ci * @xfer_ack: completion for host command transfer. 408c2ecf20Sopenharmony_ci * @host_event_work: Work struct for pending host event. 418c2ecf20Sopenharmony_ci * @ept: The rpmsg endpoint of this channel. 428c2ecf20Sopenharmony_ci * @has_pending_host_event: Boolean used to check if there is a pending event. 438c2ecf20Sopenharmony_ci * @probe_done: Flag to indicate that probe is done. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistruct cros_ec_rpmsg { 468c2ecf20Sopenharmony_ci struct rpmsg_device *rpdev; 478c2ecf20Sopenharmony_ci struct completion xfer_ack; 488c2ecf20Sopenharmony_ci struct work_struct host_event_work; 498c2ecf20Sopenharmony_ci struct rpmsg_endpoint *ept; 508c2ecf20Sopenharmony_ci bool has_pending_host_event; 518c2ecf20Sopenharmony_ci bool probe_done; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * cros_ec_cmd_xfer_rpmsg - Transfer a message over rpmsg and receive the reply 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * @ec_dev: ChromeOS EC device 588c2ecf20Sopenharmony_ci * @ec_msg: Message to transfer 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * This is only used for old EC proto version, and is not supported for this 618c2ecf20Sopenharmony_ci * driver. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Return: -EINVAL 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic int cros_ec_cmd_xfer_rpmsg(struct cros_ec_device *ec_dev, 668c2ecf20Sopenharmony_ci struct cros_ec_command *ec_msg) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * cros_ec_pkt_xfer_rpmsg - Transfer a packet over rpmsg and receive the reply 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * @ec_dev: ChromeOS EC device 758c2ecf20Sopenharmony_ci * @ec_msg: Message to transfer 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * Return: number of bytes of the reply on success or negative error code. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev, 808c2ecf20Sopenharmony_ci struct cros_ec_command *ec_msg) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv; 838c2ecf20Sopenharmony_ci struct ec_host_response *response; 848c2ecf20Sopenharmony_ci unsigned long timeout; 858c2ecf20Sopenharmony_ci int len; 868c2ecf20Sopenharmony_ci int ret; 878c2ecf20Sopenharmony_ci u8 sum; 888c2ecf20Sopenharmony_ci int i; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ec_msg->result = 0; 918c2ecf20Sopenharmony_ci len = cros_ec_prepare_tx(ec_dev, ec_msg); 928c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci reinit_completion(&ec_rpmsg->xfer_ack); 958c2ecf20Sopenharmony_ci ret = rpmsg_send(ec_rpmsg->ept, ec_dev->dout, len); 968c2ecf20Sopenharmony_ci if (ret) { 978c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, "rpmsg send failed\n"); 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci timeout = msecs_to_jiffies(EC_MSG_TIMEOUT_MS); 1028c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ec_rpmsg->xfer_ack, timeout); 1038c2ecf20Sopenharmony_ci if (!ret) { 1048c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, "rpmsg send timeout\n"); 1058c2ecf20Sopenharmony_ci return -EIO; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* check response error code */ 1098c2ecf20Sopenharmony_ci response = (struct ec_host_response *)ec_dev->din; 1108c2ecf20Sopenharmony_ci ec_msg->result = response->result; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = cros_ec_check_result(ec_dev, ec_msg); 1138c2ecf20Sopenharmony_ci if (ret) 1148c2ecf20Sopenharmony_ci goto exit; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (response->data_len > ec_msg->insize) { 1178c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", 1188c2ecf20Sopenharmony_ci response->data_len, ec_msg->insize); 1198c2ecf20Sopenharmony_ci ret = -EMSGSIZE; 1208c2ecf20Sopenharmony_ci goto exit; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* copy response packet payload and compute checksum */ 1248c2ecf20Sopenharmony_ci memcpy(ec_msg->data, ec_dev->din + sizeof(*response), 1258c2ecf20Sopenharmony_ci response->data_len); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci sum = 0; 1288c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(*response) + response->data_len; i++) 1298c2ecf20Sopenharmony_ci sum += ec_dev->din[i]; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (sum) { 1328c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, "bad packet checksum, calculated %x\n", 1338c2ecf20Sopenharmony_ci sum); 1348c2ecf20Sopenharmony_ci ret = -EBADMSG; 1358c2ecf20Sopenharmony_ci goto exit; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ret = response->data_len; 1398c2ecf20Sopenharmony_ciexit: 1408c2ecf20Sopenharmony_ci if (ec_msg->command == EC_CMD_REBOOT_EC) 1418c2ecf20Sopenharmony_ci msleep(EC_REBOOT_DELAY_MS); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void 1478c2ecf20Sopenharmony_cicros_ec_rpmsg_host_event_function(struct work_struct *host_event_work) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct cros_ec_rpmsg *ec_rpmsg = container_of(host_event_work, 1508c2ecf20Sopenharmony_ci struct cros_ec_rpmsg, 1518c2ecf20Sopenharmony_ci host_event_work); 1528c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = dev_get_drvdata(&ec_rpmsg->rpdev->dev); 1538c2ecf20Sopenharmony_ci bool ec_has_more_events; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci do { 1568c2ecf20Sopenharmony_ci ec_has_more_events = cros_ec_handle_event(ec_dev); 1578c2ecf20Sopenharmony_ci } while (ec_has_more_events); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data, 1618c2ecf20Sopenharmony_ci int len, void *priv, u32 src) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev); 1648c2ecf20Sopenharmony_ci struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv; 1658c2ecf20Sopenharmony_ci struct cros_ec_rpmsg_response *resp; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!len) { 1688c2ecf20Sopenharmony_ci dev_warn(ec_dev->dev, "rpmsg received empty response"); 1698c2ecf20Sopenharmony_ci return -EINVAL; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci resp = data; 1738c2ecf20Sopenharmony_ci len -= offsetof(struct cros_ec_rpmsg_response, data); 1748c2ecf20Sopenharmony_ci if (resp->type == HOST_COMMAND_MARK) { 1758c2ecf20Sopenharmony_ci if (len > ec_dev->din_size) { 1768c2ecf20Sopenharmony_ci dev_warn(ec_dev->dev, 1778c2ecf20Sopenharmony_ci "received length %d > din_size %d, truncating", 1788c2ecf20Sopenharmony_ci len, ec_dev->din_size); 1798c2ecf20Sopenharmony_ci len = ec_dev->din_size; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci memcpy(ec_dev->din, resp->data, len); 1838c2ecf20Sopenharmony_ci complete(&ec_rpmsg->xfer_ack); 1848c2ecf20Sopenharmony_ci } else if (resp->type == HOST_EVENT_MARK) { 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * If the host event is sent before cros_ec_register is 1878c2ecf20Sopenharmony_ci * finished, queue the host event. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci if (ec_rpmsg->probe_done) 1908c2ecf20Sopenharmony_ci schedule_work(&ec_rpmsg->host_event_work); 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci ec_rpmsg->has_pending_host_event = true; 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci dev_warn(ec_dev->dev, "rpmsg received invalid type = %d", 1958c2ecf20Sopenharmony_ci resp->type); 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct rpmsg_endpoint * 2038c2ecf20Sopenharmony_cicros_ec_rpmsg_create_ept(struct rpmsg_device *rpdev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct rpmsg_channel_info chinfo = {}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); 2088c2ecf20Sopenharmony_ci chinfo.src = rpdev->src; 2098c2ecf20Sopenharmony_ci chinfo.dst = RPMSG_ADDR_ANY; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return rpmsg_create_ept(rpdev, cros_ec_rpmsg_callback, NULL, chinfo); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct device *dev = &rpdev->dev; 2178c2ecf20Sopenharmony_ci struct cros_ec_rpmsg *ec_rpmsg; 2188c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev; 2198c2ecf20Sopenharmony_ci int ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!ec_dev) 2238c2ecf20Sopenharmony_ci return -ENOMEM; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ec_rpmsg = devm_kzalloc(dev, sizeof(*ec_rpmsg), GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!ec_rpmsg) 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ec_dev->dev = dev; 2308c2ecf20Sopenharmony_ci ec_dev->priv = ec_rpmsg; 2318c2ecf20Sopenharmony_ci ec_dev->cmd_xfer = cros_ec_cmd_xfer_rpmsg; 2328c2ecf20Sopenharmony_ci ec_dev->pkt_xfer = cros_ec_pkt_xfer_rpmsg; 2338c2ecf20Sopenharmony_ci ec_dev->phys_name = dev_name(&rpdev->dev); 2348c2ecf20Sopenharmony_ci ec_dev->din_size = sizeof(struct ec_host_response) + 2358c2ecf20Sopenharmony_ci sizeof(struct ec_response_get_protocol_info); 2368c2ecf20Sopenharmony_ci ec_dev->dout_size = sizeof(struct ec_host_request); 2378c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ec_dev); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ec_rpmsg->rpdev = rpdev; 2408c2ecf20Sopenharmony_ci init_completion(&ec_rpmsg->xfer_ack); 2418c2ecf20Sopenharmony_ci INIT_WORK(&ec_rpmsg->host_event_work, 2428c2ecf20Sopenharmony_ci cros_ec_rpmsg_host_event_function); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ec_rpmsg->ept = cros_ec_rpmsg_create_ept(rpdev); 2458c2ecf20Sopenharmony_ci if (!ec_rpmsg->ept) 2468c2ecf20Sopenharmony_ci return -ENOMEM; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = cros_ec_register(ec_dev); 2498c2ecf20Sopenharmony_ci if (ret < 0) { 2508c2ecf20Sopenharmony_ci rpmsg_destroy_ept(ec_rpmsg->ept); 2518c2ecf20Sopenharmony_ci cancel_work_sync(&ec_rpmsg->host_event_work); 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ec_rpmsg->probe_done = true; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (ec_rpmsg->has_pending_host_event) 2588c2ecf20Sopenharmony_ci schedule_work(&ec_rpmsg->host_event_work); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev); 2668c2ecf20Sopenharmony_ci struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci cros_ec_unregister(ec_dev); 2698c2ecf20Sopenharmony_ci rpmsg_destroy_ept(ec_rpmsg->ept); 2708c2ecf20Sopenharmony_ci cancel_work_sync(&ec_rpmsg->host_event_work); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2748c2ecf20Sopenharmony_cistatic int cros_ec_rpmsg_suspend(struct device *dev) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return cros_ec_suspend(ec_dev); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int cros_ec_rpmsg_resume(struct device *dev) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return cros_ec_resume(ec_dev); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci#endif 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cros_ec_rpmsg_pm_ops, cros_ec_rpmsg_suspend, 2908c2ecf20Sopenharmony_ci cros_ec_rpmsg_resume); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic const struct of_device_id cros_ec_rpmsg_of_match[] = { 2938c2ecf20Sopenharmony_ci { .compatible = "google,cros-ec-rpmsg", }, 2948c2ecf20Sopenharmony_ci { } 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cros_ec_rpmsg_of_match); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct rpmsg_driver cros_ec_driver_rpmsg = { 2998c2ecf20Sopenharmony_ci .drv = { 3008c2ecf20Sopenharmony_ci .name = "cros-ec-rpmsg", 3018c2ecf20Sopenharmony_ci .of_match_table = cros_ec_rpmsg_of_match, 3028c2ecf20Sopenharmony_ci .pm = &cros_ec_rpmsg_pm_ops, 3038c2ecf20Sopenharmony_ci }, 3048c2ecf20Sopenharmony_ci .probe = cros_ec_rpmsg_probe, 3058c2ecf20Sopenharmony_ci .remove = cros_ec_rpmsg_remove, 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cimodule_rpmsg_driver(cros_ec_driver_rpmsg); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ChromeOS EC multi function device (rpmsg)"); 312