linux/drivers/platform/chrome/cros_ec_proto.c
<<
>>
Prefs
   1/*
   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
  25static int prepare_packet(struct cros_ec_device *ec_dev,
  26                          struct cros_ec_command *msg)
  27{
  28        struct ec_host_request *request;
  29        u8 *out;
  30        int i;
  31        u8 csum = 0;
  32
  33        BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
  34        BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
  35
  36        out = ec_dev->dout;
  37        request = (struct ec_host_request *)out;
  38        request->struct_version = EC_HOST_REQUEST_VERSION;
  39        request->checksum = 0;
  40        request->command = msg->command;
  41        request->command_version = msg->version;
  42        request->reserved = 0;
  43        request->data_len = msg->outsize;
  44
  45        for (i = 0; i < sizeof(*request); i++)
  46                csum += out[i];
  47
  48        /* Copy data and update checksum */
  49        memcpy(out + sizeof(*request), msg->data, msg->outsize);
  50        for (i = 0; i < msg->outsize; i++)
  51                csum += msg->data[i];
  52
  53        request->checksum = -csum;
  54
  55        return sizeof(*request) + msg->outsize;
  56}
  57
  58static int send_command(struct cros_ec_device *ec_dev,
  59                        struct cros_ec_command *msg)
  60{
  61        int ret;
  62
  63        if (ec_dev->proto_version > 2)
  64                ret = ec_dev->pkt_xfer(ec_dev, msg);
  65        else
  66                ret = ec_dev->cmd_xfer(ec_dev, msg);
  67
  68        if (msg->result == EC_RES_IN_PROGRESS) {
  69                int i;
  70                struct cros_ec_command *status_msg;
  71                struct ec_response_get_comms_status *status;
  72
  73                status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
  74                                     GFP_KERNEL);
  75                if (!status_msg)
  76                        return -ENOMEM;
  77
  78                status_msg->version = 0;
  79                status_msg->command = EC_CMD_GET_COMMS_STATUS;
  80                status_msg->insize = sizeof(*status);
  81                status_msg->outsize = 0;
  82
  83                /*
  84                 * Query the EC's status until it's no longer busy or
  85                 * we encounter an error.
  86                 */
  87                for (i = 0; i < EC_COMMAND_RETRIES; i++) {
  88                        usleep_range(10000, 11000);
  89
  90                        ret = ec_dev->cmd_xfer(ec_dev, status_msg);
  91                        if (ret < 0)
  92                                break;
  93
  94                        msg->result = status_msg->result;
  95                        if (status_msg->result != EC_RES_SUCCESS)
  96                                break;
  97
  98                        status = (struct ec_response_get_comms_status *)
  99                                 status_msg->data;
 100                        if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
 101                                break;
 102                }
 103
 104                kfree(status_msg);
 105        }
 106
 107        return ret;
 108}
 109
 110int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
 111                       struct cros_ec_command *msg)
 112{
 113        u8 *out;
 114        u8 csum;
 115        int i;
 116
 117        if (ec_dev->proto_version > 2)
 118                return prepare_packet(ec_dev, msg);
 119
 120        BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
 121        out = ec_dev->dout;
 122        out[0] = EC_CMD_VERSION0 + msg->version;
 123        out[1] = msg->command;
 124        out[2] = msg->outsize;
 125        csum = out[0] + out[1] + out[2];
 126        for (i = 0; i < msg->outsize; i++)
 127                csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
 128        out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum;
 129
 130        return EC_MSG_TX_PROTO_BYTES + msg->outsize;
 131}
 132EXPORT_SYMBOL(cros_ec_prepare_tx);
 133
 134int cros_ec_check_result(struct cros_ec_device *ec_dev,
 135                         struct cros_ec_command *msg)
 136{
 137        switch (msg->result) {
 138        case EC_RES_SUCCESS:
 139                return 0;
 140        case EC_RES_IN_PROGRESS:
 141                dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
 142                        msg->command);
 143                return -EAGAIN;
 144        default:
 145                dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
 146                        msg->command, msg->result);
 147                return 0;
 148        }
 149}
 150EXPORT_SYMBOL(cros_ec_check_result);
 151
 152static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
 153                                            int devidx,
 154                                            struct cros_ec_command *msg)
 155{
 156        /*
 157         * Try using v3+ to query for supported protocols. If this
 158         * command fails, fall back to v2. Returns the highest protocol
 159         * supported by the EC.
 160         * Also sets the max request/response/passthru size.
 161         */
 162        int ret;
 163
 164        if (!ec_dev->pkt_xfer)
 165                return -EPROTONOSUPPORT;
 166
 167        memset(msg, 0, sizeof(*msg));
 168        msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO;
 169        msg->insize = sizeof(struct ec_response_get_protocol_info);
 170
 171        ret = send_command(ec_dev, msg);
 172
 173        if (ret < 0) {
 174                dev_dbg(ec_dev->dev,
 175                        "failed to check for EC[%d] protocol version: %d\n",
 176                        devidx, ret);
 177                return ret;
 178        }
 179
 180        if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND)
 181                return -ENODEV;
 182        else if (msg->result != EC_RES_SUCCESS)
 183                return msg->result;
 184
 185        return 0;
 186}
 187
 188static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
 189{
 190        struct cros_ec_command *msg;
 191        struct ec_params_hello *hello_params;
 192        struct ec_response_hello *hello_response;
 193        int ret;
 194        int len = max(sizeof(*hello_params), sizeof(*hello_response));
 195
 196        msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
 197        if (!msg)
 198                return -ENOMEM;
 199
 200        msg->version = 0;
 201        msg->command = EC_CMD_HELLO;
 202        hello_params = (struct ec_params_hello *)msg->data;
 203        msg->outsize = sizeof(*hello_params);
 204        hello_response = (struct ec_response_hello *)msg->data;
 205        msg->insize = sizeof(*hello_response);
 206
 207        hello_params->in_data = 0xa0b0c0d0;
 208
 209        ret = send_command(ec_dev, msg);
 210
 211        if (ret < 0) {
 212                dev_dbg(ec_dev->dev,
 213                        "EC failed to respond to v2 hello: %d\n",
 214                        ret);
 215                goto exit;
 216        } else if (msg->result != EC_RES_SUCCESS) {
 217                dev_err(ec_dev->dev,
 218                        "EC responded to v2 hello with error: %d\n",
 219                        msg->result);
 220                ret = msg->result;
 221                goto exit;
 222        } else if (hello_response->out_data != 0xa1b2c3d4) {
 223                dev_err(ec_dev->dev,
 224                        "EC responded to v2 hello with bad result: %u\n",
 225                        hello_response->out_data);
 226                ret = -EBADMSG;
 227                goto exit;
 228        }
 229
 230        ret = 0;
 231
 232 exit:
 233        kfree(msg);
 234        return ret;
 235}
 236
 237int cros_ec_query_all(struct cros_ec_device *ec_dev)
 238{
 239        struct device *dev = ec_dev->dev;
 240        struct cros_ec_command *proto_msg;
 241        struct ec_response_get_protocol_info *proto_info;
 242        int ret;
 243
 244        proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
 245                            GFP_KERNEL);
 246        if (!proto_msg)
 247                return -ENOMEM;
 248
 249        /* First try sending with proto v3. */
 250        ec_dev->proto_version = 3;
 251        ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg);
 252
 253        if (ret == 0) {
 254                proto_info = (struct ec_response_get_protocol_info *)
 255                        proto_msg->data;
 256                ec_dev->max_request = proto_info->max_request_packet_size -
 257                        sizeof(struct ec_host_request);
 258                ec_dev->max_response = proto_info->max_response_packet_size -
 259                        sizeof(struct ec_host_response);
 260                ec_dev->proto_version =
 261                        min(EC_HOST_REQUEST_VERSION,
 262                                        fls(proto_info->protocol_versions) - 1);
 263                dev_dbg(ec_dev->dev,
 264                        "using proto v%u\n",
 265                        ec_dev->proto_version);
 266
 267                ec_dev->din_size = ec_dev->max_response +
 268                        sizeof(struct ec_host_response) +
 269                        EC_MAX_RESPONSE_OVERHEAD;
 270                ec_dev->dout_size = ec_dev->max_request +
 271                        sizeof(struct ec_host_request) +
 272                        EC_MAX_REQUEST_OVERHEAD;
 273
 274                /*
 275                 * Check for PD
 276                 */
 277                ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg);
 278
 279                if (ret) {
 280                        dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret);
 281                        ec_dev->max_passthru = 0;
 282                } else {
 283                        dev_dbg(ec_dev->dev, "found PD chip\n");
 284                        ec_dev->max_passthru =
 285                                proto_info->max_request_packet_size -
 286                                sizeof(struct ec_host_request);
 287                }
 288        } else {
 289                /* Try querying with a v2 hello message. */
 290                ec_dev->proto_version = 2;
 291                ret = cros_ec_host_command_proto_query_v2(ec_dev);
 292
 293                if (ret == 0) {
 294                        /* V2 hello succeeded. */
 295                        dev_dbg(ec_dev->dev, "falling back to proto v2\n");
 296
 297                        ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE;
 298                        ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE;
 299                        ec_dev->max_passthru = 0;
 300                        ec_dev->pkt_xfer = NULL;
 301                        ec_dev->din_size = EC_MSG_BYTES;
 302                        ec_dev->dout_size = EC_MSG_BYTES;
 303                } else {
 304                        /*
 305                         * It's possible for a test to occur too early when
 306                         * the EC isn't listening. If this happens, we'll
 307                         * test later when the first command is run.
 308                         */
 309                        ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
 310                        dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret);
 311                        goto exit;
 312                }
 313        }
 314
 315        devm_kfree(dev, ec_dev->din);
 316        devm_kfree(dev, ec_dev->dout);
 317
 318        ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
 319        if (!ec_dev->din) {
 320                ret = -ENOMEM;
 321                goto exit;
 322        }
 323
 324        ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
 325        if (!ec_dev->dout) {
 326                devm_kfree(dev, ec_dev->din);
 327                ret = -ENOMEM;
 328                goto exit;
 329        }
 330
 331exit:
 332        kfree(proto_msg);
 333        return ret;
 334}
 335EXPORT_SYMBOL(cros_ec_query_all);
 336
 337int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
 338                     struct cros_ec_command *msg)
 339{
 340        int ret;
 341
 342        mutex_lock(&ec_dev->lock);
 343        if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
 344                ret = cros_ec_query_all(ec_dev);
 345                if (ret) {
 346                        dev_err(ec_dev->dev,
 347                                "EC version unknown and query failed; aborting command\n");
 348                        mutex_unlock(&ec_dev->lock);
 349                        return ret;
 350                }
 351        }
 352
 353        if (msg->insize > ec_dev->max_response) {
 354                dev_dbg(ec_dev->dev, "clamping message receive buffer\n");
 355                msg->insize = ec_dev->max_response;
 356        }
 357
 358        if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) {
 359                if (msg->outsize > ec_dev->max_request) {
 360                        dev_err(ec_dev->dev,
 361                                "request of size %u is too big (max: %u)\n",
 362                                msg->outsize,
 363                                ec_dev->max_request);
 364                        mutex_unlock(&ec_dev->lock);
 365                        return -EMSGSIZE;
 366                }
 367        } else {
 368                if (msg->outsize > ec_dev->max_passthru) {
 369                        dev_err(ec_dev->dev,
 370                                "passthru rq of size %u is too big (max: %u)\n",
 371                                msg->outsize,
 372                                ec_dev->max_passthru);
 373                        mutex_unlock(&ec_dev->lock);
 374                        return -EMSGSIZE;
 375                }
 376        }
 377        ret = send_command(ec_dev, msg);
 378        mutex_unlock(&ec_dev->lock);
 379
 380        return ret;
 381}
 382EXPORT_SYMBOL(cros_ec_cmd_xfer);
 383