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#include <asm/unaligned.h>
  23
  24#define EC_COMMAND_RETRIES      50
  25
  26static int prepare_packet(struct cros_ec_device *ec_dev,
  27                          struct cros_ec_command *msg)
  28{
  29        struct ec_host_request *request;
  30        u8 *out;
  31        int i;
  32        u8 csum = 0;
  33
  34        BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
  35        BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
  36
  37        out = ec_dev->dout;
  38        request = (struct ec_host_request *)out;
  39        request->struct_version = EC_HOST_REQUEST_VERSION;
  40        request->checksum = 0;
  41        request->command = msg->command;
  42        request->command_version = msg->version;
  43        request->reserved = 0;
  44        request->data_len = msg->outsize;
  45
  46        for (i = 0; i < sizeof(*request); i++)
  47                csum += out[i];
  48
  49        /* Copy data and update checksum */
  50        memcpy(out + sizeof(*request), msg->data, msg->outsize);
  51        for (i = 0; i < msg->outsize; i++)
  52                csum += msg->data[i];
  53
  54        request->checksum = -csum;
  55
  56        return sizeof(*request) + msg->outsize;
  57}
  58
  59static int send_command(struct cros_ec_device *ec_dev,
  60                        struct cros_ec_command *msg)
  61{
  62        int ret;
  63        int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg);
  64
  65        if (ec_dev->proto_version > 2)
  66                xfer_fxn = ec_dev->pkt_xfer;
  67        else
  68                xfer_fxn = ec_dev->cmd_xfer;
  69
  70        ret = (*xfer_fxn)(ec_dev, msg);
  71        if (msg->result == EC_RES_IN_PROGRESS) {
  72                int i;
  73                struct cros_ec_command *status_msg;
  74                struct ec_response_get_comms_status *status;
  75
  76                status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
  77                                     GFP_KERNEL);
  78                if (!status_msg)
  79                        return -ENOMEM;
  80
  81                status_msg->version = 0;
  82                status_msg->command = EC_CMD_GET_COMMS_STATUS;
  83                status_msg->insize = sizeof(*status);
  84                status_msg->outsize = 0;
  85
  86                /*
  87                 * Query the EC's status until it's no longer busy or
  88                 * we encounter an error.
  89                 */
  90                for (i = 0; i < EC_COMMAND_RETRIES; i++) {
  91                        usleep_range(10000, 11000);
  92
  93                        ret = (*xfer_fxn)(ec_dev, status_msg);
  94                        if (ret == -EAGAIN)
  95                                continue;
  96                        if (ret < 0)
  97                                break;
  98
  99                        msg->result = status_msg->result;
 100                        if (status_msg->result != EC_RES_SUCCESS)
 101                                break;
 102
 103                        status = (struct ec_response_get_comms_status *)
 104                                 status_msg->data;
 105                        if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
 106                                break;
 107                }
 108
 109                kfree(status_msg);
 110        }
 111
 112        return ret;
 113}
 114
 115int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
 116                       struct cros_ec_command *msg)
 117{
 118        u8 *out;
 119        u8 csum;
 120        int i;
 121
 122        if (ec_dev->proto_version > 2)
 123                return prepare_packet(ec_dev, msg);
 124
 125        BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
 126        out = ec_dev->dout;
 127        out[0] = EC_CMD_VERSION0 + msg->version;
 128        out[1] = msg->command;
 129        out[2] = msg->outsize;
 130        csum = out[0] + out[1] + out[2];
 131        for (i = 0; i < msg->outsize; i++)
 132                csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
 133        out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum;
 134
 135        return EC_MSG_TX_PROTO_BYTES + msg->outsize;
 136}
 137EXPORT_SYMBOL(cros_ec_prepare_tx);
 138
 139int cros_ec_check_result(struct cros_ec_device *ec_dev,
 140                         struct cros_ec_command *msg)
 141{
 142        switch (msg->result) {
 143        case EC_RES_SUCCESS:
 144                return 0;
 145        case EC_RES_IN_PROGRESS:
 146                dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
 147                        msg->command);
 148                return -EAGAIN;
 149        default:
 150                dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
 151                        msg->command, msg->result);
 152                return 0;
 153        }
 154}
 155EXPORT_SYMBOL(cros_ec_check_result);
 156
 157/*
 158 * cros_ec_get_host_event_wake_mask
 159 *
 160 * Get the mask of host events that cause wake from suspend.
 161 *
 162 * @ec_dev: EC device to call
 163 * @msg: message structure to use
 164 * @mask: result when function returns >=0.
 165 *
 166 * LOCKING:
 167 * the caller has ec_dev->lock mutex, or the caller knows there is
 168 * no other command in progress.
 169 */
 170static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev,
 171                                            struct cros_ec_command *msg,
 172                                            uint32_t *mask)
 173{
 174        struct ec_response_host_event_mask *r;
 175        int ret;
 176
 177        msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK;
 178        msg->version = 0;
 179        msg->outsize = 0;
 180        msg->insize = sizeof(*r);
 181
 182        ret = send_command(ec_dev, msg);
 183        if (ret > 0) {
 184                r = (struct ec_response_host_event_mask *)msg->data;
 185                *mask = r->mask;
 186        }
 187
 188        return ret;
 189}
 190
 191static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
 192                                            int devidx,
 193                                            struct cros_ec_command *msg)
 194{
 195        /*
 196         * Try using v3+ to query for supported protocols. If this
 197         * command fails, fall back to v2. Returns the highest protocol
 198         * supported by the EC.
 199         * Also sets the max request/response/passthru size.
 200         */
 201        int ret;
 202
 203        if (!ec_dev->pkt_xfer)
 204                return -EPROTONOSUPPORT;
 205
 206        memset(msg, 0, sizeof(*msg));
 207        msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO;
 208        msg->insize = sizeof(struct ec_response_get_protocol_info);
 209
 210        ret = send_command(ec_dev, msg);
 211
 212        if (ret < 0) {
 213                dev_dbg(ec_dev->dev,
 214                        "failed to check for EC[%d] protocol version: %d\n",
 215                        devidx, ret);
 216                return ret;
 217        }
 218
 219        if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND)
 220                return -ENODEV;
 221        else if (msg->result != EC_RES_SUCCESS)
 222                return msg->result;
 223
 224        return 0;
 225}
 226
 227static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
 228{
 229        struct cros_ec_command *msg;
 230        struct ec_params_hello *hello_params;
 231        struct ec_response_hello *hello_response;
 232        int ret;
 233        int len = max(sizeof(*hello_params), sizeof(*hello_response));
 234
 235        msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
 236        if (!msg)
 237                return -ENOMEM;
 238
 239        msg->version = 0;
 240        msg->command = EC_CMD_HELLO;
 241        hello_params = (struct ec_params_hello *)msg->data;
 242        msg->outsize = sizeof(*hello_params);
 243        hello_response = (struct ec_response_hello *)msg->data;
 244        msg->insize = sizeof(*hello_response);
 245
 246        hello_params->in_data = 0xa0b0c0d0;
 247
 248        ret = send_command(ec_dev, msg);
 249
 250        if (ret < 0) {
 251                dev_dbg(ec_dev->dev,
 252                        "EC failed to respond to v2 hello: %d\n",
 253                        ret);
 254                goto exit;
 255        } else if (msg->result != EC_RES_SUCCESS) {
 256                dev_err(ec_dev->dev,
 257                        "EC responded to v2 hello with error: %d\n",
 258                        msg->result);
 259                ret = msg->result;
 260                goto exit;
 261        } else if (hello_response->out_data != 0xa1b2c3d4) {
 262                dev_err(ec_dev->dev,
 263                        "EC responded to v2 hello with bad result: %u\n",
 264                        hello_response->out_data);
 265                ret = -EBADMSG;
 266                goto exit;
 267        }
 268
 269        ret = 0;
 270
 271 exit:
 272        kfree(msg);
 273        return ret;
 274}
 275
 276/*
 277 * cros_ec_get_host_command_version_mask
 278 *
 279 * Get the version mask of a given command.
 280 *
 281 * @ec_dev: EC device to call
 282 * @msg: message structure to use
 283 * @cmd: command to get the version of.
 284 * @mask: result when function returns 0.
 285 *
 286 * @return 0 on success, error code otherwise
 287 *
 288 * LOCKING:
 289 * the caller has ec_dev->lock mutex or the caller knows there is
 290 * no other command in progress.
 291 */
 292static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev,
 293        u16 cmd, u32 *mask)
 294{
 295        struct ec_params_get_cmd_versions *pver;
 296        struct ec_response_get_cmd_versions *rver;
 297        struct cros_ec_command *msg;
 298        int ret;
 299
 300        msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)),
 301                      GFP_KERNEL);
 302        if (!msg)
 303                return -ENOMEM;
 304
 305        msg->version = 0;
 306        msg->command = EC_CMD_GET_CMD_VERSIONS;
 307        msg->insize = sizeof(*rver);
 308        msg->outsize = sizeof(*pver);
 309
 310        pver = (struct ec_params_get_cmd_versions *)msg->data;
 311        pver->cmd = cmd;
 312
 313        ret = send_command(ec_dev, msg);
 314        if (ret > 0) {
 315                rver = (struct ec_response_get_cmd_versions *)msg->data;
 316                *mask = rver->version_mask;
 317        }
 318
 319        kfree(msg);
 320
 321        return ret;
 322}
 323
 324int cros_ec_query_all(struct cros_ec_device *ec_dev)
 325{
 326        struct device *dev = ec_dev->dev;
 327        struct cros_ec_command *proto_msg;
 328        struct ec_response_get_protocol_info *proto_info;
 329        u32 ver_mask = 0;
 330        int ret;
 331
 332        proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
 333                            GFP_KERNEL);
 334        if (!proto_msg)
 335                return -ENOMEM;
 336
 337        /* First try sending with proto v3. */
 338        ec_dev->proto_version = 3;
 339        ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg);
 340
 341        if (ret == 0) {
 342                proto_info = (struct ec_response_get_protocol_info *)
 343                        proto_msg->data;
 344                ec_dev->max_request = proto_info->max_request_packet_size -
 345                        sizeof(struct ec_host_request);
 346                ec_dev->max_response = proto_info->max_response_packet_size -
 347                        sizeof(struct ec_host_response);
 348                ec_dev->proto_version =
 349                        min(EC_HOST_REQUEST_VERSION,
 350                                        fls(proto_info->protocol_versions) - 1);
 351                dev_dbg(ec_dev->dev,
 352                        "using proto v%u\n",
 353                        ec_dev->proto_version);
 354
 355                ec_dev->din_size = ec_dev->max_response +
 356                        sizeof(struct ec_host_response) +
 357                        EC_MAX_RESPONSE_OVERHEAD;
 358                ec_dev->dout_size = ec_dev->max_request +
 359                        sizeof(struct ec_host_request) +
 360                        EC_MAX_REQUEST_OVERHEAD;
 361
 362                /*
 363                 * Check for PD
 364                 */
 365                ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg);
 366
 367                if (ret) {
 368                        dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret);
 369                        ec_dev->max_passthru = 0;
 370                } else {
 371                        dev_dbg(ec_dev->dev, "found PD chip\n");
 372                        ec_dev->max_passthru =
 373                                proto_info->max_request_packet_size -
 374                                sizeof(struct ec_host_request);
 375                }
 376        } else {
 377                /* Try querying with a v2 hello message. */
 378                ec_dev->proto_version = 2;
 379                ret = cros_ec_host_command_proto_query_v2(ec_dev);
 380
 381                if (ret == 0) {
 382                        /* V2 hello succeeded. */
 383                        dev_dbg(ec_dev->dev, "falling back to proto v2\n");
 384
 385                        ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE;
 386                        ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE;
 387                        ec_dev->max_passthru = 0;
 388                        ec_dev->pkt_xfer = NULL;
 389                        ec_dev->din_size = EC_PROTO2_MSG_BYTES;
 390                        ec_dev->dout_size = EC_PROTO2_MSG_BYTES;
 391                } else {
 392                        /*
 393                         * It's possible for a test to occur too early when
 394                         * the EC isn't listening. If this happens, we'll
 395                         * test later when the first command is run.
 396                         */
 397                        ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
 398                        dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret);
 399                        goto exit;
 400                }
 401        }
 402
 403        devm_kfree(dev, ec_dev->din);
 404        devm_kfree(dev, ec_dev->dout);
 405
 406        ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
 407        if (!ec_dev->din) {
 408                ret = -ENOMEM;
 409                goto exit;
 410        }
 411
 412        ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
 413        if (!ec_dev->dout) {
 414                devm_kfree(dev, ec_dev->din);
 415                ret = -ENOMEM;
 416                goto exit;
 417        }
 418
 419        /* Probe if MKBP event is supported */
 420        ret = cros_ec_get_host_command_version_mask(ec_dev,
 421                                                    EC_CMD_GET_NEXT_EVENT,
 422                                                    &ver_mask);
 423        if (ret < 0 || ver_mask == 0)
 424                ec_dev->mkbp_event_supported = 0;
 425        else
 426                ec_dev->mkbp_event_supported = 1;
 427
 428        /*
 429         * Get host event wake mask, assume all events are wake events
 430         * if unavailable.
 431         */
 432        ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg,
 433                                               &ec_dev->host_event_wake_mask);
 434        if (ret < 0)
 435                ec_dev->host_event_wake_mask = U32_MAX;
 436
 437        ret = 0;
 438
 439exit:
 440        kfree(proto_msg);
 441        return ret;
 442}
 443EXPORT_SYMBOL(cros_ec_query_all);
 444
 445int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
 446                     struct cros_ec_command *msg)
 447{
 448        int ret;
 449
 450        mutex_lock(&ec_dev->lock);
 451        if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
 452                ret = cros_ec_query_all(ec_dev);
 453                if (ret) {
 454                        dev_err(ec_dev->dev,
 455                                "EC version unknown and query failed; aborting command\n");
 456                        mutex_unlock(&ec_dev->lock);
 457                        return ret;
 458                }
 459        }
 460
 461        if (msg->insize > ec_dev->max_response) {
 462                dev_dbg(ec_dev->dev, "clamping message receive buffer\n");
 463                msg->insize = ec_dev->max_response;
 464        }
 465
 466        if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) {
 467                if (msg->outsize > ec_dev->max_request) {
 468                        dev_err(ec_dev->dev,
 469                                "request of size %u is too big (max: %u)\n",
 470                                msg->outsize,
 471                                ec_dev->max_request);
 472                        mutex_unlock(&ec_dev->lock);
 473                        return -EMSGSIZE;
 474                }
 475        } else {
 476                if (msg->outsize > ec_dev->max_passthru) {
 477                        dev_err(ec_dev->dev,
 478                                "passthru rq of size %u is too big (max: %u)\n",
 479                                msg->outsize,
 480                                ec_dev->max_passthru);
 481                        mutex_unlock(&ec_dev->lock);
 482                        return -EMSGSIZE;
 483                }
 484        }
 485        ret = send_command(ec_dev, msg);
 486        mutex_unlock(&ec_dev->lock);
 487
 488        return ret;
 489}
 490EXPORT_SYMBOL(cros_ec_cmd_xfer);
 491
 492int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev,
 493                            struct cros_ec_command *msg)
 494{
 495        int ret;
 496
 497        ret = cros_ec_cmd_xfer(ec_dev, msg);
 498        if (ret < 0) {
 499                dev_err(ec_dev->dev, "Command xfer error (err:%d)\n", ret);
 500        } else if (msg->result != EC_RES_SUCCESS) {
 501                dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result);
 502                return -EPROTO;
 503        }
 504
 505        return ret;
 506}
 507EXPORT_SYMBOL(cros_ec_cmd_xfer_status);
 508
 509static int get_next_event(struct cros_ec_device *ec_dev)
 510{
 511        u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)];
 512        struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
 513        int ret;
 514
 515        if (ec_dev->suspended) {
 516                dev_dbg(ec_dev->dev, "Device suspended.\n");
 517                return -EHOSTDOWN;
 518        }
 519
 520        msg->version = 0;
 521        msg->command = EC_CMD_GET_NEXT_EVENT;
 522        msg->insize = sizeof(ec_dev->event_data);
 523        msg->outsize = 0;
 524
 525        ret = cros_ec_cmd_xfer(ec_dev, msg);
 526        if (ret > 0) {
 527                ec_dev->event_size = ret - 1;
 528                memcpy(&ec_dev->event_data, msg->data,
 529                       sizeof(ec_dev->event_data));
 530        }
 531
 532        return ret;
 533}
 534
 535static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
 536{
 537        u8 buffer[sizeof(struct cros_ec_command) +
 538                  sizeof(ec_dev->event_data.data)];
 539        struct cros_ec_command *msg = (struct cros_ec_command *)&buffer;
 540
 541        msg->version = 0;
 542        msg->command = EC_CMD_MKBP_STATE;
 543        msg->insize = sizeof(ec_dev->event_data.data);
 544        msg->outsize = 0;
 545
 546        ec_dev->event_size = cros_ec_cmd_xfer(ec_dev, msg);
 547        ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX;
 548        memcpy(&ec_dev->event_data.data, msg->data,
 549               sizeof(ec_dev->event_data.data));
 550
 551        return ec_dev->event_size;
 552}
 553
 554int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
 555{
 556        u32 host_event;
 557        int ret;
 558
 559        if (!ec_dev->mkbp_event_supported) {
 560                ret = get_keyboard_state_event(ec_dev);
 561                if (ret < 0)
 562                        return ret;
 563
 564                if (wake_event)
 565                        *wake_event = true;
 566
 567                return ret;
 568        }
 569
 570        ret = get_next_event(ec_dev);
 571        if (ret < 0)
 572                return ret;
 573
 574        if (wake_event) {
 575                host_event = cros_ec_get_host_event(ec_dev);
 576
 577                /* Consider non-host_event as wake event */
 578                *wake_event = !host_event ||
 579                              !!(host_event & ec_dev->host_event_wake_mask);
 580        }
 581
 582        return ret;
 583}
 584EXPORT_SYMBOL(cros_ec_get_next_event);
 585
 586u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
 587{
 588        u32 host_event;
 589
 590        BUG_ON(!ec_dev->mkbp_event_supported);
 591
 592        if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT)
 593                return 0;
 594
 595        if (ec_dev->event_size != sizeof(host_event)) {
 596                dev_warn(ec_dev->dev, "Invalid host event size\n");
 597                return 0;
 598        }
 599
 600        host_event = get_unaligned_le32(&ec_dev->event_data.data.host_event);
 601
 602        return host_event;
 603}
 604EXPORT_SYMBOL(cros_ec_get_host_event);
 605