linux/drivers/platform/chrome/cros_ec_rpmsg.c
<<
>>
Prefs
   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/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       200
  19#define HOST_COMMAND_MARK       1
  20#define HOST_EVENT_MARK         2
  21
  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 or
  26 *              HOST_EVENT_MARK, representing that the message is a response to
  27 *              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 to
  39 * @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 reply
  56 *
  57 * @ec_dev: ChromeOS EC device
  58 * @ec_msg: Message to transfer
  59 *
  60 * This is only used for old EC proto version, and is not supported for this
  61 * driver.
  62 *
  63 * Return: -EINVAL
  64 */
  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 reply
  73 *
  74 * @ec_dev: ChromeOS EC device
  75 * @ec_msg: Message to transfer
  76 *
  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        dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
  93
  94        reinit_completion(&ec_rpmsg->xfer_ack);
  95        ret = rpmsg_send(ec_rpmsg->ept, ec_dev->dout, len);
  96        if (ret) {
  97                dev_err(ec_dev->dev, "rpmsg send failed\n");
  98                return ret;
  99        }
 100
 101        timeout = msecs_to_jiffies(EC_MSG_TIMEOUT_MS);
 102        ret = wait_for_completion_timeout(&ec_rpmsg->xfer_ack, timeout);
 103        if (!ret) {
 104                dev_err(ec_dev->dev, "rpmsg send timeout\n");
 105                return -EIO;
 106        }
 107
 108        /* check response error code */
 109        response = (struct ec_host_response *)ec_dev->din;
 110        ec_msg->result = response->result;
 111
 112        ret = cros_ec_check_result(ec_dev, ec_msg);
 113        if (ret)
 114                goto exit;
 115
 116        if (response->data_len > ec_msg->insize) {
 117                dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
 118                        response->data_len, ec_msg->insize);
 119                ret = -EMSGSIZE;
 120                goto exit;
 121        }
 122
 123        /* copy response packet payload and compute checksum */
 124        memcpy(ec_msg->data, ec_dev->din + sizeof(*response),
 125               response->data_len);
 126
 127        sum = 0;
 128        for (i = 0; i < sizeof(*response) + response->data_len; i++)
 129                sum += ec_dev->din[i];
 130
 131        if (sum) {
 132                dev_err(ec_dev->dev, "bad packet checksum, calculated %x\n",
 133                        sum);
 134                ret = -EBADMSG;
 135                goto exit;
 136        }
 137
 138        ret = response->data_len;
 139exit:
 140        if (ec_msg->command == EC_CMD_REBOOT_EC)
 141                msleep(EC_REBOOT_DELAY_MS);
 142
 143        return ret;
 144}
 145
 146static void
 147cros_ec_rpmsg_host_event_function(struct work_struct *host_event_work)
 148{
 149        struct cros_ec_rpmsg *ec_rpmsg = container_of(host_event_work,
 150                                                      struct cros_ec_rpmsg,
 151                                                      host_event_work);
 152
 153        cros_ec_irq_thread(0, dev_get_drvdata(&ec_rpmsg->rpdev->dev));
 154}
 155
 156static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
 157                                  int len, void *priv, u32 src)
 158{
 159        struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);
 160        struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
 161        struct cros_ec_rpmsg_response *resp;
 162
 163        if (!len) {
 164                dev_warn(ec_dev->dev, "rpmsg received empty response");
 165                return -EINVAL;
 166        }
 167
 168        resp = data;
 169        len -= offsetof(struct cros_ec_rpmsg_response, data);
 170        if (resp->type == HOST_COMMAND_MARK) {
 171                if (len > ec_dev->din_size) {
 172                        dev_warn(ec_dev->dev,
 173                                 "received length %d > din_size %d, truncating",
 174                                 len, ec_dev->din_size);
 175                        len = ec_dev->din_size;
 176                }
 177
 178                memcpy(ec_dev->din, resp->data, len);
 179                complete(&ec_rpmsg->xfer_ack);
 180        } else if (resp->type == HOST_EVENT_MARK) {
 181                /*
 182                 * If the host event is sent before cros_ec_register is
 183                 * finished, queue the host event.
 184                 */
 185                if (ec_rpmsg->probe_done)
 186                        schedule_work(&ec_rpmsg->host_event_work);
 187                else
 188                        ec_rpmsg->has_pending_host_event = true;
 189        } else {
 190                dev_warn(ec_dev->dev, "rpmsg received invalid type = %d",
 191                         resp->type);
 192                return -EINVAL;
 193        }
 194
 195        return 0;
 196}
 197
 198static struct rpmsg_endpoint *
 199cros_ec_rpmsg_create_ept(struct rpmsg_device *rpdev)
 200{
 201        struct rpmsg_channel_info chinfo = {};
 202
 203        strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
 204        chinfo.src = rpdev->src;
 205        chinfo.dst = RPMSG_ADDR_ANY;
 206
 207        return rpmsg_create_ept(rpdev, cros_ec_rpmsg_callback, NULL, chinfo);
 208}
 209
 210static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
 211{
 212        struct device *dev = &rpdev->dev;
 213        struct cros_ec_rpmsg *ec_rpmsg;
 214        struct cros_ec_device *ec_dev;
 215        int ret;
 216
 217        ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
 218        if (!ec_dev)
 219                return -ENOMEM;
 220
 221        ec_rpmsg = devm_kzalloc(dev, sizeof(*ec_rpmsg), GFP_KERNEL);
 222        if (!ec_rpmsg)
 223                return -ENOMEM;
 224
 225        ec_dev->dev = dev;
 226        ec_dev->priv = ec_rpmsg;
 227        ec_dev->cmd_xfer = cros_ec_cmd_xfer_rpmsg;
 228        ec_dev->pkt_xfer = cros_ec_pkt_xfer_rpmsg;
 229        ec_dev->phys_name = dev_name(&rpdev->dev);
 230        ec_dev->din_size = sizeof(struct ec_host_response) +
 231                           sizeof(struct ec_response_get_protocol_info);
 232        ec_dev->dout_size = sizeof(struct ec_host_request);
 233        dev_set_drvdata(dev, ec_dev);
 234
 235        ec_rpmsg->rpdev = rpdev;
 236        init_completion(&ec_rpmsg->xfer_ack);
 237        INIT_WORK(&ec_rpmsg->host_event_work,
 238                  cros_ec_rpmsg_host_event_function);
 239
 240        ec_rpmsg->ept = cros_ec_rpmsg_create_ept(rpdev);
 241        if (!ec_rpmsg->ept)
 242                return -ENOMEM;
 243
 244        ret = cros_ec_register(ec_dev);
 245        if (ret < 0) {
 246                rpmsg_destroy_ept(ec_rpmsg->ept);
 247                cancel_work_sync(&ec_rpmsg->host_event_work);
 248                return ret;
 249        }
 250
 251        ec_rpmsg->probe_done = true;
 252
 253        if (ec_rpmsg->has_pending_host_event)
 254                schedule_work(&ec_rpmsg->host_event_work);
 255
 256        return 0;
 257}
 258
 259static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev)
 260{
 261        struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev);
 262        struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv;
 263
 264        cros_ec_unregister(ec_dev);
 265        rpmsg_destroy_ept(ec_rpmsg->ept);
 266        cancel_work_sync(&ec_rpmsg->host_event_work);
 267}
 268
 269#ifdef CONFIG_PM_SLEEP
 270static int cros_ec_rpmsg_suspend(struct device *dev)
 271{
 272        struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
 273
 274        return cros_ec_suspend(ec_dev);
 275}
 276
 277static int cros_ec_rpmsg_resume(struct device *dev)
 278{
 279        struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
 280
 281        return cros_ec_resume(ec_dev);
 282}
 283#endif
 284
 285static SIMPLE_DEV_PM_OPS(cros_ec_rpmsg_pm_ops, cros_ec_rpmsg_suspend,
 286                         cros_ec_rpmsg_resume);
 287
 288static const struct of_device_id cros_ec_rpmsg_of_match[] = {
 289        { .compatible = "google,cros-ec-rpmsg", },
 290        { }
 291};
 292MODULE_DEVICE_TABLE(of, cros_ec_rpmsg_of_match);
 293
 294static struct rpmsg_driver cros_ec_driver_rpmsg = {
 295        .drv = {
 296                .name   = "cros-ec-rpmsg",
 297                .of_match_table = cros_ec_rpmsg_of_match,
 298                .pm     = &cros_ec_rpmsg_pm_ops,
 299        },
 300        .probe          = cros_ec_rpmsg_probe,
 301        .remove         = cros_ec_rpmsg_remove,
 302};
 303
 304module_rpmsg_driver(cros_ec_driver_rpmsg);
 305
 306MODULE_LICENSE("GPL v2");
 307MODULE_DESCRIPTION("ChromeOS EC multi function device (rpmsg)");
 308