| .. | .. |
|---|
| 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 | | - */ |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | +// ChromeOS EC communication protocol helper functions |
|---|
| 3 | +// |
|---|
| 4 | +// Copyright (C) 2015 Google, Inc |
|---|
| 16 | 5 | |
|---|
| 17 | | -#include <linux/mfd/cros_ec.h> |
|---|
| 18 | 6 | #include <linux/delay.h> |
|---|
| 19 | 7 | #include <linux/device.h> |
|---|
| 20 | 8 | #include <linux/module.h> |
|---|
| 9 | +#include <linux/platform_data/cros_ec_commands.h> |
|---|
| 10 | +#include <linux/platform_data/cros_ec_proto.h> |
|---|
| 21 | 11 | #include <linux/slab.h> |
|---|
| 22 | 12 | #include <asm/unaligned.h> |
|---|
| 23 | 13 | |
|---|
| 14 | +#include "cros_ec_trace.h" |
|---|
| 15 | + |
|---|
| 24 | 16 | #define EC_COMMAND_RETRIES 50 |
|---|
| 17 | + |
|---|
| 18 | +static const int cros_ec_error_map[] = { |
|---|
| 19 | + [EC_RES_INVALID_COMMAND] = -EOPNOTSUPP, |
|---|
| 20 | + [EC_RES_ERROR] = -EIO, |
|---|
| 21 | + [EC_RES_INVALID_PARAM] = -EINVAL, |
|---|
| 22 | + [EC_RES_ACCESS_DENIED] = -EACCES, |
|---|
| 23 | + [EC_RES_INVALID_RESPONSE] = -EPROTO, |
|---|
| 24 | + [EC_RES_INVALID_VERSION] = -ENOPROTOOPT, |
|---|
| 25 | + [EC_RES_INVALID_CHECKSUM] = -EBADMSG, |
|---|
| 26 | + [EC_RES_IN_PROGRESS] = -EINPROGRESS, |
|---|
| 27 | + [EC_RES_UNAVAILABLE] = -ENODATA, |
|---|
| 28 | + [EC_RES_TIMEOUT] = -ETIMEDOUT, |
|---|
| 29 | + [EC_RES_OVERFLOW] = -EOVERFLOW, |
|---|
| 30 | + [EC_RES_INVALID_HEADER] = -EBADR, |
|---|
| 31 | + [EC_RES_REQUEST_TRUNCATED] = -EBADR, |
|---|
| 32 | + [EC_RES_RESPONSE_TOO_BIG] = -EFBIG, |
|---|
| 33 | + [EC_RES_BUS_ERROR] = -EFAULT, |
|---|
| 34 | + [EC_RES_BUSY] = -EBUSY, |
|---|
| 35 | + [EC_RES_INVALID_HEADER_VERSION] = -EBADMSG, |
|---|
| 36 | + [EC_RES_INVALID_HEADER_CRC] = -EBADMSG, |
|---|
| 37 | + [EC_RES_INVALID_DATA_CRC] = -EBADMSG, |
|---|
| 38 | + [EC_RES_DUP_UNAVAILABLE] = -ENODATA, |
|---|
| 39 | +}; |
|---|
| 40 | + |
|---|
| 41 | +static int cros_ec_map_error(uint32_t result) |
|---|
| 42 | +{ |
|---|
| 43 | + int ret = 0; |
|---|
| 44 | + |
|---|
| 45 | + if (result != EC_RES_SUCCESS) { |
|---|
| 46 | + if (result < ARRAY_SIZE(cros_ec_error_map) && cros_ec_error_map[result]) |
|---|
| 47 | + ret = cros_ec_error_map[result]; |
|---|
| 48 | + else |
|---|
| 49 | + ret = -EPROTO; |
|---|
| 50 | + } |
|---|
| 51 | + |
|---|
| 52 | + return ret; |
|---|
| 53 | +} |
|---|
| 25 | 54 | |
|---|
| 26 | 55 | static int prepare_packet(struct cros_ec_device *ec_dev, |
|---|
| 27 | 56 | struct cros_ec_command *msg) |
|---|
| .. | .. |
|---|
| 78 | 107 | return -EIO; |
|---|
| 79 | 108 | } |
|---|
| 80 | 109 | |
|---|
| 110 | + trace_cros_ec_request_start(msg); |
|---|
| 81 | 111 | ret = (*xfer_fxn)(ec_dev, msg); |
|---|
| 112 | + trace_cros_ec_request_done(msg, ret); |
|---|
| 82 | 113 | if (msg->result == EC_RES_IN_PROGRESS) { |
|---|
| 83 | 114 | int i; |
|---|
| 84 | 115 | struct cros_ec_command *status_msg; |
|---|
| .. | .. |
|---|
| 101 | 132 | for (i = 0; i < EC_COMMAND_RETRIES; i++) { |
|---|
| 102 | 133 | usleep_range(10000, 11000); |
|---|
| 103 | 134 | |
|---|
| 135 | + trace_cros_ec_request_start(status_msg); |
|---|
| 104 | 136 | ret = (*xfer_fxn)(ec_dev, status_msg); |
|---|
| 137 | + trace_cros_ec_request_done(status_msg, ret); |
|---|
| 105 | 138 | if (ret == -EAGAIN) |
|---|
| 106 | 139 | continue; |
|---|
| 107 | 140 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 123 | 156 | return ret; |
|---|
| 124 | 157 | } |
|---|
| 125 | 158 | |
|---|
| 159 | +/** |
|---|
| 160 | + * cros_ec_prepare_tx() - Prepare an outgoing message in the output buffer. |
|---|
| 161 | + * @ec_dev: Device to register. |
|---|
| 162 | + * @msg: Message to write. |
|---|
| 163 | + * |
|---|
| 164 | + * This is intended to be used by all ChromeOS EC drivers, but at present |
|---|
| 165 | + * only SPI uses it. Once LPC uses the same protocol it can start using it. |
|---|
| 166 | + * I2C could use it now, with a refactor of the existing code. |
|---|
| 167 | + * |
|---|
| 168 | + * Return: 0 on success or negative error code. |
|---|
| 169 | + */ |
|---|
| 126 | 170 | int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, |
|---|
| 127 | 171 | struct cros_ec_command *msg) |
|---|
| 128 | 172 | { |
|---|
| .. | .. |
|---|
| 147 | 191 | } |
|---|
| 148 | 192 | EXPORT_SYMBOL(cros_ec_prepare_tx); |
|---|
| 149 | 193 | |
|---|
| 194 | +/** |
|---|
| 195 | + * cros_ec_check_result() - Check ec_msg->result. |
|---|
| 196 | + * @ec_dev: EC device. |
|---|
| 197 | + * @msg: Message to check. |
|---|
| 198 | + * |
|---|
| 199 | + * This is used by ChromeOS EC drivers to check the ec_msg->result for |
|---|
| 200 | + * errors and to warn about them. |
|---|
| 201 | + * |
|---|
| 202 | + * Return: 0 on success or negative error code. |
|---|
| 203 | + */ |
|---|
| 150 | 204 | int cros_ec_check_result(struct cros_ec_device *ec_dev, |
|---|
| 151 | 205 | struct cros_ec_command *msg) |
|---|
| 152 | 206 | { |
|---|
| .. | .. |
|---|
| 191 | 245 | msg->insize = sizeof(*r); |
|---|
| 192 | 246 | |
|---|
| 193 | 247 | ret = send_command(ec_dev, msg); |
|---|
| 248 | + if (ret >= 0) { |
|---|
| 249 | + if (msg->result == EC_RES_INVALID_COMMAND) |
|---|
| 250 | + return -EOPNOTSUPP; |
|---|
| 251 | + if (msg->result != EC_RES_SUCCESS) |
|---|
| 252 | + return -EPROTO; |
|---|
| 253 | + } |
|---|
| 194 | 254 | if (ret > 0) { |
|---|
| 195 | 255 | r = (struct ec_response_host_event_mask *)msg->data; |
|---|
| 196 | 256 | *mask = r->mask; |
|---|
| .. | .. |
|---|
| 341 | 401 | return ret; |
|---|
| 342 | 402 | } |
|---|
| 343 | 403 | |
|---|
| 404 | +/** |
|---|
| 405 | + * cros_ec_query_all() - Query the protocol version supported by the |
|---|
| 406 | + * ChromeOS EC. |
|---|
| 407 | + * @ec_dev: Device to register. |
|---|
| 408 | + * |
|---|
| 409 | + * Return: 0 on success or negative error code. |
|---|
| 410 | + */ |
|---|
| 344 | 411 | int cros_ec_query_all(struct cros_ec_device *ec_dev) |
|---|
| 345 | 412 | { |
|---|
| 346 | 413 | struct device *dev = ec_dev->dev; |
|---|
| .. | .. |
|---|
| 440 | 507 | ret = cros_ec_get_host_command_version_mask(ec_dev, |
|---|
| 441 | 508 | EC_CMD_GET_NEXT_EVENT, |
|---|
| 442 | 509 | &ver_mask); |
|---|
| 443 | | - if (ret < 0 || ver_mask == 0) |
|---|
| 510 | + if (ret < 0 || ver_mask == 0) { |
|---|
| 444 | 511 | ec_dev->mkbp_event_supported = 0; |
|---|
| 445 | | - else |
|---|
| 446 | | - ec_dev->mkbp_event_supported = 1; |
|---|
| 512 | + } else { |
|---|
| 513 | + ec_dev->mkbp_event_supported = fls(ver_mask); |
|---|
| 447 | 514 | |
|---|
| 448 | | - /* |
|---|
| 449 | | - * Get host event wake mask, assume all events are wake events |
|---|
| 450 | | - * if unavailable. |
|---|
| 451 | | - */ |
|---|
| 515 | + dev_dbg(ec_dev->dev, "MKBP support version %u\n", ec_dev->mkbp_event_supported - 1); |
|---|
| 516 | + } |
|---|
| 517 | + |
|---|
| 518 | + /* Probe if host sleep v1 is supported for S0ix failure detection. */ |
|---|
| 519 | + ret = cros_ec_get_host_command_version_mask(ec_dev, |
|---|
| 520 | + EC_CMD_HOST_SLEEP_EVENT, |
|---|
| 521 | + &ver_mask); |
|---|
| 522 | + ec_dev->host_sleep_v1 = (ret >= 0 && (ver_mask & EC_VER_MASK(1))); |
|---|
| 523 | + |
|---|
| 524 | + /* Get host event wake mask. */ |
|---|
| 452 | 525 | ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg, |
|---|
| 453 | 526 | &ec_dev->host_event_wake_mask); |
|---|
| 454 | | - if (ret < 0) |
|---|
| 455 | | - ec_dev->host_event_wake_mask = U32_MAX; |
|---|
| 527 | + if (ret < 0) { |
|---|
| 528 | + /* |
|---|
| 529 | + * If the EC doesn't support EC_CMD_HOST_EVENT_GET_WAKE_MASK, |
|---|
| 530 | + * use a reasonable default. Note that we ignore various |
|---|
| 531 | + * battery, AC status, and power-state events, because (a) |
|---|
| 532 | + * those can be quite common (e.g., when sitting at full |
|---|
| 533 | + * charge, on AC) and (b) these are not actionable wake events; |
|---|
| 534 | + * if anything, we'd like to continue suspending (to save |
|---|
| 535 | + * power), not wake up. |
|---|
| 536 | + */ |
|---|
| 537 | + ec_dev->host_event_wake_mask = U32_MAX & |
|---|
| 538 | + ~(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED) | |
|---|
| 539 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED) | |
|---|
| 540 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW) | |
|---|
| 541 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL) | |
|---|
| 542 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY) | |
|---|
| 543 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | |
|---|
| 544 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS)); |
|---|
| 545 | + /* |
|---|
| 546 | + * Old ECs may not support this command. Complain about all |
|---|
| 547 | + * other errors. |
|---|
| 548 | + */ |
|---|
| 549 | + if (ret != -EOPNOTSUPP) |
|---|
| 550 | + dev_err(ec_dev->dev, |
|---|
| 551 | + "failed to retrieve wake mask: %d\n", ret); |
|---|
| 552 | + } |
|---|
| 456 | 553 | |
|---|
| 457 | 554 | ret = 0; |
|---|
| 458 | 555 | |
|---|
| .. | .. |
|---|
| 462 | 559 | } |
|---|
| 463 | 560 | EXPORT_SYMBOL(cros_ec_query_all); |
|---|
| 464 | 561 | |
|---|
| 465 | | -int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, |
|---|
| 466 | | - struct cros_ec_command *msg) |
|---|
| 562 | +/** |
|---|
| 563 | + * cros_ec_cmd_xfer() - Send a command to the ChromeOS EC. |
|---|
| 564 | + * @ec_dev: EC device. |
|---|
| 565 | + * @msg: Message to write. |
|---|
| 566 | + * |
|---|
| 567 | + * Call this to send a command to the ChromeOS EC. This should be used instead |
|---|
| 568 | + * of calling the EC's cmd_xfer() callback directly. This function does not |
|---|
| 569 | + * convert EC command execution error codes to Linux error codes. Most |
|---|
| 570 | + * in-kernel users will want to use cros_ec_cmd_xfer_status() instead since |
|---|
| 571 | + * that function implements the conversion. |
|---|
| 572 | + * |
|---|
| 573 | + * Return: |
|---|
| 574 | + * >0 - EC command was executed successfully. The return value is the number |
|---|
| 575 | + * of bytes returned by the EC (excluding the header). |
|---|
| 576 | + * =0 - EC communication was successful. EC command execution results are |
|---|
| 577 | + * reported in msg->result. The result will be EC_RES_SUCCESS if the |
|---|
| 578 | + * command was executed successfully or report an EC command execution |
|---|
| 579 | + * error. |
|---|
| 580 | + * <0 - EC communication error. Return value is the Linux error code. |
|---|
| 581 | + */ |
|---|
| 582 | +int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) |
|---|
| 467 | 583 | { |
|---|
| 468 | 584 | int ret; |
|---|
| 469 | 585 | |
|---|
| .. | .. |
|---|
| 502 | 618 | return -EMSGSIZE; |
|---|
| 503 | 619 | } |
|---|
| 504 | 620 | } |
|---|
| 621 | + |
|---|
| 505 | 622 | ret = send_command(ec_dev, msg); |
|---|
| 506 | 623 | mutex_unlock(&ec_dev->lock); |
|---|
| 507 | 624 | |
|---|
| .. | .. |
|---|
| 509 | 626 | } |
|---|
| 510 | 627 | EXPORT_SYMBOL(cros_ec_cmd_xfer); |
|---|
| 511 | 628 | |
|---|
| 629 | +/** |
|---|
| 630 | + * cros_ec_cmd_xfer_status() - Send a command to the ChromeOS EC. |
|---|
| 631 | + * @ec_dev: EC device. |
|---|
| 632 | + * @msg: Message to write. |
|---|
| 633 | + * |
|---|
| 634 | + * Call this to send a command to the ChromeOS EC. This should be used instead of calling the EC's |
|---|
| 635 | + * cmd_xfer() callback directly. It returns success status only if both the command was transmitted |
|---|
| 636 | + * successfully and the EC replied with success status. |
|---|
| 637 | + * |
|---|
| 638 | + * Return: |
|---|
| 639 | + * >=0 - The number of bytes transferred. |
|---|
| 640 | + * <0 - Linux error code |
|---|
| 641 | + */ |
|---|
| 512 | 642 | int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, |
|---|
| 513 | 643 | struct cros_ec_command *msg) |
|---|
| 514 | 644 | { |
|---|
| 515 | | - int ret; |
|---|
| 645 | + int ret, mapped; |
|---|
| 516 | 646 | |
|---|
| 517 | 647 | ret = cros_ec_cmd_xfer(ec_dev, msg); |
|---|
| 518 | | - if (ret < 0) { |
|---|
| 519 | | - dev_err(ec_dev->dev, "Command xfer error (err:%d)\n", ret); |
|---|
| 520 | | - } else if (msg->result != EC_RES_SUCCESS) { |
|---|
| 521 | | - dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result); |
|---|
| 522 | | - return -EPROTO; |
|---|
| 648 | + if (ret < 0) |
|---|
| 649 | + return ret; |
|---|
| 650 | + |
|---|
| 651 | + mapped = cros_ec_map_error(msg->result); |
|---|
| 652 | + if (mapped) { |
|---|
| 653 | + dev_dbg(ec_dev->dev, "Command result (err: %d [%d])\n", |
|---|
| 654 | + msg->result, mapped); |
|---|
| 655 | + ret = mapped; |
|---|
| 523 | 656 | } |
|---|
| 524 | 657 | |
|---|
| 525 | 658 | return ret; |
|---|
| .. | .. |
|---|
| 528 | 661 | |
|---|
| 529 | 662 | static int get_next_event_xfer(struct cros_ec_device *ec_dev, |
|---|
| 530 | 663 | struct cros_ec_command *msg, |
|---|
| 664 | + struct ec_response_get_next_event_v1 *event, |
|---|
| 531 | 665 | int version, uint32_t size) |
|---|
| 532 | 666 | { |
|---|
| 533 | 667 | int ret; |
|---|
| .. | .. |
|---|
| 537 | 671 | msg->insize = size; |
|---|
| 538 | 672 | msg->outsize = 0; |
|---|
| 539 | 673 | |
|---|
| 540 | | - ret = cros_ec_cmd_xfer(ec_dev, msg); |
|---|
| 674 | + ret = cros_ec_cmd_xfer_status(ec_dev, msg); |
|---|
| 541 | 675 | if (ret > 0) { |
|---|
| 542 | 676 | ec_dev->event_size = ret - 1; |
|---|
| 543 | | - memcpy(&ec_dev->event_data, msg->data, ret); |
|---|
| 677 | + ec_dev->event_data = *event; |
|---|
| 544 | 678 | } |
|---|
| 545 | 679 | |
|---|
| 546 | 680 | return ret; |
|---|
| .. | .. |
|---|
| 548 | 682 | |
|---|
| 549 | 683 | static int get_next_event(struct cros_ec_device *ec_dev) |
|---|
| 550 | 684 | { |
|---|
| 551 | | - u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)]; |
|---|
| 552 | | - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; |
|---|
| 553 | | - static int cmd_version = 1; |
|---|
| 554 | | - int ret; |
|---|
| 685 | + struct { |
|---|
| 686 | + struct cros_ec_command msg; |
|---|
| 687 | + struct ec_response_get_next_event_v1 event; |
|---|
| 688 | + } __packed buf; |
|---|
| 689 | + struct cros_ec_command *msg = &buf.msg; |
|---|
| 690 | + struct ec_response_get_next_event_v1 *event = &buf.event; |
|---|
| 691 | + const int cmd_version = ec_dev->mkbp_event_supported - 1; |
|---|
| 555 | 692 | |
|---|
| 693 | + memset(msg, 0, sizeof(*msg)); |
|---|
| 556 | 694 | if (ec_dev->suspended) { |
|---|
| 557 | 695 | dev_dbg(ec_dev->dev, "Device suspended.\n"); |
|---|
| 558 | 696 | return -EHOSTDOWN; |
|---|
| 559 | 697 | } |
|---|
| 560 | 698 | |
|---|
| 561 | | - if (cmd_version == 1) { |
|---|
| 562 | | - ret = get_next_event_xfer(ec_dev, msg, cmd_version, |
|---|
| 563 | | - sizeof(struct ec_response_get_next_event_v1)); |
|---|
| 564 | | - if (ret < 0 || msg->result != EC_RES_INVALID_VERSION) |
|---|
| 565 | | - return ret; |
|---|
| 566 | | - |
|---|
| 567 | | - /* Fallback to version 0 for future send attempts */ |
|---|
| 568 | | - cmd_version = 0; |
|---|
| 569 | | - } |
|---|
| 570 | | - |
|---|
| 571 | | - ret = get_next_event_xfer(ec_dev, msg, cmd_version, |
|---|
| 699 | + if (cmd_version == 0) |
|---|
| 700 | + return get_next_event_xfer(ec_dev, msg, event, 0, |
|---|
| 572 | 701 | sizeof(struct ec_response_get_next_event)); |
|---|
| 573 | 702 | |
|---|
| 574 | | - return ret; |
|---|
| 703 | + return get_next_event_xfer(ec_dev, msg, event, cmd_version, |
|---|
| 704 | + sizeof(struct ec_response_get_next_event_v1)); |
|---|
| 575 | 705 | } |
|---|
| 576 | 706 | |
|---|
| 577 | 707 | static int get_keyboard_state_event(struct cros_ec_device *ec_dev) |
|---|
| .. | .. |
|---|
| 585 | 715 | msg->insize = sizeof(ec_dev->event_data.data); |
|---|
| 586 | 716 | msg->outsize = 0; |
|---|
| 587 | 717 | |
|---|
| 588 | | - ec_dev->event_size = cros_ec_cmd_xfer(ec_dev, msg); |
|---|
| 718 | + ec_dev->event_size = cros_ec_cmd_xfer_status(ec_dev, msg); |
|---|
| 589 | 719 | ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX; |
|---|
| 590 | 720 | memcpy(&ec_dev->event_data.data, msg->data, |
|---|
| 591 | 721 | sizeof(ec_dev->event_data.data)); |
|---|
| .. | .. |
|---|
| 593 | 723 | return ec_dev->event_size; |
|---|
| 594 | 724 | } |
|---|
| 595 | 725 | |
|---|
| 596 | | -int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event) |
|---|
| 726 | +/** |
|---|
| 727 | + * cros_ec_get_next_event() - Fetch next event from the ChromeOS EC. |
|---|
| 728 | + * @ec_dev: Device to fetch event from. |
|---|
| 729 | + * @wake_event: Pointer to a bool set to true upon return if the event might be |
|---|
| 730 | + * treated as a wake event. Ignored if null. |
|---|
| 731 | + * @has_more_events: Pointer to bool set to true if more than one event is |
|---|
| 732 | + * pending. |
|---|
| 733 | + * Some EC will set this flag to indicate cros_ec_get_next_event() |
|---|
| 734 | + * can be called multiple times in a row. |
|---|
| 735 | + * It is an optimization to prevent issuing a EC command for |
|---|
| 736 | + * nothing or wait for another interrupt from the EC to process |
|---|
| 737 | + * the next message. |
|---|
| 738 | + * Ignored if null. |
|---|
| 739 | + * |
|---|
| 740 | + * Return: negative error code on errors; 0 for no data; or else number of |
|---|
| 741 | + * bytes received (i.e., an event was retrieved successfully). Event types are |
|---|
| 742 | + * written out to @ec_dev->event_data.event_type on success. |
|---|
| 743 | + */ |
|---|
| 744 | +int cros_ec_get_next_event(struct cros_ec_device *ec_dev, |
|---|
| 745 | + bool *wake_event, |
|---|
| 746 | + bool *has_more_events) |
|---|
| 597 | 747 | { |
|---|
| 598 | 748 | u8 event_type; |
|---|
| 599 | 749 | u32 host_event; |
|---|
| 600 | 750 | int ret; |
|---|
| 751 | + u32 ver_mask; |
|---|
| 601 | 752 | |
|---|
| 602 | | - if (!ec_dev->mkbp_event_supported) { |
|---|
| 603 | | - ret = get_keyboard_state_event(ec_dev); |
|---|
| 604 | | - if (ret < 0) |
|---|
| 605 | | - return ret; |
|---|
| 753 | + /* |
|---|
| 754 | + * Default value for wake_event. |
|---|
| 755 | + * Wake up on keyboard event, wake up for spurious interrupt or link |
|---|
| 756 | + * error to the EC. |
|---|
| 757 | + */ |
|---|
| 758 | + if (wake_event) |
|---|
| 759 | + *wake_event = true; |
|---|
| 606 | 760 | |
|---|
| 607 | | - if (wake_event) |
|---|
| 608 | | - *wake_event = true; |
|---|
| 761 | + /* |
|---|
| 762 | + * Default value for has_more_events. |
|---|
| 763 | + * EC will raise another interrupt if AP does not process all events |
|---|
| 764 | + * anyway. |
|---|
| 765 | + */ |
|---|
| 766 | + if (has_more_events) |
|---|
| 767 | + *has_more_events = false; |
|---|
| 609 | 768 | |
|---|
| 610 | | - return ret; |
|---|
| 611 | | - } |
|---|
| 769 | + if (!ec_dev->mkbp_event_supported) |
|---|
| 770 | + return get_keyboard_state_event(ec_dev); |
|---|
| 612 | 771 | |
|---|
| 613 | 772 | ret = get_next_event(ec_dev); |
|---|
| 614 | | - if (ret < 0) |
|---|
| 773 | + /* |
|---|
| 774 | + * -ENOPROTOOPT is returned when EC returns EC_RES_INVALID_VERSION. |
|---|
| 775 | + * This can occur when EC based device (e.g. Fingerprint MCU) jumps to |
|---|
| 776 | + * the RO image which doesn't support newer version of the command. In |
|---|
| 777 | + * this case we will attempt to update maximum supported version of the |
|---|
| 778 | + * EC_CMD_GET_NEXT_EVENT. |
|---|
| 779 | + */ |
|---|
| 780 | + if (ret == -ENOPROTOOPT) { |
|---|
| 781 | + dev_dbg(ec_dev->dev, |
|---|
| 782 | + "GET_NEXT_EVENT returned invalid version error.\n"); |
|---|
| 783 | + ret = cros_ec_get_host_command_version_mask(ec_dev, |
|---|
| 784 | + EC_CMD_GET_NEXT_EVENT, |
|---|
| 785 | + &ver_mask); |
|---|
| 786 | + if (ret < 0 || ver_mask == 0) |
|---|
| 787 | + /* |
|---|
| 788 | + * Do not change the MKBP supported version if we can't |
|---|
| 789 | + * obtain supported version correctly. Please note that |
|---|
| 790 | + * calling EC_CMD_GET_NEXT_EVENT returned |
|---|
| 791 | + * EC_RES_INVALID_VERSION which means that the command |
|---|
| 792 | + * is present. |
|---|
| 793 | + */ |
|---|
| 794 | + return -ENOPROTOOPT; |
|---|
| 795 | + |
|---|
| 796 | + ec_dev->mkbp_event_supported = fls(ver_mask); |
|---|
| 797 | + dev_dbg(ec_dev->dev, "MKBP support version changed to %u\n", |
|---|
| 798 | + ec_dev->mkbp_event_supported - 1); |
|---|
| 799 | + |
|---|
| 800 | + /* Try to get next event with new MKBP support version set. */ |
|---|
| 801 | + ret = get_next_event(ec_dev); |
|---|
| 802 | + } |
|---|
| 803 | + |
|---|
| 804 | + if (ret <= 0) |
|---|
| 615 | 805 | return ret; |
|---|
| 806 | + |
|---|
| 807 | + if (has_more_events) |
|---|
| 808 | + *has_more_events = ec_dev->event_data.event_type & |
|---|
| 809 | + EC_MKBP_HAS_MORE_EVENTS; |
|---|
| 810 | + ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK; |
|---|
| 616 | 811 | |
|---|
| 617 | 812 | if (wake_event) { |
|---|
| 618 | 813 | event_type = ec_dev->event_data.event_type; |
|---|
| .. | .. |
|---|
| 628 | 823 | else if (host_event && |
|---|
| 629 | 824 | !(host_event & ec_dev->host_event_wake_mask)) |
|---|
| 630 | 825 | *wake_event = false; |
|---|
| 631 | | - /* Consider all other events as wake events. */ |
|---|
| 632 | | - else |
|---|
| 633 | | - *wake_event = true; |
|---|
| 634 | 826 | } |
|---|
| 635 | 827 | |
|---|
| 636 | 828 | return ret; |
|---|
| 637 | 829 | } |
|---|
| 638 | 830 | EXPORT_SYMBOL(cros_ec_get_next_event); |
|---|
| 639 | 831 | |
|---|
| 832 | +/** |
|---|
| 833 | + * cros_ec_get_host_event() - Return a mask of event set by the ChromeOS EC. |
|---|
| 834 | + * @ec_dev: Device to fetch event from. |
|---|
| 835 | + * |
|---|
| 836 | + * When MKBP is supported, when the EC raises an interrupt, we collect the |
|---|
| 837 | + * events raised and call the functions in the ec notifier. This function |
|---|
| 838 | + * is a helper to know which events are raised. |
|---|
| 839 | + * |
|---|
| 840 | + * Return: 0 on error or non-zero bitmask of one or more EC_HOST_EVENT_*. |
|---|
| 841 | + */ |
|---|
| 640 | 842 | u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev) |
|---|
| 641 | 843 | { |
|---|
| 642 | 844 | u32 host_event; |
|---|
| .. | .. |
|---|
| 656 | 858 | return host_event; |
|---|
| 657 | 859 | } |
|---|
| 658 | 860 | EXPORT_SYMBOL(cros_ec_get_host_event); |
|---|
| 861 | + |
|---|
| 862 | +/** |
|---|
| 863 | + * cros_ec_check_features() - Test for the presence of EC features |
|---|
| 864 | + * |
|---|
| 865 | + * @ec: EC device, does not have to be connected directly to the AP, |
|---|
| 866 | + * can be daisy chained through another device. |
|---|
| 867 | + * @feature: One of ec_feature_code bit. |
|---|
| 868 | + * |
|---|
| 869 | + * Call this function to test whether the ChromeOS EC supports a feature. |
|---|
| 870 | + * |
|---|
| 871 | + * Return: 1 if supported, 0 if not |
|---|
| 872 | + */ |
|---|
| 873 | +int cros_ec_check_features(struct cros_ec_dev *ec, int feature) |
|---|
| 874 | +{ |
|---|
| 875 | + struct cros_ec_command *msg; |
|---|
| 876 | + int ret; |
|---|
| 877 | + |
|---|
| 878 | + if (ec->features[0] == -1U && ec->features[1] == -1U) { |
|---|
| 879 | + /* features bitmap not read yet */ |
|---|
| 880 | + msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); |
|---|
| 881 | + if (!msg) |
|---|
| 882 | + return -ENOMEM; |
|---|
| 883 | + |
|---|
| 884 | + msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; |
|---|
| 885 | + msg->insize = sizeof(ec->features); |
|---|
| 886 | + |
|---|
| 887 | + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); |
|---|
| 888 | + if (ret < 0) { |
|---|
| 889 | + dev_warn(ec->dev, "cannot get EC features: %d/%d\n", |
|---|
| 890 | + ret, msg->result); |
|---|
| 891 | + memset(ec->features, 0, sizeof(ec->features)); |
|---|
| 892 | + } else { |
|---|
| 893 | + memcpy(ec->features, msg->data, sizeof(ec->features)); |
|---|
| 894 | + } |
|---|
| 895 | + |
|---|
| 896 | + dev_dbg(ec->dev, "EC features %08x %08x\n", |
|---|
| 897 | + ec->features[0], ec->features[1]); |
|---|
| 898 | + |
|---|
| 899 | + kfree(msg); |
|---|
| 900 | + } |
|---|
| 901 | + |
|---|
| 902 | + return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); |
|---|
| 903 | +} |
|---|
| 904 | +EXPORT_SYMBOL_GPL(cros_ec_check_features); |
|---|
| 905 | + |
|---|
| 906 | +/** |
|---|
| 907 | + * cros_ec_get_sensor_count() - Return the number of MEMS sensors supported. |
|---|
| 908 | + * |
|---|
| 909 | + * @ec: EC device, does not have to be connected directly to the AP, |
|---|
| 910 | + * can be daisy chained through another device. |
|---|
| 911 | + * Return: < 0 in case of error. |
|---|
| 912 | + */ |
|---|
| 913 | +int cros_ec_get_sensor_count(struct cros_ec_dev *ec) |
|---|
| 914 | +{ |
|---|
| 915 | + /* |
|---|
| 916 | + * Issue a command to get the number of sensor reported. |
|---|
| 917 | + * If not supported, check for legacy mode. |
|---|
| 918 | + */ |
|---|
| 919 | + int ret, sensor_count; |
|---|
| 920 | + struct ec_params_motion_sense *params; |
|---|
| 921 | + struct ec_response_motion_sense *resp; |
|---|
| 922 | + struct cros_ec_command *msg; |
|---|
| 923 | + struct cros_ec_device *ec_dev = ec->ec_dev; |
|---|
| 924 | + u8 status; |
|---|
| 925 | + |
|---|
| 926 | + msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)), |
|---|
| 927 | + GFP_KERNEL); |
|---|
| 928 | + if (!msg) |
|---|
| 929 | + return -ENOMEM; |
|---|
| 930 | + |
|---|
| 931 | + msg->version = 1; |
|---|
| 932 | + msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; |
|---|
| 933 | + msg->outsize = sizeof(*params); |
|---|
| 934 | + msg->insize = sizeof(*resp); |
|---|
| 935 | + |
|---|
| 936 | + params = (struct ec_params_motion_sense *)msg->data; |
|---|
| 937 | + params->cmd = MOTIONSENSE_CMD_DUMP; |
|---|
| 938 | + |
|---|
| 939 | + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); |
|---|
| 940 | + if (ret < 0) { |
|---|
| 941 | + sensor_count = ret; |
|---|
| 942 | + } else { |
|---|
| 943 | + resp = (struct ec_response_motion_sense *)msg->data; |
|---|
| 944 | + sensor_count = resp->dump.sensor_count; |
|---|
| 945 | + } |
|---|
| 946 | + kfree(msg); |
|---|
| 947 | + |
|---|
| 948 | + /* |
|---|
| 949 | + * Check legacy mode: Let's find out if sensors are accessible |
|---|
| 950 | + * via LPC interface. |
|---|
| 951 | + */ |
|---|
| 952 | + if (sensor_count < 0 && ec->cmd_offset == 0 && ec_dev->cmd_readmem) { |
|---|
| 953 | + ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, |
|---|
| 954 | + 1, &status); |
|---|
| 955 | + if (ret >= 0 && |
|---|
| 956 | + (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) { |
|---|
| 957 | + /* |
|---|
| 958 | + * We have 2 sensors, one in the lid, one in the base. |
|---|
| 959 | + */ |
|---|
| 960 | + sensor_count = 2; |
|---|
| 961 | + } else { |
|---|
| 962 | + /* |
|---|
| 963 | + * EC uses LPC interface and no sensors are presented. |
|---|
| 964 | + */ |
|---|
| 965 | + sensor_count = 0; |
|---|
| 966 | + } |
|---|
| 967 | + } |
|---|
| 968 | + return sensor_count; |
|---|
| 969 | +} |
|---|
| 970 | +EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count); |
|---|